Search Unity

shader mul vs Unity API matrix MultiplyPoint

Discussion in 'Shaders' started by SunnySunshine, Mar 20, 2016.

  1. SunnySunshine

    SunnySunshine

    Joined:
    May 18, 2009
    Posts:
    977
    I'm having some issues doing matrix multiplication on the GPU using ComputeShader.

    C#:

    Code (CSharp):
    1. var localToWorld = transform.localToWorldMatrix;
    2.  
    3. Debug.Log(localToWorld.MultiplyPoint3x4(Vector3.right));
    4.  
    5. shader.SetFloats("localToWorldMatrix", localToWorld.m00, localToWorld.m10, localToWorld.m20, localToWorld.m30, localToWorld.m01, localToWorld.m11, localToWorld.m21, localToWorld.m31, localToWorld.m02, localToWorld.m12, localToWorld.m22, localToWorld.m32);
    6.  
    7. shader.SetBuffer(0, "results", results);
    8.  
    9. shader.Dispatch(0, 1, 1, 1);
    10.  
    Compute shader:

    Code (CSharp):
    1. #pragma kernel CSMain
    2.  
    3. float3x4 localToWorldMatrix;
    4. RWStructuredBuffer<float3> results;
    5.  
    6. [numthreads(1,1,1)]
    7. void CSMain (uint3 id : SV_DispatchThreadID)
    8. {
    9.     results[id.x + id.y] = mul(localToWorldMatrix, float3(1,0,0));
    10. }
    The result of MultiplyPoint and the ComputeShader will be the same when rotating the object, but when moving the object it's like mul doesn't care for it at all. The point will always be 1,0,0 regardless of world position.

    Am I doing something wrong here?

    I want to get the world position of a local point using the passed in matrix.
     
    Last edited: Mar 20, 2016
    KyryloKuzyk likes this.
  2. Burchmore500

    Burchmore500

    Joined:
    Oct 20, 2014
    Posts:
    11
    I know this is an old post, but did you ever figure out what was the issue here?

    I'm having a similar problem where Matrix4x4.MultiplyPoint does not seem to give the same results as mul in a shader (specifically, rotation is the same but translation only happens when I use Matrix4x4.MultiplyPoint).
     
    deus0 likes this.
  3. SunnySunshine

    SunnySunshine

    Joined:
    May 18, 2009
    Posts:
    977
    I didn't at the time, but out of curiosity I revisited this topic again just now. It seems like the position requires a float4 rather than float3, with the last component set to 1 (i.e. float4(x,y,z,1)). Then it works. Also, seems like an easier way to set matrixes is to use a buffer instead.

    Final code:

    C#:

    Code (CSharp):
    1. var localToWorld = transform.localToWorldMatrix;
    2.  
    3. var matrixBuffer = new ComputeBuffer(1, sizeof(float) * 4 * 4);
    4. matrixBuffer.SetData(new Matrix4x4[] { localToWorld });
    5. var results = new ComputeBuffer(1, sizeof(float) * 3);
    6.  
    7. int kernel = shader.FindKernel("CSMain");
    8.  
    9. shader.SetBuffer(kernel, "localToWorldMatrix", matrixBuffer);
    10. shader.SetBuffer(kernel, "results", results);
    11.  
    12. shader.Dispatch(kernel, 1, 1, 1);
    13.  
    14. var data = new Vector3[1];
    15. results.GetData(data);
    Compute shader:

    Code (CSharp):
    1. #pragma kernel CSMain
    2. StructuredBuffer<float4x4> localToWorldMatrix;
    3. RWStructuredBuffer<float3> results;
    4. [numthreads(1,1,1)]
    5. void CSMain (uint3 id : SV_DispatchThreadID)
    6. {
    7.     results[id.x] = mul(localToWorldMatrix[0], float4(1,0,0,1)).xyz;
    8. }
     
    Last edited: Sep 20, 2017
    Scoth_ and Arkade like this.
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    A 3x3 matrix is rotation and scale only. A 4x4 (or 3x4) matrix holds translation. When you multiply a 4x4 matrix and a vector together, if you use float3(xyz) or float4(xyz, 0.0), only the 3x3 part of the matrix is applied, thus you get rotation and scale but no translation. That zero in the w component of the float4 is basically saying "ignore the translation".

    Matrix4x4.MultiplyPoint(Vector3 vector) converts the Vector3 into a Vector4 with w == 1. Matrix4x4.MultiplyVector(Vector3 vector) does not.
     
  5. deus0

    deus0

    Joined:
    May 12, 2015
    Posts:
    256
    I eventually fixed it, thanks for the float4 tip. I typed it in right before i read this but didn't click play until after. Very weird timing, maybe I had a preemptive vision. Anyway I tried a bunch of things, and the float4 trick did it.
    Code (CSharp):
    1.  
    2. float3 vertex = v.vertex.xyz;
    3. float4x4 originalInverseMatrix = originalBones[boneIndex];
    4. float4x4 currentMatrix = currentBones[boneIndex];
    5. //vertex = mul(mul(currentMatrix, originalInverseMatrix), vertex);    // matrix transform
    6. //vertex = mul(vertex, mul(originalInverseMatrix, currentMatrix));    // matrix transform
    7. float4 vertex4 = float4(vertex, 1);
    8. float4 vertex42 = mul(currentMatrix, mul(originalInverseMatrix, vertex4));
    9. //float4 vertex4 = float4(vertex, 1);
    10. vertOutput.vertex = UnityObjectToClipPos(vertex42); // draws it using the bounds or matrix
    11.  
     
    MaxEden likes this.