Search Unity

How to Translate Instanced Meshes Relative to Transform?

Discussion in 'Shaders' started by amasinton, Jul 25, 2019.

  1. amasinton

    amasinton

    Joined:
    Aug 12, 2006
    Posts:
    138
    I'm working on an effect which relies on DrawMeshInstancedIndirect. I'm adapting the first shader (surface shader) which the scripting documentation provides as an example: here.

    What I would like is to position the instanced meshes relative to the Transform of a GameObject in-scene, and be able to have those mesh positions move with the Transform when the GameObject moves.

    I suspect this can be achieved by transforming the Vector3 array of instanced positions in the shader by the Transform of a specified GameObject.

    I just have now clue how to do this.

    Any suggestions?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    If you want to do it from script, then you would do this:
    https://docs.unity3d.com/ScriptReference/Transform.TransformPoint.html

    If you want to do it in the shader, you would have to pass the local to world matrix for that game object to the shader, either by setting it on the material used, or by setting it as a global shader property, or on the material property block.
    https://docs.unity3d.com/ScriptReference/Transform-localToWorldMatrix.html
    https://docs.unity3d.com/ScriptReference/Material.SetMatrix.html
    https://docs.unity3d.com/ScriptReference/Shader.SetGlobalMatrix.html
    https://docs.unity3d.com/ScriptReference/MaterialPropertyBlock.SetMatrix.html
     
    Kbpro likes this.
  3. amasinton

    amasinton

    Joined:
    Aug 12, 2006
    Posts:
    138
    Thank you so much - and for such a quick reply.

    Erm ... I'm a little stuck, tho...

    Two things:
    1. I don't know how to declare and initialize a matrix in the shader itself - I assume it's a float 4x4. When I attempt to declare it in the shader's property block I get a TVAL error no matter what I do:

    Code (CSharp):
    1. _TransformMatrix("TransformMatrix", float4x4) = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
    or

    Code (CSharp):
    1. _TransformMatrix("TransformMatrix", float4x4)
    When I attempt to declare it in the 'Setup' function of the shader like this:

    Code (CSharp):
    1. void setup()
    2.     {
    3. #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    4.      
    5.         float4x4 _TransformMatrix = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    6.      
    7. #endif
    8.     }
    I only seem to be setting it back to zero rather than using the values I'm passing in from my transform's local to world matrix.

    2. I guess I don't know what to do with this matrix should I ever succeed in passing it into the shader. I'm using the example shader in the Unity docs for DrawMeshInstancedIndirect (as linked I'm my original post, above). The position of each instanced mesh is determined by an array of float3. This array is in world space. (Just to recap:) I would like to transform this array into the object's transform space so that the instanced meshes will move when the transform moves.
    So, I thought I could just multiply the transform's local to world matrix by each position float3 from the array of positions in the shader and that would be that. But I really don't understand what is going on in the sample shader in the 'setup' function at this point:

    Code (CSharp):
    1. void setup()
    2.     {
    3. #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    4.         float4 data = positionBuffer[unity_InstanceID];
    5.  
    6.         unity_ObjectToWorld._11_21_31_41 = float4(data.w, 0, 0, 0);
    7.         unity_ObjectToWorld._12_22_32_42 = float4(0, data.w, 0, 0);
    8.         unity_ObjectToWorld._13_23_33_43 = float4(0, 0, data.w, 0);
    9.         unity_ObjectToWorld._14_24_34_44 = float4(dataTrans.xyz, 1);
    10.         unity_WorldToObject = unity_ObjectToWorld;
    11.         unity_WorldToObject._14_24_34 *= -1;
    12.         unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
    13.      
    14. #endif
    15.     }
    So, I don't know where to do the multiply operation in the shader.

    Any advice?

    Thank you!
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    See the documentation I linked to above, it has examples for the first few questions:
    https://docs.unity3d.com/ScriptReference/Material.SetMatrix.html

    One big confusion you're having is there's no need to put the matrix in the shader's properties. A shader's properties mainly exist for serialization and display in the inspector, and matrix properties aren't supported. So just skip that. All you need to do is define it in the shader code itself, inside the CGPROGRAM block, but outside a function. Again, see the above documentation.

    Sounds like you need to read up on how to use transform matrices. Unfortunately I don't know of any good tutorials on this. Most tutorials on matrix math tend to go deep into the math itself, which is great if that's what you want to learn, but is more confusing than it needs to be for people starting out who just want to know what they're for and how they're used.

    No, that's not actually what you want. You want to transform the array of positions into world space positions relative to the object's transform. The positions already are in "object space". I'll explain.

    By default those positions you pass to the instanced objects are "in world space". What makes them in world space? Nothing, really, just they're treated as if they're world space positions by virtue of those positions being used when defining the matrices used to transform the mesh vertices into world space. In your case you want to treat the positions as being in the space of another game object. To do that you need to transform those positions from that object's space into world space and use those new positions to build the object to world matrices. Transforming vectors from one space to another is what a transform matrix exists for.

    So, for your use case you'll want to do something like this:
    Code (csharp):
    1. // in the shader code, outside a function
    2. float4x4 _TransformMatrix;
    3.  
    4. // in the setup function
    5. float4 data = positionBuffer[unity_InstanceID];
    6.  
    7. // apply transform matrix to position data
    8. // You need to transform in a float4 with a w of 1 otherwise it'll be transformed as a direction
    9. // instead of a position. Read up on matrix math if you want to understand why.
    10. data.xyz = mul(_TransformMatrix, float4(data.xyz, 1)).xyz;
    11.  
    12. // then the rest of the code as is
    An additional note. The object to world transform matrices the example code generates has no rotation, only position and scale. This means that your instanced objects will follow the transform you pass in, but stay aligned to the world. If you want the objects to rotate and scale with the object, that'll require handling things in the shader code a little different.
     
    Kbpro likes this.
  5. amasinton

    amasinton

    Joined:
    Aug 12, 2006
    Posts:
    138
    Thank you for your gentle explanation, your concrete examples, and the theory you've shared. I've learned a lot from your posts throughout this forum over the past few years!

    It's starting to make a lot more sense to me. I was getting very close, but I didn't understand that properties could be declared outside the Properties block. That's very good to know. Also, I'm educating myself about transformation matrices in general - fascinating stuff.

    It makes so much more sense that I want to transform the positions array into world space relative to the object's transform. Yes, that's exactly it, of course!

    So, I've declared a float4x4 in the shader as a property called '_TransformMatrix' as per above (not the best name for clarity's sake now, I see that). And in my C# script I've set it using MaterialPropertyBlock.SetMatrix - using the transform's local to world space matrix. Using MaterialPropertyBlock.GetMatrix, I can confirm in the C# script that I've successfully set my '_TransformMatrix' property. So far so good. I set the matrix on Update.

    But this:

    Code (CSharp):
    1. data.xyz = mul(_TransformMatrix, float4(data.xyz, 1)).xyz;
    ... seems to have no effect. The instanced objects still render relative to world 0,0,0. I've tried sending the Renderer's local to world matrix rather than the Transform, just to check (and also sending the world to local matrices, too) and nothing seems to make any difference. It's like this multiplication step is being ignored.

    For now, when I transform the position array in the C# script using TransformPoint (as you suggested earlier) it works okay, but this may start to chug once I'm updating tens or hundreds of thousands of points.

    Any thoughts, again, on why that multiply doesn't seem to have any effect?
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    I just noticed that’s “dataTrans” instead of “data”. Not sure where that variable name came from, but that should probably be “data”.

    Otherwise, post your entire shader as is, and at least some of your script code.
     
  7. amasinton

    amasinton

    Joined:
    Aug 12, 2006
    Posts:
    138
    That's certainly odd. It's not actually in the shader in my project - must be a copy-paste typo on my part. Sadly. It would be great if this was the problem...

    Okay here's the script (based very largely on this example by noisecrime except that I've modified it to use a List of position arrays so I can change 'models' when pressing up or down arrows):
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [System.Serializable]
    6. public class PositionGroup
    7. {
    8.     public Vector3[] positionSet;
    9. }
    10.  
    11. public class MyInstancedController : MonoBehaviour
    12. {
    13.     public List<PositionGroup> positionVectors = new List<PositionGroup>();
    14.     public float baseSize = 0.04f;
    15.     public float randSizeVariation = 0.01f;
    16.     public Mesh instanceMesh;
    17.     public Material instanceMaterial;
    18.  
    19.     private MaterialPropertyBlock block;
    20.     public Matrix4x4 myTransformMatrix;
    21.  
    22.     public int instanceCount = 0;
    23.     public int cachedInstanceCount = -1;
    24.  
    25.     private ComputeBuffer positionBuffer;
    26.     private ComputeBuffer argsBuffer;
    27.  
    28.     private int counter = 0;
    29.  
    30.     private uint[] args = new uint[5] { 0, 0, 0, 0, 0 };
    31.  
    32.     void Start()
    33.     {
    34.         myTransformMatrix = transform.localToWorldMatrix;
    35.         block = new MaterialPropertyBlock();
    36.         block.SetMatrix("_TransformMatrix", myTransformMatrix);
    37.  
    38.         instanceCount = positionVectors[counter].positionSet.Length;
    39.         argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
    40.         UpdateBuffers();
    41.     }
    42.  
    43.     public void UpdateBuffers()
    44.     {
    45.         instanceCount = positionVectors[counter].positionSet.Length;
    46.  
    47.         if (instanceCount < 1) instanceCount = 1;
    48.  
    49.         // Positions
    50.         if (positionBuffer != null) positionBuffer.Release();
    51.  
    52.         positionBuffer = new ComputeBuffer(instanceCount, 16);
    53.  
    54.         Vector4[] positions = new Vector4[instanceCount];
    55.  
    56.         Vector3[] tempPositions = new Vector3[instanceCount];
    57.         tempPositions = positionVectors[counter].positionSet;
    58.  
    59.         for (int i = 0; i < instanceCount; i++)
    60.         {
    61.             float randSize = baseSize + Random.Range(-randSizeVariation, randSizeVariation);
    62.             positions[i] = new Vector4(tempPositions[i].x, tempPositions[i].y, tempPositions[i].z, randSize);
    63.         }
    64.  
    65.         positionBuffer.SetData(positions);
    66.  
    67.         instanceMaterial.SetBuffer("positionBuffer", positionBuffer);
    68.  
    69.         // indirect args
    70.         uint numIndices = (instanceMesh != null) ? (uint)instanceMesh.GetIndexCount(0) : 0;
    71.         args[0] = numIndices;
    72.         args[1] = (uint)instanceCount;
    73.         argsBuffer.SetData(args);
    74.  
    75.         cachedInstanceCount = instanceCount;
    76.  
    77.     }
    78.  
    79.     void Update()
    80.     {
    81.         // Update starting position buffer
    82.         if (cachedInstanceCount != instanceCount) UpdateBuffers();
    83.  
    84.         myTransformMatrix = transform.localToWorldMatrix;
    85.         block.SetMatrix("_TransformMatrix", myTransformMatrix);
    86.         shaderMatrix = block.GetMatrix("_TransformMatrix");
    87.  
    88.         if (Input.GetKeyDown(KeyCode.UpArrow))
    89.         {
    90.             counter++;
    91.             if (counter == positionVectors.Count)
    92.             {
    93.                 counter = 0;
    94.             }
    95.             UpdateBuffers();
    96.         }
    97.  
    98.         if (Input.GetKeyDown(KeyCode.DownArrow))
    99.         {
    100.             counter--;
    101.             if (counter <= 0)
    102.             {
    103.                 counter = positionVectors.Count - 1;
    104.             }
    105.             UpdateBuffers();
    106.         }
    107.  
    108.         // Render
    109.         //  instanceMaterial.SetBuffer("positionBuffer", positionBuffer);
    110.         Graphics.DrawMeshInstancedIndirect(instanceMesh, 0, instanceMaterial, new Bounds(Vector3.zero, new Vector3(100.0f, 100.0f, 100.0f)), argsBuffer);
    111.     }
    112.  
    113.     void OnDisable()
    114.     {
    115.         if (positionBuffer != null) positionBuffer.Release();
    116.         positionBuffer = null;
    117.  
    118.         if (argsBuffer != null) argsBuffer.Release();
    119.         argsBuffer = null;
    120.     }
    121. }
    122.  
    And here's the shader - based almost entirely on the sample shader in the Unity docs for DrawMeshInstancedIndirect (as linked in my original post here):
    Code (CSharp):
    1. Shader "Instanced/MyInstancedTest"
    2. {
    3.     Properties{
    4.         [HDR]_EmissiveColor("Emissive Color (RGB)", Color) = (1, 1, 1, 1)
    5.     }
    6.         SubShader{
    7.         Tags{ "RenderType" = "Opaque" }
    8.         LOD 200
    9.  
    10.         CGPROGRAM
    11.         // Physically based Standard lighting model
    12. #pragma surface surf Standard addshadow
    13. #pragma multi_compile_instancing
    14. #pragma instancing_options procedural:setup
    15.  
    16.         sampler2D _MainTex;
    17.      
    18.         float4x4 _TransformMatrix;
    19.  
    20.     struct Input {
    21.         float2 uv_MainTex;
    22.     };
    23.  
    24. #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    25.     StructuredBuffer<float4> positionBuffer;
    26. #endif
    27.  
    28.     void setup()
    29.     {
    30. #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    31.         float4 data = positionBuffer[unity_InstanceID];
    32.              
    33.         data.xyz = mul(_TransformMatrix, float4(data.xyz, 1)).xyz;
    34.  
    35.         unity_ObjectToWorld._11_21_31_41 = float4(data.w, 0, 0, 0);
    36.         unity_ObjectToWorld._12_22_32_42 = float4(0, data.w, 0, 0);
    37.         unity_ObjectToWorld._13_23_33_43 = float4(0, 0, data.w, 0);
    38.         unity_ObjectToWorld._14_24_34_44 = float4(data.xyz, 1);
    39.      
    40.         unity_WorldToObject = unity_ObjectToWorld;
    41.         unity_WorldToObject._14_24_34 *= -1;
    42.         unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
    43.      
    44. #endif
    45.     }
    46.  
    47.     half3 _EmissiveColor;
    48.  
    49.     void surf(Input IN, inout SurfaceOutputStandard o)
    50.     {
    51.         o.Emission = _EmissiveColor.rgb;
    52.     }
    53.     ENDCG
    54.     }
    55.         FallBack "Diffuse"
    56. }
    57.  
    Soooo - seems like all of the data is getting from the script to the shader, but I'm not certain why the multiply line doesn't seem to be actually multiplying. I'm certain I'm doing something obviously wrong, but I can't see it.

    Thanks for taking a look!
     
    Kbpro likes this.
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    No, the data is never making it to the shader. You create a material property block and set the matrix on it, but never actually use the material property block, so that data just sits in the material property block unused.

    Look at the documentation for DrawMeshInstancedIndirect again for how to use that material property block.
     
  9. amasinton

    amasinton

    Joined:
    Aug 12, 2006
    Posts:
    138
    That was it! Of course that was it. It says right in the documentation (for DrawMesh, but NOT in DrawMeshInstancedIndirect):
    "modifying material properties between calls to this function won't make the meshes pick up them. If you want to draw series of meshes with the same material, but slightly different properties (e.g. change color of each mesh), use MaterialPropertyBlock parameter."​

    Thank you! I've learned so much over the past couple of days.
     
  10. flyer19

    flyer19

    Joined:
    Aug 26, 2016
    Posts:
    126
    How make WorldToLocalMatix.multipyDirection() and WorldToLocalMatix.multipyVector() in shader? localPos=mul(WorldToLocalMatix,float4(worldPos,1))? localDirection=mul(WorldToLocalMatix,float3(worldDirection))?
     
  11. amasinton

    amasinton

    Joined:
    Aug 12, 2006
    Posts:
    138
    That's a good question! It has been a couple of years and I don't exactly remember. I've pasted in the entire shader code - much of which is commented out because I ended up not needing certain features, but wanted to keep them in just in case I wanted to play with this kind of thing later.

    However, what IS working in this shader is the instances moving with the transform that has the instancing controller script attached, which passes all of the data to the shader. So, I've included that, too. Again, it's warts-and-all and it works (mostly) for my purposes. If it can help you at all, you're welcome. I just hope it isn't more confusing than helpful.

    The shader:
    Code (CSharp):
    1. Shader "Instanced/MyInstancedTest"
    2. {
    3.     Properties{
    4.         //_MainTex("Albedo (RGB)", 2D) = "white" {}
    5.         //_Glossiness("Smoothness", Range(0,1)) = 0.5
    6.         //_Metallic("Metallic", Range(0,1)) = 0.0
    7.         _FadeControl("FadeControl", Range(0,1)) = 1.0
    8.         [HDR]_EmissiveColor("Emissive Color (RGB)", Color) = (1, 1, 1, 1)
    9.     }
    10.         SubShader{
    11.         Tags{ //"RenderType" = "Opaque"
    12.               "IgnoreProjector" = "True"
    13.               "RenderType" = "Transparent"
    14.               "Queue" = "Transparent"
    15.         }
    16.         Blend SrcAlpha OneMinusSrcAlpha
    17.         LOD 200
    18.  
    19.         CGPROGRAM
    20.         // Physically based Standard lighting model
    21. #pragma surface surf Standard addshadow alpha:fade
    22. #pragma multi_compile_instancing
    23. #pragma instancing_options procedural:setup
    24.  
    25.         sampler2D _MainTex;
    26.      
    27.         float4x4 _TransformMatrix;
    28.  
    29.     struct Input {
    30.         float2 uv_MainTex;
    31.     };
    32.  
    33. #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    34.     StructuredBuffer<float4> positionBuffer;
    35.     StructuredBuffer<float4> colorBuffer;
    36. #endif
    37.  
    38.  
    39.     void rotate2D(inout float2 v, float r)
    40.     {
    41.         float s, c;
    42.         sincos(r, s, c);
    43.         v = float2(v.x * c - v.y * s, v.x * s + v.y * c);
    44.     }
    45.  
    46.     void setScale(inout float4 v, float s)
    47.     {
    48.         v = float4(v.x, v.y, v.z, s);
    49.     }
    50.  
    51.     float rand(float w)
    52.     {
    53.         float s = frac(sin(_Time.y * w) * 10.0);
    54.         return s;
    55.     }
    56.  
    57.     void setup()
    58.     {
    59. #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    60.         float4 data = positionBuffer[unity_InstanceID];
    61.      
    62.         //float rotation = data.w * data.w * _Time.y * 0.5f;
    63.         //rotate2D(data.xz, rotation);
    64.      
    65.         //float scale = data.w * data.w * _Time.y * 0.5f;
    66.         //float scale = clamp( data.w * _SinTime.y, 0.0, 1.0 );
    67.         //float scale = _Time.y < 2.0 ? data.w + _Time.y : 2.0;
    68.         //float scale = data.w + _Time.y;
    69.         //float scale = sin(_Time.y * data.w) * 10.0 < 0.0 ? data.w - _SinTime.y : data.w + _SinTime.y;
    70.         //float scale = abs(sin(_Time.y * data.w)) * 10.0 > 0.5 ? data.w : 0.0;
    71.         //float scale = frac(sin(_Time.y * data.w) * 10.0) > 0.5 ? data.w : 0.0;
    72.         float scale = rand(data.w) > 0.1 ? data.w : 0.0;
    73.         setScale(data.xyzw, scale);
    74.              
    75.         data.xyz = mul(_TransformMatrix, float4(data.xyz, 1)).xyz;
    76.  
    77.         unity_ObjectToWorld._11_21_31_41 = float4(data.w, 0, 0, 0);
    78.         unity_ObjectToWorld._12_22_32_42 = float4(0, data.w, 0, 0);
    79.         unity_ObjectToWorld._13_23_33_43 = float4(0, 0, data.w, 0);
    80.         unity_ObjectToWorld._14_24_34_44 = float4(data.xyz, 1);
    81.      
    82.         unity_WorldToObject = unity_ObjectToWorld;
    83.         unity_WorldToObject._14_24_34 *= -1;
    84.         unity_WorldToObject._11_22_33 = 1.0f / unity_WorldToObject._11_22_33;
    85.      
    86. #endif
    87.     }
    88.  
    89.     //half _Glossiness;
    90.     //half _Metallic;
    91.     half _FadeControl;
    92.     half3 _EmissiveColor;
    93.  
    94.     void surf(Input IN, inout SurfaceOutputStandard o)
    95.     {
    96.         //float4 col = 1.0f;
    97.  
    98. #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
    99.         ////col.gb = (float)(unity_InstanceID % 256) / 255.0f;
    100.         //col = colorBuffer[unity_InstanceID];
    101. #else
    102.         ////col.gb = float4(0, 0, 1, 1);
    103.         //col = float4(0, 0, 1, 1);
    104. #endif
    105.  
    106.  
    107.  
    108.         //fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * col;
    109.         //o.Albedo = c.rgb;
    110.         //o.Metallic = _Metallic;
    111.         //o.Smoothness = _Glossiness;
    112.         //o.Alpha = c.a;
    113.         o.Alpha = _FadeControl;
    114.         o.Emission = _EmissiveColor.rgb;
    115.     }
    116.     ENDCG
    117.     }
    118.         FallBack "Diffuse"
    119. }
    120.  
    The Controller Script (attach this to a transform in the scene):
    ***This script relies on two other libraries: LeanTween by Dented Pixel (for wider game-specific stuff - you can probably ignore these things), and Advanced C# Messenger by Ilya Suzdalnitski (Which controls when buffers are sent - again, you can probably adapt the code to ignore this and just use your own systems.)***

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [System.Serializable]
    6. public class PositionGroup
    7. {
    8.     public Vector3[] positionSet;
    9. }
    10.  
    11. public class MyInstancedController : MonoBehaviour
    12. {
    13.     public List<PositionGroup> positionVectors = new List<PositionGroup>();
    14.     //public float[] sizeArray;
    15.     public float baseSize = 0.04f;
    16.     public float randSizeVariation = 0.01f;
    17.     public Mesh instanceMesh;
    18.     public Material originalMaterial;
    19.     public Material instanceMaterial;
    20.  
    21.     private MaterialPropertyBlock block;
    22.     private Matrix4x4 myTransformMatrix;
    23.  
    24.     public bool doModelSwitching = true;
    25.  
    26.     public float randTimerDuration = 10.0f;
    27.     public float randTimerMin = 3.0f;
    28.     public float randTimerMax = 15.0f;
    29.  
    30.     public float scaleTime = 0.5f;
    31.     public float scaleZeroTime = 1.0f;
    32.  
    33.     private float fadeValue = 1.0f;
    34.     public float fadeMax = 1.0f;
    35.     public float fadeTime = 0.2f;
    36.  
    37.     public int instanceCount = 0;
    38.     public int cachedInstanceCount = -1;
    39.     public int positionVectorsCount = 0;
    40.     private ComputeBuffer positionBuffer;
    41.     private ComputeBuffer argsBuffer;
    42.     private ComputeBuffer colorBuffer;
    43.  
    44.     public int counter = 0;
    45.     private int startupCounter = 0;
    46.     private int updateCounter = 0;
    47.  
    48.     private uint[] args = new uint[5] { 0, 0, 0, 0, 0 };
    49.  
    50.     void Start()
    51.     {
    52.         startupCounter++;
    53.  
    54.         fadeValue = fadeMax;
    55.  
    56.         instanceMaterial = new Material(originalMaterial);
    57.  
    58.         myTransformMatrix = transform.localToWorldMatrix;
    59.         block = new MaterialPropertyBlock();
    60.  
    61.         instanceMaterial.SetFloat("_FadeControl", fadeValue);
    62.  
    63.         instanceCount = positionVectors[counter].positionSet.Length;
    64.         argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
    65.         UpdateBuffers();
    66.     }
    67.  
    68.     public void SetRandTimer()
    69.     {
    70.         randTimerDuration = Random.Range(randTimerMin, randTimerMax);
    71.     }
    72.  
    73.     public void SelectModel()
    74.     {
    75.         counter = Random.Range(0, positionVectors.Count);
    76.         //UpdateBuffers();
    77.         //ScaleOut();
    78.         FadeOut();
    79.         SetRandTimer();
    80.     }
    81.  
    82.     public void UpdateFade(float val)
    83.     {
    84.         fadeValue = val;
    85.         instanceMaterial.SetFloat("_FadeControl", fadeValue);
    86.     }
    87.  
    88.     public void FadeOut()
    89.     {
    90.         LeanTween.value(gameObject, UpdateFade, fadeValue, 0.0f, fadeTime).setOnComplete(FadeOutComplete);
    91.     }
    92.  
    93.     public void FadeOutComplete()
    94.     {
    95.         UpdateBuffers();
    96.         FadeIn();
    97.     }
    98.  
    99.     public void FadeIn()
    100.     {
    101.         LeanTween.value(gameObject, UpdateFade, fadeValue, fadeMax, fadeTime);
    102.     }
    103.  
    104.     public void ScaleOut()
    105.     {
    106.         LeanTween.scaleY(gameObject, 100.0f, scaleTime);
    107.         LeanTween.scaleX(gameObject, 0.0f, scaleTime);
    108.         LeanTween.scaleZ(gameObject, 0.0f, scaleTime);
    109.         //UpdateBuffers();
    110.         LeanTween.value(0.0f, 1.0f, scaleZeroTime).setOnComplete(ScaleOutComplete);
    111.     }
    112.  
    113.     public void ScaleOutComplete()
    114.     {
    115.         UpdateBuffers();
    116.         LeanTween.scaleY(gameObject, 1.0f, scaleTime);
    117.         LeanTween.scaleX(gameObject, 1.0f, scaleTime);
    118.         LeanTween.scaleZ(gameObject, 1.0f, scaleTime);
    119.     }
    120.  
    121.     public void UpdateBuffers()
    122.     {
    123.         //Debug.Log(gameObject.name + " UpdateBuffers()");
    124.         instanceCount = positionVectors[counter].positionSet.Length;
    125.  
    126.         if (instanceCount < 1) instanceCount = 1;
    127.  
    128.         // Positions & Colors
    129.         if (positionBuffer != null) positionBuffer.Release();
    130.         //if (colorBuffer != null) colorBuffer.Release();
    131.  
    132.         positionBuffer = new ComputeBuffer(instanceCount, 16);
    133.         //colorBuffer = new ComputeBuffer(instanceCount, 4 * 4);
    134.  
    135.         Vector4[] positions = new Vector4[instanceCount];
    136.         //Vector4[] colors = new Vector4[instanceCount];
    137.  
    138.         Vector3[] tempPositions = new Vector3[instanceCount];
    139.         tempPositions = positionVectors[counter].positionSet;
    140.  
    141.         for (int i = 0; i < instanceCount; i++)
    142.         {
    143.             float randSize = baseSize + Random.Range(-randSizeVariation, randSizeVariation);
    144.             positions[i] = new Vector4(tempPositions[i].x, tempPositions[i].y, tempPositions[i].z, randSize);
    145.             //colors[i] = new Vector4(Random.value, Random.value, Random.value, 1f);
    146.         }
    147.  
    148.         positionBuffer.SetData(positions);
    149.         //colorBuffer.SetData(colors);
    150.  
    151.         instanceMaterial.SetBuffer("positionBuffer", positionBuffer);
    152.         //instanceMaterial.SetBuffer("colorBuffer", colorBuffer);
    153.  
    154.         // indirect args
    155.         uint numIndices = (instanceMesh != null) ? (uint)instanceMesh.GetIndexCount(0) : 0;
    156.         args[0] = numIndices;
    157.         args[1] = (uint)instanceCount;
    158.         if (args != null)
    159.         {
    160.             argsBuffer.SetData(args);
    161.         }
    162.  
    163.         cachedInstanceCount = instanceCount;
    164.     }
    165.  
    166.     void Update()
    167.     {
    168.         //Random Timer Stuff
    169.         //Initialization
    170.         if (updateCounter < 2)
    171.         {
    172.             updateCounter++;
    173.         }
    174.         else if (updateCounter == 2)
    175.         {
    176.             if (positionVectors.Count > 1 && doModelSwitching)
    177.             {
    178.                 SetRandTimer();
    179.                 updateCounter++;
    180.             }
    181.         }
    182.         else if (updateCounter == 3)
    183.         {
    184.             randTimerDuration -= Time.deltaTime;
    185.         }
    186.  
    187.         if (randTimerDuration < 0.0f)
    188.         {
    189.             SelectModel();
    190.         }
    191.  
    192.         // Update starting position buffer
    193.         if (cachedInstanceCount != instanceCount) UpdateBuffers();
    194.  
    195.         myTransformMatrix = transform.localToWorldMatrix;
    196.         block.SetMatrix("_TransformMatrix", myTransformMatrix);
    197.  
    198.         if (Input.GetKeyDown(KeyCode.UpArrow))
    199.         {
    200.             counter++;
    201.             if (counter == positionVectors.Count)
    202.             {
    203.                 counter = 0;
    204.             }
    205.             UpdateBuffers();
    206.         }
    207.  
    208.         if (Input.GetKeyDown(KeyCode.DownArrow))
    209.         {
    210.             counter--;
    211.             if (counter <= 0)
    212.             {
    213.                 counter = positionVectors.Count - 1;
    214.             }
    215.             UpdateBuffers();
    216.         }
    217.  
    218.         // Render
    219.         Graphics.DrawMeshInstancedIndirect(instanceMesh, 0, instanceMaterial, new Bounds(Vector3.zero, new Vector3(100.0f, 100.0f, 100.0f)), argsBuffer, 0, block);
    220.     }
    221.  
    222.     private void OnEnable()
    223.     {
    224.         Messenger.AddListener("UpdateBuffers", UpdateBuffers);
    225.  
    226.         //myTransformMatrix = transform.localToWorldMatrix;
    227.         //block = new MaterialPropertyBlock();
    228.         //block.SetMatrix("_TransformMatrix", myTransformMatrix);
    229.  
    230.         instanceCount = positionVectors[counter].positionSet.Length;
    231.         argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments);
    232.  
    233.         //Messenger.Broadcast("UpdateBuffers");
    234.  
    235.         if (startupCounter > 0)
    236.         {
    237.             UpdateBuffers();
    238.         }
    239.     }
    240.  
    241.     void OnDisable()
    242.     {
    243.         Messenger.RemoveListener("UpdateBuffers", UpdateBuffers);
    244.  
    245.         if (positionBuffer != null) positionBuffer.Release();
    246.         positionBuffer = null;
    247.  
    248.         //if (colorBuffer != null) colorBuffer.Release();
    249.         //colorBuffer = null;
    250.  
    251.         if (argsBuffer != null) argsBuffer.Release();
    252.         argsBuffer = null;
    253.  
    254.         Messenger.Broadcast("UpdateBuffers");
    255.     }
    256. }
    257.  
     
  12. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Assuming
    myVector
    is a
    float3
    in the shader:
    c#
    myMatrix.MultiplyPoint(myVector)

    hlsl
    mul(myMatrix, float4(myVector.xyz, 1.0));


    c#
    myMatrix.MultiplyVector(myVector)

    hlsl
    mul(myMatrix, float4(myVector.xyz, 0.0))
    or
    mul((float3x3)myMatrix, myVector)


    There's no direct equivalent to
    MultiplyPoint3x4()
    . HLSL doesn't support matrices with a different number of rows and columns. GLSL does, but there's generally only hardware support for
    float4
    dot products, which is how GPUs handle matrix multiplies, so a
    float3x4
    matrix might get expanded to a
    float4x4
    anyway. Basically there's a performance advantage on the CPU to do a 3x4 multiply that doesn't exist on most GPUs. But most of the time it'll produce the same output as
    MultiplyPoint()
    , so just use
    mul(myMatrix, float4(myVector.xyz, 1.0));
    for that.
     
    flyer19 and amasinton like this.
  13. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Btw, the case where MultiplyPoint() and MultiplyPoint3x4() diverge is on perspective projection matrices. That actually uses the full 4x4 matrix, where as generally any object transform matrix is only going to use the values in a 3x4 matrix, meaning the last row is "blank" (really 0,0,0,1).
     
    amasinton likes this.
  14. amasinton

    amasinton

    Joined:
    Aug 12, 2006
    Posts:
    138
    Thank you @bgolus ! This is clear, to-the-point, and useful. As ever!

    Always a pleasure to learn from what you share.