I'm computing my surface normals in the fragment program - so why does Unity still require per-vertex normals/tangents?

Hi all,

In computer graphics there exist techniques for computing a surface normal in the fragment shader, without needing per-vertex information. This is done by using ddx/ddy of the fragments world space to find two vectors which lie on the triangle’s surface, and then taking the cross product to find the normal. That is, you can achive a flat-shaded lighting effect even if your vertices only contain positions.

I have implemented this in Unity with the following simple surface shader:

Shader "FlatShadedMesh"
{
  SubShader
  {
    Tags { "RenderType" = "Opaque" }
    CGPROGRAM
    #pragma surface surf Lambert
    #pragma target 3.0
    #pragma only_renderers d3d9
      
    struct Input
    {
      float4 color : COLOR;
      float3 worldPos;
    };
      
    void surf (Input IN, inout SurfaceOutput o)
    {
      // Compute the surface normal in the fragment shader.
      float3 surfaceNormal = normalize(cross(ddx(IN.worldPos.xyz), ddy(IN.worldPos.xyz)));
        
      o.Albedo = IN.color;
      o.Normal = surfaceNormal;
    }
    ENDCG
  }
  Fallback "Diffuse"
}

I then generate a mesh using the following snippet of code:

Mesh mesh = new Mesh();
mesh.name = "testMesh";

mesh.Clear();		
		
Vector3[] vertices = new Vector3[resultVertLength / 4];
Vector3[] normals = new Vector3[resultVertLength / 4];
Vector4[] tangents = new Vector4[resultVertLength / 4];
for(int ct = 0; ct < resultVertLength / 4; ct++)
{
	vertices[ct] = new Vector3(resultVertices[ct * 4 + 0], resultVertices[ct * 4 + 1], resultVertices[ct * 4 + 2]);

	normals[ct] = new Vector3(0.0f, 0.0f, 1.0f); // <-- Dummy normals required by Unity
	tangents[ct] = new Vector4(1.0f, 0.0f, 0.0f, 1.0f); //<-- Dummy tangents required by Unity

}
mesh.vertices = vertices; 
mesh.normals = normals; // What a waste!
mesh.tangents = tangents; // What a waste!
mesh.triangles = resultIndices;

Note that I am providing dummy normals and tangents. These properties are the same for every vertex, are basically meaningless, and are never used by my surface shader. But if I don’t include these dummy properties then Unity complains:

Shader wants tangents but the mesh testMesh doesn’t have them

Shader wants normals, but the mesh testMesh doesn’t have them

So what must I do so that Unity will let me remove these properties? They are basically tripling the size of my vertex data. My best guess is that Unity wants them to do lighting calculation in tangent space. Can I make it do lighting in world space instead? Any other thoughts on how I can eliminate this unused data?

You need to write a vertex/fragment shader - surface shaders have a lot of pre-written code that gets included that need inputs based on the script you actually write.