问题
I was following a tutorial made by Sam Wronski aka. World of Zero (Tutorial by World of Zero) where he codes a geometry shader for a point cloud grass generator. Great tutorial but I am wondering (and didn't find a proper solution after days of research) how I could implement shadows to the shader (casting and receiving shadows). I am trying to dig deeper into shaders, but this is a level to high for me yet.
My question is : how can I implement shadow casting and receiving for this grass shader? The code that exists and works fine so far is the following:
Shader "Custom/GrassGeometryShader" {
// https://www.youtube.com/watch?v=HY6qFbmbij8 und http://www.battlemaze.com/?p=153
Properties {
// --> HDR allows High Dynamic Colors
[HDR]_BackgroundColor ("Background Color", Color) = (1,0,0,1) // default to red
[HDR]_ForegroundColor ("Foreground Color", Color) = (0,1,0,1) // default to green
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_Cutoff("Alpha Cuttoff", Range (0,1)) = 0.15 // Wieviel abgeschnitten sien soll
_GrassHeight("GrasHeight", Float) = 0.25
_GrassWidt("GrasWidth", Float) = 0.25
_WindSpeed ("WindSpeed", Float) = 100
_WindStrength("WindStrength", Float) = 0.05
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
Pass
{
Cull OFF
CGPROGRAM
#include "UnityCG.cginc" // like: "using" in C#
// Vertex-Shader with vert-function
#pragma vertex vert
// Fragment-Shader with frag-function
#pragma fragment frag
// Geometry-Shader with geom-function
#pragma geometry geom
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 4.0 // needs to be 4.0 !
sampler2D _MainTex;
// vertex to graphics (v2g)
struct v2g
{
float4 pos : SV_POSITION;
float3 norm : NORMAL;
float2 uv : TEXCOORD0;
float3 color : TEXCOORD1;
};
//graphics to fragments (g2f)
struct g2f
{
float4 pos : SV_POSITION;
float3 norm : NORMAL;
float2 uv : TEXCOORD0;
float3 diffuseColor : TEXCOORD1;
//float3 specularColor : TEXCOORD2;
};
half _Glossiness;
half _Metallic;
fixed4 _BackgroundColor;
fixed4 _ForegroundColor;
half _GrassHeight;
half _GrassWidth;
half _Cutoff;
half _WindStrength;
half _WindSpeed;
// Vertex-Shader from Battlemaze.com
v2g vert(appdata_full v)
{
float3 v0 = mul(unity_ObjectToWorld, v.vertex).xyz;
v2g OUT;
OUT.pos = v.vertex;
OUT.norm = v.normal;
OUT.uv = v.texcoord;
OUT.color = tex2Dlod(_MainTex, v.texcoord).rgb;
return OUT;
}
void buldQuad(inout TriangleStream<g2f> triStream, float3 points[4], float3 color) {
g2f OUT;
float3 faceNormal = cross(points[1]-points[0], points[2]-points[0]);
for(int i; i < 4; ++i) {
OUT.pos = UnityObjectToClipPos(points[i]);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(i%2, (int)i/2);
triStream.Append(OUT);
}
triStream.RestartStrip();
}
// geom-Funktion
[maxvertexcount(24)]
void geom(point v2g IN[1], inout TriangleStream<g2f> triStream)
{
float3 lightPosition = _WorldSpaceLightPos0;
float3 perpendicularAngle = float3(0,0,1);
float3 faceNormal = cross(perpendicularAngle, IN[0].norm); // normal of gras
float3 v0 = IN[0].pos.xyz; // Tip of the gras
float3 v1 = IN[0].pos.xyz + IN[0].norm * _GrassHeight; // base of the gras
float3 v2 = IN[0].pos.xyz + IN[0].norm * _GrassHeight / 2; // middle part (?)
float3 wind = float3(sin(_Time.x * _WindSpeed + v0.x) + sin(_Time.x * _WindSpeed + v0.z * 2), 0, cos(_Time.x * _WindSpeed + v0.x * 2) + cos(_Time.x * _WindSpeed + v0.z)); // Anzahl oder Stärke der Manipulation an den Eckpunkten
// (_Time.x + v0.x + v0.z looks "random", because it's using time + coordinates)
v1 += wind * _WindStrength;
v2 += (wind * _WindStrength/2)/2;
float3 color = (IN[0].color); // color of the gras
float sin30 = 0.5;
float sin60 = 0.866f;
float cos30 = sin60;
float cos60 = sin30;
g2f OUT;
// Quad 1 - the following code could fit in one function (BUT!) it did not work on MacOSX, that's why it's still calculated the long way
OUT.pos = UnityObjectToClipPos(v0 + perpendicularAngle * 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(1, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1 + perpendicularAngle * 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(1, 1);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v0);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 1);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1 - perpendicularAngle * 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0, 1);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v0 - perpendicularAngle * 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v0);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 1);
triStream.Append(OUT);
// Quad 2
OUT.pos = UnityObjectToClipPos(v0 + float3(sin60, 0, -cos60) * 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(1, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1 + float3(sin60, 0, -cos60)* 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(1, 1);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v0);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 1);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v0 - float3(sin60, 0, -cos60) * 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1 - float3(sin60, 0, -cos60) * 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0, 1);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v0);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 1);
triStream.Append(OUT);
// Quad 3 - Positive
OUT.pos = UnityObjectToClipPos(v0 + float3(sin60, 0, cos60) * 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(1, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1 + float3(sin60, 0, cos60)* 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(1, 1);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v0);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 1);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v0 - float3(sin60, 0, cos60) * 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1 - float3(sin60, 0, cos60) * 0.5 * _GrassHeight);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0, 1);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v0);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 0);
triStream.Append(OUT);
OUT.pos = UnityObjectToClipPos(v1);
OUT.norm = faceNormal;
OUT.diffuseColor = color;
OUT.uv = float2(0.5, 1);
triStream.Append(OUT);
}
// Fragment-Shader by Battlemaze.com --> gets input v2g and renders it on screen
half4 frag(g2f IN) : COLOR
{
fixed4 c = tex2D(_MainTex, IN.uv);
clip(c.a - _Cutoff);
return c;
//return float4 (IN.diffuseColor.rgb, 1.0);
}
ENDCG
}
}
}
As I mentioned I work with a machine running MacOS which does unfortunately not work with compute shaders.
I would appreciate any help on this.
回答1:
To add support for shadow receiving
To the top of the CGPROGRAM block, add this:
#include "AutoLight.cginc"
It is also possible that you might need to add this:
#pragma multi_compile_fwdbase
Inside your v2g struct, add the following line:
SHADOW_COORDS(3) // (3) means we are using TEXCOORD3
Inside your geometry shader, for each new vertex, add this line after you've assigned OUT.pos:
TRANSFER_SHADOW(OUT)
And finally, in your fragment function, add this:
half shadow = SHADOW_ATTENUATION(IN)
Now, the "shadow" variable contains your shadow mask. In a lit shader, you'd multiply this with the light color, but in your case you can just multiply it with the output albedo color.
To add support for shadow casting
Add the following pass to your shader:
Pass {
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
ZWrite On ZTest LEqual
CGPROGRAM
#pragma target 2.0
#pragma multi_compile_shadowcaster
#pragma vertex vertShadowCaster
#pragma fragment fragShadowCaster
#include "UnityStandardShadow.cginc"
ENDCG
}
来源:https://stackoverflow.com/questions/48194981/receive-and-cast-shadows-custom-geometry-grass-shader-unity-c-sharp