Jelly Shader, Part 2: A Basic Sine Wave

Michael Sanders
4 min readFeb 9, 2018

--

Part 2 — A Basic Sine Wave

This is article 2 in our multi-part series on developing a custom shader for Unity. In this article we will be modifying our shader to create a basic sine wave that moves along the surface of our 3D model.

While the default ShaderLab file we created in Part 1 comes with a surface shader (also known as a fragment or pixel shader) function built in, what we need is a vertex shader. A vertex shader allows us to operate on the actual vertex positions and normals of the model instead of just the colors. To do that, first we need to tell Unity that we have a vertex shader. Modify this line:

#pragma surface surf Standard fullforwardshadow

to

#pragma surface surf Standard fullforwardshadow addshadow vertex:vert

addshadow needs to be added in whenever you plan to modify vertex positions so that Unity makes a shadow caster pass on the object. Otherwise the shadows on your model will be generated based on the model’s original shape.

vertex: tells Unity that we’re going to be creating a vertex shader and vert tells Unity the name of the function where we’ll be doing that. So next let’s add that function. Right above our surface function, add the following:

appdata_base is a part of another collection of structures provided by Unity. This structure determines what data will be available to use while modifying the vertex. appdata_base contains the following fields:

Now that all that setup is out of the way, let’s actually make something happen. Inside our vert function, add the following line:

v.vertex.xyz += v.normal * sin(_Time.y);

Fire up Unity and you should see a sphere growing and shrinking in size. It’s not much, but it’s the start of our jelly shader. Let’s breakdown what’s happening.

Because v is specified as inout any modifications we make to the vertex will persist to the other stages of the shader. We’re then modifying our vertex’s position, v.vertex.xyz, by moving it along the axis of its normal, v.normal. This creates our breathing motion. How far along we move on that axis is determined by multiplying by our sine function, which we’re feeding a time variable.

_Time

_Time is yet another Unity convenience built-in to save us time. _Time is a vector4 prefilled with the following values:

Just using a plain time variable for our sine wave means our modification is uniform across the entire model. Let’s change that. Define the following variables just below our “_Glossiness”, “_Metallic”, and “_Color” variables:

For now, ignore the fact that we’ve marked these variables as static. This allows us to assign values on declaration, which is handy, but we will change this later on. Let’s rework our vertex function. First, instead of affecting all vertices the same, let’s have our sine function take into account x position of the vertice:

v.vertex.xyz += v.normal * sin(v.vertex.x + _Time.y);

At this point our sphere is doing something, but it still doesn’t look like a wave. Let’s try adjusting the frequency of the waves on our sphere:

v.vertex.xyz += v.normal * sin(v.vertex.x * _Frequency + _Time.y);

Closer, but the waves are a little intense, let’s knock down the amplitude a bit.

v.vertex.xyz += v.normal * sin(v.vertex.x * _Frequency + _Time.y) * _Amplitude;

Alright! Now those look like waves. We now have waves on our model moving along the x-axis (thanks to the >v.vertex.x being inside our sin function) with configurable parameters. At this point, I really encourage you to take a few minutes to play with vertex function and see what other cool effects you can create just by modifying _Frequency, _Amplitude, _Time, and even v.vertex. Here’s our full shader code currently so you can copy and paste it back in when you’re done experimenting.

Part 3 of this series can be found here.

Originally published at heliosinteractive.com on February 9, 2018.

--

--

Michael Sanders

Director of Interactive Development at Helios Interactive. And part-time Pirate.