Search Unity

Cancel Object inspector rotation from Shader but keep movement?

Discussion in 'Shaders' started by resetme, Oct 11, 2019.

  1. resetme

    resetme

    Joined:
    Jun 27, 2012
    Posts:
    192
    Thats it, im making a shader and I have to cancel the rotation of my mesh, it need to be always 0,0,0.
    Scale and Position can be accessible from the inspector.

    I have tried many shader around the web but none gave me the results, Nordeus GitHub have one similar but it also cancel the position of the object.

    this is the nordeous one that cancel position and rotation

    Code (CSharp):
    1. // The position parameter is stored in the normal
    2.     float4 wp = mul(UNITY_MATRIX_V, input.pos);
    3.     float3 position = half3(wp.x,wp.y,wp.z);
    4.  
    5.     // Construct our orientation axes
    6.     float3 turnDir = _WorldSpaceCameraPos.xyz - position;
    7.     float3 y = float3(0, 1, 0);
    8.     float3 x = normalize(cross(turnDir, y));
    9.     float3 z = normalize(cross(y, x));
    10.  
    11.     // Construct the transposed model matrix
    12.     float4x4 modelMatrix = float4x4
    13.     (
    14.         x.x, y.x, z.x, position.x,
    15.         x.y, y.y, z.y, position.y,
    16.         x.z, y.z, z.z, position.z,
    17.         0, 0, 0, 1
    18.     );
    19.  
    20.     // Calculate the position
    21.  
    22.     float4 worldPos = mul(modelMatrix, input.pos);
    23.     output.pos = mul(UNITY_MATRIX_VP, worldPos);
    24.  
    25.  
    26.  
    27.     // Texture coordinates remain the same
    28.     output.texcoord = input.texcoord.xy;
    29.  
    30.     output.color = input.color;
    31.  
    32.     return output;
    help?
     
    Last edited: Oct 13, 2019
  2. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    938
    Curious, why not just have a script on the object that prevents changes to rotation? This would be better in most cases than added more GPU overhead, especially if you implement it with the ECS Job system.
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    7,672
    If you also want to cancel the scale, this is really trivial to do, and super cheap. If you need to keep scale that gets more complex and expensive, though still doable. The one big caveat here is if your meshes are getting batched none of this will work and it does need to be done with a C# script as the rotation is all getting baked into the mesh.

    That's because that is getting the object position offset from specially encoded vertex data rather than using the original matrix's position. Note that comment in line 1:
    It's also overriding the rotation with a new rotation rather than canceling it entirely.

    For cancelling the rotation and scale, you just need to do this near the start of the vertex shader:
    Code (csharp):
    1. unity_ObjectToWorld._m00_m10_m20 = float3(1,0,0);
    2. unity_ObjectToWorld._m01_m11_m21 = float3(0,1,0);
    3. unity_ObjectToWorld._m02_m12_m22 = float3(0,0,1);
    For cancelling just the rotation, but keeping the scale:
    Code (csharp):
    1. float3 scale = float3(
    2.     length(unity_ObjectToWorld._m00_m10_m20),
    3.     length(unity_ObjectToWorld._m01_m11_m21),
    4.     length(unity_ObjectToWorld._m02_m12_m22)
    5.     );
    6.  
    7. unity_ObjectToWorld._m00_m10_m20 = float3(scale.x, 0, 0);
    8. unity_ObjectToWorld._m01_m11_m21 = float3(0, scale.y, 0);
    9. unity_ObjectToWorld._m02_m12_m22 = float3(0, 0, scale.z);
    Those length() calls are relatively expensive, but really not that bad.

    If you need object normals, you'll also need to update the unity_WorldToObject matrix as well. For no scale it's the same code as above, just applied to unity_WorldToObject. For scale preserving it'd be:
    Code (csharp):
    1. unity_WorldToObject._m00_m10_m20 = float3(1 / scale.x, 0, 0);
    2. unity_WorldToObject._m01_m11_m21 = float3(0, 1 / scale.y, 0);
    3. unity_WorldToObject._m02_m12_m22 = float3(0, 0, 1 / scale.z);

    Because that'll be way more expensive. For a past project I had maybe 50~100 objects resetting their rotation in a semi-jobified manner (predated real jobs), and that came up as the #2 most expensive operation in the entire game and was preventing us from hitting our framerate targets for PSVR. We had to limit the number of objects we modified the rotation of in C# and moved the rest to shader based. Setting the rotation on a transform makes Unity calculate the internal transform matrix, and I suspect there's no fast-pass for zero rotation, so it's still doing all of the calculations for that even though the resulting matrix is going to be nearly an identity matrix with the position offset. Technically the script in question was aiming objects toward the camera rather than cancelling the rotation, but similar idea.
     
    Last edited: Oct 11, 2019
    resetme likes this.
  4. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    938
    As you say, that's to create an aim direction and avoid setting rotation altogether though, while this user is trying to negate changes in rotation that have already happened and thus matrix recalc is happening regardless. Also, the system Unity has made does a lot of behind the scenes optimizations that you wouldn't have been able to achieve with a classical job system without engine source access and considerable work no? Not to mention multi-threading.
     
  5. resetme

    resetme

    Joined:
    Jun 27, 2012
    Posts:
    192
    thank you all! i dont have time to check it yet.

    Yes is expensive and im canceling the rotation using c# right now. The deal is that my game is CPU bounded and i barely use the GPU, so im trying to put all i can inside shaders now.

    The main is to create a fake particle effect (i have to draw around 20 particles that right now cost me 4 draw call per particle in diferent positions) those draw call (80) are eating more than 5 fps + the 2Ms of particles effect Modules. So now im saving in Maya around 80 quad mesh inside one mesh and inserted a fake id inside the tangent of the vertexs, later inside my shaders i animate the vertex by the tangent id. it works nicely, looks like a very expensive particle effect but is one mesh, one draw call, one set pass and diferent positions. They also are billboard rotated to the camera, the issue is that if the designer change the fake particle rotation inside the inspector the billboard effect is broke, so i need it to be 0,0,0 if is posible inside the shader. I saw this technique on EA for Battlefield Particles and old Nordeus Tech Talk optimization techcniques.

    Ofcorse if it do not work i just leave it with the c# code but i trust bgolus god, right now the cpu cost (rotation cancel) is lot less than having 80 draw calls as it was before, fragment shader is one texture and color multiplications, the vertex shaders have calculations but, 36 vertex * 80 ? i barely can check the clock diference or ms using Snapdragon profiler, is looks like win win for my project.

    Also, it must run on OpenGL2.0, our target device are around 2015, the game is already released and we have to make space for new functions.

    bgolus thank you!!!!!!!!!!!!!
     
  6. resetme

    resetme

    Joined:
    Jun 27, 2012
    Posts:
    192
    works! I used the simple rotation cancel only. not the length.
    The scale I just multiply before the vertex output.

    Thank you so much! Need to optimize the texture and bake the color to ramp texture and done.

     
    Invertex and bgolus like this.