I’ve been poking around with UE4 recently, and figured out this little trick i couldn’t seem to pull off in UDK. When animating objects with vertex animation in a shader, the vertex normals must be rebuilt in order to allow your object to shade correctly. There are many ways to do this, and the one i used may not be the cheapest but it works in most situations. I offset 2 fake vert positions in the tangent and binormal directions of the actual vert and compare them to get the rebuilt normal. It costs 3 times the amount of normal vertex animation, but as long the calculations are done on the vertex shader it should still be fairly cheap. This is where UE4 comes in handy.
Previously in UDK, a calculation could not be forced onto the vertex shader, so calculations like this were super expensive. However in UE4 by declaring a few customized UVs, calculations can be later retrieved with a TexCoord node. Beware though: Each channel only carries 2 channels and extra channels will be dropped. In order to pass a Vector3 or 4 you would need to split it and override 2 UV slots. Since i was passing normals i just decided to use a DeriveNormalZ rather than taking up another UV channel. I use 3 customized UVs and let the first 2 pass through (so that my base and lightmap UVs are unaffected.

Here are a few comparison shots. Below is a plane with some basic sine wave animation. Besides the SSAO UDK and UE4 have, there really isn’t much in terms of lighting. Its just all very flat looking.

Rebuilt Normals help a lot. Here is the same material with rebuilt vertex normals. The shape is much more defined and our normal maps (if we had any) will read correctly. In order to combine the normals with a normal map you could just use the BlendAngleCorrectedNormals node in UE4 or some other detail normal function.
If we were to do this on the pixel shader, it would be much more expensive for almost no visual difference (If i didn’t have the stat counts to tell i wouldn’t know which was which). Remember this is only rebuilding normals for a single waveform. Imagine doing it for 3 or 4 waves…
This kind of technique would be super useful for anyone looking to optimize Unreal shaders for mobile, as you could push a lot more instructions back into the vertex shader where it belongs. I think it should definitely help with animating larger waves.
EDIT: Here is how I rebuild the normals in the shader. Everything is handed built in, so that I don’t break the constant folding UE4 employs. I use a lot of embedded material functions these days to keep things flexible. Here is an example of comparing 3 positions to make a normal starting at the material level.
To make the positions I simply add a value in each direction. The function itself provides a tangent and binormal offset, which I add to those positions before subtracting the original vert position plus the offset. This gives us tangent and binormal, which I then normalize and get a cross product to produce normals. 

This is a slight change from my earlier method, in that i am building the normals outside the function. This allows me to add multiple offsets together and rebuild the normals for all of them at once. The G_Waves_Fourier function employs this by using 3 G_Waves_Normal functions and adding them together. 

G_Waves_Normal itself is where i actually make the fake vert positions by employing the same method as seen in the material. Embedding the G_Waves function 3 times and passing them the 3 positions allows me to output the offsets for later use. All other parameters are exactly the same, to ensure they get the same offset. You could use pretty much any offset function in place of G_Waves and this method should allow you to rebuild the normals.
Looking at it now, I should really make that constant of 150 a parameter or see if i can eliminate the multiplication all together…

4 Responses

  1. 11/7/2014 01:46:13 am
    Thanks much for this writeup. I’ve got a couple questions, if you have a minute…

    I’m assuming you’re doing the tangent & binormals comparisons inside of your sine wave function. Are you handling that with built-in operations, or are you running a Custom HLSL block?

    Would you be willing to expand on that a bit?

    Thanks in advance!

    -eaa

    1. 11/7/2014 05:10:34 am
      Yeah no problem. I handle everything built in, since i want all the constant folding i can get. I updated the post with a more in depth explanation of how I rebuild the normals and how it could work for other wave functions.

      Let me know what you think and if you have any more questions about it

      Delete

      1. You’re a boss! Thanks much for breaking that down! You’re one of very few people online talking in-depth about rebuilding normals for vertex-offset shaders… and it’s much appreciated.

        And now that I know about your blog, I’m RSS subscribed… can’t wait to see more of your experiments in the future!

        Thanks again!

        1. Awesome thanks man! I was so stoked to get this working in UE4 so its super cool to see other people getting some mileage out of it. Hope you find more useful stuff here in the future 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *