Individual Learning – Shadertoy

Last week I took a dive into Shadertoy, a website that allows users to write shaders and share them publicly, all within the browser. Many of the effects are truly awe inspiring with incredibly detailed, procedurally generated, 3D scenes ranging from lifelike forests as in the example below by Inigo Quilez (iq), the creator of shadertoy, to abstract and fractal, scenes like the following by Kali.

Rainforest Generators
Rainforest Generators

I created a more simple 2D shader rendering circles by following guides on youtube, mainly this one by iq https://www.youtube.com/watch?v=0ifChJ0nJfM

From there i started to draw more circles, adding some offsets using sine and cosine functions and i eventually came up with this cool perspective effect which makes a grayscale mask which i then mix between white and the default shader’s colour effect. (Click to see the shader on Shadertoy.com)

shader

I continued to experiment further, using some of the other inputs that shadertoy gives you and also by taking in feedback from the comments (which came in very quickly) to create these next shaders.

Using the Date input and the same circle drawing from the first shader to create a functional clock
Pixelation effect to make make it look like the resolution of the canvas is changing and get a pixel art effect. This shader also includes mouse input to adjust the resolution and the blur on the circle
A smoothstepped Value noise function is used to blend smoothly between two textures. Similar to an effect i have used in unity to blend cameras

Finally I took this one step further and implemented the first effect in a Unity shader so that it can be used as both a material on an object but also as a post process image effect. This wasn’t too difficult, even though Shadertoy uses GLSL and Unity used HLSL, they’re both very similar and most of the code worked fine bar a few vec2s needing to be float2s.

I hope you have enjoyed this week’s post, i certainly had a lot of fun researching the effects and demos that people have created using shadertoy, especially the 3D ones. If you would like to have a go at writing some shadertoy shaders i would recommend looking at iq‘s website here and The Art of Coding on YouTube.

Individual Learning – Snow Effect

This week I did some more work with vertex displacement and started work on an effect I had wanted to make for a while, even before I started writing shaders: Snow resting on top of an object.

Everything went pretty smoothly, in the vertex function i find the slope angle, if it’s flat enough for snow to be there i set a mask value to white and if not it’s black;  I also displace the vert vertically.

Then in the fragment function I use the mask to lerp between the object’s texture and a snow texture (I left it white as I didn’t have one to hand). The effect is quite appealing though it does stretch the UVs around the displacement and the transition from snow to object can be jagged where there are sharp changes in geometry.

Here’s a video of the shader in action using a rock from the asset store for demonstration.

 

Once I had finished i decided to look into using tessellation which would give me more vertices to work with and in theory a better result. I found this page on the documentation which demonstrates how to implement tessellation in a surface shader.

I had been writing individual vertex and fragment shaders so I would have to re-write them however it did show me that I could perform vertex functions inside a surface shader using the vertex:vertFunction define in my shader code.

I used the Surface shader examples page on the unity docs to help me with my syntax and quickly got this very similar result the only major differance was that i could now store my mask value as a float in the Input struct and not a float4 in the v2f struct.

SnowShader_SurfaceExample

I did manage to get Tessellation and Phong Tessellation working without the snow effect but whenever I tried to include my vertex function I would get an error which i assume is caused because unity uses the vertex modifier after tessellation as it would usually be used to perform some sort of height map displacement. The tessellation was surprisingly easy to get up and running though.

This slideshow requires JavaScript.

The next step I would like to take is finding a way to use the tessellation with my snow effect to hopefully see an improvement compared to the original shader.

Individual Learning – Vertex Displacement

Last week I wrote a vertex displacement shader that uses a heightmap to displace vertices on a mesh. I managed to get a simple terrain example working very quickly by using an existing heightmap downloaded from google Images.

Here’s what it looks like with a standard plane:

VertexDisplacement_WorkingonPlane

I also created a higher resolution plane in Max to get this more detailed look. It is probably possible to use Tessellation to get the same result at runtime.

VertexDisplacement_HighPoly

Following this i decided to experiment with Unity’s Perlin noise function using what I had learned from creating my own procedurally generated terrain in Monogame. It’s not a very practical approach to terrain generation but it was interesting to experiment with. I also added some colour and a water plane.

VertexDisplacement_fractalterrain

I had some trouble with the noise generation as I discovered Unity’s perlin noise function returns the same value at integer values but after i added some values to tweak the scale I was using floats anyway. I also had an issue where the texture didn’t appear to generate as I was getting no displacement or colour on the plane, this however was because I had forgotten to call Texture.Apply() after setting the pixels.

It would be interesting to explore the use of tessellation to skip having to create a pre-subdivided mesh.

Heightmap available here.

Individual Learning – Toon Shader

This week I continued some work I started over Christmas that consisted mainly of reading through Unity’s Shader documentation and downloading a few assets from Turbosquid to play with as I start writing shaders.

I have decided on using Unity’s “Surface Shader” which combines Vertex and Pixel shaders into one, and does some clever compilation behind the scenes to create the individual vertex and pixel programs. They also allow you to use either built in lighting models or to write your own lighting functions, which I have used for the following shaders.

After a little bit of messing around set out with the goal of creating a toon/cel-shader. Toon shaders come in many forms but commonly they feature more flat shading and hard borders between light areas and shadow. I wrote two shaders: a colour ramp shader which samples a ramp texture, usually a gradient, and no diffuse texture to determine what a fragment colour should be when lit; the second shader I wrote uses hard coded light levels to create the effect while still sampling a diffuse texture.

Colour Ramp

AverageMan_ToonRampShading

Here you can see a a mesh on the left using a more standard Diffuse/Specular lighting model (lambert), and on the right, my modified mesh using a series of different ramps to colour the individual mesh parts. The ramp texture works by using the light’s intensity at each pixel to sample a texture, the brighter the intensity, the further left is sampled and the darker tones are found on the right.

The colour Ramp approach requires separating a mesh into multiple parts so that different materials can be applied for each ramp. You can see the individual ramps I created below and how I modified the mesh in 3DS Max

AverageMan_Ramps

AverageMan_MeshSetup

One the one hand I like how minimal and simplistic the mesh looks using just the colour ramp however as the mesh had one, I added a normal map to the shader which massively improved the look of the material. I also added it to the standard material on the left for comparison along with some colourful point lights to test how the lights tinted the surface.

This slideshow requires JavaScript.

Textured Toon

Next, I replaced the Lambert light function on the standard shader with my own custom function, which hard codes 3 light levels (full bright above 50%, half bright above 25% and a quarter below that) that are multiplied by the diffuse texture to create a similar but textured effect on the left.

AverageMan_BothRamped

While i was testing the the shader on other meshes i realized that the normal maps could be too strong and cause the surface to look ugly and noisy. To fix this I added a slider to the shader that lets me set the strength of the normal map the same way as Unity’s built in standard shader.
AverageMan_NormalIntensity

As you can see this gives me artistic control over how the normal map is affecting the surface.

Bonus Fun

For a bit of fun and as a bit of testing i dragged over the Sponza mesh from my Rendering Engine, threw some garish point lights into the scene and slapped my Textured Toon shader onto the walls, which, to me, looks pretty cool.
Toon_Sponza

My Thoughts

I think that both shaders look interesting and give a unique feel each however they both have some technical annoyances that may get in the way. Firstly, the textured shader, while it does not require multiple Ramp textures and can use a mesh without being edited does currently have everything related to the lighting hard coded so if I want to tweak any of the settings I have to change magic numbers in the shader code. I could probably fix this by creating a grey-scale Light ramp similar to the ramped shader.

Secondly the Colour Ramped shader requires a number of individual textures to be created for a mesh, dependent on the number of colours that it requires which is a pain for this simple mesh but would be impractical for a more detailed object. However the artist can do any number of things with a ramp, including shifting the hue of the colour as it moves towards the darker shades or even use completely different colours which could lead to a number of interesting effects.

In the future I would like to delve into Unity’s GUI Editor scripts which allow you to set how the properties are laid out in a material and I imagine even add a gradient field to it which would remove the need to create the ramp textures externally.