Question by
AMSantiago · Aug 26, 2019 at 11:16 AM ·
shadersgrasssurface shadervertex shadermerging
" Unexpected identifier "InputPatch". Expected: ')' " error when merging Surface and Vertex-Fragment shaders
Relatively new to shaders here. I'm facing a problem when trying to merge a vertex-fragment shader and surface shader. Both behave as expected when used independently, but the surface shader does not work properly in the merged shader, resulting in this:
Instead of this (obtained by applying two different materials, one for each independent shader):
Additionally, the Unity console outputs the following error when using the merged shader:
Shader error in 'Custom/Grass': Unexpected identifier "InputPatch". Expected: ')' at line 68
Line 68 is this one:
TessellationFactors PatchConstantFunction (InputPatch<vertexInput, 3> patch)
The merged shader is described below, followed by the independent shaders:
Shader "Custom/Grass"
{
Properties
{
[Header(Mesh Shading)]
_MainTex("Color Scheme", 2D) = "white" {}
_Color("Tint", Color) = (1,1,1,1)
_Glossiness("Smoothness", Range(0,1)) = 0.5
_Metallic("Metallic", Range(0,1)) = 0.0
[Header(Grass Shading)]
_TopColor("Top Color", Color) = (1,1,1,1)
_BottomColor("Bottom Color", Color) = (1,1,1,1)
_TranslucentGain("Translucent Gain", Range(0,1)) = 0.5
_BendRotationRandom("Bend Rotation Random", Range(0,1)) = 0.2
_BladeDisplacementRandom("Blade Displacement Random", Float) = 0.1
_BladeWidth("Blade Width", Float) = 0.05
_BladeWidthRandom("Blade Width Random", Float) = 0.02
_BladeHeight("Blade Height", Float) = 0.5
_BladeHeightRandom("Blade Height Random", Float) = 0.3
_BladeForward("Blade Forward Tilt", Float) = 0.38
_BladeCurve("Blade Curvature", Range(1,4)) = 2
_TessellationUniform("Tessellation Uniform", Range(1, 64)) = 1
_WindDistortionMap("Wind Distortion Map", 2D) = "white" {}
_WindFrequency("Wind Frequency", Vector) = (0.05, 0.05, 0, 0)
_WindStrength("Wind Strength", Float) = 1
}
CGINCLUDE
#include "UnityCG.cginc"
#include "Autolight.cginc"
float _TessellationUniform;
struct vertexInput
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct vertexOutput
{
float4 vertex : SV_POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct TessellationFactors
{
float edge[3] : SV_TessFactor;
float inside : SV_InsideTessFactor;
};
vertexInput VertexProgram(vertexInput v)
{
return v;
}
vertexOutput TessellationVertexProgram(vertexInput v)
{
vertexOutput o;
o.vertex = v.vertex;
o.normal = v.normal;
o.tangent = v.tangent;
return o;
}
TessellationFactors PatchConstantFunction (InputPatch<vertexInput, 3> patch)
{
TessellationFactors f;
f.edge[0] = _TessellationUniform;
f.edge[1] = _TessellationUniform;
f.edge[2] = _TessellationUniform;
f.inside = _TessellationUniform;
return f;
}
[UNITY_domain("tri")]
[UNITY_outputcontrolpoints(3)]
[UNITY_outputtopology("triangle_cw")]
[UNITY_partitioning("integer")]
[UNITY_patchconstantfunc("PatchConstantFunction")]
vertexInput HullProgram (InputPatch<vertexInput, 3> patch, uint id : SV_OutputControlPointID)
{
return patch[id];
}
[UNITY_domain("tri")]
vertexOutput DomainProgram (TessellationFactors factors, OutputPatch<vertexInput, 3> patch, float3 barycentricCoordinates : SV_DomainLocation)
{
vertexInput v;
#define MY_DOMAIN_PROGRAM_INTERPOLATE(fieldName) v.fieldName = \
patch[0].fieldName * barycentricCoordinates.x + \
patch[1].fieldName * barycentricCoordinates.y + \
patch[2].fieldName * barycentricCoordinates.z;
MY_DOMAIN_PROGRAM_INTERPOLATE(vertex)
MY_DOMAIN_PROGRAM_INTERPOLATE(normal)
MY_DOMAIN_PROGRAM_INTERPOLATE(tangent)
return TessellationVertexProgram(v);
}
#define BLADE_SEGMENTS 3
float _BendRotationRandom;
float _BladeDisplacementRandom;
float _BladeHeight;
float _BladeHeightRandom;
float _BladeWidth;
float _BladeWidthRandom;
float _BladeForward;
float _BladeCurve;
sampler2D _WindDistortionMap;
float4 _WindDistortionMap_ST;
float2 _WindFrequency;
float _WindStrength;
struct geometryOutput
{
float4 pos : SV_POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
unityShadowCoord4 _ShadowCoord : TEXCOORD1;
};
geometryOutput VertexOutput(float3 pos, float3 normal, float2 uv)
{
geometryOutput o;
o.pos = UnityObjectToClipPos(pos);
o.normal = UnityObjectToWorldNormal(normal);
o.uv = uv;
o._ShadowCoord = ComputeScreenPos(o.pos);
#if UNITY_PASS_SHADOWCASTER
// Applying the bias prevents artifacts from appearing on the surface.
o.pos = UnityApplyLinearShadowBias(o.pos);
#endif
return o;
}
// Simple noise function, sourced from http://answers.unity.com/answers/624136/view.html
// Extended discussion on this function can be found at the following link:
// https://forum.unity.com/threads/am-i-over-complicating-this-random-function.454887/#post-2949326
// Returns a number in the 0...1 range.
float rand(float3 co)
{
return frac(sin(dot(co.xyz, float3(12.9898, 78.233, 53.539))) * 43758.5453);
}
// Construct a rotation matrix that rotates around the provided axis, sourced from:
// 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
);
}
geometryOutput GenerateGrassVertex(float3 vertexPosition, float width, float height, float forward, float2 uv, float3x3 transformMatrix)
{
float3 tangentPoint = float3(width, height, forward);
float3 tangentNormal = normalize(float3(0, forward, -1));
float3 localNormal = mul(transformMatrix, tangentNormal);
float3 localPosition = vertexPosition + mul(transformMatrix, tangentPoint);
return VertexOutput(localPosition, localNormal, uv);
}
[maxvertexcount(BLADE_SEGMENTS * 2 + 1)]
void geo(triangle vertexOutput IN[3], inout TriangleStream<geometryOutput> triStream)
{
float3 pos = IN[0].vertex;
float3 vNormal = IN[0].normal;
if (dot(vNormal, mul(unity_WorldToObject, float3(0,1,0)).xyz) < 1e-6)
{
return;
}
float3x3 facingRotationMatrix = AngleAxis3x3(rand(pos) * UNITY_TWO_PI, float3(0, 1, 0));
float3x3 bendRotationMatrix = AngleAxis3x3(rand(pos.zzx) * _BendRotationRandom * UNITY_PI * 0.5,
float3(-1, 0, 0));
float2 uv = pos.xz * _WindDistortionMap_ST.xy + _WindDistortionMap_ST.zw + _WindFrequency * _Time.y;
float2 windSample = (tex2Dlod(_WindDistortionMap, float4(uv, 0, 0)).xy * 2 - 1) * _WindStrength;
float3 wind = normalize(float3(windSample.x, 0, windSample.y));
float3x3 windRotation = AngleAxis3x3(UNITY_PI * windSample, wind);
float3x3 transformationMatrix = mul(mul(mul(unity_WorldToObject, windRotation), facingRotationMatrix), bendRotationMatrix);
float3x3 transformationMatrixFacing = mul(unity_WorldToObject, facingRotationMatrix);
float3 displacement = float3((rand(pos.xxz) * 2 - 1) * _BladeDisplacementRandom, 0, (rand(pos.zzx) * 2 - 1) * _BladeDisplacementRandom);
float height = (rand(pos.xyz) * 2 - 1) * _BladeHeightRandom + _BladeHeight;
float width = (rand(pos.xyz) * 2 - 1) * _BladeWidthRandom + _BladeWidth;
float forward = rand(pos.yyz) * _BladeForward;
triStream.Append(GenerateGrassVertex(pos + displacement, width, 0, 0, float2(0, 0), transformationMatrixFacing));
triStream.Append(GenerateGrassVertex(pos + displacement, -width, 0, 0, float2(1, 0), transformationMatrixFacing));
for (int i = 1; i < BLADE_SEGMENTS; i++)
{
float t = i / (float)BLADE_SEGMENTS;
float segmentHeight = height * t;
float segmentWidth = width * (1 - t);
float segmentForward = pow(t, _BladeCurve) * forward;
triStream.Append(GenerateGrassVertex(pos + displacement, segmentWidth, segmentHeight, segmentForward, float2(0, t), transformationMatrix));
triStream.Append(GenerateGrassVertex(pos + displacement, -segmentWidth, segmentHeight, segmentForward, float2(1, t), transformationMatrix));
}
triStream.Append(GenerateGrassVertex(pos + displacement, 0, height, forward, float2(0.5, 1), transformationMatrix));
}
ENDCG
SubShader
{
Cull Off
Tags
{
"RenderType" = "Opaque"
}
LOD 200
Pass
{
Tags
{
"RenderType" = "Opaque"
"LightMode" = "ForwardBase"
}
CGPROGRAM
#pragma vertex VertexProgram
#pragma fragment frag
#pragma geometry geo
#pragma hull HullProgram
#pragma domain DomainProgram
#pragma target 4.6
#pragma multi_compile_fwdbase
#include "Lighting.cginc"
float4 _TopColor;
float4 _BottomColor;
float _TranslucentGain;
float4 frag (geometryOutput i, fixed facing : VFACE) : SV_Target
{
float3 normal = (max(0, sign(facing)) * 2 - 1) * i.normal;
float shadow = SHADOW_ATTENUATION(i);
float NdotL = saturate(saturate(dot(normal, _WorldSpaceLightPos0)) + _TranslucentGain) * shadow;
float3 ambient = ShadeSH9(float4(normal, 1));
float4 lightIntensity = NdotL * _LightColor0 + float4(ambient, 1);
return lerp(_BottomColor, _TopColor * lightIntensity, i.uv.y);
}
ENDCG
}
Pass
{
Tags
{
"LightMode" = "ShadowCaster"
}
CGPROGRAM
#pragma vertex VertexProgram
#pragma fragment frag
#pragma geometry geo
#pragma hull HullProgram
#pragma domain DomainProgram
#pragma target 4.6
#pragma multi_compile_shadowcaster
float4 frag(geometryOutput i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surfx Standard vertex:vertx fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 4.6
struct Input {
float3 color : COLOR;
};
sampler2D _MainTex;
fixed4 _Color;
void vertx (inout appdata_full v) {
// the color comes from a texture tinted by color
v.color = tex2Dlod(_MainTex, v.texcoord) * _Color;
}
half _Glossiness;
half _Metallic;
void surfx (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from the vertex input
o.Albedo = IN.color;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
}
ENDCG
}
FallBack "Diffuse"
}
Shader "LowPolyShaders/LowPolyPBRShader" {
Properties {
_MainTex ("Color Scheme", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows vertex:vert
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
struct Input {
float3 color : COLOR;
};
sampler2D _MainTex;
fixed4 _Color;
void vert (inout appdata_full v) {
// the color comes from a texture tinted by color
v.color = tex2Dlod(_MainTex, v.texcoord) * _Color;
}
half _Glossiness;
half _Metallic;
void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from the vertex input
o.Albedo = IN.color;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
}
ENDCG
}
FallBack "Diffuse"
}
Shader "Roystan/Grass"
{
Properties
{
[Header(Shading)]
_TopColor("Top Color", Color) = (1,1,1,1)
_BottomColor("Bottom Color", Color) = (1,1,1,1)
_TranslucentGain("Translucent Gain", Range(0,1)) = 0.5
[Space]
_TessellationUniform ("Tessellation Uniform", Range(1, 64)) = 1
[Header(Blades)]
_BladeWidth("Blade Width", Float) = 0.05
_BladeWidthRandom("Blade Width Random", Float) = 0.02
_BladeHeight("Blade Height", Float) = 0.5
_BladeHeightRandom("Blade Height Random", Float) = 0.3
_BladeForward("Blade Forward Amount", Float) = 0.38
_BladeCurve("Blade Curvature Amount", Range(1, 4)) = 2
_BendRotationRandom("Bend Rotation Random", Range(0, 1)) = 0.2
[Header(Wind)]
_WindDistortionMap("Wind Distortion Map", 2D) = "white" {}
_WindStrength("Wind Strength", Float) = 1
_WindFrequency("Wind Frequency", Vector) = (0.05, 0.05, 0, 0)
}
CGINCLUDE
#include "UnityCG.cginc"
#include "Autolight.cginc"
#include "Shaders/CustomTessellation.cginc"
struct geometryOutput
{
float4 pos : SV_POSITION;
#if UNITY_PASS_FORWARDBASE
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
// unityShadowCoord4 is defined as a float4 in UnityShadowLibrary.cginc.
unityShadowCoord4 _ShadowCoord : TEXCOORD1;
#endif
};
// Simple noise function, sourced from http://answers.unity.com/answers/624136/view.html
// Extended discussion on this function can be found at the following link:
// https://forum.unity.com/threads/am-i-over-complicating-this-random-function.454887/#post-2949326
// Returns a number in the 0...1 range.
float rand(float3 co)
{
return frac(sin(dot(co.xyz, float3(12.9898, 78.233, 53.539))) * 43758.5453);
}
// Construct a rotation matrix that rotates around the provided axis, sourced from:
// 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
);
}
geometryOutput VertexOutput(float3 pos, float3 normal, float2 uv)
{
geometryOutput o;
o.pos = UnityObjectToClipPos(pos);
#if UNITY_PASS_FORWARDBASE
o.normal = UnityObjectToWorldNormal(normal);
o.uv = uv;
// Shadows are sampled from a screen-space shadow map texture.
o._ShadowCoord = ComputeScreenPos(o.pos);
#elif UNITY_PASS_SHADOWCASTER
// Applying the bias prevents artifacts from appearing on the surface.
o.pos = UnityApplyLinearShadowBias(o.pos);
#endif
return o;
}
geometryOutput GenerateGrassVertex(float3 vertexPosition, float width, float height, float forward, float2 uv, float3x3 transformMatrix)
{
float3 tangentPoint = float3(width, forward, height);
float3 tangentNormal = normalize(float3(0, -1, forward));
float3 localPosition = vertexPosition + mul(transformMatrix, tangentPoint);
float3 localNormal = mul(transformMatrix, tangentNormal);
return VertexOutput(localPosition, localNormal, uv);
}
float _BladeHeight;
float _BladeHeightRandom;
float _BladeWidthRandom;
float _BladeWidth;
float _BladeForward;
float _BladeCurve;
float _BendRotationRandom;
sampler2D _WindDistortionMap;
float4 _WindDistortionMap_ST;
float _WindStrength;
float2 _WindFrequency;
#define BLADE_SEGMENTS 3
// Geometry program that takes in a single triangle and outputs a blade
// of grass at that triangle first vertex position, aligned to the vertex's normal.
[maxvertexcount(BLADE_SEGMENTS * 2 + 1)]
void geo(triangle vertexOutput IN[3], inout TriangleStream<geometryOutput> triStream)
{
float3 pos = IN[0].vertex.xyz;
// Each blade of grass is constructed in tangent space with respect
// to the emitting vertex's normal and tangent vectors, where the width
// lies along the X axis and the height along Z.
// Construct random rotations to point the blade in a direction.
float3x3 facingRotationMatrix = AngleAxis3x3(rand(pos) * UNITY_TWO_PI, float3(0, 0, 1));
// Matrix to bend the blade in the direction it's facing.
float3x3 bendRotationMatrix = AngleAxis3x3(rand(pos.zzx) * _BendRotationRandom * UNITY_PI * 0.5, float3(-1, 0, 0));
// Sample the wind distortion map, and construct a normalized vector of its direction.
float2 uv = pos.xz * _WindDistortionMap_ST.xy + _WindDistortionMap_ST.zw + _WindFrequency * _Time.y;
float2 windSample = (tex2Dlod(_WindDistortionMap, float4(uv, 0, 0)).xy * 2 - 1) * _WindStrength;
float3 wind = normalize(float3(windSample.x, windSample.y, 0));
float3x3 windRotation = AngleAxis3x3(UNITY_PI * windSample, wind);
// Construct a matrix to transform our blade from tangent space
// to local space; this is the same process used when sampling normal maps.
float3 vNormal = IN[0].normal;
float4 vTangent = IN[0].tangent;
float3 vBinormal = cross(vNormal, vTangent) * vTangent.w;
float3x3 tangentToLocal = float3x3(
vTangent.x, vBinormal.x, vNormal.x,
vTangent.y, vBinormal.y, vNormal.y,
vTangent.z, vBinormal.z, vNormal.z
);
// Construct full tangent to local matrix, including our rotations.
// Construct a second matrix with only the facing rotation; this will be used
// for the root of the blade, to ensure it always faces the correct direction.
float3x3 transformationMatrix = mul(mul(mul(tangentToLocal, windRotation), facingRotationMatrix), bendRotationMatrix);
float3x3 transformationMatrixFacing = mul(tangentToLocal, facingRotationMatrix);
float height = (rand(pos.zyx) * 2 - 1) * _BladeHeightRandom + _BladeHeight;
float width = (rand(pos.xzy) * 2 - 1) * _BladeWidthRandom + _BladeWidth;
float forward = rand(pos.yyz) * _BladeForward;
for (int i = 0; i < BLADE_SEGMENTS; i++)
{
float t = i / (float)BLADE_SEGMENTS;
float segmentHeight = height * t;
float segmentWidth = width * (1 - t);
float segmentForward = pow(t, _BladeCurve) * forward;
// Select the facing-only transformation matrix for the root of the blade.
float3x3 transformMatrix = i == 0 ? transformationMatrixFacing : transformationMatrix;
triStream.Append(GenerateGrassVertex(pos, segmentWidth, segmentHeight, segmentForward, float2(0, t), transformMatrix));
triStream.Append(GenerateGrassVertex(pos, -segmentWidth, segmentHeight, segmentForward, float2(1, t), transformMatrix));
}
// Add the final vertex as the tip.
triStream.Append(GenerateGrassVertex(pos, 0, height, forward, float2(0.5, 1), transformationMatrix));
}
ENDCG
SubShader
{
Cull Off
Pass
{
Tags
{
"RenderType" = "Opaque"
"LightMode" = "ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma geometry geo
#pragma fragment frag
#pragma hull hull
#pragma domain domain
#pragma target 4.6
#pragma multi_compile_fwdbase
#include "Lighting.cginc"
float4 _TopColor;
float4 _BottomColor;
float _TranslucentGain;
float4 frag (geometryOutput i, fixed facing : VFACE) : SV_Target
{
float3 normal = facing > 0 ? i.normal : -i.normal;
float shadow = SHADOW_ATTENUATION(i);
float NdotL = saturate(saturate(dot(normal, _WorldSpaceLightPos0)) + _TranslucentGain) * shadow;
float3 ambient = ShadeSH9(float4(normal, 1));
float4 lightIntensity = NdotL * _LightColor0 + float4(ambient, 1);
float4 col = lerp(_BottomColor, _TopColor * lightIntensity, i.uv.y);
return col;
}
ENDCG
}
Pass
{
Tags
{
"LightMode" = "ShadowCaster"
}
CGPROGRAM
#pragma vertex vert
#pragma geometry geo
#pragma fragment frag
#pragma hull hull
#pragma domain domain
#pragma target 4.6
#pragma multi_compile_shadowcaster
float4 frag(geometryOutput i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
}
Thank you in advance for your help, and I apologize in case this is a simple rookie mistake.
combined-expected.png
(113.8 kB)
combined-result.png
(110.1 kB)
Comment