Search Unity

Question Rotating each separate mesh instance

Discussion in 'Shaders' started by Deeo, Sep 3, 2021.

  1. Deeo

    Deeo

    Joined:
    Dec 6, 2012
    Posts:
    39
    I have been trying to figure this out for my grass shader. I have it were it builds one blade of grass in a script and all the positions for it, passes that information to a shader that will than for each instance I wanted to be created will randomize the meshes rotation and convert that rotation to world space.

    How ever it always seems to rotation all the blades of grass around the game object in a circle that the grass script is attached too. Can't seem to figure out why or how this is happening. With out the rotation matrix I am using the grass blades render fine. Here is how it looks with my current attempt at a rotation matrix for the mesh:



    Here is the Shader code I have so far:

    Code (CSharp):
    1. Shader "Grass/GrassShader"
    2. {
    3.     Properties
    4.     {
    5.         [MainColor] _BaseColour("Base Colour", Color) = (0, 0.5, 0, 1)
    6.         _TipColour("Tip Colour", Color) = (0, 1, 0, 1)
    7.     }
    8.         SubShader
    9.     {
    10.         Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalRenderPipeline"}
    11.         LOD 100
    12.  
    13.         Pass
    14.         {
    15.  
    16.             Cull OFF // Don't need to cull grass, flat and need to see both sides
    17.             ZTest Less
    18.             Tags { "LightMode" = "UniversalForward" }
    19.  
    20.             HLSLPROGRAM //Use the newer HLSL instead of the old unity type
    21.  
    22.             #pragma vertex vert //Vertex shader
    23.             #pragma fragment frag // Framant shader
    24.  
    25.             // make fog work
    26.             #pragma multi_compile_fog
    27.  
    28.  
    29.             //Lighting and shadowing
    30.             #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
    31.             #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
    32.             #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
    33.             #pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
    34.             #pragma multi_compile _ _SHADOWS_SOFT
    35.  
    36.              #pragma multi_compile_fog
    37.  
    38.             #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    39.             #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
    40.  
    41.             #define UNITY_PI 3.14159265359f
    42.             #define UNITY_TWO_PI 6.28318530718f
    43.  
    44.  
    45.             //Vertex system to pass vertices of grass blades to GPU
    46.             struct VertsIN
    47.             {
    48.                 float3 position : POSITION;
    49.                 float4 tangent : TANGENT;
    50.                 float3 normal : NORMAL;
    51.             };
    52.  
    53.             struct FragShade
    54.             {
    55.                 float4 position : SV_POSITION;
    56.                 float4 color : COLOR;
    57.             };
    58.  
    59.            
    60.  
    61.             //Needed tyo hold all the values for the material to be used with SRP rendering and the same per pass
    62.             CBUFFER_START(UnityPerMaterial)
    63.  
    64.             float4 _BaseColour;
    65.             float4 _TipColour;
    66.  
    67.  
    68.             StructuredBuffer<float3> grassBladePositionBuffer;
    69.             CBUFFER_END
    70.  
    71.             float rand(float3 co)
    72.             {
    73.                 return frac(sin(dot(co.xyz, float3(12.9898, 78.233, 53.539))) * 43758.5453);
    74.             }
    75.  
    76.             //rotation matrix for grass from unity.
    77.             //Rotates around to the angle provided with the axis provided
    78.            /* float3x3 angleAxis3x3(float angle, float3 axis)
    79.             {
    80.                 float sina, cosa;
    81.  
    82.                 sincos(angle, sina, cosa);
    83.  
    84.                 float icosa = 1 - cosa;
    85.                 float x = axis.x;
    86.                 float y = axis.y;
    87.                 float z = axis.z;
    88.  
    89.                 return float3x3(
    90.  
    91.                     cosa + x * x * icosa,   y * x * icosa - z * sina,   z *x * icosa + y * sina,
    92.                     x * z * icosa + z * sina,   cosa + y * y * icosa,   y * z * icosa - x * sina,
    93.                     x * z * icosa - y * sina,   y * z * icosa + x * sina,   cosa + z * z * icosa
    94.                     );
    95.             }*/
    96.  
    97.             float3x3 angleAxis3x3(float angle, float3 axis)
    98.             {
    99.                 float c, s;
    100.                 sincos(angle, s, c);
    101.  
    102.                 float t = 1 - c;
    103.                 float x = axis.x;
    104.                 float y = axis.y;
    105.                 float z = axis.z;
    106.  
    107.                 return float3x3
    108.                     (
    109.                         t * x * x + c, t * x * y - s * z, t * x * z + s * y,
    110.                         t * x * y + s * z, t * y * y + c, t * y * z - s * x,
    111.                         t * x * z - s * y, t * y * z + s * x, t * z * z + c
    112.                         );
    113.             }
    114.            
    115.  
    116.             FragShade vert(VertsIN IN, uint instanceID : SV_InstanceID)
    117.             {
    118.                 FragShade OUT;
    119.                
    120.                 float3 biTangent = cross(IN.normal, IN.tangent.xyz) * IN.tangent.w; // used for tanget space calculations
    121.  
    122.                 //Matrix for converting the meshes vertices from its tangent space into unity world space
    123.                 float3x3 tangentSpaceToLocalSpace = float3x3 (
    124.  
    125.                     IN.tangent.x, biTangent.x, IN.normal.x,
    126.                     IN.tangent.y, biTangent.y, IN.normal.y,
    127.                     IN.tangent.z, biTangent.z, IN.normal.z
    128.  
    129.                     );
    130.  
    131.                 //Take the position of the grass blade, use it as the seed for the random function and create the rotation matrix
    132.                 float3x3 randomRotationMatrix = angleAxis3x3(rand(grassBladePositionBuffer[instanceID].zzx) * UNITY_TWO_PI , float3(0, 0, 1.0f));
    133.  
    134.  
    135.  
    136.                 float3x3 TransformMatrix = mul(tangentSpaceToLocalSpace, randomRotationMatrix);
    137.  
    138.                  //IN.position = mul(grassBladePositionBuffer[instanceID], IN.position);
    139.                  IN.position +=  mul(TransformMatrix, IN.position);
    140.                 //IN.position = IN.position + mul(UNITY_MATRIX_MVP, IN.position);
    141.                 //OUT.position = TransformObjectToHClip(mul(TransformMatrix, IN.position));
    142.  
    143.                 VertexPositionInputs posIn = GetVertexPositionInputs(IN.position.xyz);
    144.                 OUT.position = posIn.positionCS;
    145.                 OUT.color = _BaseColour;
    146.  
    147.                 return OUT;
    148.             }
    149.  
    150.             float4 frag(FragShade IN) : SV_Target
    151.             {
    152.                 return _BaseColour;
    153.             }
    154.  
    155.             ENDHLSL
    156.         }
    157.     }
    158. }
     
  2. Deeo

    Deeo

    Joined:
    Dec 6, 2012
    Posts:
    39
    Bump
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Two things, this line:
    Code (csharp):
    1. IN.position +=  mul(TransformMatrix, IN.position);
    should be:
    Code (csharp):
    1. IN.position = mul(TransformMatrix, IN.position);
    But that probably won't fix anything. There's nothing in the above shader doing anything with instancing, which makes me think you're trying to modify a static or dynamic batched mesh.

    There's no way to rotate the individual elements of a static or dynamic batched mesh because you no longer know the pivot of the individual meshes. Both of those forms of batching combines all of the meshes using the same material into a single larger mesh with the pivot at the world origin. When you use a 3x3 matrix to rotate something, you're rotating it around the local origin, which means it's being rotated around the world origin.
     
  4. Deeo

    Deeo

    Joined:
    Dec 6, 2012
    Posts:
    39
    Forgot to mention that I am using DrawMeshInstancedIndirect which does let me edit the mesh vertices of each mesh before it is created and rendered. I am using the position I create for each instance passed to the shader as the pivot point for the blade using the instance ID.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    In the above shader, with the code that's currently being used, you're not using the instanced position directly. If you're getting the screenshotted result with the exact shader pasted above unmodified, the offsets can only be happening if the vertex positions coming from
    IN.position
    have already been modified. If the shader you're using locally is not what you posted (ie: you have different lines uncommonted / commented out) then you'll get a different behavior and my analysis would be different as to the problem. But I can only go by the information you've provided.

    Otherwise the way it "should" work is the individual vertex positions coming from
    IN.position
    should still be in local space where the local origin is at the base of the blade. You should then be able to apply the rotation matrix to that position to rotate it around that origin, using the bit of code I corrected above. But the end result I would expect would be all of your blades of grass are sitting at the world origin still... because you haven't applied the blade position offset presumably in that
    grassBladePositionBuffer
    buffer.

    My only other guess is the shader you're using locally does not match the one you posted, and you're instead doing something like this:
    Code (csharp):
    1. IN.position = mul(TransformMatrix, IN.position + grassBladePositionBuffer[instanceID]);
    That would produce the behavior seen in the screenshot.

    If you instead rotate the vertex position, then afterwards add the grass position, and it should be rotated randomly "in place", and offset to where you want it to be.
    Code (csharp):
    1. IN.position = mul(TransformMatrix, IN.position) + grassBladePositionBuffer[instanceID];
    (Note the
    grassBladePositionBuffer
    is added after the
    mul()
    , not inside it)

    One other note. Unity is Y axis up. This code:
    Code (csharp):
    1. angleAxis3x3(rand(grassBladePositionBuffer[instanceID].zzx) * UNITY_TWO_PI , float3(0, 0, 1.0f));
    should be:
    Code (csharp):
    1. angleAxis3x3(rand(grassBladePositionBuffer[instanceID].zzx) * UNITY_TWO_PI , float3(0, 1, 0));
    Otherwise you're going to be randomly rotating around the z axis, which isn't likely to be what you want.