I am stuck writing VF shader for Particle System GPU instancing. My shader fetches normal for calculating lighting, bizarrely with the UnityObjectToWorldNormal macro, the normal seems to retain in the local space in fragment shader, but is otherwise OK in vertex shader. The following 2 particle systems are mostly identical except for the Enabling GPU Instancing property. They have randomized 3D rotations. Diffuse lighting done in vertex shader: Diffuse lighting done in fragment shader: Code (CSharp): Shader "Particle Diffuse" { Properties { _TintColor ("Tint Color", Color) = (.5,.5,.5,.5) _MainTex ("Main Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" "Queue"="Geometry"} LOD 100 ZWrite On ZTest On Cull Back Lighting Off ColorMask RGB Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_particles #pragma multi_compile_instancing #pragma instancing_options procedural:vertInstancingSetup #include "UnityCG.cginc" #include "Lighting.cginc" #include "UnityStandardParticleInstancing.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL0; float4 color : COLOR; float4 uv : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float4 vertex : SV_POSITION; float4 normal : NORMAL0; float4 color : COLOR; float2 uv : TEXCOORD0; }; float4 _TintColor; sampler2D _MainTex; float4 _MainTex_ST; float3 Diffuse(float3 normal) { float3 worldNormal = normalize(UnityObjectToWorldNormal(normal)); return max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz)) * _LightColor0; } v2f vert (appdata v) { v2f o; UNITY_SETUP_INSTANCE_ID(v); o.color = v.color * _TintColor * 2; vertInstancingColor(o.color); o.vertex = UnityObjectToClipPos(v.vertex); //o.color.rgb += Diffuse(v.normal); o.uv.xy = v.uv; vertInstancingUVs(v.uv, o.uv.xy); o.normal = float4(v.normal,1); return o; } float3 frag (v2f i) : SV_Target { float3 Main2D = tex2D(_MainTex, i.uv).rgb; float3 col = i.color.rgb * Main2D; col += Diffuse(i.normal.xyz); return col; } ENDCG } } } The particle system GPU instancing manual doesn't mention anything special needed to be done to normal. Am I missing something important or is it a bug?
I’m away now so can’t give a precise answer, but, it depends what the UnityObjectToWorldNormal macro does. The particle instancing only sets up unity_ObjectToWorld and unity_WorldToObject. You can use those to transform the normal, ensuring that normal.w==0. I.e. float3 n = mul(o2w, float4(in.normal, 0)).xyz; Is it a bug that your way doesn’t work? I don’t know until I can look at that macro in our codebase