Skip to content

Instantly share code, notes, and snippets.

@unitycoder
Created December 14, 2018 19:33
Show Gist options
  • Save unitycoder/9cc7b09a2bb54827454ea555238d4f56 to your computer and use it in GitHub Desktop.
Save unitycoder/9cc7b09a2bb54827454ea555238d4f56 to your computer and use it in GitHub Desktop.
Terrain Shader Tesselation Diffuse
// Author: Przemyslaw Zaworski
// Modified version of original:
// https://github.com/przemyslawzaworski/Unity3D-CG-programming/blob/master/terrain_shader_tessellation_diffuse.shader
// Shader supports maximum four splat textures (Splat and Normal maps) with triangle tessellation.
// Works seamlessly with built-in Terrain Component. Lambert light model (directional) with shadow casting and receiving.
// https://forum.unity.com/threads/how-to-dynamically-tessellate-a-mesh.597469/#post-3997999
Shader "Terrain Shader Tessellation Diffuse"
{
Properties
{
[HideInInspector] _Control ("Control (RGBA)", 2D) = "red" {}
[HideInInspector] _Splat3 ("Layer 3 (A)", 2D) = "white" {}
[HideInInspector] _Splat2 ("Layer 2 (B)", 2D) = "white" {}
[HideInInspector] _Splat1 ("Layer 1 (G)", 2D) = "white" {}
[HideInInspector] _Splat0 ("Layer 0 (R)", 2D) = "white" {}
[HideInInspector] _Normal3 ("Normal 3 (A)", 2D) = "bump" {}
[HideInInspector] _Normal2 ("Normal 2 (B)", 2D) = "bump" {}
[HideInInspector] _Normal1 ("Normal 1 (G)", 2D) = "bump" {}
[HideInInspector] _Normal0 ("Normal 0 (R)", 2D) = "bump" {}
_TessellationMap ("Tessellation Map", 2D) = "black" {}
}
SubShader
{
Tags {"RenderType" = "Opaque"}
Pass
{
Tags {"LightMode" = "ForwardBase" "SplatCount" = "4" "Queue" = "Geometry-100"}
CGPROGRAM
#pragma vertex vertex_shader
#pragma hull hull_shader
#pragma domain domain_shader
#pragma fragment pixel_shader
#pragma target 5.0
#pragma multi_compile_fwdbase
#include "AutoLight.cginc"
sampler2D _Control;
float4 _Control_ST;
sampler2D _Splat0;
sampler2D _Splat1;
sampler2D _Splat2;
sampler2D _Splat3;
float4 _Splat0_ST;
float4 _Splat1_ST;
float4 _Splat2_ST;
float4 _Splat3_ST;
float4 _LightColor0;
sampler2D _TessellationMap;
sampler2D _Normal0, _Normal1, _Normal2, _Normal3;
struct APPDATA
{
float2 tc_Control: COLOR;
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv_Splat0 : TEXCOORD0;
float2 uv_Splat1 : TEXCOORD1;
float2 uv_Splat2 : TEXCOORD2;
float2 uv_Splat3 : TEXCOORD3;
};
struct VS_CONTROL_POINT_OUTPUT
{
float4 position : SV_POSITION;
float3 normal : NORMAL;
float2 uv_Splat0 : TEXCOORD0;
float2 uv_Splat1 : TEXCOORD1;
float2 uv_Splat2 : TEXCOORD2;
float2 uv_Splat3 : TEXCOORD3;
float2 tc_Control : COLOR;
float4 _ShadowCoord : TEXCOORD4;
};
struct HS_CONSTANT_DATA_OUTPUT
{
float edge[3] : SV_TessFactor;
float inside : SV_InsideTessFactor;
};
float4 ComputeScreenPos (float4 p)
{
float4 o = p * 0.5;
o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
o.zw = p.zw;
return o;
}
VS_CONTROL_POINT_OUTPUT vertex_shader (APPDATA i)
{
VS_CONTROL_POINT_OUTPUT vs;
vs.position = i.vertex;
vs.normal = i.normal;
vs.uv_Splat0 = i.uv_Splat0;
vs.uv_Splat1 = i.uv_Splat1;
vs.uv_Splat2 = i.uv_Splat2;
vs.uv_Splat3 = i.uv_Splat3;
vs.tc_Control = i.tc_Control;
vs._ShadowCoord = ComputeScreenPos(vs.position);
return vs;
}
HS_CONSTANT_DATA_OUTPUT constantsHS (InputPatch<VS_CONTROL_POINT_OUTPUT,3> V, uint PatchID : SV_PrimitiveID)
{
HS_CONSTANT_DATA_OUTPUT output = (HS_CONSTANT_DATA_OUTPUT)0;
// float2 uv = (V[0].uv_Splat0 + V[1].uv_Splat0 + V[2].uv_Splat0) / 3.0;
float2 uv = V[0].uv_Splat0;
float factor = tex2Dlod(_TessellationMap,float4(uv,0,0)).r ;
output.edge[0] = output.edge[1] = output.edge[2] = lerp(1.0,64.0,factor);
output.inside = lerp(1.0,64.0,factor);
return output;
}
[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[patchconstantfunc("constantsHS")]
[outputcontrolpoints(3)]
VS_CONTROL_POINT_OUTPUT hull_shader (InputPatch<VS_CONTROL_POINT_OUTPUT,3> V, uint ID : SV_OutputControlPointID)
{
return V[ID];
}
[domain("tri")]
VS_CONTROL_POINT_OUTPUT domain_shader (HS_CONSTANT_DATA_OUTPUT input, const OutputPatch<VS_CONTROL_POINT_OUTPUT,3> P, float3 K : SV_DomainLocation)
{
APPDATA ds;
ds.vertex = UnityObjectToClipPos(P[0].position*K.x + P[1].position*K.y + P[2].position*K.z);
ds.normal = (P[0].normal*K.x + P[1].normal*K.y + P[2].normal*K.z);
ds.uv_Splat0 = (P[0].uv_Splat0*K.x + P[1].uv_Splat0*K.y + P[2].uv_Splat0*K.z)*_Splat0_ST.xy+_Splat0_ST.zw;
ds.uv_Splat1 = (P[0].uv_Splat1*K.x + P[1].uv_Splat1*K.y + P[2].uv_Splat1*K.z)*_Splat1_ST.xy+_Splat1_ST.zw;
ds.uv_Splat2 = (P[0].uv_Splat2*K.x + P[1].uv_Splat2*K.y + P[2].uv_Splat2*K.z)*_Splat2_ST.xy+_Splat2_ST.zw;
ds.uv_Splat3 = (P[0].uv_Splat3*K.x + P[1].uv_Splat3*K.y + P[2].uv_Splat3*K.z)*_Splat3_ST.xy+_Splat3_ST.zw;
ds.tc_Control = (P[0].uv_Splat0*K.x + P[1].uv_Splat0*K.y + P[2].uv_Splat0*K.z)*_Control_ST.xy+_Control_ST.zw;
return vertex_shader(ds);
}
float4 pixel_shader (VS_CONTROL_POINT_OUTPUT ps) : SV_Target
{
float3 normal = normalize(mul((float3x3)unity_ObjectToWorld,ps.normal));
float4 tangent = float4 (cross(ps.normal, float3(0,0,1)),-1);
tangent.xyz = normalize(mul((float3x3)unity_ObjectToWorld,tangent.xyz));
float3 binormal = normalize(cross(normal,tangent)*tangent.w);
float4 splat_control = tex2D (_Control, ps.tc_Control);
float weight = dot(splat_control, half4(1,1,1,1));
clip(weight == 0.0f ? -1 : 1);
splat_control /= (weight + 1e-3f);
float3 color = splat_control.r * tex2D (_Splat0, ps.uv_Splat0).rgb;
color += splat_control.g * tex2D (_Splat1, ps.uv_Splat1).rgb;
color += splat_control.b * tex2D (_Splat2, ps.uv_Splat2).rgb;
color += splat_control.a * tex2D (_Splat3, ps.uv_Splat3).rgb;
float4 nrm = splat_control.r * tex2D(_Normal0, ps.uv_Splat0);
nrm += splat_control.g * tex2D(_Normal1, ps.uv_Splat1);
nrm += splat_control.b * tex2D(_Normal2, ps.uv_Splat2);
nrm += splat_control.a * tex2D(_Normal3, ps.uv_Splat3);
nrm.rgb = float3(2.0*nrm.ag-1.0, 0.0);
nrm.b = sqrt(1.0 - dot(nrm.rgb,nrm.rgb));
float3x3 tbn = float3x3(tangent.xyz,binormal,normal);
float attenuation = SHADOW_ATTENUATION(ps);
float3 AmbientLight = UNITY_LIGHTMODEL_AMBIENT;
float3 LightDirection = normalize(_WorldSpaceLightPos0.xyz);
float3 LightColor = _LightColor0.xyz*attenuation;
float3 NormalDirection = normalize(mul(nrm.rgb, tbn));
float3 diffuse = max(dot(LightDirection, NormalDirection),0.0) * LightColor + AmbientLight;
return float4(color*weight*diffuse,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment