material rotate position and normal

I’ve implemented a shader that causes vertices to rotate by outputing to World Position Offset. However, this breaks lighting.

Thinking back to basic CG concepts, I remembered that normals in a shader need to be transformed by an inverse transpose matrix. So I tried doing this in the material. But the problem is that I don’t know how to populate InverseTransformMatrix. RotateAboutWorldAxis_cheap does not output a matrix - it outputs a transformed vertex. I think I want to get the matrix used by RotateAboutWorldAxis_cheap, then feed that as input into InverseTransformMatrix.

Update #1: I read that inverse transpose is not necessary if all I do is rotations and translation. So I tried modifying normal the same as world position offset but it does not work. I even tried creating the matrices myself and it works for World Position Offset but apparently not for Normal because the lighting still looks broken.

Update #2: Let me explain what I really am trying to do with a simplified example. I have two sets of vertices vtx 0-7 and vtx 8-15 in the same static mesh. I want to give each of these independent rotation and translation. I could make them two separate actors then it would be easy, but my understanding of how UE4 works is that this would do two separate draw calls, which seems wasteful. So I’m trying to do the rotation and translation for vtx 0-7 and vtx 8-15 in the material. My simplified example says two but in reality I may have 24 or more (maybe even 100+) of these.

In OpenGL the GLSL from this skeletal animation example would do it ( Skeletal Animation - OpenGL Wiki ). Notice the shader has (uniform mat4 Bone[10]:wink: which lets you specify mat4 transformations for 10 bones (aka group of verts). If there’s a better way to do this in UE4 please let me know.

Update #3: The manual “creating the matrices myself” is wrong because it rotates around the world’s x-axis without considering the object’s pivot point. RotateAboutWorldAxis_cheap is better in that it’s PivotPoint by default uses object pivit point. However, it breaks lighting because normals aren’t rotated. In theory I should be able to just rotate the normals the same way… Except I’m not sure how. “World Position Offset” seems to be different than “Normal” somehow. I think “World Position Offset” is a delta (an offset) while Normal is not. So just doing VertexNormalWS → RotateAboutWorldAxis_cheap → Normal, does not work.

Firstly, You should calculate new normals per-vertex, in a vertex shader. To achieve that, use Custom UVs
Secondly, download content examples project. Load PivotPainter level,and check example 1.6 and 1.7. You can reverse engineer the material to see how normals are reconstructed.

I don’t think customized UV is necessary plus TexCord is a Float2 (not a Float3). I can just use VertexNormalWS as the input, then Normal as the output. However, the terms are not well-defined so the math is not obvious.

Whatever you are doing with your normals on those screenshots, you are doing it per pixel. By using two customized UVs you can offload the math onto the vertex shader.

Then maybe I need two use two custom UVs (one for XY and one for Z and unused) since custom UV is Float2 while normal is Float3. In any case, doing the calculations per-vertex vs. per-pixel is a performance optimization.

My problem is that the functionality I implemented doesn’t work as expected. As seen in my first screen shot, I use RotateAboutWorldAxis_cheap on position and on normal, and I expected this to give me correct normals for lighting calculations. But the lighting looks broken.

To test how broken the lighting is, I hardcoded Base Color to white and I used the material editor’s preview with a sphere primitive. Since the light is fixed and both the position and normal are rotated the same, I expect the sphere’s color to look static. But it doesn’t. It looks like it’s rotating (or like the light is rotating about it).

Hey @pemcode, first of all thank you for posting your shader work, even if it is not working as you wanted it to. It helped me a lot, and I got it working almost as I need it. Strangely enough (or it’s just me missing something) I don’t seem to have the normals problem you had. So I slightly adapted it to

with the results

Problem is, if I start rotating the mesh, this happens

136161-lever03.png

Do you have any suggestion how to account for object rotation?

You could try to transform your “ForwardAxis” parameter from local space to world space like this

193063-localrotationmaterial.png

This setup is different from yours, but it should work.

1 Like

This is Exactly what I was looking for! Thank you very Much!!

It’s a pretty old question, but if someone still meets it on google, here is setup which works fine.

1 Like

Is till get artifcts when I rotate a mesh to 90/180 degrees. UE5.

I’ve found a solution how to rotate normals.
1 - You should switch OFF “Tangent Space Normal” in material settings.
2 - Get Normals World Space, Rotate Normals around 0,0,0 pivot.
3 - Plug the result to Normal input of your material node.

Basically, you should use World Space Normals. Tested in UE5.0.1.

image

1 Like

A small addition to my previous post. You can use Tangent Space Normal Maps in World Space Normals. In a case if you want to use some textures. All you need is just to convert World Normals to Tangent Normals, then merge Normal Map + Tangent Normals, then convert to World Normals.


image

2 Likes

For me, just plugging in ‘0’ for PivotPoint in the FixRotateAboutAxisNormals Node solved my issue. No need to use World Space Normals and all that (at least for me).

This can be used with tanget space textures, or just by itself.
(Without changing the shader Normal settings)

(See for reference: https://petertaschner.wixsite.com/techart/post/vertex-rotator )

1 Like