Are vertex colors used in standard lighting?

Hi,

Are vertex colors affected by either PositionFog() or DiffuseLight()?

I’m trying to pass them unmodified from the model through to the fragment shader while still using the default lighting functions.

Neither of those touch vertex colors.

To pass the vertex colors to the fragment shader you need to have a COLOR0 semantic in the appdata struct, then you can just pass it on through a float4 in the v2f struct.

struct appdata {
    float4 vertex : POSITION;
    float4 texcoord : TEXCOORD0;
    float4 color : COLOR0;
};

struct v2f {
	V2F_POS_FOG;
	float2	uv : TEXCOORD0;
	float4	color : COLOR;
};
v2f vert (appdata v)
{
	v2f o;
	PositionFog( v.vertex, o.pos, o.fog );
	o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
	o.color = v.color;
	return o;
}

That’s what I always thought, however whenever I do pass vertex colors around the lighting on the texture breaks very badly.

I’m working off the TerrainTwoLayerBumped from the wiki, trying to convert it to use vertex colors to free up the mix map texture slot.

All I’ve added are the semantics for passing the vertex colors to the fragment shader. I’ve rechecked the macro definitions in the v2f and appdata definitionas in the Unity include files and so far they don’t look like they use vertex colors.

The attached image shows two planes; the one on the left is the basic bumped shader, the one on the right shows the same shader but with one additional line: o.color = v.color. I’ve marked the line in the shader below:

Shader "Terrain/TwoLayer Bumped" {

Properties {
    _Color ("Main Color", Color) = (1,1,1,0.5)
    _MainTex ("Base (RGB)", 2D) = "white" {}
    _MainTex2 ("Base 2 (RGB)", 2D) = "white" {}
    _Mask ("Mix Mask (A)", 2D) = "gray" {}
    _BumpMap ("Bumpmap (RGB)", 2D) = "bump" {}
}
 
Category {
    Blend AppSrcAdd AppDstAdd
    Fog { Color [_AddFog] }
 
    // ------------------------------------------------------------------
    // ARB fragment program
   
    SubShader {
        // Ambient pass
        Pass {
            Tags {"LightMode" = "PixelOrNone"}
            Color [_PPLAmbient]
            SetTexture [_MainTex] { combine texture }
            SetTexture [_Mask] { combine previous, texture }
            SetTexture [_MainTex2] { combine texture lerp (previous) previous }
            SetTexture [_MainTex2] { constantColor [_Color] combine previous * primary DOUBLE, previous * constant }
        }
        // Vertex lights
        Pass {
            Tags {"LightMode" = "Vertex"}
            Lighting On
            Material {
                Diffuse [_Color]
                Emission [_PPLAmbient]
            }
            SetTexture [_MainTex] { combine texture }
            SetTexture [_Mask] { combine previous, texture }
            SetTexture [_MainTex2] { combine texture lerp (previous) previous }
            SetTexture [_MainTex2] { constantColor [_Color] combine previous * primary DOUBLE, previous * constant }
        }
       
        // Pixel lights
        Pass {
            Name "PPL"
            Tags { "LightMode" = "Pixel" }
               
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_fog_exp2
#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile_builtin
 
#include "UnityCG.cginc"
#include "AutoLight.cginc"
 
struct appdata {
    float4 vertex : POSITION;
    float4 tangent : TANGENT;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
    float4 color : COLOR0;
};

struct v2f {
    V2F_POS_FOG;
    LIGHTING_COORDS
    float4 color;
    float2  uv[4];
    float3  lightDirT;
};

float4 _MainTex_ST, _MainTex2_ST, _Mask_ST, _BumpMap_ST;
 
v2f vert (appdata v)
{
    v2f o;
    PositionFog( v.vertex, o.pos, o.fog );
    o.uv[0] = TRANSFORM_TEX(v.texcoord, _MainTex);
    o.uv[1] = TRANSFORM_TEX(v.texcoord, _MainTex2);
    o.uv[2] = TRANSFORM_TEX(v.texcoord, _Mask);
    o.uv[3] = TRANSFORM_TEX(v.texcoord, _BumpMap);
    
    // This next line causes lighting to break
    o.color = v.color;
   
    TANGENT_SPACE_ROTATION;
    o.lightDirT = mul( rotation, ObjSpaceLightDir( v.vertex ) );   
   
    TRANSFER_VERTEX_TO_FRAGMENT(o);
    return o;
}
 
uniform sampler2D _MainTex;
uniform sampler2D _MainTex2;
uniform sampler2D _Mask;
uniform sampler2D _BumpMap;
 
float4 frag (v2f i) : COLOR
{
    half4 col1 = tex2D(_MainTex,i.uv[0]);
    half4 col2 = tex2D(_MainTex2,i.uv[1]);
    half mix = tex2D(_Mask,i.uv[2]).a;
    half4 texcol = lerp(col1,col2,mix);
   
    // get normal from the normal map
    float3 normal = tex2D(_BumpMap, i.uv[3]).xyz * 2 - 1;
   
    return DiffuseLight( i.lightDirT, normal, texcol, LIGHT_ATTENUATION(i) );
}
ENDCG             
        }
    }
   
    // ------------------------------------------------------------------
    // Four texture cards
   
    SubShader {
        // Vertex lit
        Pass {
            Lighting On
            Material {
                Diffuse [_Color]
                Ambient [_Color]
            }
            SetTexture [_MainTex] { combine texture }
            SetTexture [_Mask] { combine previous, texture }
            SetTexture [_MainTex2] { combine texture lerp (previous) previous }
            SetTexture [_MainTex2] { constantColor [_Color] combine previous * primary DOUBLE, previous * constant }
        }
    }
}
 
FallBack "VertexLit"
 
}

Any idea why this breaks?

251516--9056--$test_image_121.jpg

Alright, from reading this thread:

http://forum.unity3d.com/viewtopic.php?t=42237

In summary:

Vertex colors are used for passing around the computed vertex lighting, so, yup, standard lighting really does get messed up when using vertex colors. Vertex color alpha is apparently safe to use, as lighting doesn’t use alpha.

Back to using a ramp texture… sniff

Vertex lighting overrides vertex colors, but only when using Fixed Function passes with ‘Lighting On’. And this can be fixed by using the ColorMaterial property. This does not apply to your original question on if vertex colors are affected by pixel lighting.

As an example, here’s a shader that uses both Vertex Colors and Vertex+Pixel Lights:

Shader "Diffuse with Vertex Color" {
Properties {
	_Color ("Main Color", Color) = (1,1,1,1)
	_MainTex ("Base (RGB)", 2D) = "white" {}
}

Category {
	Tags { "RenderType"="Opaque" }
	LOD 200
	Blend AppSrcAdd AppDstAdd
	Fog { Color [_AddFog] }
	
	// ------------------------------------------------------------------
	// ARB fragment program
	
	SubShader {
		// Ambient pass
		Pass {
			Name "BASE"
			Tags {"LightMode" = "PixelOrNone"}
			ColorMaterial AmbientAndDiffuse
			SetTexture [_MainTex] {constantColor [_PPLAmbient] Combine primary * constant}
			SetTexture [_MainTex] {constantColor [_Color] Combine texture * previous DOUBLE, texture * constant}
		}
		// Vertex lights
		Pass { 
			Name "BASE"
			Tags {"LightMode" = "Vertex"}
			Lighting On
			ColorMaterial AmbientAndDiffuse
			SetTexture [_MainTex] {
				Combine texture * primary, texture * primary
			}
			SetTexture [_MainTex] {
				constantColor [_Color]
				Combine previous * constant DOUBLE, previous * constant
			}
		}
		// Pixel lights
		Pass {
			Name "PPL"
			Tags { "LightMode" = "Pixel" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_builtin
#pragma fragmentoption ARB_fog_exp2
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
#include "AutoLight.cginc"

struct appdata {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
	float4 color : COLOR0;
};

struct v2f {
	V2F_POS_FOG;
	LIGHTING_COORDS
	float2	uv;
	float3	normal;
	float3	lightDir;
	float4	color : COLOR;
};

uniform float4 _MainTex_ST;

v2f vert (appdata v)
{
	v2f o;
	PositionFog( v.vertex, o.pos, o.fog );
	o.normal = v.normal;
	o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
	o.lightDir = ObjSpaceLightDir( v.vertex );
	TRANSFER_VERTEX_TO_FRAGMENT(o);
	o.color = v.color;
	return o;
}

uniform sampler2D _MainTex;

float4 frag (v2f i) : COLOR
{
	float3 normal = i.normal;
		
	half4 texcol = tex2D( _MainTex, i.uv ) * i.color;
	
	return DiffuseLight( i.lightDir, normal, texcol, LIGHT_ATTENUATION(i) );
}
ENDCG
		}
	}
}

Fallback "VertexLit with Vertex Color"

}

Thanks Shawn, that makes a lot of things clearer now. You’re right, I overlooked the effects of vertex lighting and didn’t really ask the right question.

Here’s the shader that I ended up with. I had to replace fixed function ambient as well in order to get it to match the lighting on the regular diffuse shader and to avoid leakage (this happens on the wiki terrain shader). I don’t quite understand vertex lighting well enough to get it to work in all cases, but for my current scenes I can live with only pixel lights:

Shader "Terrain/TerrainTwoLayerVertexColor" {
Properties {
   _Color ("Main Color", Color) = (1,1,1,1)
   _MainTex ("Base (RGB)", 2D) = "white" {}
   _MainTex2 ("Base2 (RGB)", 2D) = "white" {}
}

Category {
   Tags { "RenderType"="Opaque" }
   LOD 200
   Blend AppSrcAdd AppDstAdd
   Fog { Color [_AddFog] }
   
   // ------------------------------------------------------------------
   // ARB fragment program
   
   SubShader {
    Blend AppSrcAdd AppDstAdd
    Fog { Color [_AddFog] }

    Pass {
        Name "BASE"
        Tags {"LightMode" = "Always"}
CGPROGRAM
#pragma fragment frag
#pragma vertex vert
#pragma fragmentoption ARB_fog_exp2
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"

struct appdata {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
   float4 color : COLOR0;
};

struct v2f {
    V2F_POS_FOG;
    float3    viewDir;
    float3    normal;
	float4   color : COLOR;
    float2    uv[2];
};

float4 _MainTex_ST, _MainTex2_ST;

v2f vert (appdata v)
{
    v2f o;
    PositionFog( v.vertex, o.pos, o.fog );
    o.normal = v.normal;
    o.uv[0] = TRANSFORM_TEX(v.texcoord, _MainTex);
    o.uv[1] = TRANSFORM_TEX(v.texcoord, _MainTex2);
    o.viewDir = ObjSpaceViewDir( v.vertex );
    o.color = v.color;
    return o;
}

uniform float4 _Color;

uniform sampler2D _MainTex, _MainTex2;

float4 frag (v2f i) : COLOR
{
    i.normal = normalize(i.normal);
    i.viewDir = normalize(i.viewDir);

	half4 col1 = tex2D(_MainTex,i.uv[0]);
	half4 col2 = tex2D(_MainTex2,i.uv[1]);
    half4 texcol = lerp(col1,col2,i.color.r);
      
    half3 ambient = texcol.rgb * _PPLAmbient.rgb * 2;
    return float4( ambient, texcol.a * _Color.a );
}
ENDCG
    }
    
      // Vertex lights
      Pass {
         Name "BASE"
         Tags {"LightMode" = "Vertex"}
         Lighting On
         ColorMaterial AmbientAndDiffuse
         SetTexture [_MainTex] {
            Combine texture * primary, texture * primary
         }
         SetTexture [_MainTex] {
            constantColor [_Color]
            Combine previous * constant DOUBLE, previous * constant
         }
      }
      // Pixel lights
      Pass {
         Name "PPL"
         Tags { "LightMode" = "Pixel" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_builtin
#pragma fragmentoption ARB_fog_exp2
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
#include "AutoLight.cginc"

struct appdata {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
   float4 color : COLOR0;
};

struct v2f {
   V2F_POS_FOG;
   LIGHTING_COORDS
   float3   normal;
   float3   lightDir;
   float4   color : COLOR;
   float2   uv[2];
};

uniform float4 _MainTex_ST, _MainTex2_ST;

v2f vert (appdata v)
{
   v2f o;
   PositionFog( v.vertex, o.pos, o.fog );
   o.normal = v.normal;
   o.uv[0] = TRANSFORM_TEX(v.texcoord, _MainTex);
   o.uv[1] = TRANSFORM_TEX(v.texcoord, _MainTex2);
   o.lightDir = ObjSpaceLightDir( v.vertex );
   TRANSFER_VERTEX_TO_FRAGMENT(o);
   o.color = v.color;
   return o;
}

uniform sampler2D _MainTex, _MainTex2;

float4 frag (v2f i) : COLOR
{
   float3 normal = i.normal;

	half4 col1 = tex2D(_MainTex,i.uv[0]);
	half4 col2 = tex2D(_MainTex2,i.uv[1]);
    half4 texcol = lerp(col1,col2,i.color.r);
    
   return DiffuseLight( i.lightDir, normal, texcol, LIGHT_ATTENUATION(i) );
}
ENDCG
      }
   }
}

Fallback "VertexLit"

}

P.S. The CG compiler in Shaderlab is finicky when it comes to the ordering of the members of v2f and appdata structures for some reason.