您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
398 行
16 KiB
398 行
16 KiB
#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(ENABLE_TERRAIN_PERPIXEL_NORMAL)
|
|
float4 normal : TEXCOORD3; // xyz: normal, w: viewDir.x
|
|
float4 tangent : TEXCOORD4; // xyz: tangent, w: viewDir.y
|
|
float4 bitangent : TEXCOORD5; // xyz: bitangent, w: viewDir.z
|
|
#else
|
|
float3 normal : TEXCOORD3;
|
|
float3 viewDir : TEXCOORD4;
|
|
half3 vertexSH : TEXCOORD5; // SH
|
|
#endif
|
|
|
|
half4 fogFactorAndVertexLight : TEXCOORD6; // x: fogFactor, yzw: vertex light
|
|
float3 positionWS : TEXCOORD7;
|
|
float4 shadowCoord : TEXCOORD8;
|
|
float4 clipPos : SV_POSITION;
|
|
};
|
|
|
|
void InitializeInputData(Varyings IN, half3 normalTS, out InputData input)
|
|
{
|
|
input = (InputData)0;
|
|
|
|
input.positionWS = IN.positionWS;
|
|
half3 SH = half3(0, 0, 0);
|
|
|
|
#if defined(_NORMALMAP) && !defined(ENABLE_TERRAIN_PERPIXEL_NORMAL)
|
|
half3 viewDirWS = half3(IN.normal.w, IN.tangent.w, IN.bitangent.w);
|
|
input.normalWS = TransformTangentToWorld(normalTS, half3x3(IN.tangent.xyz, IN.bitangent.xyz, 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));
|
|
half3 tangentWS = cross(GetObjectToWorldMatrix()._13_23_33, normalWS);
|
|
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
|
|
|
|
input.normalWS = NormalizeNormalPerPixel(input.normalWS);
|
|
|
|
input.viewDirectionWS = viewDirWS;
|
|
#ifdef _MAIN_LIGHT_SHADOWS
|
|
input.shadowCoord = IN.shadowCoord;
|
|
#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);
|
|
}
|
|
|
|
#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)
|
|
{
|
|
half4 diffAlbedo[4];
|
|
|
|
diffAlbedo[0] = SAMPLE_TEXTURE2D(_Splat0, sampler_Splat0, uvSplat01.xy);
|
|
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);
|
|
|
|
// This might be a bit of a gamble -- the assumption here is that if the diffuseMap has no
|
|
// alpha channel, then diffAlbedo[n].a = 1.0 (and _DiffuseHasAlphaN = 0.0)
|
|
// Prior to coming in, _SmoothnessN is actually set to max(_DiffuseHasAlphaN, _SmoothnessN)
|
|
// This means that if we have an alpha channel, _SmoothnessN is locked to 1.0 and
|
|
// otherwise, the true slider value is passed down and diffAlbedo[n].a == 1.0.
|
|
defaultSmoothness = half4(diffAlbedo[0].a, diffAlbedo[1].a, diffAlbedo[2].a, diffAlbedo[3].a);
|
|
defaultSmoothness *= half4(_Smoothness0, _Smoothness1, _Smoothness2, _Smoothness3);
|
|
|
|
#ifndef _TERRAIN_BLEND_HEIGHT
|
|
// 20.0 is the number of steps in inputAlphaMask (Density mask. We decided 20 empirically)
|
|
half4 opacityAsDensity = saturate((half4(diffAlbedo[0].a, diffAlbedo[1].a, diffAlbedo[2].a, diffAlbedo[3].a) - (half4(1.0, 1.0, 1.0, 1.0) - splatControl)) * 20.0);
|
|
opacityAsDensity += 0.001h * splatControl; // if all weights are zero, default to what the blend mask says
|
|
half4 useOpacityAsDensityParam = { _DiffuseRemapScale0.w, _DiffuseRemapScale1.w, _DiffuseRemapScale2.w, _DiffuseRemapScale3.w }; // 1 is off
|
|
splatControl = lerp(opacityAsDensity, splatControl, useOpacityAsDensityParam);
|
|
#endif
|
|
|
|
// 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
|
|
half3 nrm = 0.0f;
|
|
nrm += splatControl.r * UnpackNormalScale(SAMPLE_TEXTURE2D(_Normal0, sampler_Normal0, uvSplat01.xy), _NormalScale0);
|
|
nrm += splatControl.g * UnpackNormalScale(SAMPLE_TEXTURE2D(_Normal1, sampler_Normal0, uvSplat01.zw), _NormalScale1);
|
|
nrm += splatControl.b * UnpackNormalScale(SAMPLE_TEXTURE2D(_Normal2, sampler_Normal0, uvSplat23.xy), _NormalScale2);
|
|
nrm += splatControl.a * UnpackNormalScale(SAMPLE_TEXTURE2D(_Normal3, sampler_Normal0, uvSplat23.zw), _NormalScale3);
|
|
nrm.z += 1e-5f; // avoid risk of NaN when normalizing.
|
|
mixedNormal = normalize(nrm.xyz);
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef _TERRAIN_BLEND_HEIGHT
|
|
void HeightBasedSplatModify(inout half4 splatControl, in half4 masks[4])
|
|
{
|
|
#ifndef TERRAIN_SPLAT_ADDPASS // disable for multi-pass
|
|
half4 defaultHeight = half4(masks[0].b, masks[1].b, masks[2].b, masks[3].b);
|
|
half maxHeight = max(defaultHeight.r, max(defaultHeight.g, max(defaultHeight.b, defaultHeight.a)));
|
|
|
|
// Ensure that the transition height is not zero.
|
|
half transition = max(_HeightTransition, 1e-5);
|
|
|
|
// The goal here is to have all but the highest layer at negative heights,
|
|
// then we add the transition so that if the next highest layer is near transition it will have a positive value.
|
|
// Then we clamp this to zero and normalize everything so that highest layer has a value of 1.
|
|
half4 weightedHeights = defaultHeight - maxHeight.xxxx;
|
|
// We need to add an epsilon here for active layers (hence the blendMask again)
|
|
// so that at least a layer shows up if everything's too low.
|
|
weightedHeights = (max(0, weightedHeights + transition) + 1e-5) * splatControl;
|
|
|
|
// Normalize
|
|
half sumHeight = dot(weightedHeights, half4(1, 1, 1, 1));
|
|
splatControl = weightedHeights / sumHeight.xxxx;
|
|
#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; // (xy + float2(xBase,yBase)) * skipScale
|
|
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);
|
|
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
|
|
o.uvSplat01.xy = TRANSFORM_TEX(v.texcoord, _Splat0);
|
|
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
|
|
|
|
half3 viewDirWS = GetCameraPositionWS() - Attributes.positionWS;
|
|
#if !SHADER_HINT_NICE_QUALITY
|
|
viewDirWS = SafeNormalize(viewDirWS);
|
|
#endif
|
|
|
|
#if defined(_NORMALMAP) && !defined(ENABLE_TERRAIN_PERPIXEL_NORMAL)
|
|
float4 vertexTangent = float4(cross(float3(0, 0, 1), v.normalOS), 1.0);
|
|
VertexNormalInputs normalInput = GetVertexNormalInputs(v.normalOS, vertexTangent);
|
|
|
|
o.normal = half4(normalInput.normalWS, viewDirWS.x);
|
|
o.tangent = half4(normalInput.tangentWS, viewDirWS.y);
|
|
o.bitangent = half4(normalInput.bitangentWS, viewDirWS.z);
|
|
#else
|
|
o.normal = TransformObjectToWorldNormal(v.normalOS);
|
|
o.viewDir = viewDirWS;
|
|
o.vertexSH = SampleSH(o.normal);
|
|
#endif
|
|
o.fogFactorAndVertexLight.x = ComputeFogFactor(Attributes.positionCS.z);
|
|
o.fogFactorAndVertexLight.yzw = VertexLighting(Attributes.positionWS, o.normal.xyz);
|
|
o.positionWS = Attributes.positionWS;
|
|
o.clipPos = Attributes.positionCS;
|
|
|
|
#ifdef _MAIN_LIGHT_SHADOWS
|
|
o.shadowCoord = GetShadowCoord(Attributes);
|
|
#endif
|
|
|
|
return o;
|
|
}
|
|
|
|
// Used in Standard Terrain shader
|
|
half4 SplatmapFragment(Varyings IN) : SV_TARGET
|
|
{
|
|
half3 normalTS = half3(0.0h, 0.0h, 1.0h);
|
|
#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;
|
|
half metallic = SAMPLE_TEXTURE2D(_MetallicTex, sampler_MetallicTex, IN.uvMainAndLM.xy).r;
|
|
half alpha = 1;
|
|
half occlusion = 1;
|
|
#else
|
|
half4 splatControl;
|
|
half weight;
|
|
half4 mixedDiffuse;
|
|
half4 defaultSmoothness;
|
|
|
|
half4 masks[4];
|
|
half4 hasMask = half4(_LayerHasMask0, _LayerHasMask1, _LayerHasMask2, _LayerHasMask3);
|
|
float2 splatUV = (IN.uvMainAndLM.xy * (_Control_TexelSize.zw - 1.0f) + 0.5f) * _Control_TexelSize.xy;
|
|
splatControl = SAMPLE_TEXTURE2D(_Control, sampler_Control, splatUV);
|
|
|
|
masks[0] = 1.0h;
|
|
masks[1] = 1.0h;
|
|
masks[2] = 1.0h;
|
|
masks[3] = 1.0h;
|
|
|
|
#ifdef _MASKMAP
|
|
masks[0] = lerp(masks[0], SAMPLE_TEXTURE2D(_Mask0, sampler_Mask0, IN.uvSplat01.xy), hasMask.x);
|
|
masks[1] = lerp(masks[1], SAMPLE_TEXTURE2D(_Mask1, sampler_Mask0, IN.uvSplat01.zw), hasMask.y);
|
|
masks[2] = lerp(masks[2], SAMPLE_TEXTURE2D(_Mask2, sampler_Mask0, IN.uvSplat23.xy), hasMask.z);
|
|
masks[3] = lerp(masks[3], SAMPLE_TEXTURE2D(_Mask3, sampler_Mask0, IN.uvSplat23.zw), hasMask.w);
|
|
#endif
|
|
|
|
masks[0] *= _MaskMapRemapScale0.rgba;
|
|
masks[0] += _MaskMapRemapOffset0.rgba;
|
|
masks[1] *= _MaskMapRemapScale1.rgba;
|
|
masks[1] += _MaskMapRemapOffset1.rgba;
|
|
masks[2] *= _MaskMapRemapScale2.rgba;
|
|
masks[2] += _MaskMapRemapOffset2.rgba;
|
|
masks[3] *= _MaskMapRemapScale3.rgba;
|
|
masks[3] += _MaskMapRemapOffset3.rgba;
|
|
|
|
#ifdef _TERRAIN_BLEND_HEIGHT
|
|
HeightBasedSplatModify(splatControl, masks);
|
|
#endif
|
|
|
|
SplatmapMix(IN.uvMainAndLM, IN.uvSplat01, IN.uvSplat23, splatControl, weight, mixedDiffuse, defaultSmoothness, normalTS);
|
|
half3 albedo = mixedDiffuse.rgb;
|
|
|
|
half4 defaultMetallic = half4(_Metallic0, _Metallic1, _Metallic2, _Metallic3);
|
|
half4 defaultOcclusion = half4(_MaskMapRemapScale0.g, _MaskMapRemapScale1.g, _MaskMapRemapScale2.g, _MaskMapRemapScale3.g) +
|
|
half4(_MaskMapRemapOffset0.g, _MaskMapRemapOffset1.g, _MaskMapRemapOffset2.g, _MaskMapRemapOffset3.g);
|
|
|
|
half4 maskSmoothness = half4(masks[0].a, masks[1].a, masks[2].a, masks[3].a);
|
|
defaultSmoothness = lerp(defaultSmoothness, maskSmoothness, hasMask);
|
|
half smoothness = dot(splatControl, defaultSmoothness);
|
|
|
|
half4 maskMetallic = half4(masks[0].r, masks[1].r, masks[2].r, masks[3].r);
|
|
defaultMetallic = lerp(defaultMetallic, maskMetallic, hasMask);
|
|
half metallic = dot(splatControl, defaultMetallic);
|
|
|
|
half4 maskOcclusion = half4(masks[0].g, masks[1].g, masks[2].g, masks[3].g);
|
|
defaultOcclusion = lerp(defaultOcclusion, maskOcclusion, hasMask);
|
|
half occlusion = dot(splatControl, defaultOcclusion);
|
|
|
|
half alpha = weight;
|
|
#endif
|
|
|
|
InputData inputData;
|
|
InitializeInputData(IN, normalTS, 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 position : POSITION;
|
|
float3 normalOS : NORMAL;
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
};
|
|
|
|
float4 ShadowPassVertex(AttributesLean v) : SV_POSITION
|
|
{
|
|
Varyings o;
|
|
UNITY_SETUP_INSTANCE_ID(v);
|
|
TerrainInstancing(v.position, v.normalOS);
|
|
|
|
float3 positionWS = TransformObjectToWorld(v.position.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() : SV_TARGET
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Depth pass
|
|
|
|
float4 DepthOnlyVertex(AttributesLean v) : SV_POSITION
|
|
{
|
|
Varyings o = (Varyings)0;
|
|
UNITY_SETUP_INSTANCE_ID(v);
|
|
TerrainInstancing(v.position, v.normalOS);
|
|
return TransformObjectToHClip(v.position.xyz);
|
|
}
|
|
|
|
half4 DepthOnlyFragment() : SV_TARGET
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif
|