Hello. I Have been working on a GPU Instanced Procedural Grass Shader and I am currently trying to blend each grass blade with the color of the terrain underneath. I am retrieving the color of the terrain layer underneath the grass blade and the normals of the terrain but in some places, the colors don’t match due to the lighting model of the URP Terrain Lit Shader.
Here’s what it looks like when the blending matches well and there is no light hitting the terrain:
Here’s what the blending looks like when the light is on the terrain:
This is the code I am currently using to calculate color in the frag shader:
float4 frag(v2f i) : SV_Target
{
Light mainLight = GetMainLight(i.shadowCoord);
float3 lightDir = normalize(mainLight.direction.xyz);
float3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
float3 halfWayDir = normalize(lightDir+viewDir);
float3 grassNormal = float3(0,1,0);
float3 surfaceNormal = tex2Dlod(_TerrainNormalTexture,float4(i.worldUv,1,1)).rgb;
float ndotl = saturate(dot(lightDir, normalize(grassNormal)));
float surfaceNdotl = saturate(dot(lightDir, normalize(surfaceNormal)));
float4 groundColor = tex2Dlod(_GroundColorTex,float4(i.worldUv,1,1))*mainLight.distanceAttenuation*surfaceNdotl;
float4 albedoBottom = lerp(_Albedo1,_Albedo2,i.colorNoise);
float4 albedoTop = lerp(_TipColor,_VariantTipColor,i.colorNoise);
float groundColorFactor = smoothstep(_TuneGroundColor,0.8,i.uv.y*i.uv.y);
albedoBottom = lerp(groundColor,albedoBottom,groundColorFactor*_GroundColorStrength);
float4 color = lerp(albedoBottom,albedoTop,smoothstep(_BlendThresholdBottom,_BlendThresholdTop,i.uv.y));
ndotl=1;
float4 ao = lerp(_AOColor,1.0f,i.uv.y);
float4 windFresnel = _WindColor * pow(i.wind,2)*_WindColorStrength;
windFresnel *= (i.uv.y * i.uv.y);
float4 grassColor = color;
float3 specularReflectance = _SpecularReflectance.rgb;
float3 specNormal = grassNormal;
specNormal.xz *= _SpecNormalStrength;
specNormal = normalize(specNormal);
float shadow = mainLight.shadowAttenuation;
float3 lightReflectDirection = reflect(-lightDir,specNormal);
float3 lightViewDirection = saturate(dot(lightReflectDirection,viewDir));
float3 shininessPower = pow(lightViewDirection,_SpecularShine);
float3 specular = specularReflectance*shininessPower*(1-dot(lightReflectDirection,specNormal))*saturate(pow(distance(_WorldSpaceCameraPos,i.worldPos),5)*_SpecAttentuation);
specular *= i.uv.y;
float viewDistance = length(_WorldSpaceCameraPos - i.worldPos);
float fogFactor = (_FogDensity/sqrt(log(2))) * (max(0.0f,viewDistance - _FogOffset));
fogFactor = exp2(-fogFactor*fogFactor);
float4 totalColor = (grassColor + float4(specular,1) + windFresnel)*_LightLevelScale;
//totalColor = float4(MixFog(totalColor,i.fogCoords),1);
totalColor = lerp(_FogColor,totalColor,fogFactor);
/*InputData lightingInput = (InputData)0;
lightingInput.positionWS = i.worldPos;
lightingInput.normalWS = surfaceNormal;
lightingInput.viewDirectionWS = GetWorldSpaceNormalizeViewDir(i.worldPos);
lightingInput.shadowCoord = i.shadowCoord;
SurfaceData surfaceInput = (SurfaceData)0;
surfaceInput.albedo = groundColor.rgb;
surfaceInput.alpha = groundColor.a;
surfaceInput.specular=0;
surfaceInput.metallic =0;
surfaceInput.smoothness =0;*/
//UniversalFragmentBlinnPhong(lightingInput,surfaceInput);
//return(UniversalFragmentPBR(lightingInput,surfaceInput));
return totalColor;
//return color;
//returntex2Dlod(_GroundColorTex,float4(i.worldUv,1,1))
}
I had a look at some of the code for the URP Terrain lit shader and it seemed like they were using a mix of lightmaps and dynamic lighting but I’m not exactly sure how to implement that in my custom shader. Is anyone familiar with how I could blend the base of the grass blade with the terrain color after lighting has been applied?