Search Unity

Using transformation matrix leads to type mismatch

Discussion in 'Shaders' started by Deleted User, Apr 16, 2017.

  1. Deleted User

    Deleted User

    Guest

    Hi, I am trying to learn more about ShaderLab and HLSL in Unity, and I already figured out how to get uniform variables to work. As part of what I want to try next (specular reflections) I will need, among other things, the normal of the surface in world space. Since Unity provides no builtin matrix which can do a rotation-only transformation from object to world space, I deliver my own via a uniform variable. However, the compiler complains about a type mismatch when trying to assign the result, without any details given. Anyone has an idea?

    Shader code:

    Code (csharp):
    1. Shader "Custom/CustomSpecular"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags { "RenderType"="Opaque" }
    10.         LOD 100
    11.  
    12.         Pass
    13.         {
    14.             CGPROGRAM
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.             // make fog work
    18. //            #pragma multi_compile_fog
    19.          
    20.             #include "UnityCG.cginc"
    21.          
    22.             uniform float4x4 loc2world;
    23.             uniform float4x4 rotateOnly;
    24.             uniform float4 uniCol;
    25.  
    26.             struct appdata
    27.             {
    28.                 float4 vertex : POSITION;
    29.                 float2 uv : TEXCOORD0;
    30.                 float3 normal : NORMAL;
    31.             };
    32.  
    33.             struct v2f
    34.             {
    35.                 float4 vertex : SV_POSITION;
    36.                 float2 uv : TEXCOORD0;
    37.                 float3 normal : NORMAL;
    38. //                UNITY_FOG_COORDS(1)
    39.             };
    40.  
    41.             sampler2D _MainTex;
    42.             float4 _MainTex_ST;
    43.          
    44.             v2f vert (appdata v)
    45.             {
    46.                 v2f o;
    47.                 o.vertex = UnityObjectToClipPos(v.vertex);
    48.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    49.                 o.normal = rotateOnly * float4(v.normal, 0); // ERROR: type mismatch (on d3d11)
    50.              
    51. //                UNITY_TRANSFER_FOG(o,o.vertex);
    52.                 return o;
    53.             }
    54.          
    55.             fixed4 frag (v2f i) : SV_Target
    56.             {
    57. //                fixed4 col = tex2D(_MainTex, i.uv) * uniCol;
    58.                 fixed4 col = fixed4(i.normal, 1.0f);
    59. //                UNITY_APPLY_FOG(i.fogCoord, col);
    60.                 return col;
    61.             }
    62.             ENDCG
    63.         }
    64.     }
    65. }
    C# MonoBehaviour code:

    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class SetUniforms : MonoBehaviour {
    6.  
    7.     private static int rotateOnlyID = -1;
    8.     private static bool initialized = false;
    9.  
    10.     private MeshRenderer mr;
    11.  
    12.     void Awake () {
    13.         if (!initialized) {
    14.             rotateOnlyID = Shader.PropertyToID ("rotateOnly");
    15.             initialized = true;
    16.         }
    17.         mr = GetComponent<MeshRenderer> ();
    18.     }
    19.  
    20.     void OnRenderObject() {
    21.         mr.material.SetMatrix (rotateOnlyID, Matrix4x4.TRS(Vector3.zero, transform.rotation, new Vector3(1, 1, 1)));
    22.     }
    23. }
     
    Last edited by a moderator: Apr 16, 2017
  2. Deleted User

    Deleted User

    Guest

    It appears HLSL requires the use of mul(x,y) as opposed to using the asterisk operator, so that fixed it. Oops!
     
    d11ldawson, artoonie and unit4ta like this.
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Because it's not needed. Casting the 4x4 matrix to a 3x3 matrix results in a rotation (and yes, scale) transform. For most situations where you want to rotate a vector with out translation you're dealing with unit length vectors, so you only need to normalize after transforming.

    For normals you actually want to use the inverse transpose transform matrix, and Unity has a built in function with an optimized version of this (just using the inverse WorldToObject matrix to avoid calling the costly transpose function).

    float3 worldNormal = UnityObjectToWorldNormal(v.normal)

    https://forum.unity3d.com/threads/d...unityobjecttoworldnormal.435302/#post-2814594
     
    Deleted User likes this.
  4. Deleted User

    Deleted User

    Guest

    That's very useful! Wish the docs would mention it. And in my defense, it has been a while since I've been knacking my knuckles over some matrix math.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Yeah, there's a bunch of useful functions in the UnityCG.cginc file, with some odd exclusion, but most aren't documented for some reason.