Anime Aura Shader in Unity
You know that Aura effect from every other anime ever? After playing Dragon Ball Fighter Z, I started wondering how I would implement this in Unity using shaders. Of course there are tons of different ways to achieve this. You can pre model some sprites placed around the silhouette of your character for example, with proper UVs and all, create the sprites on the fly using compute shaders, or my way, create the sprites using Geometry shaders. The way I settled for is one where just with a shader file you get the effect and don’t have to be worrying about the rest. So first of all here are my results, I didn’t put it on Github this time because the code is way too dirty. This is even more of a proof of concept than last times, since I am actually not going to use it anywhere.
So let’s start off by first mentioning which Mesh I am using. I am using this one here, which also comes with texture with prebaked (painted lighting). First we need to have the mesh on its own without the aura effect. I wanted to have more cell shading style in it, so I added a rim effect of a sort which darkens the mesh in certain area. Add this line of code to the fragment shader of the shader you created for Goku to add this rim effect, the normal of course you need to pass on from the vertex shader:
float f = smoothstep(0.7, 0.6, abs(dot(i.normal.xyz, UNITY_MATRIX_IT_MV[2].xyz)));
return col* max(f,0.3;
Though the effect is not written specifically for Goku, I wanted to imitated a bit how the character’s brightness changes constantly when it is in its super saiyan mode. To imitate this I am going to vary the brightness of the texture in the fragment shader over time, and tie it together with the rim effect from before for some cheap cell shading effect.
fixed4 col = tex2D(_MainTex, i.uv);
float f = smoothstep(0.7, 0.6, abs(dot(i.normal.xyz, UNITY_MATRIX_IT_MV[2].xyz)));
float d = .4* (((sin(_Time.y*10.) + sin(_Time.y*8.5 + 1.051)) / 4.0) + 0.5f);
return col* max(f,0.3) + f*float4(col.xy, col.z*.5,1.)*d;
Now to the Aura. Here is the basic idea of what is going to happen.
Pass one: We render first the Silhouette of the character in the Stencil buffer.
Pass two: We render the mesh again, but this time with a Geometry shader. The shader creates two new triangles per edge (6 total) which are extruded along the edges of the original Triangel with respect to the triangle normal in screen space. The spiky feel of the aura is something I am programming at this stage, I displace the faces with different amount, depending on a polar coordinate system I created. You can decide to place these triangles however you want, and that will of course effect how the final look of the aura looks like. The function I am using is:
float f = max(0., dot(vertex.vNormalOs.xyz, float3(0., 0., 1.)));
d = abs(frac((f*2. - _Time.y + p.y*10.)) - 0.5)*f;float3 Wnormals = UnityObjectToWorldNormal(vertex.vNormalOs.xyz);
float s = smoothstep(0.9, 0.8, abs(dot(Wnormals, UNITY_MATRIX_IT_MV[2].xyz)));
extruded.vPositionPs.xy += vOffsetPs.xy * g_flOutlineWidth * d *s;
Where p is the position of the vertex, but in polar coordinate.
In Pass two, we read from the stencil buffer, and only execute the fragment program of our Pass two, if the value of the stencil buffer is Zero (so only if it is outside the Silhouette of the original mesh). The aura effect happens in the fragment shader of the Pass two. For specific implementation of the Geometry shader and stencil buffer, look at the end of the article.
Last we render the mesh normally on top of the existing aura effect
You can combine pass one and 4 actually. The shader can write to the color buffer and Stencil buffer at the same time. I didn’t do this in my case for various reason, one being I wanted to have the effect being completely separate from any specific mesh. I would just duplicate the mesh and do step 1 to 3 in a different draw call. (so you could do this in a script for any given mesh)
You can control how thick the aura is by controlling how much offset you add:
Last but not least, if you add UVs to the newly created faces, you can pretty much map whatever you want on top. Another improvement you can do is to reduce the amount of triangles added. Now the entire mesh receives 6 new tringles per triangle, which get extruded. This is way too much on a high poly mesh and unnecessarily. You can get away with the same effect by just extruding triangles that are on viewing corner of the mesh. For the rest you can just add triangels to the triangles stripe which have a surface area of zero. The triangles would be discarded down the pipeline.
Thanks for reading. You can follow me on my twitter: IRCSS
Further Reading:
In VR, your typical outline shader does a very similar thing as the shader here. As promised for specific implementation of how to set up the shader like I did, for both geometry and stencil stuff, have a look at this shader by Valve which renders outlines around the mesh. Modify this and you will get to something similar: https://github.com/IBM/vr-speech-sandbox-vive/blob/master/Creation%20Sandbox/Assets/SteamVR/InteractionSystem/Core/Shaders/Silhouette.shader