752 lines
32 KiB
HLSL
752 lines
32 KiB
HLSL
#ifndef UNIVERSAL_TERRAIN_LIT_PASSES_INCLUDED
|
||
#define UNIVERSAL_TERRAIN_LIT_PASSES_INCLUDED
|
||
|
||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
|
||
|
||
#if defined(UNITY_INSTANCING_ENABLED) && defined(_TERRAIN_INSTANCED_PERPIXEL_NORMAL)
|
||
#define ENABLE_TERRAIN_PERPIXEL_NORMAL
|
||
#endif
|
||
|
||
#ifdef UNITY_INSTANCING_ENABLED
|
||
TEXTURE2D(_TerrainHeightmapTexture);
|
||
TEXTURE2D(_TerrainNormalmapTexture);
|
||
SAMPLER(sampler_TerrainNormalmapTexture);
|
||
float4 _TerrainHeightmapRecipSize; // float4(1.0f/width, 1.0f/height, 1.0f/(width-1), 1.0f/(height-1))
|
||
float4 _TerrainHeightmapScale; // float4(hmScale.x, hmScale.y / (float)(kMaxHeight), hmScale.z, 0.0f)
|
||
#endif
|
||
|
||
|
||
UNITY_INSTANCING_BUFFER_START(Terrain)
|
||
UNITY_DEFINE_INSTANCED_PROP(float4, _TerrainPatchInstanceData) // float4(xBase, yBase, skipScale, ~)
|
||
UNITY_INSTANCING_BUFFER_END(Terrain)
|
||
|
||
struct Attributes
|
||
{
|
||
float4 positionOS : POSITION;
|
||
float3 normalOS : NORMAL;
|
||
float2 texcoord : TEXCOORD0;
|
||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||
};
|
||
|
||
struct Varyings
|
||
{
|
||
float4 uvMainAndLM : TEXCOORD0; // xy: control, zw: lightmap
|
||
#ifndef TERRAIN_SPLAT_BASEPASS
|
||
float4 uvSplat01 : TEXCOORD1; // xy: splat0, zw: splat1
|
||
float4 uvSplat23 : TEXCOORD2; // xy: splat2, zw: splat3
|
||
#endif
|
||
|
||
#if ( defined(_NORMALMAP) || defined(_PARALLAX) ) && !defined(ENABLE_TERRAIN_PERPIXEL_NORMAL)
|
||
float3 normal : TEXCOORD3; // xyz: normal, w: viewDir.x
|
||
float4 tangent : TEXCOORD4; // xyz: tangent, w: viewDir.y
|
||
float3 viewDir : TEXCOORD5;
|
||
#else
|
||
float3 normal : TEXCOORD3;
|
||
float3 viewDir : TEXCOORD4;
|
||
half3 vertexSH : TEXCOORD5; // SH
|
||
#endif
|
||
|
||
half4 fogFactorAndVertexLight : TEXCOORD6; // x: fogFactor, yzw: vertex light
|
||
float3 positionWS : TEXCOORD7;
|
||
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
|
||
float4 shadowCoord : TEXCOORD8;
|
||
#endif
|
||
float4 clipPos : SV_POSITION;
|
||
|
||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||
UNITY_VERTEX_OUTPUT_STEREO
|
||
};
|
||
|
||
// -----------------------------------------------------------------------------------------------
|
||
// Procedural Mapping
|
||
|
||
real Lux_Luminance(real3 linearRgb) {
|
||
return dot(linearRgb, real3(0.2126729, 0.7151522, 0.0721750));
|
||
}
|
||
|
||
void GetProceduralBaseSample(
|
||
Texture2D sampleTex, SamplerState samplerTex, float2 uv,
|
||
#ifdef _TERRAIN_BLEND_HEIGHT
|
||
inout half result,
|
||
#else
|
||
inout half4 result,
|
||
#endif
|
||
inout float2 uv1, inout float2 uv2, inout float2 uv3,
|
||
inout half w1, inout half w2, inout half w3, inout float2 duvdx, inout float2 duvdy)
|
||
{
|
||
float2 uvScaled = uv * 3.464 * _ProceduralScale;
|
||
const float2x2 gridToSkewedGrid = float2x2(1.0, 0.0, -0.57735027, 1.15470054);
|
||
float2 skewedCoord = mul(gridToSkewedGrid, uvScaled);
|
||
int2 baseId = int2(floor(skewedCoord));
|
||
float3 temp = float3(frac(skewedCoord), 0);
|
||
temp.z = 1.0 - temp.x - temp.y;
|
||
|
||
int2 vertex1, vertex2, vertex3;
|
||
if (temp.z > 0.0) {
|
||
w1 = temp.z;
|
||
w2 = temp.y;
|
||
w3 = temp.x;
|
||
vertex1 = baseId;
|
||
vertex2 = baseId + int2(0, 1);
|
||
vertex3 = baseId + int2(1, 0);
|
||
}
|
||
else {
|
||
w1 = -temp.z;
|
||
w2 = 1.0 - temp.y;
|
||
w3 = 1.0 - temp.x;
|
||
vertex1 = baseId + int2(1, 1);
|
||
vertex2 = baseId + int2(1, 0);
|
||
vertex3 = baseId + int2(0, 1);
|
||
}
|
||
|
||
const float2x2 hashMatrix = float2x2(127.1, 311.7, 269.5, 183.3);
|
||
const float hashFactor = 3758.5453;
|
||
uv1 = uv + frac(sin(mul(hashMatrix, (float2)vertex1)) * hashFactor);
|
||
uv2 = uv + frac(sin(mul(hashMatrix, (float2)vertex2)) * hashFactor);
|
||
uv3 = uv + frac(sin(mul(hashMatrix, (float2)vertex3)) * hashFactor);
|
||
|
||
// Use a hash function which does not include sin
|
||
// Adds a little bit visible tiling...
|
||
// float2 uv1 = uv + hash22( (float2)vertex1 );
|
||
// float2 uv2 = uv + hash22( (float2)vertex2 );
|
||
// float2 uv3 = uv + hash22( (float2)vertex3 );
|
||
|
||
duvdx = ddx(uv);
|
||
duvdy = ddy(uv);
|
||
|
||
// Here we have to sample first as we want to calculate the wights based on luminance
|
||
// Albedo – Sample Gaussion values from transformed input
|
||
#ifdef _TERRAIN_BLEND_HEIGHT
|
||
half G1 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv1, duvdx, duvdy).r;
|
||
half G2 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv2, duvdx, duvdy).r;
|
||
half G3 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv3, duvdx, duvdy).r;
|
||
// Weight by Height - somehow
|
||
w1 *= G1;
|
||
w2 *= G2;
|
||
w3 *= G3;
|
||
#else
|
||
half4 G1 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv1, duvdx, duvdy);
|
||
half4 G2 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv2, duvdx, duvdy);
|
||
half4 G3 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv3, duvdx, duvdy);
|
||
// Weight by Luminance
|
||
w1 *= Lux_Luminance(G1.rgb);
|
||
w2 *= Lux_Luminance(G2.rgb);
|
||
w3 *= Lux_Luminance(G3.rgb);
|
||
#endif
|
||
|
||
// Get weights
|
||
half exponent = 1.0h + _ProceduralBlend * 15.0h;
|
||
w1 = pow(w1, exponent);
|
||
w2 = pow(w2, exponent);
|
||
w3 = pow(w3, exponent);
|
||
|
||
// Lets help the compiler here:
|
||
half sum = 1.0h / (w1 + w2 + w3);
|
||
w1 = w1 * sum;
|
||
w2 = w2 * sum;
|
||
w3 = w3 * sum;
|
||
|
||
// Result
|
||
result = w1 * G1 + w2 * G2 + w3 * G3;
|
||
}
|
||
|
||
half4 sampleProcedural(Texture2D sampleTex, SamplerState samplerTex, float2 uv, float2 uv1, float2 uv2, float2 uv3, half w1, half w2, half w3, float2 duvdx, float2 duvdy) {
|
||
|
||
half4 G1 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv1, duvdx, duvdy);
|
||
half4 G2 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv2, duvdx, duvdy);
|
||
half4 G3 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv3, duvdx, duvdy);
|
||
|
||
return w1 * G1 + w2 * G2 + w3 * G3;
|
||
}
|
||
|
||
half1 sampleProceduralHalf(Texture2D sampleTex, SamplerState samplerTex, float2 uv, float2 uv1, float2 uv2, float2 uv3, half w1, half w2, half w3, float2 duvdx, float2 duvdy) {
|
||
|
||
half G1 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv1, duvdx, duvdy).r;
|
||
half G2 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv2, duvdx, duvdy).r;
|
||
half G3 = SAMPLE_TEXTURE2D_GRAD(sampleTex, samplerTex, uv3, duvdx, duvdy).r;
|
||
|
||
return w1 * G1 + w2 * G2 + w3 * G3;
|
||
}
|
||
|
||
|
||
// -----------------------------------------------------------------------------------------------
|
||
|
||
|
||
|
||
// ---------------------------
|
||
|
||
void InitializeInputData(Varyings IN, half3 normalTS, half3x3 tangentSpaceRotation, half3 viewDirWS, out InputData input)
|
||
{
|
||
input = (InputData)0;
|
||
|
||
input.positionWS = IN.positionWS;
|
||
half3 SH = half3(0, 0, 0);
|
||
|
||
// Most of this is passed in
|
||
/*
|
||
#if ( defined(_NORMALMAP) || defined (_PARALLAX) ) && !defined(ENABLE_TERRAIN_PERPIXEL_NORMAL)
|
||
//half3 viewDirWS = half3(IN.normal.w, IN.tangent.w, IN.bitangent.w);
|
||
half3 viewDirWS = IN.viewDir;
|
||
float sgn = input.tangentWS.w; // should be either +1 or -1
|
||
float3 bitangent = sgn * cross(IN.normalWS.xyz, IN.tangentWS.xyz);
|
||
input.normalWS = TransformTangentToWorld(normalTS, half3x3(IN.tangent.xyz, bitangent, IN.normal.xyz));
|
||
SH = SampleSH(input.normalWS.xyz);
|
||
|
||
#elif defined(ENABLE_TERRAIN_PERPIXEL_NORMAL)
|
||
half3 viewDirWS = IN.viewDir;
|
||
float2 sampleCoords = (IN.uvMainAndLM.xy / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy;
|
||
half3 normalWS = TransformObjectToWorldNormal(normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, sampler_TerrainNormalmapTexture, sampleCoords).rgb * 2 - 1));
|
||
|
||
// fix orientation
|
||
half3 tangentWS = cross(GetObjectToWorldMatrix()._13_23_33, normalWS) * -1;
|
||
input.normalWS = TransformTangentToWorld(normalTS, half3x3(tangentWS, -cross(normalWS, tangentWS), normalWS));
|
||
|
||
SH = SampleSH(input.normalWS.xyz);
|
||
#else
|
||
half3 viewDirWS = IN.viewDir;
|
||
input.normalWS = IN.normal;
|
||
SH = IN.vertexSH;
|
||
#endif
|
||
#if SHADER_HINT_NICE_QUALITY
|
||
viewDirWS = SafeNormalize(viewDirWS);
|
||
#endif
|
||
*/
|
||
|
||
// So this is all that has to be done
|
||
#if defined(_NORMALMAP) || defined (_PARALLAX) || defined(ENABLE_TERRAIN_PERPIXEL_NORMAL)
|
||
input.normalWS = TransformTangentToWorld(normalTS, tangentSpaceRotation);
|
||
SH = SampleSH(input.normalWS.xyz);
|
||
#else
|
||
input.normalWS = IN.normal;
|
||
SH = IN.vertexSH;
|
||
#endif
|
||
|
||
input.normalWS = NormalizeNormalPerPixel(input.normalWS);
|
||
input.viewDirectionWS = viewDirWS;
|
||
|
||
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
|
||
input.shadowCoord = IN.shadowCoord;
|
||
#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)
|
||
input.shadowCoord = TransformWorldToShadowCoord(input.positionWS);
|
||
#else
|
||
input.shadowCoord = float4(0, 0, 0, 0);
|
||
#endif
|
||
|
||
input.fogCoord = IN.fogFactorAndVertexLight.x;
|
||
input.vertexLighting = IN.fogFactorAndVertexLight.yzw;
|
||
input.bakedGI = SAMPLE_GI(IN.uvMainAndLM.zw, SH, input.normalWS);
|
||
}
|
||
|
||
|
||
#ifdef _TERRAIN_BLEND_HEIGHT
|
||
void HeightBasedSplatModifyCombined(inout half4 splatControl, in half4 heights, inout half height) {
|
||
#ifndef TERRAIN_SPLAT_ADDPASS // disable for multi-pass
|
||
half4 defaultHeight = heights;
|
||
half4 mweights = splatControl * max(defaultHeight, 1e-5);
|
||
// Go parallel
|
||
half maxWeight = max( max(mweights.x, mweights.y), max(mweights.z, mweights.w) );
|
||
half mtransition = max(_HeightTransition * maxWeight, 1e-5);
|
||
half mthreshold = maxWeight - mtransition;
|
||
half mscale = 1.0h / mtransition;
|
||
mweights = saturate((mweights - mthreshold ) / mtransition); // * mscale );
|
||
half sumHeight = mweights.x + mweights.y + mweights.z + mweights.w;
|
||
half sumSplat = splatControl.x+splatControl.y+splatControl.z+splatControl.w;
|
||
|
||
splatControl = mweights / sumHeight;
|
||
// Must not get more than before...
|
||
splatControl *= sumSplat;
|
||
height = maxWeight;
|
||
#endif
|
||
}
|
||
#endif
|
||
|
||
|
||
// Splatting ----------------------------------------------
|
||
|
||
#ifndef TERRAIN_SPLAT_BASEPASS
|
||
|
||
void SplatmapMix(float4 uvMainAndLM, float4 uvSplat01, float4 uvSplat23, inout half4 splatControl, out half weight, out half4 mixedDiffuse, out half4 defaultSmoothness, inout half3 mixedNormal)
|
||
{
|
||
|
||
// Sample albedo and smoothness
|
||
half4 diffAlbedo[4];
|
||
|
||
// We may use procedural texturing but do not use height based blending
|
||
#if defined(_PROCEDURALTEXTURING) && !defined(_TERRAIN_BLEND_HEIGHT)
|
||
half4 diffAlbedoNull;
|
||
float2 uv1, uv2, uv3;
|
||
half w1, w2, w3;
|
||
float2 duvdx, duvdy;
|
||
GetProceduralBaseSample(_Splat0, sampler_Splat0, uvSplat01.xy, diffAlbedoNull, uv1, uv2, uv3, w1, w2, w3, duvdx, duvdy);
|
||
diffAlbedo[0] = diffAlbedoNull;
|
||
#else
|
||
diffAlbedo[0] = SAMPLE_TEXTURE2D(_Splat0, sampler_Splat0, uvSplat01.xy);
|
||
#endif
|
||
diffAlbedo[1] = SAMPLE_TEXTURE2D(_Splat1, sampler_Splat0, uvSplat01.zw);
|
||
diffAlbedo[2] = SAMPLE_TEXTURE2D(_Splat2, sampler_Splat0, uvSplat23.xy);
|
||
diffAlbedo[3] = SAMPLE_TEXTURE2D(_Splat3, sampler_Splat0, uvSplat23.zw);
|
||
|
||
defaultSmoothness = half4(diffAlbedo[0].a, diffAlbedo[1].a, diffAlbedo[2].a, diffAlbedo[3].a);
|
||
defaultSmoothness *= half4(_Smoothness0, _Smoothness1, _Smoothness2, _Smoothness3);
|
||
|
||
// Now that splatControl has changed, we can compute the final weight and normalize
|
||
weight = dot(splatControl, 1.0h);
|
||
|
||
#ifdef TERRAIN_SPLAT_ADDPASS
|
||
clip(weight <= 0.005h ? -1.0h : 1.0h);
|
||
#endif
|
||
|
||
#ifndef _TERRAIN_BASEMAP_GEN
|
||
// Normalize weights before lighting and restore weights in final modifier functions so that the overal
|
||
// lighting result can be correctly weighted.
|
||
splatControl /= (weight + HALF_MIN);
|
||
#endif
|
||
|
||
mixedDiffuse = 0.0h;
|
||
mixedDiffuse += diffAlbedo[0] * half4(_DiffuseRemapScale0.rgb * splatControl.rrr, 1.0h);
|
||
mixedDiffuse += diffAlbedo[1] * half4(_DiffuseRemapScale1.rgb * splatControl.ggg, 1.0h);
|
||
mixedDiffuse += diffAlbedo[2] * half4(_DiffuseRemapScale2.rgb * splatControl.bbb, 1.0h);
|
||
mixedDiffuse += diffAlbedo[3] * half4(_DiffuseRemapScale3.rgb * splatControl.aaa, 1.0h);
|
||
|
||
#ifdef _NORMALMAP
|
||
half4 normalSamples[4];
|
||
#if defined(_PROCEDURALTEXTURING) && !defined(_TERRAIN_BLEND_HEIGHT)
|
||
normalSamples[0] = sampleProcedural(_Normal0, sampler_Normal0, uvSplat01.xy, uv1, uv2, uv3, w1, w2, w3, duvdx, duvdy);
|
||
#else
|
||
normalSamples[0] = SAMPLE_TEXTURE2D(_Normal0, sampler_Normal0, uvSplat01.xy);
|
||
#endif
|
||
normalSamples[1] = SAMPLE_TEXTURE2D(_Normal1, sampler_Normal0, uvSplat01.zw);
|
||
normalSamples[2] = SAMPLE_TEXTURE2D(_Normal2, sampler_Normal0, uvSplat23.xy);
|
||
normalSamples[3] = SAMPLE_TEXTURE2D(_Normal3, sampler_Normal0, uvSplat23.zw);
|
||
|
||
half4 normalSample = 0;
|
||
normalSample = splatControl.r * normalSamples[0];
|
||
normalSample += splatControl.g * normalSamples[1];
|
||
normalSample += splatControl.b * normalSamples[2];
|
||
normalSample += splatControl.a * normalSamples[3];
|
||
|
||
half3 nrm = 0.0f;
|
||
#if BUMP_SCALE_NOT_SUPPORTED
|
||
nrm = UnpackNormal(normalSample);
|
||
#else
|
||
half normalScale = dot(half4(_NormalScale0, _NormalScale1, _NormalScale2, _NormalScale3), splatControl);
|
||
nrm = UnpackNormalScale(normalSample, normalScale);
|
||
#endif
|
||
|
||
// avoid risk of NaN when normalizing.
|
||
#if HAS_HALF
|
||
nrm.z += 0.01h;
|
||
#else
|
||
nrm.z += 1e-5f;
|
||
#endif
|
||
mixedNormal = normalize(nrm.xyz);
|
||
#endif
|
||
|
||
//mixedDiffuse = height.xxxx;
|
||
}
|
||
|
||
|
||
|
||
|
||
void SplatmapMixProcedural(float4 uvMainAndLM, float4 uvSplat01, float4 uvSplat23, inout half4 splatControl, out half weight, out half4 mixedDiffuse, out half4 defaultSmoothness, inout half3 mixedNormal,
|
||
float2 uv1, float2 uv2, float2 uv3, half w1, half w2, half w3, float2 duvdx, float2 duvdy)
|
||
{
|
||
|
||
// Sample albedo and smoothness
|
||
half4 diffAlbedo[4];
|
||
diffAlbedo[0] = sampleProcedural(_Splat0, sampler_Splat0, uvSplat01.xy, uv1, uv2, uv3, w1, w2, w3, duvdx, duvdy);
|
||
diffAlbedo[1] = SAMPLE_TEXTURE2D(_Splat1, sampler_Splat0, uvSplat01.zw);
|
||
diffAlbedo[2] = SAMPLE_TEXTURE2D(_Splat2, sampler_Splat0, uvSplat23.xy);
|
||
diffAlbedo[3] = SAMPLE_TEXTURE2D(_Splat3, sampler_Splat0, uvSplat23.zw);
|
||
|
||
defaultSmoothness = half4(diffAlbedo[0].a, diffAlbedo[1].a, diffAlbedo[2].a, diffAlbedo[3].a);
|
||
defaultSmoothness *= half4(_Smoothness0, _Smoothness1, _Smoothness2, _Smoothness3);
|
||
|
||
// Now that splatControl has changed, we can compute the final weight and normalize
|
||
weight = dot(splatControl, 1.0h);
|
||
|
||
#ifdef TERRAIN_SPLAT_ADDPASS
|
||
clip(weight <= 0.005h ? -1.0h : 1.0h);
|
||
#endif
|
||
|
||
#ifndef _TERRAIN_BASEMAP_GEN
|
||
// Normalize weights before lighting and restore weights in final modifier functions so that the overal
|
||
// lighting result can be correctly weighted.
|
||
splatControl /= (weight + HALF_MIN);
|
||
#endif
|
||
|
||
mixedDiffuse = 0.0h;
|
||
mixedDiffuse += diffAlbedo[0] * half4(_DiffuseRemapScale0.rgb * splatControl.rrr, 1.0h);
|
||
mixedDiffuse += diffAlbedo[1] * half4(_DiffuseRemapScale1.rgb * splatControl.ggg, 1.0h);
|
||
mixedDiffuse += diffAlbedo[2] * half4(_DiffuseRemapScale2.rgb * splatControl.bbb, 1.0h);
|
||
mixedDiffuse += diffAlbedo[3] * half4(_DiffuseRemapScale3.rgb * splatControl.aaa, 1.0h);
|
||
|
||
#ifdef _NORMALMAP
|
||
half4 normalSamples[4];
|
||
normalSamples[0] = sampleProcedural(_Normal0, sampler_Normal0, uvSplat01.xy, uv1, uv2, uv3, w1, w2, w3, duvdx, duvdy);
|
||
normalSamples[1] = SAMPLE_TEXTURE2D(_Normal1, sampler_Normal0, uvSplat01.zw);
|
||
normalSamples[2] = SAMPLE_TEXTURE2D(_Normal2, sampler_Normal0, uvSplat23.xy);
|
||
normalSamples[3] = SAMPLE_TEXTURE2D(_Normal3, sampler_Normal0, uvSplat23.zw);
|
||
|
||
half4 normalSample = 0;
|
||
normalSample = splatControl.r * normalSamples[0];
|
||
normalSample += splatControl.g * normalSamples[1];
|
||
normalSample += splatControl.b * normalSamples[2];
|
||
normalSample += splatControl.a * normalSamples[3];
|
||
|
||
half3 nrm = 0.0f;
|
||
#if BUMP_SCALE_NOT_SUPPORTED
|
||
nrm = UnpackNormal(normalSample);
|
||
#else
|
||
half normalScale = dot(half4(_NormalScale0, _NormalScale1, _NormalScale2, _NormalScale3), splatControl);
|
||
nrm = UnpackNormalScale(normalSample, normalScale);
|
||
#endif
|
||
|
||
// avoid risk of NaN when normalizing.
|
||
#if HAS_HALF
|
||
nrm.z += 0.01h;
|
||
#else
|
||
nrm.z += 1e-5f;
|
||
#endif
|
||
mixedNormal = normalize(nrm.xyz);
|
||
#endif
|
||
}
|
||
#endif
|
||
|
||
void SplatmapFinalColor(inout half4 color, half fogCoord)
|
||
{
|
||
color.rgb *= color.a;
|
||
#ifdef TERRAIN_SPLAT_ADDPASS
|
||
color.rgb = MixFogColor(color.rgb, half3(0,0,0), fogCoord);
|
||
#else
|
||
color.rgb = MixFog(color.rgb, fogCoord);
|
||
#endif
|
||
}
|
||
|
||
void TerrainInstancing(inout float4 positionOS, inout float3 normal, inout float2 uv)
|
||
{
|
||
#ifdef UNITY_INSTANCING_ENABLED
|
||
float2 patchVertex = positionOS.xy;
|
||
float4 instanceData = UNITY_ACCESS_INSTANCED_PROP(Terrain, _TerrainPatchInstanceData);
|
||
|
||
float2 sampleCoords = (patchVertex.xy + instanceData.xy) * instanceData.z;
|
||
float height = UnpackHeightmap(_TerrainHeightmapTexture.Load(int3(sampleCoords, 0)));
|
||
|
||
positionOS.xz = sampleCoords * _TerrainHeightmapScale.xz;
|
||
positionOS.y = height * _TerrainHeightmapScale.y;
|
||
|
||
#ifdef ENABLE_TERRAIN_PERPIXEL_NORMAL
|
||
normal = float3(0, 1, 0);
|
||
#else
|
||
normal = _TerrainNormalmapTexture.Load(int3(sampleCoords, 0)).rgb * 2 - 1;
|
||
#endif
|
||
uv = sampleCoords * _TerrainHeightmapRecipSize.zw;
|
||
#endif
|
||
}
|
||
|
||
void TerrainInstancing(inout float4 positionOS, inout float3 normal)
|
||
{
|
||
float2 uv = { 0, 0 };
|
||
TerrainInstancing(positionOS, normal, uv);
|
||
}
|
||
|
||
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
// Vertex and Fragment functions //
|
||
///////////////////////////////////////////////////////////////////////////////
|
||
|
||
// Used in Standard Terrain shader
|
||
Varyings SplatmapVert(Attributes v)
|
||
{
|
||
Varyings o = (Varyings)0;
|
||
UNITY_SETUP_INSTANCE_ID(v);
|
||
//UNITY_TRANSFER_INSTANCE_ID(v, o);
|
||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
|
||
|
||
TerrainInstancing(v.positionOS, v.normalOS, v.texcoord);
|
||
VertexPositionInputs Attributes = GetVertexPositionInputs(v.positionOS.xyz);
|
||
|
||
o.uvMainAndLM.xy = v.texcoord;
|
||
o.uvMainAndLM.zw = v.texcoord * unity_LightmapST.xy + unity_LightmapST.zw;
|
||
#ifndef TERRAIN_SPLAT_BASEPASS
|
||
#if defined(_PROCEDURALTEXTURING)
|
||
o.uvSplat01.xy = Attributes.positionWS.xz / _ProceduralTiling;
|
||
#else
|
||
o.uvSplat01.xy = TRANSFORM_TEX(v.texcoord, _Splat0);
|
||
#endif
|
||
o.uvSplat01.zw = TRANSFORM_TEX(v.texcoord, _Splat1);
|
||
o.uvSplat23.xy = TRANSFORM_TEX(v.texcoord, _Splat2);
|
||
o.uvSplat23.zw = TRANSFORM_TEX(v.texcoord, _Splat3);
|
||
#endif
|
||
|
||
float3 viewDirWS = GetCameraPositionWS() - Attributes.positionWS;
|
||
#if !SHADER_HINT_NICE_QUALITY
|
||
viewDirWS = SafeNormalize(viewDirWS);
|
||
#endif
|
||
|
||
#if ( defined(_NORMALMAP) || defined(_PARALLAX) ) && !defined(ENABLE_TERRAIN_PERPIXEL_NORMAL)
|
||
float4 vertexTangent = float4(cross(float3(0, 0, 1), v.normalOS), 1.0);
|
||
VertexNormalInputs normalInput = GetVertexNormalInputs(v.normalOS, vertexTangent);
|
||
// fix orientation
|
||
normalInput.tangentWS *= -1;
|
||
o.normal = normalInput.normalWS;
|
||
float sign = vertexTangent.w * GetOddNegativeScale();
|
||
o.tangent = float4(normalInput.tangentWS, sign);
|
||
#else
|
||
o.normal = TransformObjectToWorldNormal(v.normalOS);
|
||
o.vertexSH = SampleSH(o.normal);
|
||
#endif
|
||
o.viewDir = viewDirWS;
|
||
|
||
o.fogFactorAndVertexLight.x = ComputeFogFactor(Attributes.positionCS.z);
|
||
o.fogFactorAndVertexLight.yzw = VertexLighting(Attributes.positionWS, o.normal.xyz);
|
||
o.positionWS = Attributes.positionWS;
|
||
o.clipPos = Attributes.positionCS;
|
||
|
||
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
|
||
o.shadowCoord = GetShadowCoord(Attributes);
|
||
#endif
|
||
|
||
return o;
|
||
}
|
||
|
||
// Used in Standard Terrain shader
|
||
half4 SplatmapFragment(Varyings IN) : SV_TARGET {
|
||
//UNITY_SETUP_INSTANCE_ID(IN);
|
||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN);
|
||
|
||
#ifdef _ALPHATEST_ON
|
||
half hole = SAMPLE_TEXTURE2D(_TerrainHolesTexture, sampler_TerrainHolesTexture, IN.uvMainAndLM.xy).r;
|
||
clip(hole == 0.0h ? -1 : 1);
|
||
#endif
|
||
|
||
half3 normalTS = half3(0.0h, 0.0h, 1.0h);
|
||
|
||
half3x3 tangentSpaceRotation = 0;
|
||
half3 viewDirectionWS = SafeNormalize(IN.viewDir);
|
||
|
||
#if defined(_NORMALMAP) || defined(_PARALLAX) || defined(TERRAIN_SPLAT_BASEPASS)
|
||
#if !defined(ENABLE_TERRAIN_PERPIXEL_NORMAL) && ( defined(_NORMALMAP) || defined(_PARALLAX) )
|
||
// Same matrix we need to transfer the normalTS
|
||
half3 bitangentWS = cross(IN.normal, IN.tangent.xyz) * -1;
|
||
tangentSpaceRotation = half3x3(IN.tangent.xyz, bitangentWS, IN.normal.xyz);
|
||
half3 tangentWS = IN.tangent.xyz;
|
||
half3 viewDirTS = normalize( mul(tangentSpaceRotation, viewDirectionWS ) );
|
||
#elif defined(ENABLE_TERRAIN_PERPIXEL_NORMAL)
|
||
float2 sampleCoords = (IN.uvMainAndLM.xy / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy;
|
||
half3 normalWS = TransformObjectToWorldNormal(normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, sampler_TerrainNormalmapTexture, sampleCoords).rgb * 2 - 1));
|
||
// fix orientation
|
||
half3 tangentWS = cross( /*GetObjectToWorldMatrix()._13_23_33*/ half3(0, 0, 1), normalWS) * -1;
|
||
// Ups: * -1?
|
||
half3 bitangentWS = cross(normalWS, tangentWS) * -1;
|
||
tangentSpaceRotation = half3x3(tangentWS, bitangentWS, normalWS);
|
||
half3 viewDirTS = normalize( mul(tangentSpaceRotation, viewDirectionWS) );
|
||
#endif
|
||
#endif
|
||
|
||
#ifdef TERRAIN_SPLAT_BASEPASS
|
||
half3 albedo = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uvMainAndLM.xy).rgb;
|
||
half smoothness = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uvMainAndLM.xy).a;
|
||
// Unity 2019.1
|
||
half metallic = 0; //SAMPLE_TEXTURE2D(_MetallicTex, sampler_MetallicTex, IN.uvMainAndLM.xy).r;
|
||
half alpha = 1;
|
||
half occlusion = 1;
|
||
|
||
#else
|
||
half4 splatControl;
|
||
half weight;
|
||
half4 mixedDiffuse;
|
||
half4 defaultSmoothness;
|
||
|
||
float2 splatUV = (IN.uvMainAndLM.xy * (_Control_TexelSize.zw - 1.0f) + 0.5f) * _Control_TexelSize.xy;
|
||
splatControl = SAMPLE_TEXTURE2D(_Control, sampler_Control, splatUV);
|
||
|
||
// Sample heights
|
||
#ifdef _TERRAIN_BLEND_HEIGHT
|
||
half4 heights;
|
||
#if defined(_PROCEDURALTEXTURING)
|
||
half proceduralHeight;
|
||
float2 uv1, uv2, uv3;
|
||
half w1, w2, w3;
|
||
float2 duvdx, duvdy;
|
||
GetProceduralBaseSample(_HeightMaps, sampler_Splat0, IN.uvSplat01.xy, proceduralHeight, uv1, uv2, uv3, w1, w2, w3, duvdx, duvdy);
|
||
heights.x = proceduralHeight;
|
||
#else
|
||
heights.x = SAMPLE_TEXTURE2D(_HeightMaps, sampler_Splat0, IN.uvSplat01.xy).r;
|
||
#endif
|
||
heights.y = SAMPLE_TEXTURE2D(_HeightMaps, sampler_Splat0, IN.uvSplat01.zw).g;
|
||
heights.z = SAMPLE_TEXTURE2D(_HeightMaps, sampler_Splat0, IN.uvSplat23.xy).b;
|
||
heights.w = SAMPLE_TEXTURE2D(_HeightMaps, sampler_Splat0, IN.uvSplat23.zw).a;
|
||
|
||
half height;
|
||
// Adjust spaltControl and calculate 1st height
|
||
HeightBasedSplatModifyCombined(splatControl, heights, height);
|
||
|
||
// Parallax Extrusion
|
||
#if defined(_PARALLAX)
|
||
float3 v = viewDirTS;
|
||
v.z += 0.42;
|
||
v.xy /= v.z;
|
||
half halfParallax = _Parallax * 0.5h;
|
||
|
||
half parallax = height * _Parallax - halfParallax;
|
||
float2 offset1 = parallax * v.xy;
|
||
|
||
float4 splatUV1 = IN.uvSplat01 + offset1.xyxy;
|
||
float4 splatUV2 = IN.uvSplat23 + offset1.xyxy;
|
||
|
||
#if defined(_PROCEDURALTEXTURING)
|
||
uv1 += offset1.xy;
|
||
uv2 += offset1.xy;
|
||
uv2 += offset1.xy;
|
||
heights.x = sampleProceduralHalf(_HeightMaps, sampler_Splat0, splatUV1.xy, uv1, uv2, uv3, w1, w2, w3, duvdx, duvdy);
|
||
#else
|
||
heights.x = SAMPLE_TEXTURE2D(_HeightMaps, sampler_Splat0, splatUV1.xy).r;
|
||
#endif
|
||
heights.y = SAMPLE_TEXTURE2D(_HeightMaps, sampler_Splat0, splatUV1.zw).g;
|
||
heights.z = SAMPLE_TEXTURE2D(_HeightMaps, sampler_Splat0, splatUV2.xy).b;
|
||
heights.w = SAMPLE_TEXTURE2D(_HeightMaps, sampler_Splat0, splatUV2.zw).a;
|
||
|
||
// Calculate 2nd height
|
||
half height1 = max( max(heights.x, heights.y), max(heights.z, heights.w) );
|
||
parallax = height1 * _Parallax - halfParallax;
|
||
float2 offset2 = parallax * v.xy;
|
||
|
||
offset1 = (offset1 + offset2) * 0.5;
|
||
IN.uvSplat01 = IN.uvSplat01 + offset1.xyxy * float4(1,1,1,1);
|
||
IN.uvSplat23 = IN.uvSplat23 + offset1.xyxy * float4(1,1,1,1);
|
||
|
||
#endif
|
||
#endif
|
||
|
||
#if defined(_PROCEDURALTEXTURING)
|
||
#ifdef _TERRAIN_BLEND_HEIGHT
|
||
SplatmapMixProcedural(IN.uvMainAndLM, IN.uvSplat01, IN.uvSplat23, splatControl, weight, mixedDiffuse, defaultSmoothness, normalTS,
|
||
uv1, uv2, uv3, w1, w2, w3, duvdx, duvdy);
|
||
#else
|
||
SplatmapMix(IN.uvMainAndLM, IN.uvSplat01, IN.uvSplat23, splatControl, weight, mixedDiffuse, defaultSmoothness, normalTS);
|
||
#endif
|
||
#else
|
||
SplatmapMix(IN.uvMainAndLM, IN.uvSplat01, IN.uvSplat23, splatControl, weight, mixedDiffuse, defaultSmoothness, normalTS);
|
||
#endif
|
||
|
||
half3 albedo = mixedDiffuse.rgb;
|
||
// Looks broken...
|
||
defaultSmoothness *= dot(half4(_Smoothness0, _Smoothness1, _Smoothness2, _Smoothness3), splatControl);
|
||
half smoothness = dot(defaultSmoothness, splatControl);
|
||
half metallic = dot(half4(_Metallic0, _Metallic1, _Metallic2, _Metallic3), splatControl);
|
||
half occlusion = 1;
|
||
half alpha = weight;
|
||
#endif
|
||
|
||
InputData inputData;
|
||
InitializeInputData(IN, normalTS, tangentSpaceRotation, viewDirectionWS, inputData);
|
||
|
||
half4 color = UniversalFragmentPBR(inputData, albedo, metallic, /* specular */ half3(0.0h, 0.0h, 0.0h), smoothness, occlusion, /* emission */ half3(0, 0, 0), alpha);
|
||
|
||
SplatmapFinalColor(color, inputData.fogCoord);
|
||
return half4(color.rgb, 1.0h);
|
||
}
|
||
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// Shadow pass
|
||
|
||
// x: global clip space bias, y: normal world space bias
|
||
float3 _LightDirection;
|
||
|
||
struct AttributesLean {
|
||
float4 positionOS : POSITION;
|
||
float3 normalOS : NORMAL;
|
||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||
UNITY_VERTEX_OUTPUT_STEREO
|
||
};
|
||
|
||
#ifdef _ALPHATEST_ON
|
||
Varyings ShadowPassVertex (Attributes v)
|
||
{
|
||
Varyings o = (Varyings)0;
|
||
UNITY_SETUP_INSTANCE_ID(v);
|
||
TerrainInstancing(v.positionOS, v.normalOS, v.texcoord);
|
||
o.uvMainAndLM.xy = v.texcoord;
|
||
float3 positionWS = TransformObjectToWorld(v.positionOS.xyz);
|
||
float3 normalWS = TransformObjectToWorldNormal(v.normalOS);
|
||
float4 clipPos = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, _LightDirection));
|
||
#if UNITY_REVERSED_Z
|
||
clipPos.z = min(clipPos.z, clipPos.w * UNITY_NEAR_CLIP_VALUE);
|
||
#else
|
||
clipPos.z = max(clipPos.z, clipPos.w * UNITY_NEAR_CLIP_VALUE);
|
||
#endif
|
||
o.clipPos = clipPos;
|
||
return o;
|
||
}
|
||
half4 ShadowPassFragment(Varyings IN) : SV_TARGET {
|
||
//ClipHoles(IN.tc.xy);
|
||
half hole = SAMPLE_TEXTURE2D(_TerrainHolesTexture, sampler_TerrainHolesTexture, IN.uvMainAndLM.xy).r;
|
||
clip(hole == 0.0h ? -1 : 1);
|
||
return 0;
|
||
}
|
||
#else
|
||
float4 ShadowPassVertex(AttributesLean v) : SV_POSITION {
|
||
Varyings o;
|
||
UNITY_SETUP_INSTANCE_ID(v);
|
||
TerrainInstancing(v.positionOS, v.normalOS);
|
||
float3 positionWS = TransformObjectToWorld(v.positionOS.xyz);
|
||
float3 normalWS = TransformObjectToWorldNormal(v.normalOS);
|
||
float4 clipPos = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, _LightDirection));
|
||
#if UNITY_REVERSED_Z
|
||
clipPos.z = min(clipPos.z, clipPos.w * UNITY_NEAR_CLIP_VALUE);
|
||
#else
|
||
clipPos.z = max(clipPos.z, clipPos.w * UNITY_NEAR_CLIP_VALUE);
|
||
#endif
|
||
return clipPos;
|
||
}
|
||
half4 ShadowPassFragment(Varyings IN) : SV_TARGET {
|
||
return 0;
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// Depth pass
|
||
|
||
//
|
||
#ifdef _ALPHATEST_ON
|
||
Varyings DepthOnlyVertex(Attributes v) {
|
||
Varyings o = (Varyings)0;
|
||
UNITY_SETUP_INSTANCE_ID(v);
|
||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
|
||
TerrainInstancing(v.positionOS, v.normalOS, v.texcoord);
|
||
o.uvMainAndLM.xy = v.texcoord;
|
||
o.clipPos = TransformObjectToHClip(v.positionOS.xyz);
|
||
return o;
|
||
}
|
||
|
||
half4 DepthOnlyFragment(Varyings IN) : SV_TARGET {
|
||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
|
||
//ClipHoles(IN.tc.xy);
|
||
half hole = SAMPLE_TEXTURE2D(_TerrainHolesTexture, sampler_TerrainHolesTexture, IN.uvMainAndLM.xy).r;
|
||
clip(hole == 0.0h ? -1 : 1);
|
||
return 0;
|
||
}
|
||
|
||
#else
|
||
//float4 DepthOnlyVertex(AttributesLean v) : SV_POSITION {
|
||
Varyings DepthOnlyVertex(AttributesLean v) {
|
||
Varyings o = (Varyings)0;
|
||
UNITY_SETUP_INSTANCE_ID(v);
|
||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
|
||
TerrainInstancing(v.positionOS, v.normalOS);
|
||
//return TransformObjectToHClip(v.positionOS.xyz);
|
||
o.clipPos = TransformObjectToHClip(v.positionOS.xyz);
|
||
return o;
|
||
}
|
||
|
||
half4 DepthOnlyFragment(Varyings IN) : SV_TARGET {
|
||
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
|
||
return 0;
|
||
}
|
||
#endif
|
||
|
||
#endif |