I’m attempting to program a vertex-lit shader to illustrate what Gouraud shading looks like and its relative pros and cons in comparison with other shaders. However, when my implementation always shades vertex values in the x axis, regardless of the rotation of the object or indeed even where the lights are located. Could someone take a quick look and see if there is something blatantly out of the ordinary that I am missing?
Shader "Shader Demo/Gouraud5"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
_Color ("Diffuse Material Color", Color) = (1,1,1,1)
_SpecColor ("Specular Material Color", Color) = (1,1,1,1)
_Shininess ("Shininess", Float) = 1
}
SubShader
{
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// User-specified properties
uniform float4 _Color;
uniform float4 _SpecColor;
uniform float _Shininess;
// color of light source (from "Lighting.cginc")
uniform float4 _LightColor0;
// Vertex and Fragment Structs
struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 col : COLOR;
};
vertexOutput vert(vertexInput v)
{
vertexOutput output;
// Compute directional vectors
float3 normalDir = normalize(float3(mul(float4(v.normal, 0.0), _World2Object)));
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz - v.vertex);
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v.vertex);
// Compute the ambient term
float3 ambientTerm = float3(UNITY_LIGHTMODEL_AMBIENT) * float3(_Color);
// Compute the diffuse term
float diffuseLight = max(0.0, dot(normalDir, lightDir));
float3 diffuseTerm = _Color * diffuseLight;
// Compute the specular term
float3 halfwayDir = normalize(lightDir + viewDir);
float specularLight = pow(max( 0,dot(normalDir, halfwayDir)), 1);
// If we have no diffuse component, then we have no specular component
if(diffuseLight <= 0.0)
{
specularLight = 0;
}
float3 specularTerm = specularLight * _LightColor0 * _SpecColor;
// Assign final color and position output
output.col = float4(ambientTerm + diffuseTerm + specularTerm, 1.0);
output.pos = mul(UNITY_MATRIX_MVP, v.vertex);
return output;
}
float4 frag(vertexOutput input) : COLOR
{
return input.col;
}
ENDCG
}
}
}
Your light and view directions aren’t in world space. You’re currently subtracting the object space vertex position from the world space light position.
Not entirely sure what you’re doing with the normal vector. mul() usually has the matrix as the first argument and the vector as the second.
Unity has helper functions for these things, too.
So those 3 lines could become this if you want to keep everything in Object Space;
float3 normalDir = v.normal;
float3 lightDir = ObjSpaceLightDir(v.vertex);
float3 viewDir = ObjSpaceViewDir(v.vertex);
or this, if you want to keep everything in World Space;
float3 normalDir = mul((float3x3)_Object2World,v.normal);
float3 lightDir = WorldSpaceLightDir(v.vertex);
float3 viewDir = WorldSpaceViewDir(v.vertex);
Thanks for the suggestions. I’m still trying to get used to creating shaders in general and the macros you showed me helped solve a whole host of other problems I was trying to think of ways to resolve without using if statements. I’ve simplified the shader a great deal ( looking back at the code this morning its pretty clear I wasn’t thinking clearly).
What I am trying to do is model Gouraud shading in CG as part of a survey on different shading models. The basic algorithm from what I understand is to:
- Calculate the vertex normal for each vertex in the model.
- Calculate the shading value for each vertex by getting the dot product with the current’s light direction.
- Lerp the shading value from vertex to vertex.
I am using two passes using “Lightmode=ForwardBase” and “Lightmode=ForwardAdd”. to calculate for multiple lights. Currently, with the simplifications I’ve made, the shader only shades models as either two colors:
Gray if there is no light affecting it
White if there is any light affecting it.
I believe I may have made a mistake in trying to calculate the shade value or in the lerp. I know that unless the vectors are unit length, the dot product won’t necessarily be limited to values between 0 and 1. Are there any suggestions you can make Farfarer?
I’ve copied in the simplified shader below.
Shader "Gouraud6" {
Properties
{
}
SubShader
{
Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma exclude_renderers gles
#pragma exclude_renderers d3d11 xbox360
#pragma fragment frag vertex vert
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float4 col : COLOR;
float shade;
} ;
v2f vert(appdata_base INPUT)
{
v2f OUTPUT;
// Light direction in object space
float3 lightDirObjectSpace = ObjSpaceLightDir(INPUT.vertex);
// Normalized vertex normal
float3 normalDir = normalize(INPUT.normal);
// Assign final output Variables
OUTPUT.pos = mul(UNITY_MATRIX_MVP, v.vertex);
OUTPUT.shade = dot(normalDir, lightDirObjectSpace);
return OUTPUT;
}
float4 frag(v2f OUTPUT) : COLOR
{
OUTPUT.col = float4(clamp(OUTPUT.shade,0,1));
return OUTPUT.col;
}
ENDCG
}
Pass
{
Tags { "LightMode"="ForwardAdd" }
Blend One One // Additive Blending
CGPROGRAM
#pragma exclude_renderers gles
#pragma exclude_renderers d3d11 xbox360
#pragma fragment frag vertex vert
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float4 col : COLOR;
float shade;
} ;
v2f vert(appdata_base INPUT)
{
v2f OUTPUT;
// Light direction in object space
float3 lightDirObjectSpace = ObjSpaceLightDir(INPUT.vertex);
// Normalized vertex normal
float3 normalDir = normalize(INPUT.normal);
// Assign final output Variables
OUTPUT.pos = mul(UNITY_MATRIX_MVP, v.vertex);
OUTPUT.shade = dot(normalDir, lightDirObjectSpace);
return OUTPUT;
}
float4 frag(v2f OUTPUT) : COLOR
{
OUTPUT.col = float4(clamp(OUTPUT.shade,0,1));
return OUTPUT.col;
}
ENDCG
}
}
//FallBack "Diffuse"
}
Thank you for all your help so far. Its very refreshing to be able to communicate with someone more well-versed in shader practices for Unity than I.