Hey,
I’m working on a grass shader that creates the grass with a geometry shader. I’ve got directional lighting and shadows working, alongside ambient lighting and light attenuation for spot/point lights.
However, I can’t figure out how to get shadows cast by spot/point lights to render on the grass.
Here’s the full shader code, I would be very grateful for any help.
Shader "GW/Foliage/Grass"
{
Properties
{
_BottomColor("Bottom Color", Color) = (0,1,0,1)
_TopColor("Top Color", Color) = (1,1,0,1)
_GrassHeight("Grass Height", Float) = 1
_GrassWidth("Grass Width", Float) = 0.06
_RandomHeight("Grass Height Randomness", Float) = 0.25
_WindSpeed("Wind Speed", Float) = 100
_WindStrength("Wind Strength", Float) = 0.05
_Rad("Blade Radius", Range(0,1)) = 0.6
_BladeForward("Blade Forward Amount", Float) = 0.38
_BladeCurve("Blade Curvature Amount", Range(1, 4)) = 2
_AmbientStrength("Ambient Strength", Range(0,1)) = 0.5
_MinDist("Min Distance", Float) = 40
_MaxDist("Max Distance", Float) = 60
}
CGINCLUDE
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityLightingCommon.cginc"
#pragma multi_compile_fwdbase_fullforwardshadows
#pragma multi_compile_fog
#define GrassSegments 5
#define GrassBlades 4
struct v2g
{
float4 pos : SV_POSITION;
float3 norm : NORMAL;
float2 uv : TEXCOORD0;
};
struct g2f
{
float4 pos : SV_POSITION;
float3 norm : NORMAL;
float2 uv : TEXCOORD0;
float3 worldPos : TEXCOORD3;
LIGHTING_COORDS(5, 6)
UNITY_FOG_COORDS(4)
};
half _GrassHeight;
half _GrassWidth;
half _WindSpeed;
float _WindStrength;
half _Radius, _Strength;
float _Rad;
float _RandomHeight;
float _BladeForward;
float _BladeCurve;
float _MinDist, _MaxDist;
uniform float3 _PositionMoving;
v2g vert(appdata_full input)
{
float3 v0 = input.vertex.xyz;
v2g output;
output.pos = input.vertex;
output.norm = input.normal;
output.uv = input.texcoord;
return output;
}
float rand(float3 co)
{
return frac(sin(dot(co.xyz, float3(12.9898, 78.233, 53.539))) * 43758.5453);
}
// https://gist.github.com/keijiro/ee439d5e7388f3aafc5296005c8c3f33
float3x3 AngleAxis3x3(float angle, float3 axis)
{
float c, s;
sincos(angle, s, c);
float t = 1 - c;
float x = axis.x;
float y = axis.y;
float z = axis.z;
return float3x3(
t * x * x + c, t * x * y - s * z, t * x * z + s * y,
t * x * y + s * z, t * y * y + c, t * y * z - s * x,
t * x * z - s * y, t * y * z + s * x, t * z * z + c
);
}
struct unityTransferVertexToFragmentHack
{
float3 vertex : POSITION;
};
g2f GrassVertex(float3 vertexPos, float width, float height, float offset, float curve, float2 uv, float3x3 rotation, float3 faceNormal, float3 worldPos) {
g2f OUT;
OUT.pos = UnityObjectToClipPos(vertexPos + mul(rotation, float3(width, height, curve) + float3(0, 0, offset)));
OUT.norm = faceNormal;
OUT.uv = uv;
OUT.worldPos = worldPos;
// send extra vertex to forwardadd pass
unityTransferVertexToFragmentHack v;
v.vertex = vertexPos + mul(rotation, float3(width, height, curve) + float3(0, 0, offset));
TRANSFER_VERTEX_TO_FRAGMENT(OUT);
UNITY_TRANSFER_FOG(OUT, OUT.pos);
return OUT;
}
[maxvertexcount(51)]
void geom(point v2g IN[1], inout TriangleStream<g2f> triStream)
{
float forward = rand(IN[0].pos.yyz) * _BladeForward;
float3 lightPosition = _WorldSpaceLightPos0;
float3 perpendicularAngle = float3(0, 0, 1);
float3 faceNormal = cross(perpendicularAngle, IN[0].norm) * lightPosition;
float4 worldPos = mul(unity_ObjectToWorld, IN[0].pos);
float distanceFromCamera = distance(worldPos, _WorldSpaceCameraPos);
float distanceFade = 1 - saturate((distanceFromCamera - _MinDist) / _MaxDist);
float3 v0 = IN[0].pos.xyz;
float3 wind1 = float3(sin(_Time.x * _WindSpeed + v0.x) + sin(_Time.x * _WindSpeed + v0.z * 2) + sin(_Time.x * _WindSpeed * 0.1 + v0.x), 0,
cos(_Time.x * _WindSpeed + v0.x * 2) + cos(_Time.x * _WindSpeed + v0.z));
wind1 *= _WindStrength;
_GrassHeight *= clamp(rand(IN[0].pos.xyz), 1 - _RandomHeight, 1 + _RandomHeight);
for (int j = 0; j < (GrassBlades * distanceFade); j++)
{
float3x3 facingRotationMatrix = AngleAxis3x3(rand(IN[0].pos.xyz) * UNITY_TWO_PI + j, float3(0, 1, -0.1));
float3x3 transformationMatrix = facingRotationMatrix;
float radius = j / (float)GrassBlades;
float offset = (1 - radius) * _Rad;
for (int i = 0; i < GrassSegments; i++)
{
float t = i / (float)GrassSegments;
float segmentHeight = _GrassHeight * t;
float segmentWidth = _GrassWidth * (1 - t);
segmentWidth = i == 0 ? _GrassWidth * 0.3 : segmentWidth;
float segmentForward = pow(t, _BladeCurve) * forward;
float3x3 transformMatrix = i == 0 ? facingRotationMatrix : transformationMatrix;
float3 newPos = i == 0 ? v0 : v0 + (wind1 * t);
triStream.Append(GrassVertex(newPos, segmentWidth, segmentHeight, offset, segmentForward, float2(0, t), transformMatrix, faceNormal, worldPos));
triStream.Append(GrassVertex(newPos, -segmentWidth, segmentHeight, offset, segmentForward, float2(1, t), transformMatrix, faceNormal, worldPos));
}
triStream.Append(GrassVertex(v0 + wind1, 0, _GrassHeight, offset, forward, float2(0.5, 1), transformationMatrix, faceNormal, worldPos));
triStream.RestartStrip();
}
}
ENDCG
SubShader
{
Cull Off
Pass
{
Tags
{
"RenderType" = "Geometry"
"LightMode" = "ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma geometry geom
#pragma target 4.6
#pragma multi_compile DIRECTIONAL POINT SPOT SHADOWS_DEPTH SHADOWS_SCREEN
float4 _TopColor;
float4 _BottomColor;
float _AmbientStrength;
float4 frag(g2f input) : SV_Target
{
UNITY_LIGHT_ATTENUATION(atten, input, input.worldPos);
#ifdef POINT
unityShadowCoord3 lightCoord = mul(unity_WorldToLight, unityShadowCoord4(input.worldPos, 1)).xyz;
fixed shadow = UNITY_SHADOW_ATTENUATION(input, input.worldPos);
atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).r * shadow;
#elif defined(SPOT)
DECLARE_LIGHT_COORD(input, input.worldPos);
fixed shadow = UNITY_SHADOW_ATTENUATION(input, input.worldPos);
atten = (lightCoord.z > 0) * UnitySpotCookie(lightCoord) * UnitySpotAttenuate(lightCoord.xyz) * shadow;
#endif
float4 baseColor = lerp(_BottomColor, _TopColor, saturate(input.uv.y));
float3 ambient = ShadeSH9(float4(0, 1, 0, 1.0));
float4 final = baseColor * atten * _LightColor0;
final.rgb += (ambient * baseColor);
return final;
}
ENDCG
}
Pass
{
Tags
{
"LightMode" = "ForwardAdd"
}
Blend One One
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#pragma multi_compile DIRECTIONAL POINT SPOT SHADOWS_DEPTH SHADOWS_SCREEN
float4 _TopColor;
float4 _BottomColor;
float4 frag(g2f input) : SV_Target
{
UNITY_LIGHT_ATTENUATION(atten, input, input.worldPos);
#ifdef POINT
unityShadowCoord3 lightCoord = mul(unity_WorldToLight, unityShadowCoord4(input.worldPos, 1)).xyz;
fixed shadow = UNITY_SHADOW_ATTENUATION(input, input.worldPos);
atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).r * shadow;
#elif defined(SPOT)
DECLARE_LIGHT_COORD(input, input.worldPos);
fixed shadow = UNITY_SHADOW_ATTENUATION(input, input.worldPos);
atten = (lightCoord.z > 0) * UnitySpotCookie(lightCoord) * UnitySpotAttenuate(lightCoord.xyz) * shadow;
#endif
float4 baseColor = lerp(_BottomColor, _TopColor, saturate(input.uv.y));
float3 ambient = ShadeSH9(float4(0, 1, 0, 1.0));
float4 final = baseColor * atten * _LightColor0;
final.rgb += (ambient * baseColor);
return final;
}
ENDCG
}
Pass
{
Tags
{
"LightMode" = "ShadowCaster"
}
CGPROGRAM
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#pragma multi_compile_shadowcaster
float4 frag(g2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
Fallback "VertexLit"
}