293 lines
13 KiB
HLSL
293 lines
13 KiB
HLSL
|
|
// The MIT License (MIT) (see LICENSE.txt)
|
||
|
|
// Copyright (c) 2016 Unity Technologies
|
||
|
|
// Copyright © 2021 Jens Neitzel
|
||
|
|
|
||
|
|
#ifndef TERRAIN_HEIGHTBLEND_SPLATMAP_COMMON_CGINC_INCLUDED
|
||
|
|
#define TERRAIN_HEIGHTBLEND_SPLATMAP_COMMON_CGINC_INCLUDED
|
||
|
|
|
||
|
|
#ifdef _NORMALMAP
|
||
|
|
// Since 2018.3 we changed from _TERRAIN_NORMAL_MAP to _NORMALMAP to save 1 keyword.
|
||
|
|
#define _TERRAIN_NORMAL_MAP
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef USE_TEXTURE_NO_TILE
|
||
|
|
#include "textureNoTile.cginc"
|
||
|
|
#endif
|
||
|
|
|
||
|
|
struct Input
|
||
|
|
{
|
||
|
|
float4 tc;
|
||
|
|
#ifndef TERRAIN_BASE_PASS
|
||
|
|
UNITY_FOG_COORDS(0) // needed because finalcolor oppresses fog code generation.
|
||
|
|
#endif
|
||
|
|
half ViewDist;
|
||
|
|
half3 viewDir;
|
||
|
|
};
|
||
|
|
|
||
|
|
UNITY_DECLARE_TEX2D(_Control);
|
||
|
|
float4 _Control_ST;
|
||
|
|
float4 _Control_TexelSize;
|
||
|
|
sampler2D _Splat0, _Splat1, _Splat2, _Splat3;
|
||
|
|
float4 _Splat0_ST, _Splat1_ST, _Splat2_ST, _Splat3_ST;
|
||
|
|
sampler2D _HeightMap0, _HeightMap1, _HeightMap2, _HeightMap3;
|
||
|
|
UNITY_DECLARE_TEX2D_NOSAMPLER(_DistantMap);
|
||
|
|
half _DistMapBlendDistance;
|
||
|
|
fixed _DistMapInfluenceMin;
|
||
|
|
fixed _DistMapInfluenceMax;
|
||
|
|
half _OverlapDepth;
|
||
|
|
half _Parallax;
|
||
|
|
|
||
|
|
#if defined(UNITY_INSTANCING_ENABLED) && !defined(SHADER_API_D3D11_9X)
|
||
|
|
sampler2D _TerrainHeightmapTexture;
|
||
|
|
sampler2D _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)
|
||
|
|
|
||
|
|
#ifdef _NORMALMAP
|
||
|
|
sampler2D _Normal0, _Normal1, _Normal2, _Normal3;
|
||
|
|
float _NormalScale0, _NormalScale1, _NormalScale2, _NormalScale3;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(TERRAIN_BASE_PASS) && defined(UNITY_PASS_META)
|
||
|
|
// When we render albedo for GI baking, we actually need to take the ST
|
||
|
|
float4 _MainTex_ST;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
void SplatmapVert(inout appdata_full v, out Input data)
|
||
|
|
{
|
||
|
|
UNITY_INITIALIZE_OUTPUT(Input, data);
|
||
|
|
data.ViewDist = length(UnityObjectToViewPos(v.vertex).xyz);
|
||
|
|
data.viewDir = normalize(ObjSpaceViewDir(v.vertex));
|
||
|
|
|
||
|
|
#if defined(UNITY_INSTANCING_ENABLED) && !defined(SHADER_API_D3D11_9X)
|
||
|
|
|
||
|
|
float2 patchVertex = v.vertex.xy;
|
||
|
|
float4 instanceData = UNITY_ACCESS_INSTANCED_PROP(Terrain, _TerrainPatchInstanceData);
|
||
|
|
|
||
|
|
float4 uvscale = instanceData.z * _TerrainHeightmapRecipSize;
|
||
|
|
float4 uvoffset = instanceData.xyxy * uvscale;
|
||
|
|
uvoffset.xy += 0.5f * _TerrainHeightmapRecipSize.xy;
|
||
|
|
float2 sampleCoords = (patchVertex.xy * uvscale.xy + uvoffset.xy);
|
||
|
|
|
||
|
|
float hm = UnpackHeightmap(tex2Dlod(_TerrainHeightmapTexture, float4(sampleCoords, 0, 0)));
|
||
|
|
v.vertex.xz = (patchVertex.xy + instanceData.xy) * _TerrainHeightmapScale.xz * instanceData.z; //(x + xBase) * hmScale.x * skipScale;
|
||
|
|
v.vertex.y = hm * _TerrainHeightmapScale.y;
|
||
|
|
v.vertex.w = 1.0f;
|
||
|
|
|
||
|
|
v.texcoord.xy = (patchVertex.xy * uvscale.zw + uvoffset.zw);
|
||
|
|
v.texcoord3 = v.texcoord2 = v.texcoord1 = v.texcoord;
|
||
|
|
|
||
|
|
#ifdef TERRAIN_INSTANCED_PERPIXEL_NORMAL
|
||
|
|
v.normal = float3(0, 1, 0); // TODO: reconstruct the tangent space in the pixel shader. Seems to be hard with surface shader especially when other attributes are packed together with tSpace.
|
||
|
|
data.tc.zw = sampleCoords;
|
||
|
|
#else
|
||
|
|
float3 nor = tex2Dlod(_TerrainNormalmapTexture, float4(sampleCoords, 0, 0)).xyz;
|
||
|
|
v.normal = 2.0f * nor - 1.0f;
|
||
|
|
#endif
|
||
|
|
#endif
|
||
|
|
|
||
|
|
v.tangent.xyz = cross(v.normal, float3(0,0,1));
|
||
|
|
v.tangent.w = -1;
|
||
|
|
|
||
|
|
data.tc.xy = v.texcoord;
|
||
|
|
#ifdef TERRAIN_BASE_PASS
|
||
|
|
#ifdef UNITY_PASS_META
|
||
|
|
data.tc.xy = v.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
|
||
|
|
#endif
|
||
|
|
#else
|
||
|
|
float4 pos = UnityObjectToClipPos(v.vertex);
|
||
|
|
UNITY_TRANSFER_FOG(data, pos);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
half blendByHeight(half texture1height, half texture2height, half control1height, half control2height, half overlapDepth, out half textureHeightOut, out half controlHeightOut)
|
||
|
|
{
|
||
|
|
half texture1heightPrefilter = texture1height * sign(control1height);
|
||
|
|
half texture2heightPrefilter = texture2height * sign(control2height);
|
||
|
|
half height1 = texture1heightPrefilter + control1height;
|
||
|
|
half height2 = texture2heightPrefilter + control2height;
|
||
|
|
half blendFactor = (clamp(((height1 - height2) / overlapDepth), -1, 1) + 1) / 2;
|
||
|
|
// Substract positive differences of the other control height to not make one texture height benefit too much from the other.
|
||
|
|
textureHeightOut = max(0, texture1heightPrefilter - max(0, control2height-control1height)) * blendFactor + max(0, texture2heightPrefilter - max(0, control1height-control2height)) * (1 - blendFactor);
|
||
|
|
// Propagate sum of control heights to not loose height.
|
||
|
|
controlHeightOut = control1height + control2height;
|
||
|
|
return blendFactor;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef TERRAIN_BASE_PASS
|
||
|
|
|
||
|
|
#ifdef TERRAIN_STANDARD_SHADER
|
||
|
|
void SplatmapMix(Input IN, half4 defaultAlpha, out half4 splat_control, out half weight, out fixed4 mixedDiffuse, inout fixed3 mixedNormal)
|
||
|
|
#else
|
||
|
|
void SplatmapMix(Input IN, out half4 splat_control, out half weight, out fixed4 mixedDiffuse, inout fixed3 mixedNormal)
|
||
|
|
#endif
|
||
|
|
{
|
||
|
|
// adjust splatUVs so the edges of the terrain tile lie on pixel centers
|
||
|
|
float2 splatUV = (IN.tc.xy * (_Control_TexelSize.zw - 1.0f) + 0.5f) * _Control_TexelSize.xy;
|
||
|
|
splat_control = UNITY_SAMPLE_TEX2D(_Control, splatUV);
|
||
|
|
fixed4 texDistantMap = UNITY_SAMPLE_TEX2D_SAMPLER(_DistantMap, _Control, splatUV);
|
||
|
|
weight = dot(splat_control, half4(1,1,1,1));
|
||
|
|
|
||
|
|
#if !defined(SHADER_API_MOBILE) && defined(TERRAIN_SPLAT_ADDPASS)
|
||
|
|
clip(weight == 0.0f ? -1 : 1);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// Normalize weights before lighting and restore weights in final modifier functions so that the overal
|
||
|
|
// lighting result can be correctly weighted.
|
||
|
|
splat_control /= (weight + 1e-3f);
|
||
|
|
|
||
|
|
fixed2 uvSplat0 = TRANSFORM_TEX(IN.tc.xy, _Splat0);
|
||
|
|
fixed2 uvSplat1 = TRANSFORM_TEX(IN.tc.xy, _Splat1);
|
||
|
|
fixed2 uvSplat2 = TRANSFORM_TEX(IN.tc.xy, _Splat2);
|
||
|
|
fixed2 uvSplat3 = TRANSFORM_TEX(IN.tc.xy, _Splat3);
|
||
|
|
|
||
|
|
#ifdef USE_TEXTURE_NO_TILE
|
||
|
|
NoTileUVs ntuvs0 = textureNoTileCalcUVs(uvSplat0);
|
||
|
|
NoTileUVs ntuvs1 = textureNoTileCalcUVs(uvSplat1);
|
||
|
|
NoTileUVs ntuvs2 = textureNoTileCalcUVs(uvSplat2);
|
||
|
|
NoTileUVs ntuvs3 = textureNoTileCalcUVs(uvSplat3);
|
||
|
|
fixed texture0Height = textureNoTile(_HeightMap0, ntuvs0);
|
||
|
|
fixed texture1Height = textureNoTile(_HeightMap1, ntuvs1);
|
||
|
|
fixed texture2Height = textureNoTile(_HeightMap2, ntuvs2);
|
||
|
|
fixed texture3Height = textureNoTile(_HeightMap3, ntuvs3);
|
||
|
|
#else
|
||
|
|
fixed texture0Height = tex2D(_HeightMap0, uvSplat0);
|
||
|
|
fixed texture1Height = tex2D(_HeightMap1, uvSplat1);
|
||
|
|
fixed texture2Height = tex2D(_HeightMap2, uvSplat2);
|
||
|
|
fixed texture3Height = tex2D(_HeightMap3, uvSplat3);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// Calculate Blend factors
|
||
|
|
half textHeight1, textHeight2, textHeight3;
|
||
|
|
half ctrlHeight1, ctrlHeight2, ctrlHeight3;
|
||
|
|
half blendFactor01 = blendByHeight(texture0Height, texture1Height, splat_control.r, splat_control.g, _OverlapDepth, textHeight1, ctrlHeight1);
|
||
|
|
half blendFactor12 = blendByHeight(textHeight1, texture2Height, ctrlHeight1, splat_control.b, _OverlapDepth, textHeight2, ctrlHeight2);
|
||
|
|
half blendFactor23 = blendByHeight(textHeight2, texture3Height, ctrlHeight2, splat_control.a, _OverlapDepth, textHeight3, ctrlHeight3);
|
||
|
|
|
||
|
|
// Calculate Parallax after final heigth is known
|
||
|
|
fixed2 paraOffset = ParallaxOffset(textHeight3, _Parallax, IN.viewDir);
|
||
|
|
|
||
|
|
// Calculate UVs again, now with Parallax Offset
|
||
|
|
#ifdef USE_TEXTURE_NO_TILE
|
||
|
|
ntuvs0 = textureNoTileCalcUVs(uvSplat0+paraOffset);
|
||
|
|
ntuvs1 = textureNoTileCalcUVs(uvSplat1+paraOffset);
|
||
|
|
ntuvs2 = textureNoTileCalcUVs(uvSplat2+paraOffset);
|
||
|
|
ntuvs3 = textureNoTileCalcUVs(uvSplat3+paraOffset);
|
||
|
|
#else
|
||
|
|
fixed2 uvs0 = uvSplat0 + paraOffset;
|
||
|
|
fixed2 uvs1 = uvSplat1 + paraOffset;
|
||
|
|
fixed2 uvs2 = uvSplat2 + paraOffset;
|
||
|
|
fixed2 uvs3 = uvSplat3 + paraOffset;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// Sample Textures using the modified UVs
|
||
|
|
#ifdef USE_TEXTURE_NO_TILE
|
||
|
|
#ifdef TERRAIN_STANDARD_SHADER
|
||
|
|
fixed4 texture0 = textureNoTile(_Splat0, ntuvs0) * half4(1.0, 1.0, 1.0, defaultAlpha.r);
|
||
|
|
fixed4 texture1 = textureNoTile(_Splat1, ntuvs1) * half4(1.0, 1.0, 1.0, defaultAlpha.g);
|
||
|
|
fixed4 texture2 = textureNoTile(_Splat2, ntuvs2) * half4(1.0, 1.0, 1.0, defaultAlpha.b);
|
||
|
|
fixed4 texture3 = textureNoTile(_Splat3, ntuvs3) * half4(1.0, 1.0, 1.0, defaultAlpha.a);
|
||
|
|
#else
|
||
|
|
fixed4 texture0 = textureNoTile(_Splat0, ntuvs0);
|
||
|
|
fixed4 texture1 = textureNoTile(_Splat1, ntuvs1);
|
||
|
|
fixed4 texture2 = textureNoTile(_Splat2, ntuvs2);
|
||
|
|
fixed4 texture3 = textureNoTile(_Splat3, ntuvs3);
|
||
|
|
#endif
|
||
|
|
#else
|
||
|
|
#ifdef TERRAIN_STANDARD_SHADER
|
||
|
|
fixed4 texture0 = tex2D(_Splat0, uvs0) * half4(1.0, 1.0, 1.0, defaultAlpha.r);
|
||
|
|
fixed4 texture1 = tex2D(_Splat1, uvs1) * half4(1.0, 1.0, 1.0, defaultAlpha.g);
|
||
|
|
fixed4 texture2 = tex2D(_Splat2, uvs2) * half4(1.0, 1.0, 1.0, defaultAlpha.b);
|
||
|
|
fixed4 texture3 = tex2D(_Splat3, uvs3) * half4(1.0, 1.0, 1.0, defaultAlpha.a);
|
||
|
|
#else
|
||
|
|
fixed4 texture0 = tex2D(_Splat0, uvs0);
|
||
|
|
fixed4 texture1 = tex2D(_Splat1, uvs1);
|
||
|
|
fixed4 texture2 = tex2D(_Splat2, uvs2);
|
||
|
|
fixed4 texture3 = tex2D(_Splat3, uvs3);
|
||
|
|
#endif
|
||
|
|
#endif
|
||
|
|
|
||
|
|
mixedDiffuse = 0.0f;
|
||
|
|
|
||
|
|
// Blend Textures based on calculated blend factors
|
||
|
|
mixedDiffuse = texture0 * blendFactor01 + texture1 * (1 - blendFactor01);
|
||
|
|
mixedDiffuse = mixedDiffuse * blendFactor12 + texture2 * (1 - blendFactor12);
|
||
|
|
mixedDiffuse = mixedDiffuse * blendFactor23 + texture3 * (1 - blendFactor23);
|
||
|
|
|
||
|
|
// Blend with Distant Map
|
||
|
|
fixed influenceDist = clamp(IN.ViewDist/_DistMapBlendDistance+_DistMapInfluenceMin, 0, _DistMapInfluenceMax);
|
||
|
|
mixedDiffuse = texDistantMap * influenceDist + mixedDiffuse * (1-influenceDist);
|
||
|
|
|
||
|
|
#ifdef _NORMALMAP
|
||
|
|
// Sample Normal Maps using the modified UVs
|
||
|
|
#ifdef USE_TEXTURE_NO_TILE
|
||
|
|
fixed3 texture0normal = textureNoTileNormal(_Normal0, ntuvs0, _NormalScale0);
|
||
|
|
fixed3 texture1normal = textureNoTileNormal(_Normal1, ntuvs1, _NormalScale1);
|
||
|
|
fixed3 texture2normal = textureNoTileNormal(_Normal2, ntuvs2, _NormalScale2);
|
||
|
|
fixed3 texture3normal = textureNoTileNormal(_Normal3, ntuvs3, _NormalScale3);
|
||
|
|
#else
|
||
|
|
fixed3 texture0normal = UnpackNormalWithScale( tex2D(_Normal0, uvs0), _NormalScale0 );
|
||
|
|
fixed3 texture1normal = UnpackNormalWithScale( tex2D(_Normal1, uvs1), _NormalScale1 );
|
||
|
|
fixed3 texture2normal = UnpackNormalWithScale( tex2D(_Normal2, uvs2), _NormalScale2 );
|
||
|
|
fixed3 texture3normal = UnpackNormalWithScale( tex2D(_Normal3, uvs3), _NormalScale3 );
|
||
|
|
#endif
|
||
|
|
// Blend Normal maps based on calculated blend factors
|
||
|
|
mixedNormal = texture0normal * blendFactor01 + texture1normal * (1 - blendFactor01);
|
||
|
|
mixedNormal = mixedNormal * blendFactor12 + texture2normal * (1 - blendFactor12);
|
||
|
|
mixedNormal = mixedNormal * blendFactor23 + texture3normal * (1 - blendFactor23);
|
||
|
|
mixedNormal.z += 1e-5f; // to avoid nan after normalizing
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(INSTANCING_ON) && defined(SHADER_TARGET_SURFACE_ANALYSIS) && defined(TERRAIN_INSTANCED_PERPIXEL_NORMAL)
|
||
|
|
mixedNormal = float3(0, 0, 1); // make sure that surface shader compiler realizes we write to normal, as UNITY_INSTANCING_ENABLED is not defined for SHADER_TARGET_SURFACE_ANALYSIS.
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#if defined(UNITY_INSTANCING_ENABLED) && !defined(SHADER_API_D3D11_9X) && defined(TERRAIN_INSTANCED_PERPIXEL_NORMAL)
|
||
|
|
float3 geomNormal = normalize(tex2D(_TerrainNormalmapTexture, IN.tc.zw).xyz * 2 - 1);
|
||
|
|
#ifdef _NORMALMAP
|
||
|
|
float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1)));
|
||
|
|
float3 geomBitangent = normalize(cross(geomTangent, geomNormal));
|
||
|
|
mixedNormal = mixedNormal.x * geomTangent
|
||
|
|
+ mixedNormal.y * geomBitangent
|
||
|
|
+ mixedNormal.z * geomNormal;
|
||
|
|
#else
|
||
|
|
mixedNormal = geomNormal;
|
||
|
|
#endif
|
||
|
|
mixedNormal = mixedNormal.xzy;
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef TERRAIN_SURFACE_OUTPUT
|
||
|
|
#define TERRAIN_SURFACE_OUTPUT SurfaceOutput
|
||
|
|
#endif
|
||
|
|
|
||
|
|
void SplatmapFinalColor(Input IN, TERRAIN_SURFACE_OUTPUT o, inout fixed4 color)
|
||
|
|
{
|
||
|
|
color *= o.Alpha;
|
||
|
|
#ifdef TERRAIN_SPLAT_ADDPASS
|
||
|
|
UNITY_APPLY_FOG_COLOR(IN.fogCoord, color, fixed4(0,0,0,0));
|
||
|
|
#else
|
||
|
|
UNITY_APPLY_FOG(IN.fogCoord, color);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
void SplatmapFinalPrepass(Input IN, TERRAIN_SURFACE_OUTPUT o, inout fixed4 normalSpec)
|
||
|
|
{
|
||
|
|
normalSpec *= o.Alpha;
|
||
|
|
}
|
||
|
|
|
||
|
|
void SplatmapFinalGBuffer(Input IN, TERRAIN_SURFACE_OUTPUT o, inout half4 outGBuffer0, inout half4 outGBuffer1, inout half4 outGBuffer2, inout half4 emission)
|
||
|
|
{
|
||
|
|
UnityStandardDataApplyWeightToGbuffer(outGBuffer0, outGBuffer1, outGBuffer2, o.Alpha);
|
||
|
|
emission *= o.Alpha;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif // TERRAIN_BASE_PASS
|
||
|
|
|
||
|
|
#endif // TERRAIN_HEIGHTBLEND_SPLATMAP_COMMON_CGINC_INCLUDED
|