If you haven't read part 1 yet, it's right here
In this part, we're looking at, in my opinion, the most fun part of shaders. Playing with the vertex function.
Vertex and Fragment
So what are we dealing with here.
Simply speaking, the fragment shader deals with the colors, the vertex with the actual mesh. Above example has a texture scrolling down. In the fragment it just shows the texture. In the Vertex it's used to push the vertices out. The last sphere shows both combined.
An easy way to show the difference in output between vertex and fragment is to project a texture over a plane.
As you can see in the example above, the fragment shader shows the texture as you'd expect.
But if you put the same texture through the vertex shader it becomes very blurry. You can see that the colors only show in the place of the vertices of the plane. Increasing the vertex count will make the texture a bit clearer.
So vertex projected textures are used for special effects, not to show your carefully crafted textures :).
First Vertex Edit
Time for some fun stuff!
Like in part 1, let's use the standard ToonLit shader, the base for most of my own work. (Download here)
Where are the vertex and fragment parts I talked about?
This is a Surface shader, the "void surf" is basically the fragment shader here. When compiling it automatically gets converted to a (empty) vertex + fragment shader.
To play around with the vertices we have to manually add the vertex function .
void vert(inout appdata_full v, out Input o)
(copy this, it's just syntax, inout appdata_full gives the mesh info through v. , out Input lets us send a value to the "void surf" via o.).
and tell the shader to use our vertex function called "vert" at the top:
#pragma surface surf Toonramp vertex:vert
now it's ready to be used.
Here's what the ToonLit shader looks like without edits.
Add a slider(_Displacement) to the Properties at the top.
and don't forget it within the CGPROGRAM.
As you can see the whole mesh moves up at an angle, local to the object.
Not a great effect. It gets a bit more interesting if you multiply with the mesh's normals so the _Displacement value pushes outwards instead.
You may have noticed that the shadows always stay at the original position. This is fixed by adding "addshadow" after "vertex:vert".
Reading a texture in the vertex function
Copy the _MainTex property and change the copy to _DispTex , add the sampler2D and copy the texture grab from "void surf".
Reading a texture is very similar to the way it's done in the surface shader. Only instead of "tex2D" we need "tex2Dlod" and instead of "IN.uv_tex" it's "v.texcoord".
This d value can now be used as a mask, to control the displacement. Multiply the displacement with "d".
The mask texture can also be projected in world space.
Instead of v.texcoord, use the world position:
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
And for some extra fun add in "_Time" on the y axis to animate the displacement.
Sending values from the vertex to the surface function
In some cases you'll want to calculate something in the vertex function and then send it over to the surface function part.
For this all you gotta do is declare a name(float4 dispTex) in the Input struct, then assign it in the vertex function (o.dispTex = d).
Call it in the surface shader using IN. (IN.dispTex)
Here's the combined diffuse texture and moving displacement texture.
Its blurry because it's projected in the vertex function, not the surface, but since we already had this value I'm using it in this example.
It can be other things like Light Direction, or the World Position.
Try out a few different textures for the displacement! You can get some cool results
This was possibly a bit harder than the previous part, but if you followed along and want to try out some more, here are some things to try:
- Animate the texture over the width of the object instead of the height
- Add a _Scale slider to the displacement texture for more size control
- Move vertices over a sine wave. You can use the sin(_Time) function for this. Hint: use a v.vertex.x/y/or z in the sin function;
- Clamp the displacement so it doesn't happen below a certain point on the model
Thanks for reading and have fun!