//////////////////////////////////////// // MicroSplat // Copyright (c) Jason Booth // // Auto-generated shader code, don't hand edit! // // Unity Version: 2022.3.20f1 // MicroSplat Version: 3.9 // Render Pipeline: URP2022 // Platform: WindowsEditor //////////////////////////////////////// Shader "ABCPolaris_" { Properties { [HideInInspector][NoScaleOffset]unity_Lightmaps("unity_Lightmaps", 2DArray) = "" {} [HideInInspector][NoScaleOffset]unity_LightmapsInd("unity_LightmapsInd", 2DArray) = "" {} [HideInInspector][NoScaleOffset]unity_ShadowMasks("unity_ShadowMasks", 2DArray) = "" {} [HideInInspector] _Control0 ("Control0", 2D) = "red" {} [HideInInspector] _Control1 ("Control1", 2D) = "black" {} [HideInInspector] _Control2 ("Control2", 2D) = "black" {} [HideInInspector] _Control3 ("Control3", 2D) = "black" {} // Splats [NoScaleOffset]_Diffuse ("Diffuse Array", 2DArray) = "white" {} [NoScaleOffset]_NormalSAO ("Normal Array", 2DArray) = "bump" {} [NoScaleOffset]_PerTexProps("Per Texture Properties", 2D) = "black" {} [HideInInspector] _TerrainHolesTexture("Holes Map (RGB)", 2D) = "white" {} [HideInInspector] _PerPixelNormal("Per Pixel Normal", 2D) = "bump" {} _Contrast("Blend Contrast", Range(0.01, 0.99)) = 0.4 _UVScale("UV Scales", Vector) = (45, 45, 0, 0) // for Unity 2020.3 bug _MainTex("Unity Bug", 2D) = "white" {} _TerrainHeightmapTexture("", 2D) = "black" {} _TerrainNormalmapTexture("", 2D) = "bump" {} } SubShader { Tags {"RenderPipeline" = "UniversalPipeline" "RenderType" = "UniversalLitShader" "Queue" = "Geometry+100" "IgnoreProjector" = "False" "SplatCount" = "16"} Pass { Name "Universal Forward" Tags { "LightMode" = "UniversalForward" } Cull Back Blend One Zero ZTest LEqual ZWrite On HLSLPROGRAM #pragma vertex Vert #pragma fragment Frag #pragma target 3.5 #pragma prefer_hlslcc gles #pragma exclude_renderers d3d11_9x #pragma multi_compile_fog #pragma multi_compile_instancing #pragma multi_compile _ DOTS_INSTANCING_ON #pragma multi_compile_local _ _ALPHATEST_ON #pragma instancing_options renderinglayer // Keywords #pragma multi_compile _ _SCREEN_SPACE_OCCLUSION #pragma multi_compile _ LIGHTMAP_ON #pragma multi_compile _ DYNAMICLIGHTMAP_ON #pragma multi_compile _ DIRLIGHTMAP_COMBINED #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS #pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile _ _REFLECTION_PROBE_BLENDING #pragma multi_compile _ _REFLECTION_PROBE_BOX_PROJECTION #pragma multi_compile _ _SHADOWS_SOFT #pragma multi_compile _ LIGHTMAP_SHADOW_MIXING #pragma multi_compile _ SHADOWS_SHADOWMASK #pragma multi_compile _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3 #pragma multi_compile _ _LIGHT_LAYERS #pragma multi_compile _ DEBUG_DISPLAY #pragma multi_compile _ _LIGHT_COOKIES #pragma multi_compile_fragment _ _WRITE_RENDERING_LAYERS #pragma multi_compile _ _FORWARD_PLUS // GraphKeywords: #define SHADER_PASS SHADERPASS_FORWARD #define VARYINGS_NEED_FOG_AND_VERTEX_LIGHT #define _FOG_FRAGMENT 1 #define _PASSFORWARD 1 #if _SIMPLELIT #define _SPECULAR_COLOR #endif #define _MICROSPLAT 1 #define _MICROPOLARISMESH 1 #define _USEGRADMIP 1 #define _PERTEXUVSCALEOFFSET 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _MSRENDERLOOP_UNITYLD 1 #define _MSRENDERLOOP_UNITYURP2020 1 #define _MSRENDERLOOP_UNITYURP2021 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _URP 1 // this has to be here or specular color will be ignored. Not in SG code #if _SIMPLELIT #define _SPECULAR_COLOR #endif // Includes #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl" #if VERSION_GREATER_EQUAL(12, 0) #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl" #endif #include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl" #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, float3x3(d.worldSpaceTangent, cross(d.worldSpaceTangent, d.worldSpaceNormal), d.worldSpaceNormal)) #define UnityObjectToWorldNormal(normal) mul(GetObjectToWorldMatrix(), normal) #define _WorldSpaceLightPos0 _MainLightPosition #define UNITY_DECLARE_TEX2D(name) TEXTURE2D(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2D_NOSAMPLER(name) TEXTURE2D(name); #define UNITY_DECLARE_TEX2DARRAY(name) TEXTURE2D_ARRAY(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2DARRAY_NOSAMPLER(name) TEXTURE2D_ARRAY(name); #define UNITY_SAMPLE_TEX2DARRAY(tex,coord) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, coord.xy, coord.z) #define UNITY_SAMPLE_TEX2DARRAY_LOD(tex,coord,lod) SAMPLE_TEXTURE2D_ARRAY_LOD(tex, sampler##tex, coord.xy, coord.z, lod) #define UNITY_SAMPLE_TEX2D(tex, coord) SAMPLE_TEXTURE2D(tex, sampler##tex, coord) #define UNITY_SAMPLE_TEX2D_SAMPLER(tex, samp, coord) SAMPLE_TEXTURE2D(tex, sampler##samp, coord) #if defined(UNITY_COMPILER_HLSL) #define UNITY_INITIALIZE_OUTPUT(type,name) name = (type)0; #else #define UNITY_INITIALIZE_OUTPUT(type,name) #endif #define sampler2D_float sampler2D #define sampler2D_half sampler2D // data across stages, stripped like the above. struct VertexToPixel { float4 pos : SV_POSITION; float3 worldPos : TEXCOORD0; float3 worldNormal : TEXCOORD1; float4 worldTangent : TEXCOORD2; float4 texcoord0 : TEXCCOORD3; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 texcoord1 : TEXCCOORD4; // float4 texcoord2 : TEXCCOORD5; #endif // float4 texcoord3 : TEXCCOORD6; // float4 screenPos : TEXCOORD7; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD13; // float4 extraV2F1 : TEXCOORD14; // float4 extraV2F2 : TEXCOORD15; // float4 extraV2F3 : TEXCOORD16; // float4 extraV2F4 : TEXCOORD17; // float4 extraV2F5 : TEXCOORD18; // float4 extraV2F6 : TEXCOORD19; // float4 extraV2F7 : TEXCOORD20; #if defined(LIGHTMAP_ON) float2 lightmapUV : TEXCOORD8; #endif #if defined(DYNAMICLIGHTMAP_ON) float2 dynamicLightmapUV : TEXCOORD9; #endif #if !defined(LIGHTMAP_ON) float3 sh : TEXCOORD10; #endif float4 fogFactorAndVertexLight : TEXCOORD11; float4 shadowCoord : TEXCOORD12; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : CUSTOM_INSTANCE_ID; #endif #if (defined(UNITY_STEREO_MULTIVIEW_ENABLED)) || (defined(UNITY_STEREO_INSTANCING_ENABLED) && (defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE))) uint stereoTargetEyeIndexAsBlendIdx0 : BLENDINDICES0; #endif #if (defined(UNITY_STEREO_INSTANCING_ENABLED)) uint stereoTargetEyeIndexAsRTArrayIdx : SV_RenderTargetArrayIndex; #endif #if defined(SHADER_STAGE_FRAGMENT) && defined(VARYINGS_NEED_CULLFACE) FRONT_FACE_TYPE cullFace : FRONT_FACE_SEMANTIC; #endif }; // TEMPLATE_SHARED // data describing the user output of a pixel struct Surface { half3 Albedo; half Height; half3 Normal; half Smoothness; half3 Emission; half Metallic; half3 Specular; half Occlusion; half Alpha; // HDRP Only half SpecularOcclusion; half SubsurfaceMask; half Thickness; half CoatMask; half Anisotropy; half IridescenceMask; half IridescenceThickness; }; // data the user might need, this will grow to be big. But easy to strip struct ShaderData { float3 localSpacePosition; float3 localSpaceNormal; float3 localSpaceTangent; float3 worldSpacePosition; float3 worldSpaceNormal; float3 worldSpaceTangent; float3 worldSpaceViewDir; float3 tangentSpaceViewDir; float4 texcoord0; float4 texcoord1; float4 texcoord2; float4 texcoord3; float2 screenUV; float4 screenPos; float4 vertexColor; float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; float3x3 TBNMatrix; }; struct VertexData { #if SHADER_TARGET > 30 && _PLANETCOMPUTE // // uint vertexID : SV_VertexID; #endif float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD4; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD5; // Add Precomputed Velocity (Alembic computes velocities on runtime side). #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID }; struct TessVertex { float4 vertex : INTERNALTESSPOS; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD4; // float4 extraV2F1 : TEXCOORD5; // float4 extraV2F2 : TEXCOORD6; // float4 extraV2F3 : TEXCOORD7; // float4 extraV2F4 : TEXCOORD8; // float4 extraV2F5 : TEXCOORD9; // float4 extraV2F6 : TEXCOORD10; // float4 extraV2F7 : TEXCOORD11; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD12; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD13; #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; struct ExtraV2F { float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; }; float3 WorldToTangentSpace(ShaderData d, float3 normal) { return mul(d.TBNMatrix, normal); } float3 TangentToWorldSpace(ShaderData d, float3 normal) { return mul(normal, d.TBNMatrix); } // in this case, make standard more like SRPs, because we can't fix // GetWorldToObjectMatrix() in HDRP, since it already does macro-fu there #if _STANDARD float3 TransformWorldToObject(float3 p) { return mul(GetWorldToObjectMatrix(), float4(p, 1)); }; float3 TransformObjectToWorld(float3 p) { return mul(GetObjectToWorldMatrix(), float4(p, 1)); }; float4 TransformWorldToObject(float4 p) { return mul(GetWorldToObjectMatrix(), p); }; float4 TransformObjectToWorld(float4 p) { return mul(GetObjectToWorldMatrix(), p); }; float4x4 GetWorldToObjectMatrix() { return GetWorldToObjectMatrix(); } float4x4 GetObjectToWorldMatrix() { return GetObjectToWorldMatrix(); } #endif float3 GetCameraWorldPosition() { #if _HDRP return GetCameraRelativePositionWS(_WorldSpaceCameraPos); #else return _WorldSpaceCameraPos; #endif } #if _HDRP half3 UnpackNormalmapRGorAG(half4 packednormal) { // This do the trick packednormal.x *= packednormal.w; half3 normal; normal.xy = packednormal.xy * 2 - 1; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } half3 UnpackNormal(half4 packednormal) { #if defined(UNITY_NO_DXT5nm) return packednormal.xyz * 2 - 1; #else return UnpackNormalmapRGorAG(packednormal); #endif } #endif #if _HDRP || _URP half3 UnpackScaleNormal(half4 packednormal, half scale) { #ifndef UNITY_NO_DXT5nm // Unpack normal as DXT5nm (1, y, 1, x) or BC5 (x, y, 0, 1) // Note neutral texture like "bump" is (0, 0, 1, 1) to work with both plain RGB normal and DXT5nm/BC5 packednormal.x *= packednormal.w; #endif half3 normal; normal.xy = (packednormal.xy * 2 - 1) * scale; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } #endif void GetSun(out float3 lightDir, out float3 color) { lightDir = float3(0.5, 0.5, 0); color = 1; #if _HDRP if (_DirectionalLightCount > 0) { DirectionalLightData light = _DirectionalLightDatas[0]; lightDir = -light.forward.xyz; color = light.color; } #elif _STANDARD lightDir = normalize(_WorldSpaceLightPos0.xyz); color = _LightColor0.rgb; #elif _URP Light light = GetMainLight(); lightDir = light.direction; color = light.color; #endif } CBUFFER_START(UnityPerMaterial) #if _MESHSUBARRAY half4 _MeshSubArrayIndexes; #endif float4 _Diffuse_TexelSize; float4 _NormalSAO_TexelSize; #if _HYBRIDHEIGHTBLEND float _HybridHeightBlendDistance; #endif #if _PACKINGHQ float4 _SmoothAO_TexelSize; #endif #ifdef _ALPHATEST_ON float4 _TerrainHolesTexture_TexelSize; #endif #if _USESPECULARWORKFLOW float4 _Specular_TexelSize; #endif #if _USEEMISSIVEMETAL float4 _EmissiveMetal_TexelSize; #endif #if _USEEMISSIVEMETAL half _EmissiveMult; #endif #if _AUTONORMAL half _AutoNormalHeightScale; #endif float4 _UVScale; // scale and offset half _Contrast; #if _VSSHADOWMAP float4 gVSSunDirection; #endif #if _FORCELOCALSPACE && _PLANETVECTORS float4x4 _PQSToLocal; #endif #if _ORIGINSHIFT float4x4 _GlobalOriginMTX; #endif float4 _Control0_TexelSize; #if _CUSTOMSPLATTEXTURES float4 _CustomControl0_TexelSize; #endif float4 _PerPixelNormal_TexelSize; #if _CONTROLNOISEUV || _GLOBALNOISEUV float2 _NoiseUVParams; #endif float4 _PerTexProps_TexelSize; #if _SURFACENORMALS float3 surfTangent; float3 surfBitangent; float3 surfNormal; #endif CBUFFER_END #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, data.TBN) // In Unity 2020.3LTS, Unity will spew tons of errors about missing this sampler in // URP, even though it shouldn't be required. TEXTURE2D(_MainTex); // globals, outside of CBuffer, but used by more than one module float3 _gGlitterLightDir; float3 _gGlitterLightWorldPos; half3 _gGlitterLightColor; #if (_MICROTERRAIN || _MICROMESHTERRAIN) 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) float4 _TerrainNormalmapTexture_TexelSize; #endif #if (_MICROTERRAIN || _MICROMESHTERRAIN) TEXTURE2D(_TerrainHeightmapTexture); float4 _TerrainHeightmapTexture_TexelSize; TEXTURE2D(_TerrainNormalmapTexture); #endif UNITY_INSTANCING_BUFFER_START(Terrain) UNITY_DEFINE_INSTANCED_PROP(float4, _TerrainPatchInstanceData) // float4(xBase, yBase, skipScale, ~) UNITY_INSTANCING_BUFFER_END(Terrain) // dynamic branching helpers, for regular and aggressive branching // debug mode shows how many samples using branching will save us. // // These macros are always used instead of the UNITY_BRANCH macro // to maintain debug displays and allow branching to be disabled // on as granular level as we want. #if _BRANCHSAMPLES #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; if (w > 0) #else #define MSBRANCH(w) UNITY_BRANCH if (w > 0) #endif #else #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; #else #define MSBRANCH(w) #endif #endif #if _BRANCHSAMPLESAGR #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER ||_DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; if (w > 0.001) #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; if (w > 0.001) #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; if (w > 0.001) #else #define MSBRANCHTRIPLANAR(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHCLUSTER(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHOTHER(w) UNITY_BRANCH if (w > 0.001) #endif #else #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER || _DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; #else #define MSBRANCHTRIPLANAR(w) #define MSBRANCHCLUSTER(w) #define MSBRANCHOTHER(w) #endif #endif #if _DEBUG_SAMPLECOUNT int _sampleCount; #define COUNTSAMPLE { _sampleCount++; } #else #define COUNTSAMPLE #endif #if _DEBUG_PROCLAYERS int _procLayerCount; #define COUNTPROCLAYER { _procLayerCount++; } #else #define COUNTPROCLAYER #endif #if _DEBUG_USE_TOPOLOGY TEXTURE2D(_DebugWorldPos); TEXTURE2D(_DebugWorldNormal); #endif // splat UNITY_DECLARE_TEX2DARRAY(_Diffuse); UNITY_DECLARE_TEX2DARRAY(_NormalSAO); #if _CONTROLNOISEUV || _GLOBALNOISEUV TEXTURE2D(_NoiseUV); #endif #if _PACKINGHQ UNITY_DECLARE_TEX2DARRAY(_SmoothAO); #endif #if _USESPECULARWORKFLOW UNITY_DECLARE_TEX2DARRAY(_Specular); #endif #if _USEEMISSIVEMETAL UNITY_DECLARE_TEX2DARRAY(_EmissiveMetal); #endif TEXTURE2D(_PerPixelNormal); SamplerState shared_linear_clamp_sampler; SamplerState shared_point_clamp_sampler; TEXTURE2D(_Control0); #if _CUSTOMSPLATTEXTURES TEXTURE2D(_CustomControl0); #if !_MAX4TEXTURES TEXTURE2D(_CustomControl1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_CustomControl2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_CustomControl3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl6); #endif #if _MAX32TEXTURES TEXTURE2D(_CustomControl7); #endif #else #if !_MAX4TEXTURES TEXTURE2D(_Control1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_Control2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_Control3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control6); #endif #if _MAX32TEXTURES TEXTURE2D(_Control7); #endif #endif TEXTURE2D_FLOAT(_PerTexProps); struct DecalLayer { float3 uv; float2 dx; float2 dy; int decalIndex; bool dynamic; }; struct DecalOutput { DecalLayer l0; DecalLayer l1; DecalLayer l2; DecalLayer l3; half4 Weights; half4 Indexes; half4 fxLevels; }; struct TriGradMipFormat { float4 d0; float4 d1; float4 d2; }; float InverseLerp(float x, float y, float v) { return (v-x)/max(y-x, 0.001); } float2 InverseLerp(float2 x, float2 y, float2 v) { return (v-x)/max(y-x, float2(0.001, 0.001)); } float3 InverseLerp(float3 x, float3 y, float3 v) { return (v-x)/max(y-x, float3(0.001, 0.001, 0.001)); } float4 InverseLerp(float4 x, float4 y, float4 v) { return (v-x)/max(y-x, float4(0.001, 0.001, 0.001, 0.001)); } // 2019.3 holes #ifdef _ALPHATEST_ON TEXTURE2D(_TerrainHolesTexture); void ClipHoles(float2 uv) { float hole = SAMPLE_TEXTURE2D(_TerrainHolesTexture, shared_linear_clamp_sampler, uv).r; COUNTSAMPLE clip(hole < 0.5f ? -1 : 1); } #endif #if _TRIPLANAR #if _USEGRADMIP #define MIPFORMAT TriGradMipFormat #define INITMIPFORMAT (TriGradMipFormat)0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float3 #define INITMIPFORMAT 0; #define MIPFROMATRAW float3 #endif #else #if _USEGRADMIP #define MIPFORMAT float4 #define INITMIPFORMAT 0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float #define INITMIPFORMAT 0; #define MIPFROMATRAW float #endif #endif float2 TotalOne(float2 v) { return v * (1.0 / max(v.x + v.y, 0.001)); } float3 TotalOne(float3 v) { return v * (1.0 / max(v.x + v.y + v.z, 0.001)); } float4 TotalOne(float4 v) { return v * (1.0 / max(v.x + v.y + v.z + v.w, 0.001)); } float2 RotateUV(float2 uv, float amt) { uv -=0.5; float s = sin ( amt); float c = cos ( amt ); float2x2 mtx = float2x2( c, -s, s, c); mtx *= 0.5; mtx += 0.5; mtx = mtx * 2-1; uv = mul ( uv, mtx ); uv += 0.5; return uv; } float4 DecodeToFloat4(float v) { uint vi = (uint)(v * (256.0f * 256.0f * 256.0f * 256.0f)); int ex = (int)(vi / (256 * 256 * 256) % 256); int ey = (int)((vi / (256 * 256)) % 256); int ez = (int)((vi / (256)) % 256); int ew = (int)(vi % 256); float4 e = float4(ex / 255.0, ey / 255.0, ez / 255.0, ew / 255.0); return e; } struct Input { ShaderData shaderData; float2 uv_Control0; float2 uv2_Diffuse; float worldHeight; float3 worldUpVector; float3 viewDir; float3 worldPos; float3 worldNormal; float4 color; float3x3 TBN; // vertex/digger workflow data half4 w0; half4 w1; half4 w2; half4 w3; half4 w4; half4 w5; half4 w6; // megasplat data half4 layer0; half4 layer1; half3 baryWeights; half4 scatter0; half4 scatter1; // wetness, puddles, streams, lava from vertex or megasplat half4 fx; // snow min, snow max half4 fx2; }; struct TriplanarConfig { float3x3 uv0; float3x3 uv1; float3x3 uv2; float3x3 uv3; half3 pN; half3 pN0; half3 pN1; half3 pN2; half3 pN3; half3 axisSign; Input IN; }; struct Config { float2 uv; float3 uv0; float3 uv1; float3 uv2; float3 uv3; half4 cluster0; half4 cluster1; half4 cluster2; half4 cluster3; }; struct MicroSplatLayer { half3 Albedo; half3 Normal; half Smoothness; half Occlusion; half Metallic; half Height; half3 Emission; #if _USESPECULARWORKFLOW half3 Specular; #endif half Alpha; }; // raw, unblended samples from arrays struct RawSamples { half4 albedo0; half4 albedo1; half4 albedo2; half4 albedo3; #if _SURFACENORMALS half3 surf0; half3 surf1; half3 surf2; half3 surf3; #endif half4 normSAO0; half4 normSAO1; half4 normSAO2; half4 normSAO3; #if _USEEMISSIVEMETAL || _GLOBALEMIS || _GLOBALSMOOTHAOMETAL || _PERTEXSSS || _PERTEXRIMLIGHT half4 emisMetal0; half4 emisMetal1; half4 emisMetal2; half4 emisMetal3; #endif #if _USESPECULARWORKFLOW half3 specular0; half3 specular1; half3 specular2; half3 specular3; #endif }; void InitRawSamples(inout RawSamples s) { s.normSAO0 = half4(0,0,0,1); s.normSAO1 = half4(0,0,0,1); s.normSAO2 = half4(0,0,0,1); s.normSAO3 = half4(0,0,0,1); #if _SURFACENORMALS s.surf0 = half3(0,0,1); s.surf1 = half3(0,0,1); s.surf2 = half3(0,0,1); s.surf3 = half3(0,0,1); #endif } float3 GetGlobalLightDir(Input i) { float3 lightDir = float3(1,0,0); #if _HDRP || PASS_DEFERRED lightDir = normalize(_gGlitterLightDir.xyz); #elif _URP lightDir = GetMainLight().direction; #else #ifndef USING_DIRECTIONAL_LIGHT lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); #else lightDir = normalize(_WorldSpaceLightPos0.xyz); #endif #endif return lightDir; } float3x3 GetTBN(Input i) { return i.TBN; } float3 GetGlobalLightDirTS(Input i) { float3 lightDirWS = GetGlobalLightDir(i); return mul(GetTBN(i), lightDirWS); } half3 GetGlobalLightColor() { #if _HDRP || PASS_DEFERRED return _gGlitterLightColor; #elif _URP return (GetMainLight().color); #else return _LightColor0.rgb; #endif } half3 FuzzyShade(half3 color, half3 normal, half coreMult, half edgeMult, half power, float3 viewDir) { half dt = saturate(dot(viewDir, normal)); half dark = 1.0 - (coreMult * dt); half edge = pow(1-dt, power) * edgeMult; return color * (dark + edge); } half3 ComputeSSS(Input i, float3 V, float3 N, half3 tint, half thickness, half distortion, half scale, half power) { float3 L = GetGlobalLightDir(i); half3 lightColor = GetGlobalLightColor(); float3 H = normalize(L + N * distortion); float VdotH = pow(saturate(dot(V, -H)), power) * scale; float3 I = (VdotH) * thickness; return lightColor * I * tint; } #if _MAX2LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y; } #elif _MAX3LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } #else inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } #endif #if _MAX3LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #elif _MAX2LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #else #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##3 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv3.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #endif half2 BlendNormal2(half2 base, half2 blend) { return normalize(half3(base.xy + blend.xy, 1)).xy; } half3 BlendOverlay(half3 base, half3 blend) { return (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))); } half3 BlendMult2X(half3 base, half3 blend) { return (base * (blend * 2)); } half3 BlendLighterColor(half3 s, half3 d) { return (s.x + s.y + s.z > d.x + d.y + d.z) ? s : d; } #if _SURFACENORMALS #define HALF_EPS 4.8828125e-4 // 2^-11, machine epsilon: 1 + EPS = 1 (half of the ULP for 1.0f) void ConstructSurfaceGradientTBN(Input i) { float3x3 tbn = GetTBN(i); float3 t = tbn[0]; float3 b = tbn[1]; float3 n = tbn[2]; surfNormal = n;//mul(GetWorldToObjectMatrix(), float4(n, 1)).xyz; surfTangent = t;//mul(GetWorldToObjectMatrix(), float4(t, 1)).xyz; surfBitangent = b;//cross(surfNormal, surfTangent); float renormFactor = 1.0 / length(surfNormal); surfNormal *= renormFactor; surfTangent *= renormFactor; surfBitangent *= renormFactor; } half3 SurfaceGradientFromTBN(half2 deriv) { return deriv.x * surfTangent + deriv.y * surfBitangent; } // Input: vM is tangent space normal in [-1;1]. // Output: convert vM to a derivative. half2 TspaceNormalToDerivative(half3 vM) { const half scale = 1.0/128.0; // Ensure vM delivers a positive third component using abs() and // constrain vM.z so the range of the derivative is [-128; 128]. const half3 vMa = abs(vM); const half z_ma = max(vMa.z, scale*max(vMa.x, vMa.y)); return -half2(vM.x, vM.y)/z_ma; } // Used to produce a surface gradient from the gradient of a volume // bump function such as 3D Perlin noise. Equation 2 in [Mik10]. half3 SurfgradFromVolumeGradient(half3 grad) { return grad - dot(surfNormal, grad) * surfNormal; } half3 SurfgradFromTriplanarProjection(half3 pN, half2 xPlaneTN, half2 yPlaneTN, half2 zPlaneTN) { const half w0 = pN.x; const half w1 = pN.y; const half w2 = pN.z; // X-plane tangent normal to gradient derivative xPlaneTN = xPlaneTN * 2.0 - 1.0; half xPlaneRcpZ = rsqrt(max(1 - dot(xPlaneTN.x, xPlaneTN.x) - dot(xPlaneTN.y, xPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_xplane = xPlaneTN * -xPlaneRcpZ; // Y-plane tangent normal to gradient derivative yPlaneTN = yPlaneTN * 2.0 - 1.0; half yPlaneRcpZ = rsqrt(max(1 - dot(yPlaneTN.x, yPlaneTN.x) - dot(yPlaneTN.y, yPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_yplane = yPlaneTN * -yPlaneRcpZ; // Z-plane tangent normal to gradient derivative zPlaneTN = zPlaneTN * 2.0 - 1.0; half zPlaneRcpZ = rsqrt(max(1 - dot(zPlaneTN.x, zPlaneTN.x) - dot(zPlaneTN.y, zPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_zplane = zPlaneTN * -zPlaneRcpZ; // Assume deriv xplane, deriv yplane, and deriv zplane are // sampled using (z,y), (x,z), and (x,y), respectively. // Positive scales of the lookup coordinate will work // as well, but for negative scales the derivative components // will need to be negated accordingly. float3 grad = float3(w2*d_zplane.x + w1*d_yplane.x, w2*d_zplane.y + w0*d_xplane.y, w0*d_xplane.x + w1*d_yplane.y); return SurfgradFromVolumeGradient(grad); } half3 ConvertNormalToGradient(half3 normal) { half2 deriv = TspaceNormalToDerivative(normal); return SurfaceGradientFromTBN(deriv); } half3 ConvertNormal2ToGradient(half2 packedNormal) { half2 tNormal = packedNormal; half rcpZ = rsqrt(max(1 - dot(tNormal.x, tNormal.x) - dot(tNormal.y, tNormal.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 deriv = tNormal * -rcpZ; return SurfaceGradientFromTBN(deriv); } half3 ResolveNormalFromSurfaceGradient(half3 gradient) { return normalize(surfNormal - gradient); } #endif // _SURFACENORMALS void BlendNormalPerTex(inout RawSamples o, half2 noise, float4 fades) { #if _SURFACENORMALS float3 grad = ConvertNormal2ToGradient(noise.xy); o.surf0 += grad * fades.x; o.surf1 += grad * fades.y; #if !_MAX2LAYER o.surf2 += grad * fades.z; #endif #if !_MAX2LAYER && !_MAX3LAYER o.surf3 += grad * fades.w; #endif #else o.normSAO0.xy = lerp(o.normSAO0.xy, BlendNormal2(o.normSAO0.xy, noise.xy), fades.x); o.normSAO1.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #if !_MAX2LAYER o.normSAO2.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO2.xy, noise.xy), fades.y); #endif #if !_MAX2LAYER && !_MAX3LAYER o.normSAO3.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #endif #endif } half3 BlendNormal3(half3 n1, half3 n2) { n1 += float3( 0, 0, 1); n2 *= float3(-1, -1, 1); return n1*dot(n1, n2) / n1.z - n2; } half2 TransformTriplanarNormal(Input IN, float3x3 t2w, half3 axisSign, half3 absVertNormal, half3 pN, half2 a0, half2 a1, half2 a2) { a0 = a0 * 2 - 1; a1 = a1 * 2 - 1; a2 = a2 * 2 - 1; a0.x *= axisSign.x; a1.x *= axisSign.y; a2.x *= axisSign.z; half3 n0 = half3(a0.xy, 1); half3 n1 = half3(a1.xy, 1); half3 n2 = half3(a2.xy, 1); float3 wn = IN.worldNormal; n0 = BlendNormal3(half3(wn.zy, absVertNormal.x), n0); n1 = BlendNormal3(half3(wn.xz, absVertNormal.y), n1 * float3(-1, 1, 1)); n2 = BlendNormal3(half3(wn.xy, absVertNormal.z), n2); n0.z *= axisSign.x; n1.z *= axisSign.y; n2.z *= -axisSign.z; half3 worldNormal = (n0.zyx * pN.x + n1.xzy * pN.y + n2.xyz * pN.z); return mul(t2w, worldNormal).xy; } // funcs inline half MSLuminance(half3 rgb) { #ifdef UNITY_COLORSPACE_GAMMA return dot(rgb, half3(0.22, 0.707, 0.071)); #else return dot(rgb, half3(0.0396819152, 0.458021790, 0.00609653955)); #endif } float2 Hash2D( float2 x ) { float2 k = float2( 0.3183099, 0.3678794 ); x = x*k + k.yx; return -1.0 + 2.0*frac( 16.0 * k*frac( x.x*x.y*(x.x+x.y)) ); } float Noise2D(float2 p ) { float2 i = floor( p ); float2 f = frac( p ); float2 u = f*f*(3.0-2.0*f); return lerp( lerp( dot( Hash2D( i + float2(0.0,0.0) ), f - float2(0.0,0.0) ), dot( Hash2D( i + float2(1.0,0.0) ), f - float2(1.0,0.0) ), u.x), lerp( dot( Hash2D( i + float2(0.0,1.0) ), f - float2(0.0,1.0) ), dot( Hash2D( i + float2(1.0,1.0) ), f - float2(1.0,1.0) ), u.x), u.y); } float FBM2D(float2 uv) { float f = 0.5000*Noise2D( uv ); uv *= 2.01; f += 0.2500*Noise2D( uv ); uv *= 1.96; f += 0.1250*Noise2D( uv ); return f; } float3 Hash3D( float3 p ) { p = float3( dot(p,float3(127.1,311.7, 74.7)), dot(p,float3(269.5,183.3,246.1)), dot(p,float3(113.5,271.9,124.6))); return -1.0 + 2.0*frac(sin(p)*437.5453123); } float Noise3D( float3 p ) { float3 i = floor( p ); float3 f = frac( p ); float3 u = f*f*(3.0-2.0*f); return lerp( lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,0.0) ), f - float3(0.0,0.0,0.0) ), dot( Hash3D( i + float3(1.0,0.0,0.0) ), f - float3(1.0,0.0,0.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,0.0) ), f - float3(0.0,1.0,0.0) ), dot( Hash3D( i + float3(1.0,1.0,0.0) ), f - float3(1.0,1.0,0.0) ), u.x), u.y), lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,1.0) ), f - float3(0.0,0.0,1.0) ), dot( Hash3D( i + float3(1.0,0.0,1.0) ), f - float3(1.0,0.0,1.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,1.0) ), f - float3(0.0,1.0,1.0) ), dot( Hash3D( i + float3(1.0,1.0,1.0) ), f - float3(1.0,1.0,1.0) ), u.x), u.y), u.z ); } float FBM3D(float3 uv) { float f = 0.5000*Noise3D( uv ); uv *= 2.01; f += 0.2500*Noise3D( uv ); uv *= 1.96; f += 0.1250*Noise3D( uv ); return f; } float GetSaturation(float3 c) { float mi = min(min(c.x, c.y), c.z); float ma = max(max(c.x, c.y), c.z); return (ma - mi)/(ma + 1e-7); } // Better Color Lerp, does not have darkening issue float3 BetterColorLerp(float3 a, float3 b, float x) { float3 ic = lerp(a, b, x) + float3(1e-6,0.0,0.0); float sd = abs(GetSaturation(ic) - lerp(GetSaturation(a), GetSaturation(b), x)); float3 dir = normalize(float3(2.0 * ic.x - ic.y - ic.z, 2.0 * ic.y - ic.x - ic.z, 2.0 * ic.z - ic.y - ic.x)); float lgt = dot(float3(1.0, 1.0, 1.0), ic); float ff = dot(dir, normalize(ic)); const float dsp_str = 1.5; ic += dsp_str * dir * sd * ff * lgt; return saturate(ic); } half4 ComputeWeights(half4 iWeights, half h0, half h1, half h2, half h3, half contrast) { #if _DISABLEHEIGHTBLENDING return iWeights; #else // compute weight with height map //half4 weights = half4(iWeights.x * h0, iWeights.y * h1, iWeights.z * h2, iWeights.w * h3); half4 weights = half4(iWeights.x * max(h0,0.001), iWeights.y * max(h1,0.001), iWeights.z * max(h2,0.001), iWeights.w * max(h3,0.001)); // Contrast weights half maxWeight = max(max(weights.x, max(weights.y, weights.z)), weights.w); half transition = max(contrast * maxWeight, 0.0001); half threshold = maxWeight - transition; half scale = 1.0 / transition; weights = saturate((weights - threshold) * scale); weights = TotalOne(weights); return weights; #endif } half HeightBlend(half h1, half h2, half slope, half contrast) { #if _DISABLEHEIGHTBLENDING return slope; #else h2 = 1 - h2; half tween = saturate((slope - min(h1, h2)) / max(abs(h1 - h2), 0.001)); half blend = saturate( ( tween - (1-contrast) ) / max(contrast, 0.001)); return blend; #endif } #if _MAX4TEXTURES #define TEXCOUNT 4 #elif _MAX8TEXTURES #define TEXCOUNT 8 #elif _MAX12TEXTURES #define TEXCOUNT 12 #elif _MAX20TEXTURES #define TEXCOUNT 20 #elif _MAX24TEXTURES #define TEXCOUNT 24 #elif _MAX28TEXTURES #define TEXCOUNT 28 #elif _MAX32TEXTURES #define TEXCOUNT 32 #else #define TEXCOUNT 16 #endif #if _DECAL_SPLAT void DoMergeDecalSplats(half4 iWeights, half4 iIndexes, inout half4 indexes, inout half4 weights) { for (int i = 0; i < 4; ++i) { half w = iWeights[i]; half index = iIndexes[i]; if (w > weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = index; } else if (w > weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = index; } else if (w > weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = index; } else if (w > weights[3]) { weights[3] = w; indexes[3] = index; } } } #endif void Setup(out half4 weights, float2 uv, out Config config, half4 w0, half4 w1, half4 w2, half4 w3, half4 w4, half4 w5, half4 w6, half4 w7, float3 worldPos, DecalOutput decalOutput) { config = (Config)0; half4 indexes = 0; config.uv = uv; #if _WORLDUV uv = worldPos.xz; #endif #if _DISABLESPLATMAPS float2 scaledUV = uv; #else float2 scaledUV = uv * _UVScale.xy + _UVScale.zw; #endif // if only 4 textures, and blending 4 textures, skip this whole thing.. // this saves about 25% of the ALU of the base shader on low end. However if // we rely on sorted texture weights (distance resampling) we have to sort.. float4 defaultIndexes = float4(0,1,2,3); #if _MESHSUBARRAY defaultIndexes = _MeshSubArrayIndexes; #endif #if _MESHSUBARRAY && !_DECAL_SPLAT || (_MAX4TEXTURES && !_MAX3LAYER && !_MAX2LAYER && !_DISTANCERESAMPLE && !_POM && !_DECAL_SPLAT) weights = w0; config.uv0 = float3(scaledUV, defaultIndexes.x); config.uv1 = float3(scaledUV, defaultIndexes.y); config.uv2 = float3(scaledUV, defaultIndexes.z); config.uv3 = float3(scaledUV, defaultIndexes.w); return; #endif #if _DISABLESPLATMAPS weights = float4(1,0,0,0); return; #else half splats[TEXCOUNT]; splats[0] = w0.x; splats[1] = w0.y; splats[2] = w0.z; splats[3] = w0.w; #if !_MAX4TEXTURES splats[4] = w1.x; splats[5] = w1.y; splats[6] = w1.z; splats[7] = w1.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES splats[8] = w2.x; splats[9] = w2.y; splats[10] = w2.z; splats[11] = w2.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES splats[12] = w3.x; splats[13] = w3.y; splats[14] = w3.z; splats[15] = w3.w; #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[16] = w4.x; splats[17] = w4.y; splats[18] = w4.z; splats[19] = w4.w; #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[20] = w5.x; splats[21] = w5.y; splats[22] = w5.z; splats[23] = w5.w; #endif #if _MAX28TEXTURES || _MAX32TEXTURES splats[24] = w6.x; splats[25] = w6.y; splats[26] = w6.z; splats[27] = w6.w; #endif #if _MAX32TEXTURES splats[28] = w7.x; splats[29] = w7.y; splats[30] = w7.z; splats[31] = w7.w; #endif weights[0] = 0; weights[1] = 0; weights[2] = 0; weights[3] = 0; indexes[0] = 0; indexes[1] = 0; indexes[2] = 0; indexes[3] = 0; int i = 0; for (i = 0; i < TEXCOUNT; ++i) { half w = splats[i]; if (w >= weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = i; } else if (w >= weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = i; } else if (w >= weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = i; } else if (w >= weights[3]) { weights[3] = w; indexes[3] = i; } } // NaN Prevention if (weights.x <= 0) weights = float4(1, 0, 0, 0); #if _DECAL_SPLAT DoMergeDecalSplats(decalOutput.Weights, decalOutput.Indexes, weights, indexes); #endif // clamp and renormalize #if _MAX2LAYER weights.zw = 0; weights.xy = TotalOne(weights.xy); #elif _MAX3LAYER weights.w = 0; weights.xyz = TotalOne(weights.xyz); #elif !_DISABLEHEIGHTBLENDING || _NORMALIZEWEIGHTS // prevents black when painting, which the unity shader does not prevent. weights = normalize(weights); #endif config.uv0 = float3(scaledUV, indexes.x); config.uv1 = float3(scaledUV, indexes.y); config.uv2 = float3(scaledUV, indexes.z); config.uv3 = float3(scaledUV, indexes.w); #endif //_DISABLESPLATMAPS } float3 HeightToNormal(float height, float3 worldPos) { float3 dx = ddx(worldPos); float3 dy = ddy(worldPos); float3 crossX = cross(float3(0,1,0), dx); float3 crossY = cross(float3(0,1,0), dy); float3 d = abs(dot(crossY, dx)); float3 n = ((((height + ddx(height)) - height) * crossY) + (((height + ddy(height)) - height) * crossX)) * sign(d); n.z *= -1; return normalize((d * float3(0,1,0)) - n).xzy; } float ComputeMipLevel(float2 uv, float2 textureSize) { uv *= textureSize; float2 dx_vtc = ddx(uv); float2 dy_vtc = ddy(uv); float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); return 0.5 * log2(delta_max_sqr); } inline half2 UnpackNormal2(half4 packednormal) { return packednormal.wy * 2 - 1; } half3 TriplanarHBlend(half h0, half h1, half h2, half3 pN, half contrast) { half3 blend = pN / dot(pN, half3(1,1,1)); float3 heights = float3(h0, h1, h2) + (blend * 3.0); half height_start = max(max(heights.x, heights.y), heights.z) - contrast; half3 h = max(heights - height_start.xxx, half3(0,0,0)); blend = h / dot(h, half3(1,1,1)); return blend; } void ClearAllButAlbedo(inout MicroSplatLayer o, half3 display) { o.Albedo = display.rgb; o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } void ClearAllButAlbedo(inout MicroSplatLayer o, half display) { o.Albedo = half3(display, display, display); o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } half MicroShadow(float3 lightDir, half3 normal, half ao, half strength) { half shadow = saturate(abs(dot(normal, lightDir)) + (ao * ao * 2.0) - 1.0); return 1 - ((1-shadow) * strength); } void DoDebugOutput(inout MicroSplatLayer l) { #if _DEBUG_OUTPUT_ALBEDO ClearAllButAlbedo(l, l.Albedo); #elif _DEBUG_OUTPUT_NORMAL // oh unit shader compiler normal stripping, how I hate you so.. // must multiply by albedo to stop the normal from being white. Why, fuck knows? ClearAllButAlbedo(l, float3(l.Normal.xy * 0.5 + 0.5, l.Normal.z * saturate(l.Albedo.z+1))); #elif _DEBUG_OUTPUT_SMOOTHNESS ClearAllButAlbedo(l, l.Smoothness.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_METAL ClearAllButAlbedo(l, l.Metallic.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_AO ClearAllButAlbedo(l, l.Occlusion.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_EMISSION ClearAllButAlbedo(l, l.Emission * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_HEIGHT ClearAllButAlbedo(l, l.Height.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_SPECULAR && _USESPECULARWORKFLOW ClearAllButAlbedo(l, l.Specular * saturate(l.Albedo.z+1)); #elif _DEBUG_BRANCHCOUNT_WEIGHT ClearAllButAlbedo(l, _branchWeightCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TRIPLANAR ClearAllButAlbedo(l, _branchTriplanarCount / 24 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_CLUSTER ClearAllButAlbedo(l, _branchClusterCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_OTHER ClearAllButAlbedo(l, _branchOtherCount / 8 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TOTAL l.Albedo.r = _branchWeightCount / 12; l.Albedo.g = _branchTriplanarCount / 24; l.Albedo.b = _branchClusterCount / 12; ClearAllButAlbedo(l, (l.Albedo.r + l.Albedo.g + l.Albedo.b + (_branchOtherCount / 8)) / 4); #elif _DEBUG_OUTPUT_MICROSHADOWS ClearAllButAlbedo(l,l.Albedo); #elif _DEBUG_SAMPLECOUNT float sdisp = (float)_sampleCount / max(_SampleCountDiv, 1); half3 sdcolor = float3(sdisp, sdisp > 1 ? 1 : 0, 0); ClearAllButAlbedo(l, sdcolor * saturate(l.Albedo.z + 1)); #elif _DEBUG_PROCLAYERS ClearAllButAlbedo(l, (float)_procLayerCount / (float)_PCLayerCount * saturate(l.Albedo.z + 1)); #endif } // abstraction around sampler mode #if _USELODMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_LOD(tex, sampler##tex, u, l.x) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u, l.x) #elif _USEGRADMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_GRAD(tex, sampler##tex, u, l.xy, l.zw) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY_GRAD(tex, ss, u.xy, u.z, l.xy, l.zw) #else #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, u.xy, u.z) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u.xy, y.z) #endif #define MICROSPLAT_SAMPLE_DIFFUSE(u, cl, l) MICROSPLAT_SAMPLE(_Diffuse, u, l) #define MICROSPLAT_SAMPLE_EMIS(u, cl, l) MICROSPLAT_SAMPLE(_EmissiveMetal, u, l) #define MICROSPLAT_SAMPLE_DIFFUSE_LOD(u, cl, l) UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, u, l) #if _PACKINGHQ #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) half4(MICROSPLAT_SAMPLE(_NormalSAO, u, l).ga, MICROSPLAT_SAMPLE(_SmoothAO, u, l).ga).brag #else #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) MICROSPLAT_SAMPLE(_NormalSAO, u, l) #endif #if _USESPECULARWORKFLOW #define MICROSPLAT_SAMPLE_SPECULAR(u, cl, l) MICROSPLAT_SAMPLE(_Specular, u, l) #endif struct SimpleTriplanarConfig { float3 pn; float2 uv0; float2 uv1; float2 uv2; }; void PrepSimpleTriplanarConfig(inout SimpleTriplanarConfig tc, float3 worldPos, float3 normal, float contrast) { tc.pn = pow(abs(normal), contrast); tc.pn = tc.pn / (tc.pn.x + tc.pn.y + tc.pn.z); half3 axisSign = sign(normal); tc.uv0 = worldPos.zy * axisSign.x; tc.uv1 = worldPos.xz * axisSign.y; tc.uv2 = worldPos.xy * axisSign.z; } #define SimpleTriplanarSample(tex, tc, scale) (SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv0 * scale) * tc.pn.x + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv1 * scale) * tc.pn.y + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv2 * scale) * tc.pn.z) #define SimpleTriplanarSampleLOD(tex, tc, scale, lod) (SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv0 * scale, lod) * tc.pn.x + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv1 * scale, lod) * tc.pn.y + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv2 * scale, lod) * tc.pn.z) #define SimpleTriplanarSampleGrad(tex, tc, scale) (SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv0 * scale, ddx(tc.uv0) * scale, ddy(tc.uv0) * scale) * tc.pn.x + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv1 * scale, ddx(tc.uv1) * scale, ddy(tc.uv1) * scale) * tc.pn.y + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv2 * scale, ddx(tc.uv2) * scale, ddy(tc.uv2) * scale) * tc.pn.z) inline half3 MicroSplatDiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity) { specColor = lerp (half3(0,0,0), albedo, metallic); oneMinusReflectivity = (1-metallic); return albedo * oneMinusReflectivity; } Input DescToInput(ShaderData IN) { Input s = (Input)0; s.shaderData = IN; s.TBN = IN.TBNMatrix; s.worldNormal = IN.worldSpaceNormal; s.worldPos = IN.worldSpacePosition; s.viewDir = IN.tangentSpaceViewDir; s.uv_Control0 = IN.texcoord0.xy; s.worldUpVector = float3(0,1,0); s.worldHeight = IN.worldSpacePosition.y; #if _PLANETVECTORS float3 rwp = mul(_PQSToLocal, float4(IN.worldSpacePosition, 1)); s.worldHeight = distance(rwp, float3(0,0,0)); s.worldUpVector = normalize(rwp); #endif #if _MICROMESH && _MESHUV2 s.uv2_Diffuse = IN.texcoord1.xy; #endif #if _MEGASPLAT UnpackMegaSplat(s, IN); #endif #if _MICROVERTEXMESH || _MICRODIGGERMESH UnpackVertexWorkflow(s, IN); #endif #if _PLANETVECTORS DoPlanetDataInputCopy(s, IN); #endif return s; } void SampleAlbedo(inout Config config, inout TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half4 contrasts = _Contrast.xxxx; #if _PERTEXTRIPLANARCONTRAST SAMPLE_PER_TEX(ptc, 9.5, config, half4(1,0.5,0,0)); contrasts = half4(ptc0.y, ptc1.y, ptc2.y, ptc3.y); #endif #if _PERTEXTRIPLANAR SAMPLE_PER_TEX(pttri, 9.5, config, half4(0,0,0,0)); #endif { // For per-texture triplanar, we modify the view based blending factor of the triplanar // such that you get a pure blend of either top down projection, or with the top down projection // removed and renormalized. This causes dynamic flow control optimizations to kick in and avoid // the extra texture samples while keeping the code simple. Yay.. // We also only have to do this in the Albedo, because the pN values will be adjusted after the // albedo is sampled, causing future samples to use this data. #if _PERTEXTRIPLANAR if (pttri0.x > 0.66) { tc.pN0 = half3(0,1,0); } else if (pttri0.x > 0.33) { tc.pN0.y = 0; tc.pN0.xz = TotalOne(tc.pN0.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } half3 bf = tc.pN0; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN0, contrasts.x); tc.pN0 = bf; #endif s.albedo0 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } MSBRANCH(weights.y) { #if _PERTEXTRIPLANAR if (pttri1.x > 0.66) { tc.pN1 = half3(0,1,0); } else if (pttri1.x > 0.33) { tc.pN1.y = 0; tc.pN1.xz = TotalOne(tc.pN1.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { COUNTSAMPLE a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[2], config.cluster1, d2); } half3 bf = tc.pN1; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN1, contrasts.x); tc.pN1 = bf; #endif s.albedo1 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { #if _PERTEXTRIPLANAR if (pttri2.x > 0.66) { tc.pN2 = half3(0,1,0); } else if (pttri2.x > 0.33) { tc.pN2.y = 0; tc.pN2.xz = TotalOne(tc.pN2.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } half3 bf = tc.pN2; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN2, contrasts.x); tc.pN2 = bf; #endif s.albedo2 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { #if _PERTEXTRIPLANAR if (pttri3.x > 0.66) { tc.pN3 = half3(0,1,0); } else if (pttri3.x > 0.33) { tc.pN3.y = 0; tc.pN3.xz = TotalOne(tc.pN3.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } half3 bf = tc.pN3; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN3, contrasts.x); tc.pN3 = bf; #endif s.albedo3 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #else s.albedo0 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.albedo1 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.albedo2 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.albedo3 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #if _PERTEXHEIGHTOFFSET || _PERTEXHEIGHTCONTRAST SAMPLE_PER_TEX(ptHeight, 10.5, config, 1); #if _PERTEXHEIGHTOFFSET s.albedo0.a = saturate(s.albedo0.a + ptHeight0.b - 1); s.albedo1.a = saturate(s.albedo1.a + ptHeight1.b - 1); s.albedo2.a = saturate(s.albedo2.a + ptHeight2.b - 1); s.albedo3.a = saturate(s.albedo3.a + ptHeight3.b - 1); #endif #if _PERTEXHEIGHTCONTRAST s.albedo0.a = saturate(pow(s.albedo0.a + 0.5, abs(ptHeight0.a)) - 0.5); s.albedo1.a = saturate(pow(s.albedo1.a + 0.5, abs(ptHeight1.a)) - 0.5); s.albedo2.a = saturate(pow(s.albedo2.a + 0.5, abs(ptHeight2.a)) - 0.5); s.albedo3.a = saturate(pow(s.albedo3.a + 0.5, abs(ptHeight3.a)) - 0.5); #endif #endif } void SampleNormal(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _NONORMALMAP || _AUTONORMAL s.normSAO0 = half4(0,0, 0, 1); s.normSAO1 = half4(0,0, 0, 1); s.normSAO2 = half4(0,0, 0, 1); s.normSAO3 = half4(0,0, 0, 1); return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half3 absVertNormal = abs(tc.IN.worldNormal); float3x3 t2w = tc.IN.TBN; { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[0], config.cluster0, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[1], config.cluster0, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[2], config.cluster0, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf0 = SurfgradFromTriplanarProjection(tc.pN0, a0.xy, a1.xy, a2.xy); #else s.normSAO0.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN0, a0.xy, a1.xy, a2.xy); #endif s.normSAO0.zw = a0.zw * tc.pN0.x + a1.zw * tc.pN0.y + a2.zw * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[0], config.cluster1, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[1], config.cluster1, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[2], config.cluster1, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf1 = SurfgradFromTriplanarProjection(tc.pN1, a0.xy, a1.xy, a2.xy); #else s.normSAO1.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN1, a0.xy, a1.xy, a2.xy); #endif s.normSAO1.zw = a0.zw * tc.pN1.x + a1.zw * tc.pN1.y + a2.zw * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[0], config.cluster2, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[1], config.cluster2, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[2], config.cluster2, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf2 = SurfgradFromTriplanarProjection(tc.pN2, a0.xy, a1.xy, a2.xy); #else s.normSAO2.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN2, a0.xy, a1.xy, a2.xy); #endif s.normSAO2.zw = a0.zw * tc.pN2.x + a1.zw * tc.pN2.y + a2.zw * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[0], config.cluster3, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[1], config.cluster3, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[2], config.cluster3, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf3 = SurfgradFromTriplanarProjection(tc.pN3, a0.xy, a1.xy, a2.xy); #else s.normSAO3.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN3, a0.xy, a1.xy, a2.xy); #endif s.normSAO3.zw = a0.zw * tc.pN3.x + a1.zw * tc.pN3.y + a2.zw * tc.pN3.z; } #endif #else s.normSAO0 = MICROSPLAT_SAMPLE_NORMAL(config.uv0, config.cluster0, mipLevel).agrb; COUNTSAMPLE s.normSAO0.xy = s.normSAO0.xy * 2 - 1; #if _SURFACENORMALS s.surf0 = ConvertNormal2ToGradient(s.normSAO0.xy); #endif MSBRANCH(weights.y) { s.normSAO1 = MICROSPLAT_SAMPLE_NORMAL(config.uv1, config.cluster1, mipLevel).agrb; COUNTSAMPLE s.normSAO1.xy = s.normSAO1.xy * 2 - 1; #if _SURFACENORMALS s.surf1 = ConvertNormal2ToGradient(s.normSAO1.xy); #endif } #if !_MAX2LAYER MSBRANCH(weights.z) { s.normSAO2 = MICROSPLAT_SAMPLE_NORMAL(config.uv2, config.cluster2, mipLevel).agrb; COUNTSAMPLE s.normSAO2.xy = s.normSAO2.xy * 2 - 1; #if _SURFACENORMALS s.surf2 = ConvertNormal2ToGradient(s.normSAO2.xy); #endif } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.normSAO3 = MICROSPLAT_SAMPLE_NORMAL(config.uv3, config.cluster3, mipLevel).agrb; COUNTSAMPLE s.normSAO3.xy = s.normSAO3.xy * 2 - 1; #if _SURFACENORMALS s.surf3 = ConvertNormal2ToGradient(s.normSAO3.xy); #endif } #endif #endif } void SampleEmis(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USEEMISSIVEMETAL #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.emisMetal0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.emisMetal1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.emisMetal2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.emisMetal3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.emisMetal0 = MICROSPLAT_SAMPLE_EMIS(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.emisMetal1 = MICROSPLAT_SAMPLE_EMIS(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.emisMetal2 = MICROSPLAT_SAMPLE_EMIS(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.emisMetal3 = MICROSPLAT_SAMPLE_EMIS(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } void SampleSpecular(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USESPECULARWORKFLOW #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.specular0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.specular1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.specular2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.specular3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.specular0 = MICROSPLAT_SAMPLE_SPECULAR(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.specular1 = MICROSPLAT_SAMPLE_SPECULAR(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.specular2 = MICROSPLAT_SAMPLE_SPECULAR(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.specular3 = MICROSPLAT_SAMPLE_SPECULAR(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } MicroSplatLayer Sample(Input i, half4 weights, inout Config config, float camDist, float3 worldNormalVertex, DecalOutput decalOutput) { MicroSplatLayer o = (MicroSplatLayer)0; UNITY_INITIALIZE_OUTPUT(MicroSplatLayer,o); RawSamples samples = (RawSamples)0; InitRawSamples(samples); half4 albedo = 0; half4 normSAO = half4(0,0,0,1); half3 surfGrad = half3(0,0,0); half4 emisMetal = 0; half3 specular = 0; float worldHeight = i.worldPos.y; float3 upVector = float3(0,1,0); #if _GLOBALTINT || _GLOBALNORMALS || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _GLOBALSPECULAR float globalSlopeFilter = 1; #if _GLOBALSLOPEFILTER float2 gfilterUV = float2(1 - saturate(dot(worldNormalVertex, upVector) * 0.5 + 0.49), 0.5); globalSlopeFilter = SAMPLE_TEXTURE2D(_GlobalSlopeTex, sampler_Diffuse, gfilterUV).a; #endif #endif // declare outside of branchy areas.. half4 fxLevels = half4(0,0,0,0); half burnLevel = 0; half wetLevel = 0; half3 waterNormalFoam = half3(0, 0, 0); half porosity = 0.4; float streamFoam = 1.0f; half pud = 0; half snowCover = 0; half SSSThickness = 0; half3 SSSTint = half3(1,1,1); float traxBuffer = 0; float3 traxNormal = 0; float2 noiseUV = 0; #if _SPLATFADE MSBRANCHOTHER(1 - saturate(camDist - _SplatFade.y)) { #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE || _SNOWFOOTSTEPS traxBuffer = SampleTraxBuffer(i.worldPos, worldNormalVertex, traxNormal); #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA #if _MICROMESH fxLevels = SampleFXLevels(InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, config.uv), wetLevel, burnLevel, traxBuffer); #elif _MICROVERTEXMESH || _MICRODIGGERMESH || _MEGASPLAT fxLevels = ProcessFXLevels(i.fx, traxBuffer); #else fxLevels = SampleFXLevels(config.uv, wetLevel, burnLevel, traxBuffer); #endif #endif #if _DECAL fxLevels = max(fxLevels, decalOutput.fxLevels); #endif TriplanarConfig tc = (TriplanarConfig)0; UNITY_INITIALIZE_OUTPUT(TriplanarConfig,tc); MIPFORMAT albedoLOD = INITMIPFORMAT MIPFORMAT normalLOD = INITMIPFORMAT MIPFORMAT emisLOD = INITMIPFORMAT MIPFORMAT specLOD = INITMIPFORMAT MIPFORMAT origAlbedoLOD = INITMIPFORMAT; #if _TRIPLANAR && !_DISABLESPLATMAPS PrepTriplanar(i.shaderData.texcoord0, worldNormalVertex, i.worldPos, config, tc, weights, albedoLOD, normalLOD, emisLOD, origAlbedoLOD); tc.IN = i; #endif #if !_TRIPLANAR && !_DISABLESPLATMAPS #if _USELODMIP albedoLOD = ComputeMipLevel(config.uv0.xy, _Diffuse_TexelSize.zw); normalLOD = ComputeMipLevel(config.uv0.xy, _NormalSAO_TexelSize.zw); #if _USEEMISSIVEMETAL emisLOD = ComputeMipLevel(config.uv0.xy, _EmissiveMetal_TexelSize.zw); #endif #if _USESPECULARWORKFLOW specLOD = ComputeMipLevel(config.uv0.xy, _Specular_TexelSize.zw);; #endif #elif _USEGRADMIP albedoLOD = float4(ddx(config.uv0.xy), ddy(config.uv0.xy)); normalLOD = albedoLOD; #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #endif origAlbedoLOD = albedoLOD; #endif #if _PERTEXCURVEWEIGHT SAMPLE_PER_TEX(ptCurveWeight, 19.5, config, half4(0.5,1,1,1)); weights.x = lerp(smoothstep(0.5 - ptCurveWeight0.r, 0.5 + ptCurveWeight0.r, weights.x), weights.x, ptCurveWeight0.r*2); weights.y = lerp(smoothstep(0.5 - ptCurveWeight1.r, 0.5 + ptCurveWeight1.r, weights.y), weights.y, ptCurveWeight1.r*2); weights.z = lerp(smoothstep(0.5 - ptCurveWeight2.r, 0.5 + ptCurveWeight2.r, weights.z), weights.z, ptCurveWeight2.r*2); weights.w = lerp(smoothstep(0.5 - ptCurveWeight3.r, 0.5 + ptCurveWeight3.r, weights.w), weights.w, ptCurveWeight3.r*2); weights = TotalOne(weights); #endif // uvScale before anything #if _PERTEXUVSCALEOFFSET && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); config.uv0.xy = config.uv0.xy * ptUVScale0.rg + ptUVScale0.ba; config.uv1.xy = config.uv1.xy * ptUVScale1.rg + ptUVScale1.ba; #if !_MAX2LAYER config.uv2.xy = config.uv2.xy * ptUVScale2.rg + ptUVScale2.ba; #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = config.uv3.xy * ptUVScale3.rg + ptUVScale3.ba; #endif // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = albedoLOD * ptUVScale0.rgrg * weights.x + albedoLOD * ptUVScale1.rgrg * weights.y + albedoLOD * ptUVScale2.rgrg * weights.z + albedoLOD * ptUVScale3.rgrg * weights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #if _PERTEXUVROTATION && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVRot, 16.5, config, half4(0,0,0,0)); config.uv0.xy = RotateUV(config.uv0.xy, ptUVRot0.x); config.uv1.xy = RotateUV(config.uv1.xy, ptUVRot1.x); #if !_MAX2LAYER config.uv2.xy = RotateUV(config.uv2.xy, ptUVRot2.x); #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = RotateUV(config.uv3.xy, ptUVRot0.x); #endif #endif o.Alpha = 1; #if _POM && !_DISABLESPLATMAPS DoPOM(i, config, tc, albedoLOD, weights, camDist, worldNormalVertex); #endif SampleAlbedo(config, tc, samples, albedoLOD, weights); #if _NOISEHEIGHT ApplyNoiseHeight(samples, config.uv, config, i.worldPos, worldNormalVertex); #endif #if _STREAMS || (_PARALLAX && !_DISABLESPLATMAPS) half earlyHeight = BlendWeights(samples.albedo0.w, samples.albedo1.w, samples.albedo2.w, samples.albedo3.w, weights); #endif #if _STREAMS waterNormalFoam = GetWaterNormal(i, config.uv, worldNormalVertex); DoStreamRefract(config, tc, waterNormalFoam, fxLevels.b, earlyHeight); #endif #if _PARALLAX && !_DISABLESPLATMAPS DoParallax(i, earlyHeight, config, tc, samples, weights, camDist); #endif // Blend results #if _PERTEXINTERPCONTRAST && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptContrasts, 1.5, config, 0.5); half4 contrast = 0.5; contrast.x = ptContrasts0.a; contrast.y = ptContrasts1.a; #if !_MAX2LAYER contrast.z = ptContrasts2.a; #endif #if !_MAX3LAYER || !_MAX2LAYER contrast.w = ptContrasts3.a; #endif contrast = clamp(contrast + _Contrast, 0.0001, 1.0); half cnt = contrast.x * weights.x + contrast.y * weights.y + contrast.z * weights.z + contrast.w * weights.w; half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, cnt); #else half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, _Contrast); #endif #if _HYBRIDHEIGHTBLEND heightWeights = lerp(heightWeights, TotalOne(weights), saturate(camDist/max(1.0, _HybridHeightBlendDistance))); #endif // rescale derivatives after height weighting. Basically, in gradmip mode we blend the mip levels, // but this is before height mapping is sampled, so reblending them after alpha will make sure the other // channels (normal, etc) are sharper, which likely matters most.. #if _PERTEXUVSCALEOFFSET && !_DISABLESPLATMAPS #if _TRIPLANAR #if _USEGRADMIP SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); albedoLOD.d0 = origAlbedoLOD.d0 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d0 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d0 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d0 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d1 = origAlbedoLOD.d1 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d1 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d1 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d1 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d2 = origAlbedoLOD.d2 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d2 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d2 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d2 * ptUVScale3.xyxy * heightWeights.w; normalLOD.d0 = albedoLOD.d0; normalLOD.d1 = albedoLOD.d1; normalLOD.d2 = albedoLOD.d2; #if _USEEMISSIVEMETAL emisLOD.d0 = albedoLOD.d0; emisLOD.d1 = albedoLOD.d1; emisLOD.d2 = albedoLOD.d2; #endif #endif // gradmip #else // not triplanar // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = origAlbedoLOD * ptUVScale0.rgrg * heightWeights.x + origAlbedoLOD * ptUVScale1.rgrg * heightWeights.y + origAlbedoLOD * ptUVScale2.rgrg * heightWeights.z + origAlbedoLOD * ptUVScale3.rgrg * heightWeights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #endif #if _PARALLAX || _STREAMS SampleAlbedo(config, tc, samples, albedoLOD, heightWeights); #endif SampleNormal(config, tc, samples, normalLOD, heightWeights); #if _USEEMISSIVEMETAL SampleEmis(config, tc, samples, emisLOD, heightWeights); #endif #if _USESPECULARWORKFLOW SampleSpecular(config, tc, samples, specLOD, heightWeights); #endif #if _DISTANCERESAMPLE && !_DISABLESPLATMAPS DistanceResample(samples, config, tc, camDist, i.viewDir, fxLevels, albedoLOD, i.worldPos, heightWeights, worldNormalVertex); #endif #if _STARREACHFORMAT samples.normSAO0.w = length(samples.normSAO0.xy); samples.normSAO1.w = length(samples.normSAO1.xy); samples.normSAO2.w = length(samples.normSAO2.xy); samples.normSAO3.w = length(samples.normSAO3.xy); #endif // PerTexture sampling goes here, passing the samples structure #if _PERTEXMICROSHADOWS || _PERTEXFUZZYSHADE SAMPLE_PER_TEX(ptFuzz, 17.5, config, half4(0, 0, 1, 1)); #endif #if _PERTEXMICROSHADOWS #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) { half3 lightDir = GetGlobalLightDirTS(i); half4 microShadows = half4(1,1,1,1); microShadows.x = MicroShadow(lightDir, half3(samples.normSAO0.xy, 1), samples.normSAO0.a, ptFuzz0.a); microShadows.y = MicroShadow(lightDir, half3(samples.normSAO1.xy, 1), samples.normSAO1.a, ptFuzz1.a); microShadows.z = MicroShadow(lightDir, half3(samples.normSAO2.xy, 1), samples.normSAO2.a, ptFuzz2.a); microShadows.w = MicroShadow(lightDir, half3(samples.normSAO3.xy, 1), samples.normSAO3.a, ptFuzz3.a); samples.normSAO0.a *= microShadows.x; samples.normSAO1.a *= microShadows.y; #if !_MAX2LAYER samples.normSAO2.a *= microShadows.z; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a *= microShadows.w; #endif #if _DEBUG_OUTPUT_MICROSHADOWS o.Albedo = BlendWeights(microShadows.x, microShadows.y, microShadows.z, microShadows.a, heightWeights); return o; #endif } #endif #endif // _PERTEXMICROSHADOWS #if _PERTEXFUZZYSHADE samples.albedo0.rgb = FuzzyShade(samples.albedo0.rgb, half3(samples.normSAO0.rg, 1), ptFuzz0.r, ptFuzz0.g, ptFuzz0.b, i.viewDir); samples.albedo1.rgb = FuzzyShade(samples.albedo1.rgb, half3(samples.normSAO1.rg, 1), ptFuzz1.r, ptFuzz1.g, ptFuzz1.b, i.viewDir); #if !_MAX2LAYER samples.albedo2.rgb = FuzzyShade(samples.albedo2.rgb, half3(samples.normSAO2.rg, 1), ptFuzz2.r, ptFuzz2.g, ptFuzz2.b, i.viewDir); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = FuzzyShade(samples.albedo3.rgb, half3(samples.normSAO3.rg, 1), ptFuzz3.r, ptFuzz3.g, ptFuzz3.b, i.viewDir); #endif #endif #if _PERTEXSATURATION && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptSaturattion, 9.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = lerp(MSLuminance(samples.albedo0.rgb), samples.albedo0.rgb, ptSaturattion0.a); samples.albedo1.rgb = lerp(MSLuminance(samples.albedo1.rgb), samples.albedo1.rgb, ptSaturattion1.a); #if !_MAX2LAYER samples.albedo2.rgb = lerp(MSLuminance(samples.albedo2.rgb), samples.albedo2.rgb, ptSaturattion2.a); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = lerp(MSLuminance(samples.albedo3.rgb), samples.albedo3.rgb, ptSaturattion3.a); #endif #endif #if _PERTEXTINT && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptTints, 1.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb *= ptTints0.rgb; samples.albedo1.rgb *= ptTints1.rgb; #if !_MAX2LAYER samples.albedo2.rgb *= ptTints2.rgb; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb *= ptTints3.rgb; #endif #endif #if _PCHEIGHTGRADIENT || _PCHEIGHTHSV || _PCSLOPEGRADIENT || _PCSLOPEHSV ProceduralGradients(i, samples, config, worldHeight, worldNormalVertex); #endif #if _WETNESS || _PUDDLES || _STREAMS porosity = _GlobalPorosity; #endif #if _PERTEXCOLORINTENSITY SAMPLE_PER_TEX(ptCI, 23.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = saturate(samples.albedo0.rgb * (1 + ptCI0.rrr)); samples.albedo1.rgb = saturate(samples.albedo1.rgb * (1 + ptCI1.rrr)); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb * (1 + ptCI2.rrr)); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb * (1 + ptCI3.rrr)); #endif #endif #if (_PERTEXBRIGHTNESS || _PERTEXCONTRAST || _PERTEXPOROSITY || _PERTEXFOAM) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptBC, 3.5, config, half4(1, 1, 1, 1)); #if _PERTEXCONTRAST samples.albedo0.rgb = saturate(((samples.albedo0.rgb - 0.5) * ptBC0.g) + 0.5); samples.albedo1.rgb = saturate(((samples.albedo1.rgb - 0.5) * ptBC1.g) + 0.5); #if !_MAX2LAYER samples.albedo2.rgb = saturate(((samples.albedo2.rgb - 0.5) * ptBC2.g) + 0.5); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(((samples.albedo3.rgb - 0.5) * ptBC3.g) + 0.5); #endif #endif #if _PERTEXBRIGHTNESS samples.albedo0.rgb = saturate(samples.albedo0.rgb + ptBC0.rrr); samples.albedo1.rgb = saturate(samples.albedo1.rgb + ptBC1.rrr); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb + ptBC2.rrr); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb + ptBC3.rrr); #endif #endif #if _PERTEXPOROSITY porosity = BlendWeights(ptBC0.b, ptBC1.b, ptBC2.b, ptBC3.b, heightWeights); #endif #if _PERTEXFOAM streamFoam = BlendWeights(ptBC0.a, ptBC1.a, ptBC2.a, ptBC3.a, heightWeights); #endif #endif #if (_PERTEXNORMSTR || _PERTEXAOSTR || _PERTEXSMOOTHSTR || _PERTEXMETALLIC) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(perTexMatSettings, 2.5, config, half4(1.0, 1.0, 1.0, 0.0)); #endif #if _PERTEXNORMSTR && !_DISABLESPLATMAPS #if _SURFACENORMALS samples.surf0 *= perTexMatSettings0.r; samples.surf1 *= perTexMatSettings1.r; samples.surf2 *= perTexMatSettings2.r; samples.surf3 *= perTexMatSettings3.r; #else samples.normSAO0.xy *= perTexMatSettings0.r; samples.normSAO1.xy *= perTexMatSettings1.r; samples.normSAO2.xy *= perTexMatSettings2.r; samples.normSAO3.xy *= perTexMatSettings3.r; #endif #endif #if _PERTEXAOSTR && !_DISABLESPLATMAPS samples.normSAO0.a = pow(abs(samples.normSAO0.a), perTexMatSettings0.b); samples.normSAO1.a = pow(abs(samples.normSAO1.a), perTexMatSettings1.b); #if !_MAX2LAYER samples.normSAO2.a = pow(abs(samples.normSAO2.a), perTexMatSettings2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a = pow(abs(samples.normSAO3.a), perTexMatSettings3.b); #endif #endif #if _PERTEXSMOOTHSTR && !_DISABLESPLATMAPS samples.normSAO0.b += perTexMatSettings0.g; samples.normSAO1.b += perTexMatSettings1.g; samples.normSAO0.b = saturate(samples.normSAO0.b); samples.normSAO1.b = saturate(samples.normSAO1.b); #if !_MAX2LAYER samples.normSAO2.b += perTexMatSettings2.g; samples.normSAO2.b = saturate(samples.normSAO2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.b += perTexMatSettings3.g; samples.normSAO3.b = saturate(samples.normSAO3.b); #endif #endif #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) #if _PERTEXSSS { SAMPLE_PER_TEX(ptSSS, 18.5, config, half4(1, 1, 1, 1)); // tint, thickness half4 vals = ptSSS0 * heightWeights.x + ptSSS1 * heightWeights.y + ptSSS2 * heightWeights.z + ptSSS3 * heightWeights.w; SSSThickness = vals.a; SSSTint = vals.rgb; } #endif #endif #if _PERTEXRIMLIGHT { SAMPLE_PER_TEX(ptRimA, 26.5, config, half4(1, 1, 1, 1)); SAMPLE_PER_TEX(ptRimB, 27.5, config, half4(1, 1, 1, 0)); samples.emisMetal0.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO0.xy, 1))), max(0.0001, ptRimA0.g)) * ptRimB0.rgb * ptRimB0.a; samples.emisMetal1.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO1.xy, 1))), max(0.0001, ptRimA1.g)) * ptRimB1.rgb * ptRimB1.a; samples.emisMetal2.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO2.xy, 1))), max(0.0001, ptRimA2.g)) * ptRimB2.rgb * ptRimB2.a; samples.emisMetal3.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO3.xy, 1))), max(0.0001, ptRimA3.g)) * ptRimB3.rgb * ptRimB3.a; } #endif #if (((_DETAILNOISE && _PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && _PERTEXDISTANCENOISESTRENGTH)) || (_NORMALNOISE && _PERTEXNORMALNOISESTRENGTH)) && !_DISABLESPLATMAPS ApplyDetailDistanceNoisePerTex(samples, config, camDist, i.worldPos, worldNormalVertex); #endif #if _GLOBALNOISEUV // noise defaults so that a value of 1, 1 is 4 pixels in size and moves the uvs by 1 pixel max. #if _CUSTOMSPLATTEXTURES noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #else noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE ApplyTrax(samples, config, i.worldPos, traxBuffer, traxNormal); #endif #if (_ANTITILEARRAYDETAIL || _ANTITILEARRAYDISTANCE || _ANTITILEARRAYNORMAL) && !_DISABLESPLATMAPS ApplyAntiTilePerTex(samples, config, camDist, i.worldPos, worldNormalVertex, heightWeights); #endif #if _GEOMAP && !_DISABLESPLATMAPS GeoTexturePerTex(samples, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _GLOBALTINT && _PERTEXGLOBALTINTSTRENGTH && !_DISABLESPLATMAPS GlobalTintTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALNORMALS && _PERTEXGLOBALNORMALSTRENGTH && !_DISABLESPLATMAPS GlobalNormalTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && _PERTEXGLOBALSAOMSTRENGTH && !_DISABLESPLATMAPS GlobalSAOMTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && _PERTEXGLOBALEMISSTRENGTH && !_DISABLESPLATMAPS GlobalEmisTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && _PERTEXGLOBALSPECULARSTRENGTH && !_DISABLESPLATMAPS && _USESPECULARWORKFLOW GlobalSpecularTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _PERTEXMETALLIC && !_DISABLESPLATMAPS half metallic = BlendWeights(perTexMatSettings0.a, perTexMatSettings1.a, perTexMatSettings2.a, perTexMatSettings3.a, heightWeights); o.Metallic = metallic; #endif #if _GLITTER && !_DISABLESPLATMAPS DoGlitter(i, samples, config, camDist, worldNormalVertex, i.worldPos); #endif // Blend em.. #if _DISABLESPLATMAPS // If we don't sample from the _Diffuse, then the shader compiler will strip the sampler on // some platforms, which will cause everything to break. So we sample from the lowest mip // and saturate to 1 to keep the cost minimal. Annoying, but the compiler removes the texture // and sampler, even though the sampler is still used. albedo = saturate(UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, float3(0,0,0), 12) + 1); albedo.a = 0.5; // make height something we can blend with for the combined mesh mode, since it still height blends. normSAO = half4(0,0,0,1); #else albedo = BlendWeights(samples.albedo0, samples.albedo1, samples.albedo2, samples.albedo3, heightWeights); normSAO = BlendWeights(samples.normSAO0, samples.normSAO1, samples.normSAO2, samples.normSAO3, heightWeights); #if _SURFACENORMALS surfGrad = BlendWeights(samples.surf0, samples.surf1, samples.surf2, samples.surf3, heightWeights); #endif #if (_USEEMISSIVEMETAL || _PERTEXRIMLIGHT) && !_DISABLESPLATMAPS emisMetal = BlendWeights(samples.emisMetal0, samples.emisMetal1, samples.emisMetal2, samples.emisMetal3, heightWeights); #endif #if _USESPECULARWORKFLOW && !_DISABLESPLATMAPS specular = BlendWeights(samples.specular0, samples.specular1, samples.specular2, samples.specular3, heightWeights); #endif #if _PERTEXOUTLINECOLOR SAMPLE_PER_TEX(ptOutlineColor, 28.5, config, half4(0.5, 0.5, 0.5, 1)); half4 outlineColor = BlendWeights(ptOutlineColor0, ptOutlineColor1, ptOutlineColor2, ptOutlineColor3, heightWeights); half4 tstr = saturate(abs(heightWeights - 0.5) * 2); half transitionBlend = min(min(min(tstr.x, tstr.y), tstr.z), tstr.w); albedo.rgb = lerp(albedo.rgb * outlineColor.rgb * 2, albedo.rgb, outlineColor.a * transitionBlend); #endif #endif #if _MESHOVERLAYSPLATS || _MESHCOMBINED o.Alpha = 1.0; if (config.uv0.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.x; else if (config.uv1.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.y; else if (config.uv2.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.z; else if (config.uv3.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.w; #endif // effects which don't require per texture adjustments and are part of the splats sample go here. // Often, as an optimization, you can compute the non-per tex version of above effects here.. #if ((_DETAILNOISE && !_PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && !_PERTEXDISTANCENOISESTRENGTH) || (_NORMALNOISE && !_PERTEXNORMALNOISESTRENGTH)) ApplyDetailDistanceNoise(albedo.rgb, normSAO, surfGrad, config, camDist, i.worldPos, worldNormalVertex); #endif #if _SPLATFADE } #endif #if _SPLATFADE float2 sfDX = ddx(config.uv * _UVScale); float2 sfDY = ddy(config.uv * _UVScale); MSBRANCHOTHER(camDist - _SplatFade.x) { float falloff = saturate(InverseLerp(_SplatFade.x, _SplatFade.y, camDist)); half4 sfalb = SAMPLE_TEXTURE2D_ARRAY_GRAD(_Diffuse, sampler_Diffuse, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY); COUNTSAMPLE albedo.rgb = lerp(albedo.rgb, sfalb.rgb, falloff); #if !_NONORMALMAP && !_AUTONORMAL half4 sfnormSAO = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalSAO, sampler_NormalSAO, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY).agrb; COUNTSAMPLE sfnormSAO.xy = sfnormSAO.xy * 2 - 1; normSAO = lerp(normSAO, sfnormSAO, falloff); #if _SURFACENORMALS surfGrad = lerp(surfGrad, ConvertNormal2ToGradient(sfnormSAO.xy), falloff); #endif #endif } #endif #if _AUTONORMAL float3 autoNormal = HeightToNormal(albedo.a * _AutoNormalHeightScale, i.worldPos); normSAO.xy = autoNormal; normSAO.z = 0; normSAO.w = (autoNormal.z * autoNormal.z); #endif #if _MESHCOMBINED SampleMeshCombined(albedo, normSAO, surfGrad, emisMetal, specular, o.Alpha, SSSThickness, SSSTint, config, heightWeights); #endif #if _ISOBJECTSHADER SampleObjectShader(i, albedo, normSAO, surfGrad, emisMetal, specular, config); #endif #if _GEOMAP GeoTexture(albedo.rgb, normSAO, surfGrad, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _SCATTER ApplyScatter( #if _MEGASPLAT config, #endif i, albedo, normSAO, surfGrad, config.uv, camDist); #endif #if _DECAL DoDecalBlend(decalOutput, albedo, normSAO, surfGrad, emisMetal, i.uv_Control0); #endif #if _GLOBALTINT && !_PERTEXGLOBALTINTSTRENGTH GlobalTintTexture(albedo.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif #if _VSGRASSMAP VSGrassTexture(albedo.rgb, config, camDist); #endif #if _GLOBALNORMALS && !_PERTEXGLOBALNORMALSTRENGTH GlobalNormalTexture(normSAO, surfGrad, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && !_PERTEXGLOBALSAOMSTRENGTH GlobalSAOMTexture(normSAO, emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && !_PERTEXGLOBALEMISSTRENGTH GlobalEmisTexture(emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && !_PERTEXGLOBALSPECULARSTRENGTH && _USESPECULARWORKFLOW GlobalSpecularTexture(specular.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif o.Albedo = albedo.rgb; o.Height = albedo.a; #if _NONORMALMAP o.Normal = half3(0,0,1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #elif _SURFACENORMALS o.Normal = ResolveNormalFromSurfaceGradient(surfGrad); o.Normal = mul(GetTBN(i), o.Normal); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #else o.Normal = half3(normSAO.xy, 1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #endif #if _USEEMISSIVEMETAL || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _PERTEXRIMLIGHT #if _USEEMISSIVEMETAL emisMetal.rgb *= _EmissiveMult; #endif o.Emission += emisMetal.rgb; o.Metallic = emisMetal.a; #endif #if _USESPECULARWORKFLOW o.Specular = specular; #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA pud = DoStreams(i, o, fxLevels, config.uv, porosity, waterNormalFoam, worldNormalVertex, streamFoam, wetLevel, burnLevel, i.worldPos); #endif #if _SNOW snowCover = DoSnow(i, o, config.uv, WorldNormalVector(i, o.Normal), worldNormalVertex, i.worldPos, pud, porosity, camDist, config, weights, SSSTint, SSSThickness, traxBuffer, traxNormal); #endif #if _PERTEXSSS || _MESHCOMBINEDUSESSS || (_SNOW && _SNOWSSS) { half3 worldView = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); o.Emission += ComputeSSS(i, worldView, WorldNormalVector(i, o.Normal), SSSTint, SSSThickness, _SSSDistance, _SSSScale, _SSSPower); } #endif #if _SNOWGLITTER DoSnowGlitter(i, config, o, camDist, worldNormalVertex, snowCover); #endif #if _WINDPARTICULATE || _SNOWPARTICULATE DoWindParticulate(i, o, config, weights, camDist, worldNormalVertex, snowCover); #endif o.Normal.z = sqrt(1 - saturate(dot(o.Normal.xy, o.Normal.xy))); #if _SPECULARFADE { float specFade = saturate((i.worldPos.y - _SpecularFades.x) / max(_SpecularFades.y - _SpecularFades.x, 0.0001)); o.Metallic *= specFade; o.Smoothness *= specFade; } #endif #if _VSSHADOWMAP VSShadowTexture(o, i, config, camDist); #endif #if _TOONWIREFRAME ToonWireframe(config.uv, o.Albedo, camDist); #endif #if _SEETHROUGHSHADER SeethroughShader(o.Albedo, o.Emission, o.Alpha, i.worldPos, o.Normal, i.worldNormal); #endif #if _DEBUG_TRAXBUFFER ClearAllButAlbedo(o, half3(traxBuffer, 0, 0) * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMALVERTEX ClearAllButAlbedo(o, worldNormalVertex * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMAL ClearAllButAlbedo(o, WorldNormalVector(i, o.Normal) * saturate(o.Albedo.z+1)); #endif #if _DEBUG_MEGABARY && _MEGASPLAT o.Albedo = i.baryWeights.xyz; #endif return o; } void SampleSplats(float2 controlUV, inout half4 w0, inout half4 w1, inout half4 w2, inout half4 w3, inout half4 w4, inout half4 w5, inout half4 w6, inout half4 w7) { #if _CUSTOMSPLATTEXTURES #if !_MICROMESH controlUV = (controlUV * (_CustomControl0_TexelSize.zw - 1.0f) + 0.5f) * _CustomControl0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_CustomControl0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_CustomControl1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_CustomControl2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_CustomControl3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_CustomControl4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_CustomControl5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_CustomControl6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_CustomControl7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #else #if !_MICROMESH controlUV = (controlUV * (_Control0_TexelSize.zw - 1.0f) + 0.5f) * _Control0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_Control0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_Control1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_Control2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_Control3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_Control4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_Control5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_Control6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_Control7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #endif } MicroSplatLayer SurfImpl(Input i, float3 worldNormalVertex) { #if _MEGANOUV i.uv_Control0 = i.worldPos.xz; #endif float camDist = distance(_WorldSpaceCameraPos, i.worldPos); #if _FORCELOCALSPACE worldNormalVertex = mul((float3x3)GetWorldToObjectMatrix(), worldNormalVertex).xyz; i.worldPos = i.worldPos - mul(GetObjectToWorldMatrix(), float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _ORIGINSHIFT i.worldPos = i.worldPos + mul(_GlobalOriginMTX, float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _DEBUG_USE_TOPOLOGY i.worldPos = SAMPLE_TEXTURE2D(_DebugWorldPos, sampler_Diffuse, i.uv_Control0); worldNormalVertex = SAMPLE_TEXTURE2D(_DebugWorldNormal, sampler_Diffuse, i.uv_Control0); i.worldHeight = i.worldPos.y; #endif #if _ALPHABELOWHEIGHT && !_TBDISABLEALPHAHOLES ClipWaterLevel(i.worldPos); #endif #if !_TBDISABLEALPHAHOLES && defined(_ALPHATEST_ON) // UNITY 2019.3 holes ClipHoles(i.uv_Control0); #endif float2 origUV = i.uv_Control0; #if _MICROMESH && _MESHUV2 float2 controlUV = i.uv2_Diffuse; #else float2 controlUV = i.uv_Control0; #endif #if _MICROMESH controlUV = InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, controlUV); #endif half4 weights = half4(1,0,0,0); Config config = (Config)0; UNITY_INITIALIZE_OUTPUT(Config,config); config.uv = origUV; DecalOutput decalOutput = (DecalOutput)0; #if _DECAL decalOutput = DoDecals(i.uv_Control0, i.worldPos, camDist, worldNormalVertex); #endif #if _SURFACENORMALS // Initialize the surface gradient basis vectors ConstructSurfaceGradientTBN(i); #endif #if _SPLATFADE MSBRANCHOTHER(_SplatFade.y - camDist) #endif // _SPLATFADE { #if !_DISABLESPLATMAPS // Sample the splat data, from textures or vertices, and setup the config.. #if _MICRODIGGERMESH DiggerSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLAT MegaSplatVertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLATTEXTURE MegaSplatTextureSetup(controlUV, weights, origUV, config, i.worldPos, decalOutput); #elif _MICROVERTEXMESH VertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif !_PROCEDURALTEXTURE || _PROCEDURALBLENDSPLATS half4 w0 = 0; half4 w1 = 0; half4 w2 = 0; half4 w3 = 0; half4 w4 = 0; half4 w5 = 0; half4 w6 = 0; half4 w7 = 0; SampleSplats(controlUV, w0, w1, w2, w3, w4, w5, w6, w7); Setup(weights, origUV, config, w0, w1, w2, w3, w4, w5, w6, w7, i.worldPos, decalOutput); #endif #if _PROCEDURALTEXTURE float3 procNormal = worldNormalVertex; float3 worldPos = i.worldPos; ProceduralSetup(i, worldPos, i.worldHeight, procNormal, i.worldUpVector, weights, origUV, config, ddx(origUV), ddy(origUV), ddx(worldPos), ddy(worldPos), decalOutput); #endif #else // _DISABLESPLATMAPS Setup(weights, origUV, config, half4(1,0,0,0), 0, 0, 0, 0, 0, 0, 0, i.worldPos, decalOutput); #endif #if _SLOPETEXTURE SlopeTexture(config, weights, worldNormalVertex); #endif } // _SPLATFADE else case #if _TOONFLATTEXTURE float2 quv = floor(origUV * _ToonTerrainSize); float2 fuv = frac(origUV * _ToonTerrainSize); #if !_TOONFLATTEXTUREQUAD quv = Hash2D((fuv.x > fuv.y) ? quv : quv * 0.333); #endif float2 uvq = quv / _ToonTerrainSize; config.uv0.xy = uvq; config.uv1.xy = uvq; config.uv2.xy = uvq; config.uv3.xy = uvq; #endif #if (_TEXTURECLUSTER2 || _TEXTURECLUSTER3) && !_DISABLESPLATMAPS PrepClusters(origUV, config, i.worldPos, worldNormalVertex); #endif #if (_ALPHAHOLE || _ALPHAHOLETEXTURE) && !_DISABLESPLATMAPS && !_TBDISABLEALPHAHOLES ClipAlphaHole(config, weights); #endif MicroSplatLayer l = Sample(i, weights, config, camDist, worldNormalVertex, decalOutput); // On windows, sometimes the shared samplers gets stripped, so we have to do this crap. // We sample from the lowest mip, so it shouldn't cost much, but still, I hate this, wtf.. float stripVal = saturate(SAMPLE_TEXTURE2D_LOD(_Diffuse, sampler_Diffuse, config.uv0, 11).r + 2); stripVal *= saturate(SAMPLE_TEXTURE2D_LOD(_NormalSAO, sampler_NormalSAO, config.uv0, 11).r + 2); l.Albedo *= stripVal; l.Normal *= stripVal; #if _PROCEDURALTEXTURE ProceduralTextureDebugOutput(l, weights, config); #endif return l; } float4 ConstructTerrainTangent(float3 normal, float3 positiveZ) { // Consider a flat terrain. It should have tangent be (1, 0, 0) and bitangent be (0, 0, 1) as the UV of the terrain grid mesh is a scale of the world XZ position. // In CreateTangentToWorld function (in SpaceTransform.hlsl), it is cross(normal, tangent) * sgn for the bitangent vector. // It is not true in a left-handed coordinate system for the terrain bitangent, if we provide 1 as the tangent.w. It would produce (0, 0, -1) instead of (0, 0, 1). // Also terrain's tangent calculation was wrong in a left handed system because cross((0,0,1), terrainNormalOS) points to the wrong direction as negative X. // Therefore all the 4 xyzw components of the tangent needs to be flipped to correct the tangent frame. // (See TerrainLitData.hlsl - GetSurfaceAndBuiltinData) float3 tangent = normalize(cross(normal, positiveZ)); return float4(tangent, -1); } void TerrainInstancing(inout float4 vertex, inout float3 normal, inout float2 uv) { #if _MICROTERRAIN && defined(UNITY_INSTANCING_ENABLED) && !_TERRAINBLENDABLESHADER float2 patchVertex = vertex.xy; float4 instanceData = UNITY_ACCESS_INSTANCED_PROP(Terrain, _TerrainPatchInstanceData); float2 sampleCoords = (patchVertex.xy + instanceData.xy) * instanceData.z; // (xy + float2(xBase,yBase)) * skipScale uv = sampleCoords * _TerrainHeightmapRecipSize.zw; float2 sampleUV = (uv / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleUV, 0)); vertex.xz = sampleCoords * _TerrainHeightmapScale.xz; vertex.y = height * _TerrainHeightmapScale.y; normal = float3(0, 1, 0); #endif } void ApplyMeshModification(inout VertexData input) { #if _MICROTERRAIN && !_TERRAINBLENDABLESHADER float2 uv = input.texcoord0.xy; TerrainInstancing(input.vertex, input.normal, uv); input.texcoord0.xy = uv; #endif #if _PERPIXNORMAL && !_TERRAINBLENDABLESHADER input.normal = float3(0,1,0); #endif #if _MICROVERSEPREVIEW float4 recipSize = _TerrainHeightmapTexture_TexelSize; recipSize.zw = (1.0f / (_TerrainHeightmapTexture_TexelSize.zw-1)); float2 sampleCoords = (input.texcoord0.xy / recipSize.zw + 0.5f) * recipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleCoords, 0)); input.vertex.xyz += float3(0,1,0) * height * _TerrainHeight * 2; #endif } // called by the template, so we can remove tangent from VertexData void ApplyTerrainTangent(inout VertexToPixel input) { #if (_MICROTERRAIN || _PERPIXNORMAL) && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif // digger meshes ain't got no tangent either.. #if _MICRODIGGERMESH && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif } void ModifyVertex(inout VertexData v, inout ExtraV2F d) { ApplyMeshModification(v); #if _MICROVERTEXMESH || _MICRODIGGERMESH EncodeVertexWorkflow(v, d); #elif _MEGASPLAT EncodeMegaSplatVertex(v, d); #endif } void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d) { #if _MICROVERSEPREVIEW v.vertex.y = OffsetVertex(v, d).y; #elif _TESSDISTANCE || _TESSEDGE v.vertex.xyz += OffsetVertex(v, d); #endif } float3 GetTessFactors () { #if _TESSEDGE return float3(_TessData1.x, _TessData1.w, 0); #endif #if _TESSDISTANCE return float3(_TessData2.x, _TessData2.y, _TessData1.x); #endif return 0; } void SurfaceFunction(inout Surface o, inout ShaderData d) { float3 worldNormalVertex = d.worldSpaceNormal; #if _MICROVERSEPREVIEW float2 sampleCoords = d.texcoord0.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif (defined(UNITY_INSTANCING_ENABLED) && _MICROTERRAIN && !_TERRAINBLENDABLESHADER) float2 sampleCoords = (d.texcoord0.xy / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif _PERPIXNORMAL && (_MICROTERRAIN || _MICROMESHTERRAIN) && !_TERRAINBLENDABLESHADER float2 sampleCoords = (d.texcoord0.xy * _PerPixelNormal_TexelSize.zw + 0.5f) * _PerPixelNormal_TexelSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_PerPixelNormal, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomTangent, geomNormal)) * -1; #if _MICROMESHTERRAIN geomBitangent *= -1; #endif worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #endif #if _TOONPOLYEDGE FlatShade(d); #endif Input i = DescToInput(d); #if _SRPTERRAINBLEND MicroSplatLayer l = BlendWithTerrain(d); #if _DEBUG_WORLDNORMAL ClearAllButAlbedo(l, normalize(TangentToWorldSpace(d, l.Normal)) * saturate(l.Albedo.z+1)); #endif #else MicroSplatLayer l = SurfImpl(i, worldNormalVertex); #endif DoDebugOutput(l); o.Albedo = l.Albedo; o.Normal = l.Normal; o.Smoothness = l.Smoothness; o.Occlusion = l.Occlusion; o.Metallic = l.Metallic; o.Emission = l.Emission; #if _USESPECULARWORKFLOW o.Specular = l.Specular; #endif o.Height = l.Height; o.Alpha = l.Alpha; } // SHADERDESC ShaderData CreateShaderData(VertexToPixel i) { ShaderData d = (ShaderData)0; d.worldSpacePosition = i.worldPos; d.worldSpaceNormal = i.worldNormal; d.worldSpaceTangent = i.worldTangent.xyz; float3 bitangent = cross(i.worldTangent.xyz, i.worldNormal) * i.worldTangent.w * -1; d.TBNMatrix = float3x3(d.worldSpaceTangent, bitangent, d.worldSpaceNormal); d.worldSpaceViewDir = normalize(_WorldSpaceCameraPos - i.worldPos); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); d.texcoord0 = i.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER d.texcoord1 = i.texcoord1; // d.texcoord2 = i.texcoord2; #endif // d.texcoord3 = i.texcoord3; // d.vertexColor = i.vertexColor; // these rarely get used, so we back transform them. Usually will be stripped. #if _HDRP // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(GetCameraRelativePositionWS(i.worldPos), 1)); #else // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(i.worldPos, 1)); #endif // d.localSpaceNormal = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldNormal)); // d.localSpaceTangent = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldTangent.xyz)); // d.screenPos = i.screenPos; // d.screenUV = i.screenPos.xy / i.screenPos.w; // d.extraV2F0 = i.extraV2F0; // d.extraV2F1 = i.extraV2F1; // d.extraV2F2 = i.extraV2F2; // d.extraV2F3 = i.extraV2F3; // d.extraV2F4 = i.extraV2F4; // d.extraV2F5 = i.extraV2F5; // d.extraV2F6 = i.extraV2F6; // d.extraV2F7 = i.extraV2F7; return d; } // CHAINS void ChainModifyVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; ModifyVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainModifyTessellatedVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; // d.extraV2F0 = v2p.extraV2F0; // d.extraV2F1 = v2p.extraV2F1; // d.extraV2F2 = v2p.extraV2F2; // d.extraV2F3 = v2p.extraV2F3; // d.extraV2F4 = v2p.extraV2F4; // d.extraV2F5 = v2p.extraV2F5; // d.extraV2F6 = v2p.extraV2F6; // d.extraV2F7 = v2p.extraV2F7; ModifyTessellatedVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainFinalColorForward(inout Surface l, inout ShaderData d, inout half4 color) { } void ChainFinalGBufferStandard(inout Surface s, inout ShaderData d, inout half4 GBuffer0, inout half4 GBuffer1, inout half4 GBuffer2, inout half4 outEmission, inout half4 outShadowMask) { } #if _PASSSHADOW float3 _LightDirection; float3 _LightPosition; #endif // vertex shader VertexToPixel Vert (VertexData v) { VertexToPixel o = (VertexToPixel)0; UNITY_SETUP_INSTANCE_ID(v); UNITY_TRANSFER_INSTANCE_ID(v, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #if !_TESSELLATION_ON ChainModifyVertex(v, o); #endif o.texcoord0 = v.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.texcoord1 = v.texcoord1; // o.texcoord2 = v.texcoord2; #endif // o.texcoord3 = v.texcoord3; // o.vertexColor = v.vertexColor; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz); o.worldPos = TransformObjectToWorld(v.vertex.xyz); o.worldNormal = TransformObjectToWorldNormal(v.normal); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float2 uv1 = v.texcoord1.xy; float2 uv2 = v.texcoord2.xy; o.worldTangent = float4(TransformObjectToWorldDir(v.tangent.xyz), v.tangent.w); #else float2 uv1 = v.texcoord0.xy; float2 uv2 = uv1; #endif // MS Only ApplyTerrainTangent(o); #if _PASSSHADOW #if _CASTING_PUNCTUAL_LIGHT_SHADOW float3 lightDirectionWS = normalize(_LightPosition - o.worldPos); #else float3 lightDirectionWS = _LightDirection; #endif // Define shadow pass specific clip position for Universal o.pos = TransformWorldToHClip(ApplyShadowBias(o.worldPos, o.worldNormal, lightDirectionWS)); #if UNITY_REVERSED_Z o.pos.z = min(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #else o.pos.z = max(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #endif #elif _PASSMETA #if _MICROTERRAIN o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), v.texcoord0.xy, v.texcoord0.xy, unity_LightmapST, unity_DynamicLightmapST); #else o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), uv1, uv2, unity_LightmapST, unity_DynamicLightmapST); #endif #else o.pos = TransformWorldToHClip(o.worldPos); #endif // o.screenPos = ComputeScreenPos(o.pos, _ProjectionParams.x); #if _PASSFORWARD || _PASSGBUFFER #if _MICROTERRAIN OUTPUT_LIGHTMAP_UV(v.texcoord0.xy, unity_LightmapST, o.lightmapUV); #else OUTPUT_LIGHTMAP_UV(uv1, unity_LightmapST, o.lightmapUV); #endif OUTPUT_SH(o.worldNormal, o.sh); #if defined(DYNAMICLIGHTMAP_ON) o.dynamicLightmapUV.xy = uv2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; #endif #endif #ifdef VARYINGS_NEED_FOG_AND_VERTEX_LIGHT half fogFactor = 0; #if defined(_FOG_FRAGMENT) fogFactor = ComputeFogFactor(o.pos.z); #endif #if _BAKEDLIT half3 vertexLight = VertexLighting(o.worldPos, o.worldNormal); o.fogFactorAndVertexLight = half4(fogFactor, vertexLight); #else o.fogFactorAndVertexLight = half4(fogFactor, 0,0,0); #endif #endif #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) o.shadowCoord = GetShadowCoord(vertexInput); #endif return o; } #if _UNLIT #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Unlit.hlsl" #endif // fragment shader void Frag (VertexToPixel IN , out half4 outColor : SV_Target0 #ifdef _WRITE_RENDERING_LAYERS , out float4 outRenderingLayers : SV_Target1 #endif #ifdef _DEPTHOFFSET_ON , out float outputDepth : SV_Depth #endif #if NEED_FACING , bool facing : SV_IsFrontFace #endif ) { UNITY_SETUP_INSTANCE_ID(IN); UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN); #if defined(LOD_FADE_CROSSFADE) LODFadeCrossFade(IN.pos); #endif ShaderData d = CreateShaderData(IN); Surface l = (Surface)0; #ifdef _DEPTHOFFSET_ON l.outputDepth = outputDepth; #endif l.Albedo = half3(0.5, 0.5, 0.5); l.Normal = float3(0,0,1); l.Occlusion = 1; l.Alpha = 1; SurfaceFunction(l, d); #ifdef _DEPTHOFFSET_ON outputDepth = l.outputDepth; #endif #if defined(_USESPECULAR) || _SIMPLELIT float3 specular = l.Specular; float metallic = 0; #else float3 specular = 0; float metallic = l.Metallic; #endif InputData inputData = (InputData)0; inputData.positionWS = IN.worldPos; inputData.normalWS = mul(l.Normal, d.TBNMatrix); inputData.viewDirectionWS = SafeNormalize(d.worldSpaceViewDir); #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) inputData.shadowCoord = IN.shadowCoord; #elif defined(MAIN_LIGHT_CALCULATE_SHADOWS) inputData.shadowCoord = TransformWorldToShadowCoord(IN.worldPos); #else inputData.shadowCoord = float4(0, 0, 0, 0); #endif #if _BAKEDLIT inputData.fogCoord = IN.fogFactorAndVertexLight.x; #elif VERSION_GREATER_EQUAL(12, 0) inputData.fogCoord = InitializeInputDataFog(float4(IN.worldPos, 1.0), IN.fogFactorAndVertexLight.x); #else inputData.fogCoord = IN.fogFactorAndVertexLight.x; #endif inputData.vertexLighting = IN.fogFactorAndVertexLight.yzw; #if defined(DYNAMICLIGHTMAP_ON) inputData.bakedGI = SAMPLE_GI(IN.lightmapUV, IN.dynamicLightmapUV.xy, IN.sh, inputData.normalWS); #else inputData.bakedGI = SAMPLE_GI(IN.lightmapUV, IN.sh, inputData.normalWS); #endif inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(IN.pos); #if !_BAKEDLIT && defined(LIGHTMAP_ON) inputData.shadowMask = SAMPLE_SHADOWMASK(IN.lightmapUV); #endif #if defined(DEBUG_DISPLAY) #if defined(DYNAMICLIGHTMAP_ON) inputData.dynamicLightmapUV = IN.dynamicLightmapUV.xy; #endif #if defined(LIGHTMAP_ON) inputData.staticLightmapUV = IN.lightmapUV; #else inputData.vertexSH = IN.sh; #endif #endif SurfaceData surface = (SurfaceData)0; surface.albedo = l.Albedo; surface.metallic = saturate(metallic); surface.specular = specular; surface.smoothness = saturate(l.Smoothness), surface.occlusion = l.Occlusion, surface.emission = l.Emission, surface.alpha = saturate(l.Alpha); surface.clearCoatMask = 0; surface.clearCoatSmoothness = 1; half4 color = half4(l.Albedo, l.Alpha); #ifdef _DBUFFER #if _BAKEDLIT ApplyDecalToBaseColorAndNormal(IN.pos, color, inputData.normalWS); #else ApplyDecalToSurfaceData(IN.pos, surface, inputData); #endif #endif #if !_UNLIT #if _SIMPLELIT color = UniversalFragmentBlinnPhong( inputData, surface); #elif _BAKEDLIT color = UniversalFragmentBakedLit(inputData, color.rgb, color.a, l.Normal); #else color = UniversalFragmentPBR(inputData, surface); #endif color.rgb = MixFog(color.rgb, inputData.fogCoord); #else #ifdef _DBUFFER ApplyDecalToSurfaceData(IN.pos, surface, inputData); #endif color = UniversalFragmentUnlit(inputData, l.Albedo, l.Alpha); #endif ChainFinalColorForward(l, d, color); outColor = color; #ifdef _WRITE_RENDERING_LAYERS uint renderingLayers = GetMeshRenderingLayer(); outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0); #endif } ENDHLSL } Pass { Name "GBuffer" Tags { "LightMode" = "UniversalGBuffer" } Blend One Zero ZTest LEqual ZWrite On HLSLPROGRAM #pragma vertex Vert #pragma fragment Frag #pragma target 3.5 #pragma prefer_hlslcc gles #pragma exclude_renderers d3d11_9x #pragma multi_compile_instancing #pragma multi_compile_fog #pragma multi_compile _ DOTS_INSTANCING_ON #pragma multi_compile_local _ _ALPHATEST_ON #pragma instancing_options renderinglayer #pragma multi_compile _ LIGHTMAP_ON #pragma multi_compile _ DYNAMICLIGHTMAP_ON #pragma multi_compile _ DIRLIGHTMAP_COMBINED #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN #pragma multi_compile _ _REFLECTION_PROBE_BLENDING #pragma multi_compile _ _REFLECTION_PROBE_BOX_PROJECTION #pragma multi_compile _ _SHADOWS_SOFT #pragma multi_compile _ LIGHTMAP_SHADOW_MIXING #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE #pragma multi_compile _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3 #pragma multi_compile _ _GBUFFER_NORMALS_OCT #pragma multi_compile _ _LIGHT_LAYERS #pragma multi_compile _ _RENDER_PASS_ENABLED #pragma multi_compile _ DEBUG_DISPLAY #pragma multi_compile _ SHADOWS_SHADOWMASK #pragma multi_compile_fragment _ _WRITE_RENDERING_LAYERS #define _FOG_FRAGMENT 1 #define _MICROSPLAT 1 #define _MICROPOLARISMESH 1 #define _USEGRADMIP 1 #define _PERTEXUVSCALEOFFSET 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _MSRENDERLOOP_UNITYLD 1 #define _MSRENDERLOOP_UNITYURP2020 1 #define _MSRENDERLOOP_UNITYURP2021 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _URP 1 #define VARYINGS_NEED_FOG_AND_VERTEX_LIGHT #define SHADERPASS SHADERPASS_GBUFFER #define _PASSGBUFFER 1 // Includes #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Texture.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl" #if VERSION_GREATER_EQUAL(12, 0) #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl" #endif #include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl" #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, float3x3(d.worldSpaceTangent, cross(d.worldSpaceTangent, d.worldSpaceNormal), d.worldSpaceNormal)) #define UnityObjectToWorldNormal(normal) mul(GetObjectToWorldMatrix(), normal) #define _WorldSpaceLightPos0 _MainLightPosition #define UNITY_DECLARE_TEX2D(name) TEXTURE2D(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2D_NOSAMPLER(name) TEXTURE2D(name); #define UNITY_DECLARE_TEX2DARRAY(name) TEXTURE2D_ARRAY(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2DARRAY_NOSAMPLER(name) TEXTURE2D_ARRAY(name); #define UNITY_SAMPLE_TEX2DARRAY(tex,coord) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, coord.xy, coord.z) #define UNITY_SAMPLE_TEX2DARRAY_LOD(tex,coord,lod) SAMPLE_TEXTURE2D_ARRAY_LOD(tex, sampler##tex, coord.xy, coord.z, lod) #define UNITY_SAMPLE_TEX2D(tex, coord) SAMPLE_TEXTURE2D(tex, sampler##tex, coord) #define UNITY_SAMPLE_TEX2D_SAMPLER(tex, samp, coord) SAMPLE_TEXTURE2D(tex, sampler##samp, coord) #if defined(UNITY_COMPILER_HLSL) #define UNITY_INITIALIZE_OUTPUT(type,name) name = (type)0; #else #define UNITY_INITIALIZE_OUTPUT(type,name) #endif #define sampler2D_float sampler2D #define sampler2D_half sampler2D // data across stages, stripped like the above. struct VertexToPixel { float4 pos : SV_POSITION; float3 worldPos : TEXCOORD0; float3 worldNormal : TEXCOORD1; float4 worldTangent : TEXCOORD2; float4 texcoord0 : TEXCCOORD3; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 texcoord1 : TEXCCOORD4; // float4 texcoord2 : TEXCCOORD5; #endif // float4 texcoord3 : TEXCCOORD6; // float4 screenPos : TEXCOORD7; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD13; // float4 extraV2F1 : TEXCOORD14; // float4 extraV2F2 : TEXCOORD15; // float4 extraV2F3 : TEXCOORD16; // float4 extraV2F4 : TEXCOORD17; // float4 extraV2F5 : TEXCOORD18; // float4 extraV2F6 : TEXCOORD19; // float4 extraV2F7 : TEXCOORD20; #if defined(LIGHTMAP_ON) float2 lightmapUV : TEXCOORD8; #endif #if defined(DYNAMICLIGHTMAP_ON) float2 dynamicLightmapUV : TEXCOORD9; #endif #if !defined(LIGHTMAP_ON) float3 sh : TEXCOORD10; #endif float4 fogFactorAndVertexLight : TEXCOORD11; float4 shadowCoord : TEXCOORD12; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : CUSTOM_INSTANCE_ID; #endif #if (defined(UNITY_STEREO_MULTIVIEW_ENABLED)) || (defined(UNITY_STEREO_INSTANCING_ENABLED) && (defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE))) uint stereoTargetEyeIndexAsBlendIdx0 : BLENDINDICES0; #endif #if (defined(UNITY_STEREO_INSTANCING_ENABLED)) uint stereoTargetEyeIndexAsRTArrayIdx : SV_RenderTargetArrayIndex; #endif #if defined(SHADER_STAGE_FRAGMENT) && defined(VARYINGS_NEED_CULLFACE) FRONT_FACE_TYPE cullFace : FRONT_FACE_SEMANTIC; #endif }; // TEMPLATE_SHARED // data describing the user output of a pixel struct Surface { half3 Albedo; half Height; half3 Normal; half Smoothness; half3 Emission; half Metallic; half3 Specular; half Occlusion; half Alpha; // HDRP Only half SpecularOcclusion; half SubsurfaceMask; half Thickness; half CoatMask; half Anisotropy; half IridescenceMask; half IridescenceThickness; }; // data the user might need, this will grow to be big. But easy to strip struct ShaderData { float3 localSpacePosition; float3 localSpaceNormal; float3 localSpaceTangent; float3 worldSpacePosition; float3 worldSpaceNormal; float3 worldSpaceTangent; float3 worldSpaceViewDir; float3 tangentSpaceViewDir; float4 texcoord0; float4 texcoord1; float4 texcoord2; float4 texcoord3; float2 screenUV; float4 screenPos; float4 vertexColor; float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; float3x3 TBNMatrix; }; struct VertexData { #if SHADER_TARGET > 30 && _PLANETCOMPUTE // // uint vertexID : SV_VertexID; #endif float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD4; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD5; // Add Precomputed Velocity (Alembic computes velocities on runtime side). #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID }; struct TessVertex { float4 vertex : INTERNALTESSPOS; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD4; // float4 extraV2F1 : TEXCOORD5; // float4 extraV2F2 : TEXCOORD6; // float4 extraV2F3 : TEXCOORD7; // float4 extraV2F4 : TEXCOORD8; // float4 extraV2F5 : TEXCOORD9; // float4 extraV2F6 : TEXCOORD10; // float4 extraV2F7 : TEXCOORD11; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD12; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD13; #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; struct ExtraV2F { float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; }; float3 WorldToTangentSpace(ShaderData d, float3 normal) { return mul(d.TBNMatrix, normal); } float3 TangentToWorldSpace(ShaderData d, float3 normal) { return mul(normal, d.TBNMatrix); } // in this case, make standard more like SRPs, because we can't fix // GetWorldToObjectMatrix() in HDRP, since it already does macro-fu there #if _STANDARD float3 TransformWorldToObject(float3 p) { return mul(GetWorldToObjectMatrix(), float4(p, 1)); }; float3 TransformObjectToWorld(float3 p) { return mul(GetObjectToWorldMatrix(), float4(p, 1)); }; float4 TransformWorldToObject(float4 p) { return mul(GetWorldToObjectMatrix(), p); }; float4 TransformObjectToWorld(float4 p) { return mul(GetObjectToWorldMatrix(), p); }; float4x4 GetWorldToObjectMatrix() { return GetWorldToObjectMatrix(); } float4x4 GetObjectToWorldMatrix() { return GetObjectToWorldMatrix(); } #endif float3 GetCameraWorldPosition() { #if _HDRP return GetCameraRelativePositionWS(_WorldSpaceCameraPos); #else return _WorldSpaceCameraPos; #endif } #if _HDRP half3 UnpackNormalmapRGorAG(half4 packednormal) { // This do the trick packednormal.x *= packednormal.w; half3 normal; normal.xy = packednormal.xy * 2 - 1; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } half3 UnpackNormal(half4 packednormal) { #if defined(UNITY_NO_DXT5nm) return packednormal.xyz * 2 - 1; #else return UnpackNormalmapRGorAG(packednormal); #endif } #endif #if _HDRP || _URP half3 UnpackScaleNormal(half4 packednormal, half scale) { #ifndef UNITY_NO_DXT5nm // Unpack normal as DXT5nm (1, y, 1, x) or BC5 (x, y, 0, 1) // Note neutral texture like "bump" is (0, 0, 1, 1) to work with both plain RGB normal and DXT5nm/BC5 packednormal.x *= packednormal.w; #endif half3 normal; normal.xy = (packednormal.xy * 2 - 1) * scale; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } #endif void GetSun(out float3 lightDir, out float3 color) { lightDir = float3(0.5, 0.5, 0); color = 1; #if _HDRP if (_DirectionalLightCount > 0) { DirectionalLightData light = _DirectionalLightDatas[0]; lightDir = -light.forward.xyz; color = light.color; } #elif _STANDARD lightDir = normalize(_WorldSpaceLightPos0.xyz); color = _LightColor0.rgb; #elif _URP Light light = GetMainLight(); lightDir = light.direction; color = light.color; #endif } CBUFFER_START(UnityPerMaterial) #if _MESHSUBARRAY half4 _MeshSubArrayIndexes; #endif float4 _Diffuse_TexelSize; float4 _NormalSAO_TexelSize; #if _HYBRIDHEIGHTBLEND float _HybridHeightBlendDistance; #endif #if _PACKINGHQ float4 _SmoothAO_TexelSize; #endif #ifdef _ALPHATEST_ON float4 _TerrainHolesTexture_TexelSize; #endif #if _USESPECULARWORKFLOW float4 _Specular_TexelSize; #endif #if _USEEMISSIVEMETAL float4 _EmissiveMetal_TexelSize; #endif #if _USEEMISSIVEMETAL half _EmissiveMult; #endif #if _AUTONORMAL half _AutoNormalHeightScale; #endif float4 _UVScale; // scale and offset half _Contrast; #if _VSSHADOWMAP float4 gVSSunDirection; #endif #if _FORCELOCALSPACE && _PLANETVECTORS float4x4 _PQSToLocal; #endif #if _ORIGINSHIFT float4x4 _GlobalOriginMTX; #endif float4 _Control0_TexelSize; #if _CUSTOMSPLATTEXTURES float4 _CustomControl0_TexelSize; #endif float4 _PerPixelNormal_TexelSize; #if _CONTROLNOISEUV || _GLOBALNOISEUV float2 _NoiseUVParams; #endif float4 _PerTexProps_TexelSize; #if _SURFACENORMALS float3 surfTangent; float3 surfBitangent; float3 surfNormal; #endif CBUFFER_END #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, data.TBN) // In Unity 2020.3LTS, Unity will spew tons of errors about missing this sampler in // URP, even though it shouldn't be required. TEXTURE2D(_MainTex); // globals, outside of CBuffer, but used by more than one module float3 _gGlitterLightDir; float3 _gGlitterLightWorldPos; half3 _gGlitterLightColor; #if (_MICROTERRAIN || _MICROMESHTERRAIN) 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) float4 _TerrainNormalmapTexture_TexelSize; #endif #if (_MICROTERRAIN || _MICROMESHTERRAIN) TEXTURE2D(_TerrainHeightmapTexture); float4 _TerrainHeightmapTexture_TexelSize; TEXTURE2D(_TerrainNormalmapTexture); #endif UNITY_INSTANCING_BUFFER_START(Terrain) UNITY_DEFINE_INSTANCED_PROP(float4, _TerrainPatchInstanceData) // float4(xBase, yBase, skipScale, ~) UNITY_INSTANCING_BUFFER_END(Terrain) // dynamic branching helpers, for regular and aggressive branching // debug mode shows how many samples using branching will save us. // // These macros are always used instead of the UNITY_BRANCH macro // to maintain debug displays and allow branching to be disabled // on as granular level as we want. #if _BRANCHSAMPLES #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; if (w > 0) #else #define MSBRANCH(w) UNITY_BRANCH if (w > 0) #endif #else #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; #else #define MSBRANCH(w) #endif #endif #if _BRANCHSAMPLESAGR #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER ||_DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; if (w > 0.001) #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; if (w > 0.001) #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; if (w > 0.001) #else #define MSBRANCHTRIPLANAR(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHCLUSTER(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHOTHER(w) UNITY_BRANCH if (w > 0.001) #endif #else #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER || _DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; #else #define MSBRANCHTRIPLANAR(w) #define MSBRANCHCLUSTER(w) #define MSBRANCHOTHER(w) #endif #endif #if _DEBUG_SAMPLECOUNT int _sampleCount; #define COUNTSAMPLE { _sampleCount++; } #else #define COUNTSAMPLE #endif #if _DEBUG_PROCLAYERS int _procLayerCount; #define COUNTPROCLAYER { _procLayerCount++; } #else #define COUNTPROCLAYER #endif #if _DEBUG_USE_TOPOLOGY TEXTURE2D(_DebugWorldPos); TEXTURE2D(_DebugWorldNormal); #endif // splat UNITY_DECLARE_TEX2DARRAY(_Diffuse); UNITY_DECLARE_TEX2DARRAY(_NormalSAO); #if _CONTROLNOISEUV || _GLOBALNOISEUV TEXTURE2D(_NoiseUV); #endif #if _PACKINGHQ UNITY_DECLARE_TEX2DARRAY(_SmoothAO); #endif #if _USESPECULARWORKFLOW UNITY_DECLARE_TEX2DARRAY(_Specular); #endif #if _USEEMISSIVEMETAL UNITY_DECLARE_TEX2DARRAY(_EmissiveMetal); #endif TEXTURE2D(_PerPixelNormal); SamplerState shared_linear_clamp_sampler; SamplerState shared_point_clamp_sampler; TEXTURE2D(_Control0); #if _CUSTOMSPLATTEXTURES TEXTURE2D(_CustomControl0); #if !_MAX4TEXTURES TEXTURE2D(_CustomControl1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_CustomControl2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_CustomControl3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl6); #endif #if _MAX32TEXTURES TEXTURE2D(_CustomControl7); #endif #else #if !_MAX4TEXTURES TEXTURE2D(_Control1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_Control2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_Control3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control6); #endif #if _MAX32TEXTURES TEXTURE2D(_Control7); #endif #endif TEXTURE2D_FLOAT(_PerTexProps); struct DecalLayer { float3 uv; float2 dx; float2 dy; int decalIndex; bool dynamic; }; struct DecalOutput { DecalLayer l0; DecalLayer l1; DecalLayer l2; DecalLayer l3; half4 Weights; half4 Indexes; half4 fxLevels; }; struct TriGradMipFormat { float4 d0; float4 d1; float4 d2; }; float InverseLerp(float x, float y, float v) { return (v-x)/max(y-x, 0.001); } float2 InverseLerp(float2 x, float2 y, float2 v) { return (v-x)/max(y-x, float2(0.001, 0.001)); } float3 InverseLerp(float3 x, float3 y, float3 v) { return (v-x)/max(y-x, float3(0.001, 0.001, 0.001)); } float4 InverseLerp(float4 x, float4 y, float4 v) { return (v-x)/max(y-x, float4(0.001, 0.001, 0.001, 0.001)); } // 2019.3 holes #ifdef _ALPHATEST_ON TEXTURE2D(_TerrainHolesTexture); void ClipHoles(float2 uv) { float hole = SAMPLE_TEXTURE2D(_TerrainHolesTexture, shared_linear_clamp_sampler, uv).r; COUNTSAMPLE clip(hole < 0.5f ? -1 : 1); } #endif #if _TRIPLANAR #if _USEGRADMIP #define MIPFORMAT TriGradMipFormat #define INITMIPFORMAT (TriGradMipFormat)0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float3 #define INITMIPFORMAT 0; #define MIPFROMATRAW float3 #endif #else #if _USEGRADMIP #define MIPFORMAT float4 #define INITMIPFORMAT 0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float #define INITMIPFORMAT 0; #define MIPFROMATRAW float #endif #endif float2 TotalOne(float2 v) { return v * (1.0 / max(v.x + v.y, 0.001)); } float3 TotalOne(float3 v) { return v * (1.0 / max(v.x + v.y + v.z, 0.001)); } float4 TotalOne(float4 v) { return v * (1.0 / max(v.x + v.y + v.z + v.w, 0.001)); } float2 RotateUV(float2 uv, float amt) { uv -=0.5; float s = sin ( amt); float c = cos ( amt ); float2x2 mtx = float2x2( c, -s, s, c); mtx *= 0.5; mtx += 0.5; mtx = mtx * 2-1; uv = mul ( uv, mtx ); uv += 0.5; return uv; } float4 DecodeToFloat4(float v) { uint vi = (uint)(v * (256.0f * 256.0f * 256.0f * 256.0f)); int ex = (int)(vi / (256 * 256 * 256) % 256); int ey = (int)((vi / (256 * 256)) % 256); int ez = (int)((vi / (256)) % 256); int ew = (int)(vi % 256); float4 e = float4(ex / 255.0, ey / 255.0, ez / 255.0, ew / 255.0); return e; } struct Input { ShaderData shaderData; float2 uv_Control0; float2 uv2_Diffuse; float worldHeight; float3 worldUpVector; float3 viewDir; float3 worldPos; float3 worldNormal; float4 color; float3x3 TBN; // vertex/digger workflow data half4 w0; half4 w1; half4 w2; half4 w3; half4 w4; half4 w5; half4 w6; // megasplat data half4 layer0; half4 layer1; half3 baryWeights; half4 scatter0; half4 scatter1; // wetness, puddles, streams, lava from vertex or megasplat half4 fx; // snow min, snow max half4 fx2; }; struct TriplanarConfig { float3x3 uv0; float3x3 uv1; float3x3 uv2; float3x3 uv3; half3 pN; half3 pN0; half3 pN1; half3 pN2; half3 pN3; half3 axisSign; Input IN; }; struct Config { float2 uv; float3 uv0; float3 uv1; float3 uv2; float3 uv3; half4 cluster0; half4 cluster1; half4 cluster2; half4 cluster3; }; struct MicroSplatLayer { half3 Albedo; half3 Normal; half Smoothness; half Occlusion; half Metallic; half Height; half3 Emission; #if _USESPECULARWORKFLOW half3 Specular; #endif half Alpha; }; // raw, unblended samples from arrays struct RawSamples { half4 albedo0; half4 albedo1; half4 albedo2; half4 albedo3; #if _SURFACENORMALS half3 surf0; half3 surf1; half3 surf2; half3 surf3; #endif half4 normSAO0; half4 normSAO1; half4 normSAO2; half4 normSAO3; #if _USEEMISSIVEMETAL || _GLOBALEMIS || _GLOBALSMOOTHAOMETAL || _PERTEXSSS || _PERTEXRIMLIGHT half4 emisMetal0; half4 emisMetal1; half4 emisMetal2; half4 emisMetal3; #endif #if _USESPECULARWORKFLOW half3 specular0; half3 specular1; half3 specular2; half3 specular3; #endif }; void InitRawSamples(inout RawSamples s) { s.normSAO0 = half4(0,0,0,1); s.normSAO1 = half4(0,0,0,1); s.normSAO2 = half4(0,0,0,1); s.normSAO3 = half4(0,0,0,1); #if _SURFACENORMALS s.surf0 = half3(0,0,1); s.surf1 = half3(0,0,1); s.surf2 = half3(0,0,1); s.surf3 = half3(0,0,1); #endif } float3 GetGlobalLightDir(Input i) { float3 lightDir = float3(1,0,0); #if _HDRP || PASS_DEFERRED lightDir = normalize(_gGlitterLightDir.xyz); #elif _URP lightDir = GetMainLight().direction; #else #ifndef USING_DIRECTIONAL_LIGHT lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); #else lightDir = normalize(_WorldSpaceLightPos0.xyz); #endif #endif return lightDir; } float3x3 GetTBN(Input i) { return i.TBN; } float3 GetGlobalLightDirTS(Input i) { float3 lightDirWS = GetGlobalLightDir(i); return mul(GetTBN(i), lightDirWS); } half3 GetGlobalLightColor() { #if _HDRP || PASS_DEFERRED return _gGlitterLightColor; #elif _URP return (GetMainLight().color); #else return _LightColor0.rgb; #endif } half3 FuzzyShade(half3 color, half3 normal, half coreMult, half edgeMult, half power, float3 viewDir) { half dt = saturate(dot(viewDir, normal)); half dark = 1.0 - (coreMult * dt); half edge = pow(1-dt, power) * edgeMult; return color * (dark + edge); } half3 ComputeSSS(Input i, float3 V, float3 N, half3 tint, half thickness, half distortion, half scale, half power) { float3 L = GetGlobalLightDir(i); half3 lightColor = GetGlobalLightColor(); float3 H = normalize(L + N * distortion); float VdotH = pow(saturate(dot(V, -H)), power) * scale; float3 I = (VdotH) * thickness; return lightColor * I * tint; } #if _MAX2LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y; } #elif _MAX3LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } #else inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } #endif #if _MAX3LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #elif _MAX2LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #else #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##3 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv3.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #endif half2 BlendNormal2(half2 base, half2 blend) { return normalize(half3(base.xy + blend.xy, 1)).xy; } half3 BlendOverlay(half3 base, half3 blend) { return (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))); } half3 BlendMult2X(half3 base, half3 blend) { return (base * (blend * 2)); } half3 BlendLighterColor(half3 s, half3 d) { return (s.x + s.y + s.z > d.x + d.y + d.z) ? s : d; } #if _SURFACENORMALS #define HALF_EPS 4.8828125e-4 // 2^-11, machine epsilon: 1 + EPS = 1 (half of the ULP for 1.0f) void ConstructSurfaceGradientTBN(Input i) { float3x3 tbn = GetTBN(i); float3 t = tbn[0]; float3 b = tbn[1]; float3 n = tbn[2]; surfNormal = n;//mul(GetWorldToObjectMatrix(), float4(n, 1)).xyz; surfTangent = t;//mul(GetWorldToObjectMatrix(), float4(t, 1)).xyz; surfBitangent = b;//cross(surfNormal, surfTangent); float renormFactor = 1.0 / length(surfNormal); surfNormal *= renormFactor; surfTangent *= renormFactor; surfBitangent *= renormFactor; } half3 SurfaceGradientFromTBN(half2 deriv) { return deriv.x * surfTangent + deriv.y * surfBitangent; } // Input: vM is tangent space normal in [-1;1]. // Output: convert vM to a derivative. half2 TspaceNormalToDerivative(half3 vM) { const half scale = 1.0/128.0; // Ensure vM delivers a positive third component using abs() and // constrain vM.z so the range of the derivative is [-128; 128]. const half3 vMa = abs(vM); const half z_ma = max(vMa.z, scale*max(vMa.x, vMa.y)); return -half2(vM.x, vM.y)/z_ma; } // Used to produce a surface gradient from the gradient of a volume // bump function such as 3D Perlin noise. Equation 2 in [Mik10]. half3 SurfgradFromVolumeGradient(half3 grad) { return grad - dot(surfNormal, grad) * surfNormal; } half3 SurfgradFromTriplanarProjection(half3 pN, half2 xPlaneTN, half2 yPlaneTN, half2 zPlaneTN) { const half w0 = pN.x; const half w1 = pN.y; const half w2 = pN.z; // X-plane tangent normal to gradient derivative xPlaneTN = xPlaneTN * 2.0 - 1.0; half xPlaneRcpZ = rsqrt(max(1 - dot(xPlaneTN.x, xPlaneTN.x) - dot(xPlaneTN.y, xPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_xplane = xPlaneTN * -xPlaneRcpZ; // Y-plane tangent normal to gradient derivative yPlaneTN = yPlaneTN * 2.0 - 1.0; half yPlaneRcpZ = rsqrt(max(1 - dot(yPlaneTN.x, yPlaneTN.x) - dot(yPlaneTN.y, yPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_yplane = yPlaneTN * -yPlaneRcpZ; // Z-plane tangent normal to gradient derivative zPlaneTN = zPlaneTN * 2.0 - 1.0; half zPlaneRcpZ = rsqrt(max(1 - dot(zPlaneTN.x, zPlaneTN.x) - dot(zPlaneTN.y, zPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_zplane = zPlaneTN * -zPlaneRcpZ; // Assume deriv xplane, deriv yplane, and deriv zplane are // sampled using (z,y), (x,z), and (x,y), respectively. // Positive scales of the lookup coordinate will work // as well, but for negative scales the derivative components // will need to be negated accordingly. float3 grad = float3(w2*d_zplane.x + w1*d_yplane.x, w2*d_zplane.y + w0*d_xplane.y, w0*d_xplane.x + w1*d_yplane.y); return SurfgradFromVolumeGradient(grad); } half3 ConvertNormalToGradient(half3 normal) { half2 deriv = TspaceNormalToDerivative(normal); return SurfaceGradientFromTBN(deriv); } half3 ConvertNormal2ToGradient(half2 packedNormal) { half2 tNormal = packedNormal; half rcpZ = rsqrt(max(1 - dot(tNormal.x, tNormal.x) - dot(tNormal.y, tNormal.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 deriv = tNormal * -rcpZ; return SurfaceGradientFromTBN(deriv); } half3 ResolveNormalFromSurfaceGradient(half3 gradient) { return normalize(surfNormal - gradient); } #endif // _SURFACENORMALS void BlendNormalPerTex(inout RawSamples o, half2 noise, float4 fades) { #if _SURFACENORMALS float3 grad = ConvertNormal2ToGradient(noise.xy); o.surf0 += grad * fades.x; o.surf1 += grad * fades.y; #if !_MAX2LAYER o.surf2 += grad * fades.z; #endif #if !_MAX2LAYER && !_MAX3LAYER o.surf3 += grad * fades.w; #endif #else o.normSAO0.xy = lerp(o.normSAO0.xy, BlendNormal2(o.normSAO0.xy, noise.xy), fades.x); o.normSAO1.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #if !_MAX2LAYER o.normSAO2.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO2.xy, noise.xy), fades.y); #endif #if !_MAX2LAYER && !_MAX3LAYER o.normSAO3.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #endif #endif } half3 BlendNormal3(half3 n1, half3 n2) { n1 += float3( 0, 0, 1); n2 *= float3(-1, -1, 1); return n1*dot(n1, n2) / n1.z - n2; } half2 TransformTriplanarNormal(Input IN, float3x3 t2w, half3 axisSign, half3 absVertNormal, half3 pN, half2 a0, half2 a1, half2 a2) { a0 = a0 * 2 - 1; a1 = a1 * 2 - 1; a2 = a2 * 2 - 1; a0.x *= axisSign.x; a1.x *= axisSign.y; a2.x *= axisSign.z; half3 n0 = half3(a0.xy, 1); half3 n1 = half3(a1.xy, 1); half3 n2 = half3(a2.xy, 1); float3 wn = IN.worldNormal; n0 = BlendNormal3(half3(wn.zy, absVertNormal.x), n0); n1 = BlendNormal3(half3(wn.xz, absVertNormal.y), n1 * float3(-1, 1, 1)); n2 = BlendNormal3(half3(wn.xy, absVertNormal.z), n2); n0.z *= axisSign.x; n1.z *= axisSign.y; n2.z *= -axisSign.z; half3 worldNormal = (n0.zyx * pN.x + n1.xzy * pN.y + n2.xyz * pN.z); return mul(t2w, worldNormal).xy; } // funcs inline half MSLuminance(half3 rgb) { #ifdef UNITY_COLORSPACE_GAMMA return dot(rgb, half3(0.22, 0.707, 0.071)); #else return dot(rgb, half3(0.0396819152, 0.458021790, 0.00609653955)); #endif } float2 Hash2D( float2 x ) { float2 k = float2( 0.3183099, 0.3678794 ); x = x*k + k.yx; return -1.0 + 2.0*frac( 16.0 * k*frac( x.x*x.y*(x.x+x.y)) ); } float Noise2D(float2 p ) { float2 i = floor( p ); float2 f = frac( p ); float2 u = f*f*(3.0-2.0*f); return lerp( lerp( dot( Hash2D( i + float2(0.0,0.0) ), f - float2(0.0,0.0) ), dot( Hash2D( i + float2(1.0,0.0) ), f - float2(1.0,0.0) ), u.x), lerp( dot( Hash2D( i + float2(0.0,1.0) ), f - float2(0.0,1.0) ), dot( Hash2D( i + float2(1.0,1.0) ), f - float2(1.0,1.0) ), u.x), u.y); } float FBM2D(float2 uv) { float f = 0.5000*Noise2D( uv ); uv *= 2.01; f += 0.2500*Noise2D( uv ); uv *= 1.96; f += 0.1250*Noise2D( uv ); return f; } float3 Hash3D( float3 p ) { p = float3( dot(p,float3(127.1,311.7, 74.7)), dot(p,float3(269.5,183.3,246.1)), dot(p,float3(113.5,271.9,124.6))); return -1.0 + 2.0*frac(sin(p)*437.5453123); } float Noise3D( float3 p ) { float3 i = floor( p ); float3 f = frac( p ); float3 u = f*f*(3.0-2.0*f); return lerp( lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,0.0) ), f - float3(0.0,0.0,0.0) ), dot( Hash3D( i + float3(1.0,0.0,0.0) ), f - float3(1.0,0.0,0.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,0.0) ), f - float3(0.0,1.0,0.0) ), dot( Hash3D( i + float3(1.0,1.0,0.0) ), f - float3(1.0,1.0,0.0) ), u.x), u.y), lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,1.0) ), f - float3(0.0,0.0,1.0) ), dot( Hash3D( i + float3(1.0,0.0,1.0) ), f - float3(1.0,0.0,1.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,1.0) ), f - float3(0.0,1.0,1.0) ), dot( Hash3D( i + float3(1.0,1.0,1.0) ), f - float3(1.0,1.0,1.0) ), u.x), u.y), u.z ); } float FBM3D(float3 uv) { float f = 0.5000*Noise3D( uv ); uv *= 2.01; f += 0.2500*Noise3D( uv ); uv *= 1.96; f += 0.1250*Noise3D( uv ); return f; } float GetSaturation(float3 c) { float mi = min(min(c.x, c.y), c.z); float ma = max(max(c.x, c.y), c.z); return (ma - mi)/(ma + 1e-7); } // Better Color Lerp, does not have darkening issue float3 BetterColorLerp(float3 a, float3 b, float x) { float3 ic = lerp(a, b, x) + float3(1e-6,0.0,0.0); float sd = abs(GetSaturation(ic) - lerp(GetSaturation(a), GetSaturation(b), x)); float3 dir = normalize(float3(2.0 * ic.x - ic.y - ic.z, 2.0 * ic.y - ic.x - ic.z, 2.0 * ic.z - ic.y - ic.x)); float lgt = dot(float3(1.0, 1.0, 1.0), ic); float ff = dot(dir, normalize(ic)); const float dsp_str = 1.5; ic += dsp_str * dir * sd * ff * lgt; return saturate(ic); } half4 ComputeWeights(half4 iWeights, half h0, half h1, half h2, half h3, half contrast) { #if _DISABLEHEIGHTBLENDING return iWeights; #else // compute weight with height map //half4 weights = half4(iWeights.x * h0, iWeights.y * h1, iWeights.z * h2, iWeights.w * h3); half4 weights = half4(iWeights.x * max(h0,0.001), iWeights.y * max(h1,0.001), iWeights.z * max(h2,0.001), iWeights.w * max(h3,0.001)); // Contrast weights half maxWeight = max(max(weights.x, max(weights.y, weights.z)), weights.w); half transition = max(contrast * maxWeight, 0.0001); half threshold = maxWeight - transition; half scale = 1.0 / transition; weights = saturate((weights - threshold) * scale); weights = TotalOne(weights); return weights; #endif } half HeightBlend(half h1, half h2, half slope, half contrast) { #if _DISABLEHEIGHTBLENDING return slope; #else h2 = 1 - h2; half tween = saturate((slope - min(h1, h2)) / max(abs(h1 - h2), 0.001)); half blend = saturate( ( tween - (1-contrast) ) / max(contrast, 0.001)); return blend; #endif } #if _MAX4TEXTURES #define TEXCOUNT 4 #elif _MAX8TEXTURES #define TEXCOUNT 8 #elif _MAX12TEXTURES #define TEXCOUNT 12 #elif _MAX20TEXTURES #define TEXCOUNT 20 #elif _MAX24TEXTURES #define TEXCOUNT 24 #elif _MAX28TEXTURES #define TEXCOUNT 28 #elif _MAX32TEXTURES #define TEXCOUNT 32 #else #define TEXCOUNT 16 #endif #if _DECAL_SPLAT void DoMergeDecalSplats(half4 iWeights, half4 iIndexes, inout half4 indexes, inout half4 weights) { for (int i = 0; i < 4; ++i) { half w = iWeights[i]; half index = iIndexes[i]; if (w > weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = index; } else if (w > weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = index; } else if (w > weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = index; } else if (w > weights[3]) { weights[3] = w; indexes[3] = index; } } } #endif void Setup(out half4 weights, float2 uv, out Config config, half4 w0, half4 w1, half4 w2, half4 w3, half4 w4, half4 w5, half4 w6, half4 w7, float3 worldPos, DecalOutput decalOutput) { config = (Config)0; half4 indexes = 0; config.uv = uv; #if _WORLDUV uv = worldPos.xz; #endif #if _DISABLESPLATMAPS float2 scaledUV = uv; #else float2 scaledUV = uv * _UVScale.xy + _UVScale.zw; #endif // if only 4 textures, and blending 4 textures, skip this whole thing.. // this saves about 25% of the ALU of the base shader on low end. However if // we rely on sorted texture weights (distance resampling) we have to sort.. float4 defaultIndexes = float4(0,1,2,3); #if _MESHSUBARRAY defaultIndexes = _MeshSubArrayIndexes; #endif #if _MESHSUBARRAY && !_DECAL_SPLAT || (_MAX4TEXTURES && !_MAX3LAYER && !_MAX2LAYER && !_DISTANCERESAMPLE && !_POM && !_DECAL_SPLAT) weights = w0; config.uv0 = float3(scaledUV, defaultIndexes.x); config.uv1 = float3(scaledUV, defaultIndexes.y); config.uv2 = float3(scaledUV, defaultIndexes.z); config.uv3 = float3(scaledUV, defaultIndexes.w); return; #endif #if _DISABLESPLATMAPS weights = float4(1,0,0,0); return; #else half splats[TEXCOUNT]; splats[0] = w0.x; splats[1] = w0.y; splats[2] = w0.z; splats[3] = w0.w; #if !_MAX4TEXTURES splats[4] = w1.x; splats[5] = w1.y; splats[6] = w1.z; splats[7] = w1.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES splats[8] = w2.x; splats[9] = w2.y; splats[10] = w2.z; splats[11] = w2.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES splats[12] = w3.x; splats[13] = w3.y; splats[14] = w3.z; splats[15] = w3.w; #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[16] = w4.x; splats[17] = w4.y; splats[18] = w4.z; splats[19] = w4.w; #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[20] = w5.x; splats[21] = w5.y; splats[22] = w5.z; splats[23] = w5.w; #endif #if _MAX28TEXTURES || _MAX32TEXTURES splats[24] = w6.x; splats[25] = w6.y; splats[26] = w6.z; splats[27] = w6.w; #endif #if _MAX32TEXTURES splats[28] = w7.x; splats[29] = w7.y; splats[30] = w7.z; splats[31] = w7.w; #endif weights[0] = 0; weights[1] = 0; weights[2] = 0; weights[3] = 0; indexes[0] = 0; indexes[1] = 0; indexes[2] = 0; indexes[3] = 0; int i = 0; for (i = 0; i < TEXCOUNT; ++i) { half w = splats[i]; if (w >= weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = i; } else if (w >= weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = i; } else if (w >= weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = i; } else if (w >= weights[3]) { weights[3] = w; indexes[3] = i; } } // NaN Prevention if (weights.x <= 0) weights = float4(1, 0, 0, 0); #if _DECAL_SPLAT DoMergeDecalSplats(decalOutput.Weights, decalOutput.Indexes, weights, indexes); #endif // clamp and renormalize #if _MAX2LAYER weights.zw = 0; weights.xy = TotalOne(weights.xy); #elif _MAX3LAYER weights.w = 0; weights.xyz = TotalOne(weights.xyz); #elif !_DISABLEHEIGHTBLENDING || _NORMALIZEWEIGHTS // prevents black when painting, which the unity shader does not prevent. weights = normalize(weights); #endif config.uv0 = float3(scaledUV, indexes.x); config.uv1 = float3(scaledUV, indexes.y); config.uv2 = float3(scaledUV, indexes.z); config.uv3 = float3(scaledUV, indexes.w); #endif //_DISABLESPLATMAPS } float3 HeightToNormal(float height, float3 worldPos) { float3 dx = ddx(worldPos); float3 dy = ddy(worldPos); float3 crossX = cross(float3(0,1,0), dx); float3 crossY = cross(float3(0,1,0), dy); float3 d = abs(dot(crossY, dx)); float3 n = ((((height + ddx(height)) - height) * crossY) + (((height + ddy(height)) - height) * crossX)) * sign(d); n.z *= -1; return normalize((d * float3(0,1,0)) - n).xzy; } float ComputeMipLevel(float2 uv, float2 textureSize) { uv *= textureSize; float2 dx_vtc = ddx(uv); float2 dy_vtc = ddy(uv); float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); return 0.5 * log2(delta_max_sqr); } inline half2 UnpackNormal2(half4 packednormal) { return packednormal.wy * 2 - 1; } half3 TriplanarHBlend(half h0, half h1, half h2, half3 pN, half contrast) { half3 blend = pN / dot(pN, half3(1,1,1)); float3 heights = float3(h0, h1, h2) + (blend * 3.0); half height_start = max(max(heights.x, heights.y), heights.z) - contrast; half3 h = max(heights - height_start.xxx, half3(0,0,0)); blend = h / dot(h, half3(1,1,1)); return blend; } void ClearAllButAlbedo(inout MicroSplatLayer o, half3 display) { o.Albedo = display.rgb; o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } void ClearAllButAlbedo(inout MicroSplatLayer o, half display) { o.Albedo = half3(display, display, display); o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } half MicroShadow(float3 lightDir, half3 normal, half ao, half strength) { half shadow = saturate(abs(dot(normal, lightDir)) + (ao * ao * 2.0) - 1.0); return 1 - ((1-shadow) * strength); } void DoDebugOutput(inout MicroSplatLayer l) { #if _DEBUG_OUTPUT_ALBEDO ClearAllButAlbedo(l, l.Albedo); #elif _DEBUG_OUTPUT_NORMAL // oh unit shader compiler normal stripping, how I hate you so.. // must multiply by albedo to stop the normal from being white. Why, fuck knows? ClearAllButAlbedo(l, float3(l.Normal.xy * 0.5 + 0.5, l.Normal.z * saturate(l.Albedo.z+1))); #elif _DEBUG_OUTPUT_SMOOTHNESS ClearAllButAlbedo(l, l.Smoothness.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_METAL ClearAllButAlbedo(l, l.Metallic.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_AO ClearAllButAlbedo(l, l.Occlusion.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_EMISSION ClearAllButAlbedo(l, l.Emission * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_HEIGHT ClearAllButAlbedo(l, l.Height.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_SPECULAR && _USESPECULARWORKFLOW ClearAllButAlbedo(l, l.Specular * saturate(l.Albedo.z+1)); #elif _DEBUG_BRANCHCOUNT_WEIGHT ClearAllButAlbedo(l, _branchWeightCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TRIPLANAR ClearAllButAlbedo(l, _branchTriplanarCount / 24 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_CLUSTER ClearAllButAlbedo(l, _branchClusterCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_OTHER ClearAllButAlbedo(l, _branchOtherCount / 8 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TOTAL l.Albedo.r = _branchWeightCount / 12; l.Albedo.g = _branchTriplanarCount / 24; l.Albedo.b = _branchClusterCount / 12; ClearAllButAlbedo(l, (l.Albedo.r + l.Albedo.g + l.Albedo.b + (_branchOtherCount / 8)) / 4); #elif _DEBUG_OUTPUT_MICROSHADOWS ClearAllButAlbedo(l,l.Albedo); #elif _DEBUG_SAMPLECOUNT float sdisp = (float)_sampleCount / max(_SampleCountDiv, 1); half3 sdcolor = float3(sdisp, sdisp > 1 ? 1 : 0, 0); ClearAllButAlbedo(l, sdcolor * saturate(l.Albedo.z + 1)); #elif _DEBUG_PROCLAYERS ClearAllButAlbedo(l, (float)_procLayerCount / (float)_PCLayerCount * saturate(l.Albedo.z + 1)); #endif } // abstraction around sampler mode #if _USELODMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_LOD(tex, sampler##tex, u, l.x) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u, l.x) #elif _USEGRADMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_GRAD(tex, sampler##tex, u, l.xy, l.zw) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY_GRAD(tex, ss, u.xy, u.z, l.xy, l.zw) #else #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, u.xy, u.z) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u.xy, y.z) #endif #define MICROSPLAT_SAMPLE_DIFFUSE(u, cl, l) MICROSPLAT_SAMPLE(_Diffuse, u, l) #define MICROSPLAT_SAMPLE_EMIS(u, cl, l) MICROSPLAT_SAMPLE(_EmissiveMetal, u, l) #define MICROSPLAT_SAMPLE_DIFFUSE_LOD(u, cl, l) UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, u, l) #if _PACKINGHQ #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) half4(MICROSPLAT_SAMPLE(_NormalSAO, u, l).ga, MICROSPLAT_SAMPLE(_SmoothAO, u, l).ga).brag #else #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) MICROSPLAT_SAMPLE(_NormalSAO, u, l) #endif #if _USESPECULARWORKFLOW #define MICROSPLAT_SAMPLE_SPECULAR(u, cl, l) MICROSPLAT_SAMPLE(_Specular, u, l) #endif struct SimpleTriplanarConfig { float3 pn; float2 uv0; float2 uv1; float2 uv2; }; void PrepSimpleTriplanarConfig(inout SimpleTriplanarConfig tc, float3 worldPos, float3 normal, float contrast) { tc.pn = pow(abs(normal), contrast); tc.pn = tc.pn / (tc.pn.x + tc.pn.y + tc.pn.z); half3 axisSign = sign(normal); tc.uv0 = worldPos.zy * axisSign.x; tc.uv1 = worldPos.xz * axisSign.y; tc.uv2 = worldPos.xy * axisSign.z; } #define SimpleTriplanarSample(tex, tc, scale) (SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv0 * scale) * tc.pn.x + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv1 * scale) * tc.pn.y + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv2 * scale) * tc.pn.z) #define SimpleTriplanarSampleLOD(tex, tc, scale, lod) (SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv0 * scale, lod) * tc.pn.x + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv1 * scale, lod) * tc.pn.y + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv2 * scale, lod) * tc.pn.z) #define SimpleTriplanarSampleGrad(tex, tc, scale) (SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv0 * scale, ddx(tc.uv0) * scale, ddy(tc.uv0) * scale) * tc.pn.x + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv1 * scale, ddx(tc.uv1) * scale, ddy(tc.uv1) * scale) * tc.pn.y + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv2 * scale, ddx(tc.uv2) * scale, ddy(tc.uv2) * scale) * tc.pn.z) inline half3 MicroSplatDiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity) { specColor = lerp (half3(0,0,0), albedo, metallic); oneMinusReflectivity = (1-metallic); return albedo * oneMinusReflectivity; } Input DescToInput(ShaderData IN) { Input s = (Input)0; s.shaderData = IN; s.TBN = IN.TBNMatrix; s.worldNormal = IN.worldSpaceNormal; s.worldPos = IN.worldSpacePosition; s.viewDir = IN.tangentSpaceViewDir; s.uv_Control0 = IN.texcoord0.xy; s.worldUpVector = float3(0,1,0); s.worldHeight = IN.worldSpacePosition.y; #if _PLANETVECTORS float3 rwp = mul(_PQSToLocal, float4(IN.worldSpacePosition, 1)); s.worldHeight = distance(rwp, float3(0,0,0)); s.worldUpVector = normalize(rwp); #endif #if _MICROMESH && _MESHUV2 s.uv2_Diffuse = IN.texcoord1.xy; #endif #if _MEGASPLAT UnpackMegaSplat(s, IN); #endif #if _MICROVERTEXMESH || _MICRODIGGERMESH UnpackVertexWorkflow(s, IN); #endif #if _PLANETVECTORS DoPlanetDataInputCopy(s, IN); #endif return s; } void SampleAlbedo(inout Config config, inout TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half4 contrasts = _Contrast.xxxx; #if _PERTEXTRIPLANARCONTRAST SAMPLE_PER_TEX(ptc, 9.5, config, half4(1,0.5,0,0)); contrasts = half4(ptc0.y, ptc1.y, ptc2.y, ptc3.y); #endif #if _PERTEXTRIPLANAR SAMPLE_PER_TEX(pttri, 9.5, config, half4(0,0,0,0)); #endif { // For per-texture triplanar, we modify the view based blending factor of the triplanar // such that you get a pure blend of either top down projection, or with the top down projection // removed and renormalized. This causes dynamic flow control optimizations to kick in and avoid // the extra texture samples while keeping the code simple. Yay.. // We also only have to do this in the Albedo, because the pN values will be adjusted after the // albedo is sampled, causing future samples to use this data. #if _PERTEXTRIPLANAR if (pttri0.x > 0.66) { tc.pN0 = half3(0,1,0); } else if (pttri0.x > 0.33) { tc.pN0.y = 0; tc.pN0.xz = TotalOne(tc.pN0.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } half3 bf = tc.pN0; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN0, contrasts.x); tc.pN0 = bf; #endif s.albedo0 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } MSBRANCH(weights.y) { #if _PERTEXTRIPLANAR if (pttri1.x > 0.66) { tc.pN1 = half3(0,1,0); } else if (pttri1.x > 0.33) { tc.pN1.y = 0; tc.pN1.xz = TotalOne(tc.pN1.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { COUNTSAMPLE a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[2], config.cluster1, d2); } half3 bf = tc.pN1; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN1, contrasts.x); tc.pN1 = bf; #endif s.albedo1 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { #if _PERTEXTRIPLANAR if (pttri2.x > 0.66) { tc.pN2 = half3(0,1,0); } else if (pttri2.x > 0.33) { tc.pN2.y = 0; tc.pN2.xz = TotalOne(tc.pN2.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } half3 bf = tc.pN2; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN2, contrasts.x); tc.pN2 = bf; #endif s.albedo2 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { #if _PERTEXTRIPLANAR if (pttri3.x > 0.66) { tc.pN3 = half3(0,1,0); } else if (pttri3.x > 0.33) { tc.pN3.y = 0; tc.pN3.xz = TotalOne(tc.pN3.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } half3 bf = tc.pN3; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN3, contrasts.x); tc.pN3 = bf; #endif s.albedo3 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #else s.albedo0 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.albedo1 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.albedo2 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.albedo3 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #if _PERTEXHEIGHTOFFSET || _PERTEXHEIGHTCONTRAST SAMPLE_PER_TEX(ptHeight, 10.5, config, 1); #if _PERTEXHEIGHTOFFSET s.albedo0.a = saturate(s.albedo0.a + ptHeight0.b - 1); s.albedo1.a = saturate(s.albedo1.a + ptHeight1.b - 1); s.albedo2.a = saturate(s.albedo2.a + ptHeight2.b - 1); s.albedo3.a = saturate(s.albedo3.a + ptHeight3.b - 1); #endif #if _PERTEXHEIGHTCONTRAST s.albedo0.a = saturate(pow(s.albedo0.a + 0.5, abs(ptHeight0.a)) - 0.5); s.albedo1.a = saturate(pow(s.albedo1.a + 0.5, abs(ptHeight1.a)) - 0.5); s.albedo2.a = saturate(pow(s.albedo2.a + 0.5, abs(ptHeight2.a)) - 0.5); s.albedo3.a = saturate(pow(s.albedo3.a + 0.5, abs(ptHeight3.a)) - 0.5); #endif #endif } void SampleNormal(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _NONORMALMAP || _AUTONORMAL s.normSAO0 = half4(0,0, 0, 1); s.normSAO1 = half4(0,0, 0, 1); s.normSAO2 = half4(0,0, 0, 1); s.normSAO3 = half4(0,0, 0, 1); return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half3 absVertNormal = abs(tc.IN.worldNormal); float3x3 t2w = tc.IN.TBN; { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[0], config.cluster0, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[1], config.cluster0, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[2], config.cluster0, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf0 = SurfgradFromTriplanarProjection(tc.pN0, a0.xy, a1.xy, a2.xy); #else s.normSAO0.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN0, a0.xy, a1.xy, a2.xy); #endif s.normSAO0.zw = a0.zw * tc.pN0.x + a1.zw * tc.pN0.y + a2.zw * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[0], config.cluster1, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[1], config.cluster1, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[2], config.cluster1, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf1 = SurfgradFromTriplanarProjection(tc.pN1, a0.xy, a1.xy, a2.xy); #else s.normSAO1.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN1, a0.xy, a1.xy, a2.xy); #endif s.normSAO1.zw = a0.zw * tc.pN1.x + a1.zw * tc.pN1.y + a2.zw * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[0], config.cluster2, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[1], config.cluster2, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[2], config.cluster2, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf2 = SurfgradFromTriplanarProjection(tc.pN2, a0.xy, a1.xy, a2.xy); #else s.normSAO2.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN2, a0.xy, a1.xy, a2.xy); #endif s.normSAO2.zw = a0.zw * tc.pN2.x + a1.zw * tc.pN2.y + a2.zw * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[0], config.cluster3, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[1], config.cluster3, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[2], config.cluster3, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf3 = SurfgradFromTriplanarProjection(tc.pN3, a0.xy, a1.xy, a2.xy); #else s.normSAO3.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN3, a0.xy, a1.xy, a2.xy); #endif s.normSAO3.zw = a0.zw * tc.pN3.x + a1.zw * tc.pN3.y + a2.zw * tc.pN3.z; } #endif #else s.normSAO0 = MICROSPLAT_SAMPLE_NORMAL(config.uv0, config.cluster0, mipLevel).agrb; COUNTSAMPLE s.normSAO0.xy = s.normSAO0.xy * 2 - 1; #if _SURFACENORMALS s.surf0 = ConvertNormal2ToGradient(s.normSAO0.xy); #endif MSBRANCH(weights.y) { s.normSAO1 = MICROSPLAT_SAMPLE_NORMAL(config.uv1, config.cluster1, mipLevel).agrb; COUNTSAMPLE s.normSAO1.xy = s.normSAO1.xy * 2 - 1; #if _SURFACENORMALS s.surf1 = ConvertNormal2ToGradient(s.normSAO1.xy); #endif } #if !_MAX2LAYER MSBRANCH(weights.z) { s.normSAO2 = MICROSPLAT_SAMPLE_NORMAL(config.uv2, config.cluster2, mipLevel).agrb; COUNTSAMPLE s.normSAO2.xy = s.normSAO2.xy * 2 - 1; #if _SURFACENORMALS s.surf2 = ConvertNormal2ToGradient(s.normSAO2.xy); #endif } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.normSAO3 = MICROSPLAT_SAMPLE_NORMAL(config.uv3, config.cluster3, mipLevel).agrb; COUNTSAMPLE s.normSAO3.xy = s.normSAO3.xy * 2 - 1; #if _SURFACENORMALS s.surf3 = ConvertNormal2ToGradient(s.normSAO3.xy); #endif } #endif #endif } void SampleEmis(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USEEMISSIVEMETAL #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.emisMetal0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.emisMetal1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.emisMetal2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.emisMetal3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.emisMetal0 = MICROSPLAT_SAMPLE_EMIS(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.emisMetal1 = MICROSPLAT_SAMPLE_EMIS(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.emisMetal2 = MICROSPLAT_SAMPLE_EMIS(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.emisMetal3 = MICROSPLAT_SAMPLE_EMIS(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } void SampleSpecular(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USESPECULARWORKFLOW #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.specular0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.specular1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.specular2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.specular3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.specular0 = MICROSPLAT_SAMPLE_SPECULAR(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.specular1 = MICROSPLAT_SAMPLE_SPECULAR(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.specular2 = MICROSPLAT_SAMPLE_SPECULAR(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.specular3 = MICROSPLAT_SAMPLE_SPECULAR(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } MicroSplatLayer Sample(Input i, half4 weights, inout Config config, float camDist, float3 worldNormalVertex, DecalOutput decalOutput) { MicroSplatLayer o = (MicroSplatLayer)0; UNITY_INITIALIZE_OUTPUT(MicroSplatLayer,o); RawSamples samples = (RawSamples)0; InitRawSamples(samples); half4 albedo = 0; half4 normSAO = half4(0,0,0,1); half3 surfGrad = half3(0,0,0); half4 emisMetal = 0; half3 specular = 0; float worldHeight = i.worldPos.y; float3 upVector = float3(0,1,0); #if _GLOBALTINT || _GLOBALNORMALS || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _GLOBALSPECULAR float globalSlopeFilter = 1; #if _GLOBALSLOPEFILTER float2 gfilterUV = float2(1 - saturate(dot(worldNormalVertex, upVector) * 0.5 + 0.49), 0.5); globalSlopeFilter = SAMPLE_TEXTURE2D(_GlobalSlopeTex, sampler_Diffuse, gfilterUV).a; #endif #endif // declare outside of branchy areas.. half4 fxLevels = half4(0,0,0,0); half burnLevel = 0; half wetLevel = 0; half3 waterNormalFoam = half3(0, 0, 0); half porosity = 0.4; float streamFoam = 1.0f; half pud = 0; half snowCover = 0; half SSSThickness = 0; half3 SSSTint = half3(1,1,1); float traxBuffer = 0; float3 traxNormal = 0; float2 noiseUV = 0; #if _SPLATFADE MSBRANCHOTHER(1 - saturate(camDist - _SplatFade.y)) { #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE || _SNOWFOOTSTEPS traxBuffer = SampleTraxBuffer(i.worldPos, worldNormalVertex, traxNormal); #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA #if _MICROMESH fxLevels = SampleFXLevels(InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, config.uv), wetLevel, burnLevel, traxBuffer); #elif _MICROVERTEXMESH || _MICRODIGGERMESH || _MEGASPLAT fxLevels = ProcessFXLevels(i.fx, traxBuffer); #else fxLevels = SampleFXLevels(config.uv, wetLevel, burnLevel, traxBuffer); #endif #endif #if _DECAL fxLevels = max(fxLevels, decalOutput.fxLevels); #endif TriplanarConfig tc = (TriplanarConfig)0; UNITY_INITIALIZE_OUTPUT(TriplanarConfig,tc); MIPFORMAT albedoLOD = INITMIPFORMAT MIPFORMAT normalLOD = INITMIPFORMAT MIPFORMAT emisLOD = INITMIPFORMAT MIPFORMAT specLOD = INITMIPFORMAT MIPFORMAT origAlbedoLOD = INITMIPFORMAT; #if _TRIPLANAR && !_DISABLESPLATMAPS PrepTriplanar(i.shaderData.texcoord0, worldNormalVertex, i.worldPos, config, tc, weights, albedoLOD, normalLOD, emisLOD, origAlbedoLOD); tc.IN = i; #endif #if !_TRIPLANAR && !_DISABLESPLATMAPS #if _USELODMIP albedoLOD = ComputeMipLevel(config.uv0.xy, _Diffuse_TexelSize.zw); normalLOD = ComputeMipLevel(config.uv0.xy, _NormalSAO_TexelSize.zw); #if _USEEMISSIVEMETAL emisLOD = ComputeMipLevel(config.uv0.xy, _EmissiveMetal_TexelSize.zw); #endif #if _USESPECULARWORKFLOW specLOD = ComputeMipLevel(config.uv0.xy, _Specular_TexelSize.zw);; #endif #elif _USEGRADMIP albedoLOD = float4(ddx(config.uv0.xy), ddy(config.uv0.xy)); normalLOD = albedoLOD; #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #endif origAlbedoLOD = albedoLOD; #endif #if _PERTEXCURVEWEIGHT SAMPLE_PER_TEX(ptCurveWeight, 19.5, config, half4(0.5,1,1,1)); weights.x = lerp(smoothstep(0.5 - ptCurveWeight0.r, 0.5 + ptCurveWeight0.r, weights.x), weights.x, ptCurveWeight0.r*2); weights.y = lerp(smoothstep(0.5 - ptCurveWeight1.r, 0.5 + ptCurveWeight1.r, weights.y), weights.y, ptCurveWeight1.r*2); weights.z = lerp(smoothstep(0.5 - ptCurveWeight2.r, 0.5 + ptCurveWeight2.r, weights.z), weights.z, ptCurveWeight2.r*2); weights.w = lerp(smoothstep(0.5 - ptCurveWeight3.r, 0.5 + ptCurveWeight3.r, weights.w), weights.w, ptCurveWeight3.r*2); weights = TotalOne(weights); #endif // uvScale before anything #if _PERTEXUVSCALEOFFSET && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); config.uv0.xy = config.uv0.xy * ptUVScale0.rg + ptUVScale0.ba; config.uv1.xy = config.uv1.xy * ptUVScale1.rg + ptUVScale1.ba; #if !_MAX2LAYER config.uv2.xy = config.uv2.xy * ptUVScale2.rg + ptUVScale2.ba; #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = config.uv3.xy * ptUVScale3.rg + ptUVScale3.ba; #endif // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = albedoLOD * ptUVScale0.rgrg * weights.x + albedoLOD * ptUVScale1.rgrg * weights.y + albedoLOD * ptUVScale2.rgrg * weights.z + albedoLOD * ptUVScale3.rgrg * weights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #if _PERTEXUVROTATION && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVRot, 16.5, config, half4(0,0,0,0)); config.uv0.xy = RotateUV(config.uv0.xy, ptUVRot0.x); config.uv1.xy = RotateUV(config.uv1.xy, ptUVRot1.x); #if !_MAX2LAYER config.uv2.xy = RotateUV(config.uv2.xy, ptUVRot2.x); #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = RotateUV(config.uv3.xy, ptUVRot0.x); #endif #endif o.Alpha = 1; #if _POM && !_DISABLESPLATMAPS DoPOM(i, config, tc, albedoLOD, weights, camDist, worldNormalVertex); #endif SampleAlbedo(config, tc, samples, albedoLOD, weights); #if _NOISEHEIGHT ApplyNoiseHeight(samples, config.uv, config, i.worldPos, worldNormalVertex); #endif #if _STREAMS || (_PARALLAX && !_DISABLESPLATMAPS) half earlyHeight = BlendWeights(samples.albedo0.w, samples.albedo1.w, samples.albedo2.w, samples.albedo3.w, weights); #endif #if _STREAMS waterNormalFoam = GetWaterNormal(i, config.uv, worldNormalVertex); DoStreamRefract(config, tc, waterNormalFoam, fxLevels.b, earlyHeight); #endif #if _PARALLAX && !_DISABLESPLATMAPS DoParallax(i, earlyHeight, config, tc, samples, weights, camDist); #endif // Blend results #if _PERTEXINTERPCONTRAST && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptContrasts, 1.5, config, 0.5); half4 contrast = 0.5; contrast.x = ptContrasts0.a; contrast.y = ptContrasts1.a; #if !_MAX2LAYER contrast.z = ptContrasts2.a; #endif #if !_MAX3LAYER || !_MAX2LAYER contrast.w = ptContrasts3.a; #endif contrast = clamp(contrast + _Contrast, 0.0001, 1.0); half cnt = contrast.x * weights.x + contrast.y * weights.y + contrast.z * weights.z + contrast.w * weights.w; half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, cnt); #else half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, _Contrast); #endif #if _HYBRIDHEIGHTBLEND heightWeights = lerp(heightWeights, TotalOne(weights), saturate(camDist/max(1.0, _HybridHeightBlendDistance))); #endif // rescale derivatives after height weighting. Basically, in gradmip mode we blend the mip levels, // but this is before height mapping is sampled, so reblending them after alpha will make sure the other // channels (normal, etc) are sharper, which likely matters most.. #if _PERTEXUVSCALEOFFSET && !_DISABLESPLATMAPS #if _TRIPLANAR #if _USEGRADMIP SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); albedoLOD.d0 = origAlbedoLOD.d0 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d0 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d0 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d0 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d1 = origAlbedoLOD.d1 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d1 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d1 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d1 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d2 = origAlbedoLOD.d2 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d2 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d2 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d2 * ptUVScale3.xyxy * heightWeights.w; normalLOD.d0 = albedoLOD.d0; normalLOD.d1 = albedoLOD.d1; normalLOD.d2 = albedoLOD.d2; #if _USEEMISSIVEMETAL emisLOD.d0 = albedoLOD.d0; emisLOD.d1 = albedoLOD.d1; emisLOD.d2 = albedoLOD.d2; #endif #endif // gradmip #else // not triplanar // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = origAlbedoLOD * ptUVScale0.rgrg * heightWeights.x + origAlbedoLOD * ptUVScale1.rgrg * heightWeights.y + origAlbedoLOD * ptUVScale2.rgrg * heightWeights.z + origAlbedoLOD * ptUVScale3.rgrg * heightWeights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #endif #if _PARALLAX || _STREAMS SampleAlbedo(config, tc, samples, albedoLOD, heightWeights); #endif SampleNormal(config, tc, samples, normalLOD, heightWeights); #if _USEEMISSIVEMETAL SampleEmis(config, tc, samples, emisLOD, heightWeights); #endif #if _USESPECULARWORKFLOW SampleSpecular(config, tc, samples, specLOD, heightWeights); #endif #if _DISTANCERESAMPLE && !_DISABLESPLATMAPS DistanceResample(samples, config, tc, camDist, i.viewDir, fxLevels, albedoLOD, i.worldPos, heightWeights, worldNormalVertex); #endif #if _STARREACHFORMAT samples.normSAO0.w = length(samples.normSAO0.xy); samples.normSAO1.w = length(samples.normSAO1.xy); samples.normSAO2.w = length(samples.normSAO2.xy); samples.normSAO3.w = length(samples.normSAO3.xy); #endif // PerTexture sampling goes here, passing the samples structure #if _PERTEXMICROSHADOWS || _PERTEXFUZZYSHADE SAMPLE_PER_TEX(ptFuzz, 17.5, config, half4(0, 0, 1, 1)); #endif #if _PERTEXMICROSHADOWS #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) { half3 lightDir = GetGlobalLightDirTS(i); half4 microShadows = half4(1,1,1,1); microShadows.x = MicroShadow(lightDir, half3(samples.normSAO0.xy, 1), samples.normSAO0.a, ptFuzz0.a); microShadows.y = MicroShadow(lightDir, half3(samples.normSAO1.xy, 1), samples.normSAO1.a, ptFuzz1.a); microShadows.z = MicroShadow(lightDir, half3(samples.normSAO2.xy, 1), samples.normSAO2.a, ptFuzz2.a); microShadows.w = MicroShadow(lightDir, half3(samples.normSAO3.xy, 1), samples.normSAO3.a, ptFuzz3.a); samples.normSAO0.a *= microShadows.x; samples.normSAO1.a *= microShadows.y; #if !_MAX2LAYER samples.normSAO2.a *= microShadows.z; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a *= microShadows.w; #endif #if _DEBUG_OUTPUT_MICROSHADOWS o.Albedo = BlendWeights(microShadows.x, microShadows.y, microShadows.z, microShadows.a, heightWeights); return o; #endif } #endif #endif // _PERTEXMICROSHADOWS #if _PERTEXFUZZYSHADE samples.albedo0.rgb = FuzzyShade(samples.albedo0.rgb, half3(samples.normSAO0.rg, 1), ptFuzz0.r, ptFuzz0.g, ptFuzz0.b, i.viewDir); samples.albedo1.rgb = FuzzyShade(samples.albedo1.rgb, half3(samples.normSAO1.rg, 1), ptFuzz1.r, ptFuzz1.g, ptFuzz1.b, i.viewDir); #if !_MAX2LAYER samples.albedo2.rgb = FuzzyShade(samples.albedo2.rgb, half3(samples.normSAO2.rg, 1), ptFuzz2.r, ptFuzz2.g, ptFuzz2.b, i.viewDir); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = FuzzyShade(samples.albedo3.rgb, half3(samples.normSAO3.rg, 1), ptFuzz3.r, ptFuzz3.g, ptFuzz3.b, i.viewDir); #endif #endif #if _PERTEXSATURATION && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptSaturattion, 9.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = lerp(MSLuminance(samples.albedo0.rgb), samples.albedo0.rgb, ptSaturattion0.a); samples.albedo1.rgb = lerp(MSLuminance(samples.albedo1.rgb), samples.albedo1.rgb, ptSaturattion1.a); #if !_MAX2LAYER samples.albedo2.rgb = lerp(MSLuminance(samples.albedo2.rgb), samples.albedo2.rgb, ptSaturattion2.a); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = lerp(MSLuminance(samples.albedo3.rgb), samples.albedo3.rgb, ptSaturattion3.a); #endif #endif #if _PERTEXTINT && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptTints, 1.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb *= ptTints0.rgb; samples.albedo1.rgb *= ptTints1.rgb; #if !_MAX2LAYER samples.albedo2.rgb *= ptTints2.rgb; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb *= ptTints3.rgb; #endif #endif #if _PCHEIGHTGRADIENT || _PCHEIGHTHSV || _PCSLOPEGRADIENT || _PCSLOPEHSV ProceduralGradients(i, samples, config, worldHeight, worldNormalVertex); #endif #if _WETNESS || _PUDDLES || _STREAMS porosity = _GlobalPorosity; #endif #if _PERTEXCOLORINTENSITY SAMPLE_PER_TEX(ptCI, 23.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = saturate(samples.albedo0.rgb * (1 + ptCI0.rrr)); samples.albedo1.rgb = saturate(samples.albedo1.rgb * (1 + ptCI1.rrr)); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb * (1 + ptCI2.rrr)); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb * (1 + ptCI3.rrr)); #endif #endif #if (_PERTEXBRIGHTNESS || _PERTEXCONTRAST || _PERTEXPOROSITY || _PERTEXFOAM) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptBC, 3.5, config, half4(1, 1, 1, 1)); #if _PERTEXCONTRAST samples.albedo0.rgb = saturate(((samples.albedo0.rgb - 0.5) * ptBC0.g) + 0.5); samples.albedo1.rgb = saturate(((samples.albedo1.rgb - 0.5) * ptBC1.g) + 0.5); #if !_MAX2LAYER samples.albedo2.rgb = saturate(((samples.albedo2.rgb - 0.5) * ptBC2.g) + 0.5); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(((samples.albedo3.rgb - 0.5) * ptBC3.g) + 0.5); #endif #endif #if _PERTEXBRIGHTNESS samples.albedo0.rgb = saturate(samples.albedo0.rgb + ptBC0.rrr); samples.albedo1.rgb = saturate(samples.albedo1.rgb + ptBC1.rrr); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb + ptBC2.rrr); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb + ptBC3.rrr); #endif #endif #if _PERTEXPOROSITY porosity = BlendWeights(ptBC0.b, ptBC1.b, ptBC2.b, ptBC3.b, heightWeights); #endif #if _PERTEXFOAM streamFoam = BlendWeights(ptBC0.a, ptBC1.a, ptBC2.a, ptBC3.a, heightWeights); #endif #endif #if (_PERTEXNORMSTR || _PERTEXAOSTR || _PERTEXSMOOTHSTR || _PERTEXMETALLIC) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(perTexMatSettings, 2.5, config, half4(1.0, 1.0, 1.0, 0.0)); #endif #if _PERTEXNORMSTR && !_DISABLESPLATMAPS #if _SURFACENORMALS samples.surf0 *= perTexMatSettings0.r; samples.surf1 *= perTexMatSettings1.r; samples.surf2 *= perTexMatSettings2.r; samples.surf3 *= perTexMatSettings3.r; #else samples.normSAO0.xy *= perTexMatSettings0.r; samples.normSAO1.xy *= perTexMatSettings1.r; samples.normSAO2.xy *= perTexMatSettings2.r; samples.normSAO3.xy *= perTexMatSettings3.r; #endif #endif #if _PERTEXAOSTR && !_DISABLESPLATMAPS samples.normSAO0.a = pow(abs(samples.normSAO0.a), perTexMatSettings0.b); samples.normSAO1.a = pow(abs(samples.normSAO1.a), perTexMatSettings1.b); #if !_MAX2LAYER samples.normSAO2.a = pow(abs(samples.normSAO2.a), perTexMatSettings2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a = pow(abs(samples.normSAO3.a), perTexMatSettings3.b); #endif #endif #if _PERTEXSMOOTHSTR && !_DISABLESPLATMAPS samples.normSAO0.b += perTexMatSettings0.g; samples.normSAO1.b += perTexMatSettings1.g; samples.normSAO0.b = saturate(samples.normSAO0.b); samples.normSAO1.b = saturate(samples.normSAO1.b); #if !_MAX2LAYER samples.normSAO2.b += perTexMatSettings2.g; samples.normSAO2.b = saturate(samples.normSAO2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.b += perTexMatSettings3.g; samples.normSAO3.b = saturate(samples.normSAO3.b); #endif #endif #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) #if _PERTEXSSS { SAMPLE_PER_TEX(ptSSS, 18.5, config, half4(1, 1, 1, 1)); // tint, thickness half4 vals = ptSSS0 * heightWeights.x + ptSSS1 * heightWeights.y + ptSSS2 * heightWeights.z + ptSSS3 * heightWeights.w; SSSThickness = vals.a; SSSTint = vals.rgb; } #endif #endif #if _PERTEXRIMLIGHT { SAMPLE_PER_TEX(ptRimA, 26.5, config, half4(1, 1, 1, 1)); SAMPLE_PER_TEX(ptRimB, 27.5, config, half4(1, 1, 1, 0)); samples.emisMetal0.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO0.xy, 1))), max(0.0001, ptRimA0.g)) * ptRimB0.rgb * ptRimB0.a; samples.emisMetal1.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO1.xy, 1))), max(0.0001, ptRimA1.g)) * ptRimB1.rgb * ptRimB1.a; samples.emisMetal2.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO2.xy, 1))), max(0.0001, ptRimA2.g)) * ptRimB2.rgb * ptRimB2.a; samples.emisMetal3.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO3.xy, 1))), max(0.0001, ptRimA3.g)) * ptRimB3.rgb * ptRimB3.a; } #endif #if (((_DETAILNOISE && _PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && _PERTEXDISTANCENOISESTRENGTH)) || (_NORMALNOISE && _PERTEXNORMALNOISESTRENGTH)) && !_DISABLESPLATMAPS ApplyDetailDistanceNoisePerTex(samples, config, camDist, i.worldPos, worldNormalVertex); #endif #if _GLOBALNOISEUV // noise defaults so that a value of 1, 1 is 4 pixels in size and moves the uvs by 1 pixel max. #if _CUSTOMSPLATTEXTURES noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #else noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE ApplyTrax(samples, config, i.worldPos, traxBuffer, traxNormal); #endif #if (_ANTITILEARRAYDETAIL || _ANTITILEARRAYDISTANCE || _ANTITILEARRAYNORMAL) && !_DISABLESPLATMAPS ApplyAntiTilePerTex(samples, config, camDist, i.worldPos, worldNormalVertex, heightWeights); #endif #if _GEOMAP && !_DISABLESPLATMAPS GeoTexturePerTex(samples, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _GLOBALTINT && _PERTEXGLOBALTINTSTRENGTH && !_DISABLESPLATMAPS GlobalTintTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALNORMALS && _PERTEXGLOBALNORMALSTRENGTH && !_DISABLESPLATMAPS GlobalNormalTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && _PERTEXGLOBALSAOMSTRENGTH && !_DISABLESPLATMAPS GlobalSAOMTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && _PERTEXGLOBALEMISSTRENGTH && !_DISABLESPLATMAPS GlobalEmisTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && _PERTEXGLOBALSPECULARSTRENGTH && !_DISABLESPLATMAPS && _USESPECULARWORKFLOW GlobalSpecularTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _PERTEXMETALLIC && !_DISABLESPLATMAPS half metallic = BlendWeights(perTexMatSettings0.a, perTexMatSettings1.a, perTexMatSettings2.a, perTexMatSettings3.a, heightWeights); o.Metallic = metallic; #endif #if _GLITTER && !_DISABLESPLATMAPS DoGlitter(i, samples, config, camDist, worldNormalVertex, i.worldPos); #endif // Blend em.. #if _DISABLESPLATMAPS // If we don't sample from the _Diffuse, then the shader compiler will strip the sampler on // some platforms, which will cause everything to break. So we sample from the lowest mip // and saturate to 1 to keep the cost minimal. Annoying, but the compiler removes the texture // and sampler, even though the sampler is still used. albedo = saturate(UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, float3(0,0,0), 12) + 1); albedo.a = 0.5; // make height something we can blend with for the combined mesh mode, since it still height blends. normSAO = half4(0,0,0,1); #else albedo = BlendWeights(samples.albedo0, samples.albedo1, samples.albedo2, samples.albedo3, heightWeights); normSAO = BlendWeights(samples.normSAO0, samples.normSAO1, samples.normSAO2, samples.normSAO3, heightWeights); #if _SURFACENORMALS surfGrad = BlendWeights(samples.surf0, samples.surf1, samples.surf2, samples.surf3, heightWeights); #endif #if (_USEEMISSIVEMETAL || _PERTEXRIMLIGHT) && !_DISABLESPLATMAPS emisMetal = BlendWeights(samples.emisMetal0, samples.emisMetal1, samples.emisMetal2, samples.emisMetal3, heightWeights); #endif #if _USESPECULARWORKFLOW && !_DISABLESPLATMAPS specular = BlendWeights(samples.specular0, samples.specular1, samples.specular2, samples.specular3, heightWeights); #endif #if _PERTEXOUTLINECOLOR SAMPLE_PER_TEX(ptOutlineColor, 28.5, config, half4(0.5, 0.5, 0.5, 1)); half4 outlineColor = BlendWeights(ptOutlineColor0, ptOutlineColor1, ptOutlineColor2, ptOutlineColor3, heightWeights); half4 tstr = saturate(abs(heightWeights - 0.5) * 2); half transitionBlend = min(min(min(tstr.x, tstr.y), tstr.z), tstr.w); albedo.rgb = lerp(albedo.rgb * outlineColor.rgb * 2, albedo.rgb, outlineColor.a * transitionBlend); #endif #endif #if _MESHOVERLAYSPLATS || _MESHCOMBINED o.Alpha = 1.0; if (config.uv0.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.x; else if (config.uv1.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.y; else if (config.uv2.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.z; else if (config.uv3.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.w; #endif // effects which don't require per texture adjustments and are part of the splats sample go here. // Often, as an optimization, you can compute the non-per tex version of above effects here.. #if ((_DETAILNOISE && !_PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && !_PERTEXDISTANCENOISESTRENGTH) || (_NORMALNOISE && !_PERTEXNORMALNOISESTRENGTH)) ApplyDetailDistanceNoise(albedo.rgb, normSAO, surfGrad, config, camDist, i.worldPos, worldNormalVertex); #endif #if _SPLATFADE } #endif #if _SPLATFADE float2 sfDX = ddx(config.uv * _UVScale); float2 sfDY = ddy(config.uv * _UVScale); MSBRANCHOTHER(camDist - _SplatFade.x) { float falloff = saturate(InverseLerp(_SplatFade.x, _SplatFade.y, camDist)); half4 sfalb = SAMPLE_TEXTURE2D_ARRAY_GRAD(_Diffuse, sampler_Diffuse, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY); COUNTSAMPLE albedo.rgb = lerp(albedo.rgb, sfalb.rgb, falloff); #if !_NONORMALMAP && !_AUTONORMAL half4 sfnormSAO = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalSAO, sampler_NormalSAO, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY).agrb; COUNTSAMPLE sfnormSAO.xy = sfnormSAO.xy * 2 - 1; normSAO = lerp(normSAO, sfnormSAO, falloff); #if _SURFACENORMALS surfGrad = lerp(surfGrad, ConvertNormal2ToGradient(sfnormSAO.xy), falloff); #endif #endif } #endif #if _AUTONORMAL float3 autoNormal = HeightToNormal(albedo.a * _AutoNormalHeightScale, i.worldPos); normSAO.xy = autoNormal; normSAO.z = 0; normSAO.w = (autoNormal.z * autoNormal.z); #endif #if _MESHCOMBINED SampleMeshCombined(albedo, normSAO, surfGrad, emisMetal, specular, o.Alpha, SSSThickness, SSSTint, config, heightWeights); #endif #if _ISOBJECTSHADER SampleObjectShader(i, albedo, normSAO, surfGrad, emisMetal, specular, config); #endif #if _GEOMAP GeoTexture(albedo.rgb, normSAO, surfGrad, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _SCATTER ApplyScatter( #if _MEGASPLAT config, #endif i, albedo, normSAO, surfGrad, config.uv, camDist); #endif #if _DECAL DoDecalBlend(decalOutput, albedo, normSAO, surfGrad, emisMetal, i.uv_Control0); #endif #if _GLOBALTINT && !_PERTEXGLOBALTINTSTRENGTH GlobalTintTexture(albedo.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif #if _VSGRASSMAP VSGrassTexture(albedo.rgb, config, camDist); #endif #if _GLOBALNORMALS && !_PERTEXGLOBALNORMALSTRENGTH GlobalNormalTexture(normSAO, surfGrad, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && !_PERTEXGLOBALSAOMSTRENGTH GlobalSAOMTexture(normSAO, emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && !_PERTEXGLOBALEMISSTRENGTH GlobalEmisTexture(emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && !_PERTEXGLOBALSPECULARSTRENGTH && _USESPECULARWORKFLOW GlobalSpecularTexture(specular.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif o.Albedo = albedo.rgb; o.Height = albedo.a; #if _NONORMALMAP o.Normal = half3(0,0,1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #elif _SURFACENORMALS o.Normal = ResolveNormalFromSurfaceGradient(surfGrad); o.Normal = mul(GetTBN(i), o.Normal); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #else o.Normal = half3(normSAO.xy, 1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #endif #if _USEEMISSIVEMETAL || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _PERTEXRIMLIGHT #if _USEEMISSIVEMETAL emisMetal.rgb *= _EmissiveMult; #endif o.Emission += emisMetal.rgb; o.Metallic = emisMetal.a; #endif #if _USESPECULARWORKFLOW o.Specular = specular; #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA pud = DoStreams(i, o, fxLevels, config.uv, porosity, waterNormalFoam, worldNormalVertex, streamFoam, wetLevel, burnLevel, i.worldPos); #endif #if _SNOW snowCover = DoSnow(i, o, config.uv, WorldNormalVector(i, o.Normal), worldNormalVertex, i.worldPos, pud, porosity, camDist, config, weights, SSSTint, SSSThickness, traxBuffer, traxNormal); #endif #if _PERTEXSSS || _MESHCOMBINEDUSESSS || (_SNOW && _SNOWSSS) { half3 worldView = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); o.Emission += ComputeSSS(i, worldView, WorldNormalVector(i, o.Normal), SSSTint, SSSThickness, _SSSDistance, _SSSScale, _SSSPower); } #endif #if _SNOWGLITTER DoSnowGlitter(i, config, o, camDist, worldNormalVertex, snowCover); #endif #if _WINDPARTICULATE || _SNOWPARTICULATE DoWindParticulate(i, o, config, weights, camDist, worldNormalVertex, snowCover); #endif o.Normal.z = sqrt(1 - saturate(dot(o.Normal.xy, o.Normal.xy))); #if _SPECULARFADE { float specFade = saturate((i.worldPos.y - _SpecularFades.x) / max(_SpecularFades.y - _SpecularFades.x, 0.0001)); o.Metallic *= specFade; o.Smoothness *= specFade; } #endif #if _VSSHADOWMAP VSShadowTexture(o, i, config, camDist); #endif #if _TOONWIREFRAME ToonWireframe(config.uv, o.Albedo, camDist); #endif #if _SEETHROUGHSHADER SeethroughShader(o.Albedo, o.Emission, o.Alpha, i.worldPos, o.Normal, i.worldNormal); #endif #if _DEBUG_TRAXBUFFER ClearAllButAlbedo(o, half3(traxBuffer, 0, 0) * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMALVERTEX ClearAllButAlbedo(o, worldNormalVertex * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMAL ClearAllButAlbedo(o, WorldNormalVector(i, o.Normal) * saturate(o.Albedo.z+1)); #endif #if _DEBUG_MEGABARY && _MEGASPLAT o.Albedo = i.baryWeights.xyz; #endif return o; } void SampleSplats(float2 controlUV, inout half4 w0, inout half4 w1, inout half4 w2, inout half4 w3, inout half4 w4, inout half4 w5, inout half4 w6, inout half4 w7) { #if _CUSTOMSPLATTEXTURES #if !_MICROMESH controlUV = (controlUV * (_CustomControl0_TexelSize.zw - 1.0f) + 0.5f) * _CustomControl0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_CustomControl0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_CustomControl1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_CustomControl2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_CustomControl3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_CustomControl4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_CustomControl5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_CustomControl6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_CustomControl7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #else #if !_MICROMESH controlUV = (controlUV * (_Control0_TexelSize.zw - 1.0f) + 0.5f) * _Control0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_Control0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_Control1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_Control2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_Control3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_Control4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_Control5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_Control6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_Control7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #endif } MicroSplatLayer SurfImpl(Input i, float3 worldNormalVertex) { #if _MEGANOUV i.uv_Control0 = i.worldPos.xz; #endif float camDist = distance(_WorldSpaceCameraPos, i.worldPos); #if _FORCELOCALSPACE worldNormalVertex = mul((float3x3)GetWorldToObjectMatrix(), worldNormalVertex).xyz; i.worldPos = i.worldPos - mul(GetObjectToWorldMatrix(), float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _ORIGINSHIFT i.worldPos = i.worldPos + mul(_GlobalOriginMTX, float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _DEBUG_USE_TOPOLOGY i.worldPos = SAMPLE_TEXTURE2D(_DebugWorldPos, sampler_Diffuse, i.uv_Control0); worldNormalVertex = SAMPLE_TEXTURE2D(_DebugWorldNormal, sampler_Diffuse, i.uv_Control0); i.worldHeight = i.worldPos.y; #endif #if _ALPHABELOWHEIGHT && !_TBDISABLEALPHAHOLES ClipWaterLevel(i.worldPos); #endif #if !_TBDISABLEALPHAHOLES && defined(_ALPHATEST_ON) // UNITY 2019.3 holes ClipHoles(i.uv_Control0); #endif float2 origUV = i.uv_Control0; #if _MICROMESH && _MESHUV2 float2 controlUV = i.uv2_Diffuse; #else float2 controlUV = i.uv_Control0; #endif #if _MICROMESH controlUV = InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, controlUV); #endif half4 weights = half4(1,0,0,0); Config config = (Config)0; UNITY_INITIALIZE_OUTPUT(Config,config); config.uv = origUV; DecalOutput decalOutput = (DecalOutput)0; #if _DECAL decalOutput = DoDecals(i.uv_Control0, i.worldPos, camDist, worldNormalVertex); #endif #if _SURFACENORMALS // Initialize the surface gradient basis vectors ConstructSurfaceGradientTBN(i); #endif #if _SPLATFADE MSBRANCHOTHER(_SplatFade.y - camDist) #endif // _SPLATFADE { #if !_DISABLESPLATMAPS // Sample the splat data, from textures or vertices, and setup the config.. #if _MICRODIGGERMESH DiggerSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLAT MegaSplatVertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLATTEXTURE MegaSplatTextureSetup(controlUV, weights, origUV, config, i.worldPos, decalOutput); #elif _MICROVERTEXMESH VertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif !_PROCEDURALTEXTURE || _PROCEDURALBLENDSPLATS half4 w0 = 0; half4 w1 = 0; half4 w2 = 0; half4 w3 = 0; half4 w4 = 0; half4 w5 = 0; half4 w6 = 0; half4 w7 = 0; SampleSplats(controlUV, w0, w1, w2, w3, w4, w5, w6, w7); Setup(weights, origUV, config, w0, w1, w2, w3, w4, w5, w6, w7, i.worldPos, decalOutput); #endif #if _PROCEDURALTEXTURE float3 procNormal = worldNormalVertex; float3 worldPos = i.worldPos; ProceduralSetup(i, worldPos, i.worldHeight, procNormal, i.worldUpVector, weights, origUV, config, ddx(origUV), ddy(origUV), ddx(worldPos), ddy(worldPos), decalOutput); #endif #else // _DISABLESPLATMAPS Setup(weights, origUV, config, half4(1,0,0,0), 0, 0, 0, 0, 0, 0, 0, i.worldPos, decalOutput); #endif #if _SLOPETEXTURE SlopeTexture(config, weights, worldNormalVertex); #endif } // _SPLATFADE else case #if _TOONFLATTEXTURE float2 quv = floor(origUV * _ToonTerrainSize); float2 fuv = frac(origUV * _ToonTerrainSize); #if !_TOONFLATTEXTUREQUAD quv = Hash2D((fuv.x > fuv.y) ? quv : quv * 0.333); #endif float2 uvq = quv / _ToonTerrainSize; config.uv0.xy = uvq; config.uv1.xy = uvq; config.uv2.xy = uvq; config.uv3.xy = uvq; #endif #if (_TEXTURECLUSTER2 || _TEXTURECLUSTER3) && !_DISABLESPLATMAPS PrepClusters(origUV, config, i.worldPos, worldNormalVertex); #endif #if (_ALPHAHOLE || _ALPHAHOLETEXTURE) && !_DISABLESPLATMAPS && !_TBDISABLEALPHAHOLES ClipAlphaHole(config, weights); #endif MicroSplatLayer l = Sample(i, weights, config, camDist, worldNormalVertex, decalOutput); // On windows, sometimes the shared samplers gets stripped, so we have to do this crap. // We sample from the lowest mip, so it shouldn't cost much, but still, I hate this, wtf.. float stripVal = saturate(SAMPLE_TEXTURE2D_LOD(_Diffuse, sampler_Diffuse, config.uv0, 11).r + 2); stripVal *= saturate(SAMPLE_TEXTURE2D_LOD(_NormalSAO, sampler_NormalSAO, config.uv0, 11).r + 2); l.Albedo *= stripVal; l.Normal *= stripVal; #if _PROCEDURALTEXTURE ProceduralTextureDebugOutput(l, weights, config); #endif return l; } float4 ConstructTerrainTangent(float3 normal, float3 positiveZ) { // Consider a flat terrain. It should have tangent be (1, 0, 0) and bitangent be (0, 0, 1) as the UV of the terrain grid mesh is a scale of the world XZ position. // In CreateTangentToWorld function (in SpaceTransform.hlsl), it is cross(normal, tangent) * sgn for the bitangent vector. // It is not true in a left-handed coordinate system for the terrain bitangent, if we provide 1 as the tangent.w. It would produce (0, 0, -1) instead of (0, 0, 1). // Also terrain's tangent calculation was wrong in a left handed system because cross((0,0,1), terrainNormalOS) points to the wrong direction as negative X. // Therefore all the 4 xyzw components of the tangent needs to be flipped to correct the tangent frame. // (See TerrainLitData.hlsl - GetSurfaceAndBuiltinData) float3 tangent = normalize(cross(normal, positiveZ)); return float4(tangent, -1); } void TerrainInstancing(inout float4 vertex, inout float3 normal, inout float2 uv) { #if _MICROTERRAIN && defined(UNITY_INSTANCING_ENABLED) && !_TERRAINBLENDABLESHADER float2 patchVertex = vertex.xy; float4 instanceData = UNITY_ACCESS_INSTANCED_PROP(Terrain, _TerrainPatchInstanceData); float2 sampleCoords = (patchVertex.xy + instanceData.xy) * instanceData.z; // (xy + float2(xBase,yBase)) * skipScale uv = sampleCoords * _TerrainHeightmapRecipSize.zw; float2 sampleUV = (uv / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleUV, 0)); vertex.xz = sampleCoords * _TerrainHeightmapScale.xz; vertex.y = height * _TerrainHeightmapScale.y; normal = float3(0, 1, 0); #endif } void ApplyMeshModification(inout VertexData input) { #if _MICROTERRAIN && !_TERRAINBLENDABLESHADER float2 uv = input.texcoord0.xy; TerrainInstancing(input.vertex, input.normal, uv); input.texcoord0.xy = uv; #endif #if _PERPIXNORMAL && !_TERRAINBLENDABLESHADER input.normal = float3(0,1,0); #endif #if _MICROVERSEPREVIEW float4 recipSize = _TerrainHeightmapTexture_TexelSize; recipSize.zw = (1.0f / (_TerrainHeightmapTexture_TexelSize.zw-1)); float2 sampleCoords = (input.texcoord0.xy / recipSize.zw + 0.5f) * recipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleCoords, 0)); input.vertex.xyz += float3(0,1,0) * height * _TerrainHeight * 2; #endif } // called by the template, so we can remove tangent from VertexData void ApplyTerrainTangent(inout VertexToPixel input) { #if (_MICROTERRAIN || _PERPIXNORMAL) && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif // digger meshes ain't got no tangent either.. #if _MICRODIGGERMESH && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif } void ModifyVertex(inout VertexData v, inout ExtraV2F d) { ApplyMeshModification(v); #if _MICROVERTEXMESH || _MICRODIGGERMESH EncodeVertexWorkflow(v, d); #elif _MEGASPLAT EncodeMegaSplatVertex(v, d); #endif } void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d) { #if _MICROVERSEPREVIEW v.vertex.y = OffsetVertex(v, d).y; #elif _TESSDISTANCE || _TESSEDGE v.vertex.xyz += OffsetVertex(v, d); #endif } float3 GetTessFactors () { #if _TESSEDGE return float3(_TessData1.x, _TessData1.w, 0); #endif #if _TESSDISTANCE return float3(_TessData2.x, _TessData2.y, _TessData1.x); #endif return 0; } void SurfaceFunction(inout Surface o, inout ShaderData d) { float3 worldNormalVertex = d.worldSpaceNormal; #if _MICROVERSEPREVIEW float2 sampleCoords = d.texcoord0.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif (defined(UNITY_INSTANCING_ENABLED) && _MICROTERRAIN && !_TERRAINBLENDABLESHADER) float2 sampleCoords = (d.texcoord0.xy / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif _PERPIXNORMAL && (_MICROTERRAIN || _MICROMESHTERRAIN) && !_TERRAINBLENDABLESHADER float2 sampleCoords = (d.texcoord0.xy * _PerPixelNormal_TexelSize.zw + 0.5f) * _PerPixelNormal_TexelSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_PerPixelNormal, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomTangent, geomNormal)) * -1; #if _MICROMESHTERRAIN geomBitangent *= -1; #endif worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #endif #if _TOONPOLYEDGE FlatShade(d); #endif Input i = DescToInput(d); #if _SRPTERRAINBLEND MicroSplatLayer l = BlendWithTerrain(d); #if _DEBUG_WORLDNORMAL ClearAllButAlbedo(l, normalize(TangentToWorldSpace(d, l.Normal)) * saturate(l.Albedo.z+1)); #endif #else MicroSplatLayer l = SurfImpl(i, worldNormalVertex); #endif DoDebugOutput(l); o.Albedo = l.Albedo; o.Normal = l.Normal; o.Smoothness = l.Smoothness; o.Occlusion = l.Occlusion; o.Metallic = l.Metallic; o.Emission = l.Emission; #if _USESPECULARWORKFLOW o.Specular = l.Specular; #endif o.Height = l.Height; o.Alpha = l.Alpha; } // SHADERDESC ShaderData CreateShaderData(VertexToPixel i) { ShaderData d = (ShaderData)0; d.worldSpacePosition = i.worldPos; d.worldSpaceNormal = i.worldNormal; d.worldSpaceTangent = i.worldTangent.xyz; float3 bitangent = cross(i.worldTangent.xyz, i.worldNormal) * i.worldTangent.w * -1; d.TBNMatrix = float3x3(d.worldSpaceTangent, bitangent, d.worldSpaceNormal); d.worldSpaceViewDir = normalize(_WorldSpaceCameraPos - i.worldPos); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); d.texcoord0 = i.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER d.texcoord1 = i.texcoord1; // d.texcoord2 = i.texcoord2; #endif // d.texcoord3 = i.texcoord3; // d.vertexColor = i.vertexColor; // these rarely get used, so we back transform them. Usually will be stripped. #if _HDRP // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(GetCameraRelativePositionWS(i.worldPos), 1)); #else // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(i.worldPos, 1)); #endif // d.localSpaceNormal = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldNormal)); // d.localSpaceTangent = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldTangent.xyz)); // d.screenPos = i.screenPos; // d.screenUV = i.screenPos.xy / i.screenPos.w; // d.extraV2F0 = i.extraV2F0; // d.extraV2F1 = i.extraV2F1; // d.extraV2F2 = i.extraV2F2; // d.extraV2F3 = i.extraV2F3; // d.extraV2F4 = i.extraV2F4; // d.extraV2F5 = i.extraV2F5; // d.extraV2F6 = i.extraV2F6; // d.extraV2F7 = i.extraV2F7; return d; } // CHAINS void ChainModifyVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; ModifyVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainModifyTessellatedVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; // d.extraV2F0 = v2p.extraV2F0; // d.extraV2F1 = v2p.extraV2F1; // d.extraV2F2 = v2p.extraV2F2; // d.extraV2F3 = v2p.extraV2F3; // d.extraV2F4 = v2p.extraV2F4; // d.extraV2F5 = v2p.extraV2F5; // d.extraV2F6 = v2p.extraV2F6; // d.extraV2F7 = v2p.extraV2F7; ModifyTessellatedVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainFinalColorForward(inout Surface l, inout ShaderData d, inout half4 color) { } void ChainFinalGBufferStandard(inout Surface s, inout ShaderData d, inout half4 GBuffer0, inout half4 GBuffer1, inout half4 GBuffer2, inout half4 outEmission, inout half4 outShadowMask) { } #if _PASSSHADOW float3 _LightDirection; float3 _LightPosition; #endif // vertex shader VertexToPixel Vert (VertexData v) { VertexToPixel o = (VertexToPixel)0; UNITY_SETUP_INSTANCE_ID(v); UNITY_TRANSFER_INSTANCE_ID(v, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #if !_TESSELLATION_ON ChainModifyVertex(v, o); #endif o.texcoord0 = v.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.texcoord1 = v.texcoord1; // o.texcoord2 = v.texcoord2; #endif // o.texcoord3 = v.texcoord3; // o.vertexColor = v.vertexColor; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz); o.worldPos = TransformObjectToWorld(v.vertex.xyz); o.worldNormal = TransformObjectToWorldNormal(v.normal); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float2 uv1 = v.texcoord1.xy; float2 uv2 = v.texcoord2.xy; o.worldTangent = float4(TransformObjectToWorldDir(v.tangent.xyz), v.tangent.w); #else float2 uv1 = v.texcoord0.xy; float2 uv2 = uv1; #endif // MS Only ApplyTerrainTangent(o); #if _PASSSHADOW #if _CASTING_PUNCTUAL_LIGHT_SHADOW float3 lightDirectionWS = normalize(_LightPosition - o.worldPos); #else float3 lightDirectionWS = _LightDirection; #endif // Define shadow pass specific clip position for Universal o.pos = TransformWorldToHClip(ApplyShadowBias(o.worldPos, o.worldNormal, lightDirectionWS)); #if UNITY_REVERSED_Z o.pos.z = min(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #else o.pos.z = max(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #endif #elif _PASSMETA #if _MICROTERRAIN o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), v.texcoord0.xy, v.texcoord0.xy, unity_LightmapST, unity_DynamicLightmapST); #else o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), uv1, uv2, unity_LightmapST, unity_DynamicLightmapST); #endif #else o.pos = TransformWorldToHClip(o.worldPos); #endif // o.screenPos = ComputeScreenPos(o.pos, _ProjectionParams.x); #if _PASSFORWARD || _PASSGBUFFER #if _MICROTERRAIN OUTPUT_LIGHTMAP_UV(v.texcoord0.xy, unity_LightmapST, o.lightmapUV); #else OUTPUT_LIGHTMAP_UV(uv1, unity_LightmapST, o.lightmapUV); #endif OUTPUT_SH(o.worldNormal, o.sh); #if defined(DYNAMICLIGHTMAP_ON) o.dynamicLightmapUV.xy = uv2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; #endif #endif #ifdef VARYINGS_NEED_FOG_AND_VERTEX_LIGHT half fogFactor = 0; #if defined(_FOG_FRAGMENT) fogFactor = ComputeFogFactor(o.pos.z); #endif #if _BAKEDLIT half3 vertexLight = VertexLighting(o.worldPos, o.worldNormal); o.fogFactorAndVertexLight = half4(fogFactor, vertexLight); #else o.fogFactorAndVertexLight = half4(fogFactor, 0,0,0); #endif #endif #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) o.shadowCoord = GetShadowCoord(vertexInput); #endif return o; } #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/UnityGBuffer.hlsl" // fragment shader FragmentOutput Frag (VertexToPixel IN #ifdef _DEPTHOFFSET_ON , out float outputDepth : SV_Depth #endif ) { UNITY_SETUP_INSTANCE_ID(IN); UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN); ShaderData d = CreateShaderData(IN); Surface l = (Surface)0; #ifdef _DEPTHOFFSET_ON l.outputDepth = outputDepth; #endif l.Albedo = half3(0.5, 0.5, 0.5); l.Normal = float3(0,0,1); l.Occlusion = 1; l.Alpha = 1; SurfaceFunction(l, d); #ifdef _DEPTHOFFSET_ON outputDepth = l.outputDepth; #endif #if defined(_USESPECULAR) || _SIMPLELIT float3 specular = l.Specular; float metallic = 0; #else float3 specular = 0; float metallic = l.Metallic; #endif InputData inputData = (InputData)0; inputData.positionWS = IN.worldPos; inputData.normalWS = mul(l.Normal, d.TBNMatrix); inputData.viewDirectionWS = SafeNormalize(d.worldSpaceViewDir); #if defined(MAIN_LIGHT_CALCULATE_SHADOWS) inputData.shadowCoord = TransformWorldToShadowCoord(inputData.positionWS); #else inputData.shadowCoord = float4(0, 0, 0, 0); #endif InitializeInputDataFog(float4(IN.worldPos, 1.0), IN.fogFactorAndVertexLight.x); inputData.vertexLighting = IN.fogFactorAndVertexLight.yzw; #if defined(DYNAMICLIGHTMAP_ON) inputData.bakedGI = SAMPLE_GI(IN.lightmapUV, IN.dynamicLightmapUV.xy, IN.sh, inputData.normalWS); #else inputData.bakedGI = SAMPLE_GI(IN.lightmapUV, IN.sh, inputData.normalWS); #endif inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(IN.pos); #if defined(LIGHTMAP_ON) inputData.shadowMask = SAMPLE_SHADOWMASK(IN.lightmapUV); #endif #if defined(DEBUG_DISPLAY) #if defined(DYNAMICLIGHTMAP_ON) inputData.dynamicLightmapUV = IN.dynamicLightmapUV.xy; #endif #if defined(LIGHTMAP_ON) inputData.staticLightmapUV = IN.lightmapUV; #else inputData.vertexSH = IN.sh; #endif #endif #ifdef _DBUFFER ApplyDecal(IN.pos, l.Albedo, specular, inputData.normalWS, metallic, l.Occlusion, l.Smoothness); #endif BRDFData brdfData; InitializeBRDFData(l.Albedo, metallic, specular, l.Smoothness, l.Alpha, brdfData); Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, inputData.shadowMask); MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, inputData.shadowMask); half3 color = GlobalIllumination(brdfData, inputData.bakedGI, l.Occlusion, inputData.positionWS, inputData.normalWS, inputData.viewDirectionWS); return BRDFDataToGbuffer(brdfData, inputData, l.Smoothness, l.Emission + color, l.Occlusion); } ENDHLSL } Pass { Name "ShadowCaster" Tags { "LightMode" = "ShadowCaster" } // Render State Blend One Zero, One Zero Cull Back ZTest LEqual ZWrite On // ColorMask: HLSLPROGRAM #pragma vertex Vert #pragma fragment Frag #pragma target 3.5 #pragma prefer_hlslcc gles #pragma exclude_renderers d3d11_9x #pragma multi_compile_instancing #pragma multi_compile_local _ _ALPHATEST_ON #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW #define _NORMAL_DROPOFF_TS 1 #define ATTRIBUTES_NEED_NORMAL #define ATTRIBUTES_NEED_TANGENT #define SHADERPASS_SHADOWCASTER #define _PASSSHADOW 1 #define _MICROSPLAT 1 #define _MICROPOLARISMESH 1 #define _USEGRADMIP 1 #define _PERTEXUVSCALEOFFSET 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _MSRENDERLOOP_UNITYLD 1 #define _MSRENDERLOOP_UNITYURP2020 1 #define _MSRENDERLOOP_UNITYURP2021 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _URP 1 #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl" #include "Packages/com.unity.shadergraph/ShaderGraphLibrary/ShaderVariablesFunctions.hlsl" #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, float3x3(d.worldSpaceTangent, cross(d.worldSpaceTangent, d.worldSpaceNormal), d.worldSpaceNormal)) #define UnityObjectToWorldNormal(normal) mul(GetObjectToWorldMatrix(), normal) #define _WorldSpaceLightPos0 _MainLightPosition #define UNITY_DECLARE_TEX2D(name) TEXTURE2D(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2D_NOSAMPLER(name) TEXTURE2D(name); #define UNITY_DECLARE_TEX2DARRAY(name) TEXTURE2D_ARRAY(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2DARRAY_NOSAMPLER(name) TEXTURE2D_ARRAY(name); #define UNITY_SAMPLE_TEX2DARRAY(tex,coord) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, coord.xy, coord.z) #define UNITY_SAMPLE_TEX2DARRAY_LOD(tex,coord,lod) SAMPLE_TEXTURE2D_ARRAY_LOD(tex, sampler##tex, coord.xy, coord.z, lod) #define UNITY_SAMPLE_TEX2D(tex, coord) SAMPLE_TEXTURE2D(tex, sampler##tex, coord) #define UNITY_SAMPLE_TEX2D_SAMPLER(tex, samp, coord) SAMPLE_TEXTURE2D(tex, sampler##samp, coord) #if defined(UNITY_COMPILER_HLSL) #define UNITY_INITIALIZE_OUTPUT(type,name) name = (type)0; #else #define UNITY_INITIALIZE_OUTPUT(type,name) #endif #define sampler2D_float sampler2D #define sampler2D_half sampler2D // data across stages, stripped like the above. struct VertexToPixel { float4 pos : SV_POSITION; float3 worldPos : TEXCOORD0; float3 worldNormal : TEXCOORD1; float4 worldTangent : TEXCOORD2; float4 texcoord0 : TEXCCOORD3; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 texcoord1 : TEXCCOORD4; // float4 texcoord2 : TEXCCOORD5; #endif // float4 texcoord3 : TEXCCOORD6; // float4 screenPos : TEXCOORD7; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD13; // float4 extraV2F1 : TEXCOORD14; // float4 extraV2F2 : TEXCOORD15; // float4 extraV2F3 : TEXCOORD16; // float4 extraV2F4 : TEXCOORD17; // float4 extraV2F5 : TEXCOORD18; // float4 extraV2F6 : TEXCOORD19; // float4 extraV2F7 : TEXCOORD20; #if defined(LIGHTMAP_ON) float2 lightmapUV : TEXCOORD8; #endif #if defined(DYNAMICLIGHTMAP_ON) float2 dynamicLightmapUV : TEXCOORD9; #endif #if !defined(LIGHTMAP_ON) float3 sh : TEXCOORD10; #endif float4 fogFactorAndVertexLight : TEXCOORD11; float4 shadowCoord : TEXCOORD12; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : CUSTOM_INSTANCE_ID; #endif #if (defined(UNITY_STEREO_MULTIVIEW_ENABLED)) || (defined(UNITY_STEREO_INSTANCING_ENABLED) && (defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE))) uint stereoTargetEyeIndexAsBlendIdx0 : BLENDINDICES0; #endif #if (defined(UNITY_STEREO_INSTANCING_ENABLED)) uint stereoTargetEyeIndexAsRTArrayIdx : SV_RenderTargetArrayIndex; #endif #if defined(SHADER_STAGE_FRAGMENT) && defined(VARYINGS_NEED_CULLFACE) FRONT_FACE_TYPE cullFace : FRONT_FACE_SEMANTIC; #endif }; // TEMPLATE_SHARED // data describing the user output of a pixel struct Surface { half3 Albedo; half Height; half3 Normal; half Smoothness; half3 Emission; half Metallic; half3 Specular; half Occlusion; half Alpha; // HDRP Only half SpecularOcclusion; half SubsurfaceMask; half Thickness; half CoatMask; half Anisotropy; half IridescenceMask; half IridescenceThickness; }; // data the user might need, this will grow to be big. But easy to strip struct ShaderData { float3 localSpacePosition; float3 localSpaceNormal; float3 localSpaceTangent; float3 worldSpacePosition; float3 worldSpaceNormal; float3 worldSpaceTangent; float3 worldSpaceViewDir; float3 tangentSpaceViewDir; float4 texcoord0; float4 texcoord1; float4 texcoord2; float4 texcoord3; float2 screenUV; float4 screenPos; float4 vertexColor; float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; float3x3 TBNMatrix; }; struct VertexData { #if SHADER_TARGET > 30 && _PLANETCOMPUTE // // uint vertexID : SV_VertexID; #endif float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD4; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD5; // Add Precomputed Velocity (Alembic computes velocities on runtime side). #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID }; struct TessVertex { float4 vertex : INTERNALTESSPOS; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD4; // float4 extraV2F1 : TEXCOORD5; // float4 extraV2F2 : TEXCOORD6; // float4 extraV2F3 : TEXCOORD7; // float4 extraV2F4 : TEXCOORD8; // float4 extraV2F5 : TEXCOORD9; // float4 extraV2F6 : TEXCOORD10; // float4 extraV2F7 : TEXCOORD11; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD12; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD13; #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; struct ExtraV2F { float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; }; float3 WorldToTangentSpace(ShaderData d, float3 normal) { return mul(d.TBNMatrix, normal); } float3 TangentToWorldSpace(ShaderData d, float3 normal) { return mul(normal, d.TBNMatrix); } // in this case, make standard more like SRPs, because we can't fix // GetWorldToObjectMatrix() in HDRP, since it already does macro-fu there #if _STANDARD float3 TransformWorldToObject(float3 p) { return mul(GetWorldToObjectMatrix(), float4(p, 1)); }; float3 TransformObjectToWorld(float3 p) { return mul(GetObjectToWorldMatrix(), float4(p, 1)); }; float4 TransformWorldToObject(float4 p) { return mul(GetWorldToObjectMatrix(), p); }; float4 TransformObjectToWorld(float4 p) { return mul(GetObjectToWorldMatrix(), p); }; float4x4 GetWorldToObjectMatrix() { return GetWorldToObjectMatrix(); } float4x4 GetObjectToWorldMatrix() { return GetObjectToWorldMatrix(); } #endif float3 GetCameraWorldPosition() { #if _HDRP return GetCameraRelativePositionWS(_WorldSpaceCameraPos); #else return _WorldSpaceCameraPos; #endif } #if _HDRP half3 UnpackNormalmapRGorAG(half4 packednormal) { // This do the trick packednormal.x *= packednormal.w; half3 normal; normal.xy = packednormal.xy * 2 - 1; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } half3 UnpackNormal(half4 packednormal) { #if defined(UNITY_NO_DXT5nm) return packednormal.xyz * 2 - 1; #else return UnpackNormalmapRGorAG(packednormal); #endif } #endif #if _HDRP || _URP half3 UnpackScaleNormal(half4 packednormal, half scale) { #ifndef UNITY_NO_DXT5nm // Unpack normal as DXT5nm (1, y, 1, x) or BC5 (x, y, 0, 1) // Note neutral texture like "bump" is (0, 0, 1, 1) to work with both plain RGB normal and DXT5nm/BC5 packednormal.x *= packednormal.w; #endif half3 normal; normal.xy = (packednormal.xy * 2 - 1) * scale; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } #endif void GetSun(out float3 lightDir, out float3 color) { lightDir = float3(0.5, 0.5, 0); color = 1; #if _HDRP if (_DirectionalLightCount > 0) { DirectionalLightData light = _DirectionalLightDatas[0]; lightDir = -light.forward.xyz; color = light.color; } #elif _STANDARD lightDir = normalize(_WorldSpaceLightPos0.xyz); color = _LightColor0.rgb; #elif _URP Light light = GetMainLight(); lightDir = light.direction; color = light.color; #endif } CBUFFER_START(UnityPerMaterial) #if _MESHSUBARRAY half4 _MeshSubArrayIndexes; #endif float4 _Diffuse_TexelSize; float4 _NormalSAO_TexelSize; #if _HYBRIDHEIGHTBLEND float _HybridHeightBlendDistance; #endif #if _PACKINGHQ float4 _SmoothAO_TexelSize; #endif #ifdef _ALPHATEST_ON float4 _TerrainHolesTexture_TexelSize; #endif #if _USESPECULARWORKFLOW float4 _Specular_TexelSize; #endif #if _USEEMISSIVEMETAL float4 _EmissiveMetal_TexelSize; #endif #if _USEEMISSIVEMETAL half _EmissiveMult; #endif #if _AUTONORMAL half _AutoNormalHeightScale; #endif float4 _UVScale; // scale and offset half _Contrast; #if _VSSHADOWMAP float4 gVSSunDirection; #endif #if _FORCELOCALSPACE && _PLANETVECTORS float4x4 _PQSToLocal; #endif #if _ORIGINSHIFT float4x4 _GlobalOriginMTX; #endif float4 _Control0_TexelSize; #if _CUSTOMSPLATTEXTURES float4 _CustomControl0_TexelSize; #endif float4 _PerPixelNormal_TexelSize; #if _CONTROLNOISEUV || _GLOBALNOISEUV float2 _NoiseUVParams; #endif float4 _PerTexProps_TexelSize; #if _SURFACENORMALS float3 surfTangent; float3 surfBitangent; float3 surfNormal; #endif CBUFFER_END #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, data.TBN) // In Unity 2020.3LTS, Unity will spew tons of errors about missing this sampler in // URP, even though it shouldn't be required. TEXTURE2D(_MainTex); // globals, outside of CBuffer, but used by more than one module float3 _gGlitterLightDir; float3 _gGlitterLightWorldPos; half3 _gGlitterLightColor; #if (_MICROTERRAIN || _MICROMESHTERRAIN) 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) float4 _TerrainNormalmapTexture_TexelSize; #endif #if (_MICROTERRAIN || _MICROMESHTERRAIN) TEXTURE2D(_TerrainHeightmapTexture); float4 _TerrainHeightmapTexture_TexelSize; TEXTURE2D(_TerrainNormalmapTexture); #endif UNITY_INSTANCING_BUFFER_START(Terrain) UNITY_DEFINE_INSTANCED_PROP(float4, _TerrainPatchInstanceData) // float4(xBase, yBase, skipScale, ~) UNITY_INSTANCING_BUFFER_END(Terrain) // dynamic branching helpers, for regular and aggressive branching // debug mode shows how many samples using branching will save us. // // These macros are always used instead of the UNITY_BRANCH macro // to maintain debug displays and allow branching to be disabled // on as granular level as we want. #if _BRANCHSAMPLES #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; if (w > 0) #else #define MSBRANCH(w) UNITY_BRANCH if (w > 0) #endif #else #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; #else #define MSBRANCH(w) #endif #endif #if _BRANCHSAMPLESAGR #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER ||_DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; if (w > 0.001) #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; if (w > 0.001) #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; if (w > 0.001) #else #define MSBRANCHTRIPLANAR(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHCLUSTER(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHOTHER(w) UNITY_BRANCH if (w > 0.001) #endif #else #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER || _DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; #else #define MSBRANCHTRIPLANAR(w) #define MSBRANCHCLUSTER(w) #define MSBRANCHOTHER(w) #endif #endif #if _DEBUG_SAMPLECOUNT int _sampleCount; #define COUNTSAMPLE { _sampleCount++; } #else #define COUNTSAMPLE #endif #if _DEBUG_PROCLAYERS int _procLayerCount; #define COUNTPROCLAYER { _procLayerCount++; } #else #define COUNTPROCLAYER #endif #if _DEBUG_USE_TOPOLOGY TEXTURE2D(_DebugWorldPos); TEXTURE2D(_DebugWorldNormal); #endif // splat UNITY_DECLARE_TEX2DARRAY(_Diffuse); UNITY_DECLARE_TEX2DARRAY(_NormalSAO); #if _CONTROLNOISEUV || _GLOBALNOISEUV TEXTURE2D(_NoiseUV); #endif #if _PACKINGHQ UNITY_DECLARE_TEX2DARRAY(_SmoothAO); #endif #if _USESPECULARWORKFLOW UNITY_DECLARE_TEX2DARRAY(_Specular); #endif #if _USEEMISSIVEMETAL UNITY_DECLARE_TEX2DARRAY(_EmissiveMetal); #endif TEXTURE2D(_PerPixelNormal); SamplerState shared_linear_clamp_sampler; SamplerState shared_point_clamp_sampler; TEXTURE2D(_Control0); #if _CUSTOMSPLATTEXTURES TEXTURE2D(_CustomControl0); #if !_MAX4TEXTURES TEXTURE2D(_CustomControl1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_CustomControl2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_CustomControl3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl6); #endif #if _MAX32TEXTURES TEXTURE2D(_CustomControl7); #endif #else #if !_MAX4TEXTURES TEXTURE2D(_Control1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_Control2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_Control3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control6); #endif #if _MAX32TEXTURES TEXTURE2D(_Control7); #endif #endif TEXTURE2D_FLOAT(_PerTexProps); struct DecalLayer { float3 uv; float2 dx; float2 dy; int decalIndex; bool dynamic; }; struct DecalOutput { DecalLayer l0; DecalLayer l1; DecalLayer l2; DecalLayer l3; half4 Weights; half4 Indexes; half4 fxLevels; }; struct TriGradMipFormat { float4 d0; float4 d1; float4 d2; }; float InverseLerp(float x, float y, float v) { return (v-x)/max(y-x, 0.001); } float2 InverseLerp(float2 x, float2 y, float2 v) { return (v-x)/max(y-x, float2(0.001, 0.001)); } float3 InverseLerp(float3 x, float3 y, float3 v) { return (v-x)/max(y-x, float3(0.001, 0.001, 0.001)); } float4 InverseLerp(float4 x, float4 y, float4 v) { return (v-x)/max(y-x, float4(0.001, 0.001, 0.001, 0.001)); } // 2019.3 holes #ifdef _ALPHATEST_ON TEXTURE2D(_TerrainHolesTexture); void ClipHoles(float2 uv) { float hole = SAMPLE_TEXTURE2D(_TerrainHolesTexture, shared_linear_clamp_sampler, uv).r; COUNTSAMPLE clip(hole < 0.5f ? -1 : 1); } #endif #if _TRIPLANAR #if _USEGRADMIP #define MIPFORMAT TriGradMipFormat #define INITMIPFORMAT (TriGradMipFormat)0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float3 #define INITMIPFORMAT 0; #define MIPFROMATRAW float3 #endif #else #if _USEGRADMIP #define MIPFORMAT float4 #define INITMIPFORMAT 0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float #define INITMIPFORMAT 0; #define MIPFROMATRAW float #endif #endif float2 TotalOne(float2 v) { return v * (1.0 / max(v.x + v.y, 0.001)); } float3 TotalOne(float3 v) { return v * (1.0 / max(v.x + v.y + v.z, 0.001)); } float4 TotalOne(float4 v) { return v * (1.0 / max(v.x + v.y + v.z + v.w, 0.001)); } float2 RotateUV(float2 uv, float amt) { uv -=0.5; float s = sin ( amt); float c = cos ( amt ); float2x2 mtx = float2x2( c, -s, s, c); mtx *= 0.5; mtx += 0.5; mtx = mtx * 2-1; uv = mul ( uv, mtx ); uv += 0.5; return uv; } float4 DecodeToFloat4(float v) { uint vi = (uint)(v * (256.0f * 256.0f * 256.0f * 256.0f)); int ex = (int)(vi / (256 * 256 * 256) % 256); int ey = (int)((vi / (256 * 256)) % 256); int ez = (int)((vi / (256)) % 256); int ew = (int)(vi % 256); float4 e = float4(ex / 255.0, ey / 255.0, ez / 255.0, ew / 255.0); return e; } struct Input { ShaderData shaderData; float2 uv_Control0; float2 uv2_Diffuse; float worldHeight; float3 worldUpVector; float3 viewDir; float3 worldPos; float3 worldNormal; float4 color; float3x3 TBN; // vertex/digger workflow data half4 w0; half4 w1; half4 w2; half4 w3; half4 w4; half4 w5; half4 w6; // megasplat data half4 layer0; half4 layer1; half3 baryWeights; half4 scatter0; half4 scatter1; // wetness, puddles, streams, lava from vertex or megasplat half4 fx; // snow min, snow max half4 fx2; }; struct TriplanarConfig { float3x3 uv0; float3x3 uv1; float3x3 uv2; float3x3 uv3; half3 pN; half3 pN0; half3 pN1; half3 pN2; half3 pN3; half3 axisSign; Input IN; }; struct Config { float2 uv; float3 uv0; float3 uv1; float3 uv2; float3 uv3; half4 cluster0; half4 cluster1; half4 cluster2; half4 cluster3; }; struct MicroSplatLayer { half3 Albedo; half3 Normal; half Smoothness; half Occlusion; half Metallic; half Height; half3 Emission; #if _USESPECULARWORKFLOW half3 Specular; #endif half Alpha; }; // raw, unblended samples from arrays struct RawSamples { half4 albedo0; half4 albedo1; half4 albedo2; half4 albedo3; #if _SURFACENORMALS half3 surf0; half3 surf1; half3 surf2; half3 surf3; #endif half4 normSAO0; half4 normSAO1; half4 normSAO2; half4 normSAO3; #if _USEEMISSIVEMETAL || _GLOBALEMIS || _GLOBALSMOOTHAOMETAL || _PERTEXSSS || _PERTEXRIMLIGHT half4 emisMetal0; half4 emisMetal1; half4 emisMetal2; half4 emisMetal3; #endif #if _USESPECULARWORKFLOW half3 specular0; half3 specular1; half3 specular2; half3 specular3; #endif }; void InitRawSamples(inout RawSamples s) { s.normSAO0 = half4(0,0,0,1); s.normSAO1 = half4(0,0,0,1); s.normSAO2 = half4(0,0,0,1); s.normSAO3 = half4(0,0,0,1); #if _SURFACENORMALS s.surf0 = half3(0,0,1); s.surf1 = half3(0,0,1); s.surf2 = half3(0,0,1); s.surf3 = half3(0,0,1); #endif } float3 GetGlobalLightDir(Input i) { float3 lightDir = float3(1,0,0); #if _HDRP || PASS_DEFERRED lightDir = normalize(_gGlitterLightDir.xyz); #elif _URP lightDir = GetMainLight().direction; #else #ifndef USING_DIRECTIONAL_LIGHT lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); #else lightDir = normalize(_WorldSpaceLightPos0.xyz); #endif #endif return lightDir; } float3x3 GetTBN(Input i) { return i.TBN; } float3 GetGlobalLightDirTS(Input i) { float3 lightDirWS = GetGlobalLightDir(i); return mul(GetTBN(i), lightDirWS); } half3 GetGlobalLightColor() { #if _HDRP || PASS_DEFERRED return _gGlitterLightColor; #elif _URP return (GetMainLight().color); #else return _LightColor0.rgb; #endif } half3 FuzzyShade(half3 color, half3 normal, half coreMult, half edgeMult, half power, float3 viewDir) { half dt = saturate(dot(viewDir, normal)); half dark = 1.0 - (coreMult * dt); half edge = pow(1-dt, power) * edgeMult; return color * (dark + edge); } half3 ComputeSSS(Input i, float3 V, float3 N, half3 tint, half thickness, half distortion, half scale, half power) { float3 L = GetGlobalLightDir(i); half3 lightColor = GetGlobalLightColor(); float3 H = normalize(L + N * distortion); float VdotH = pow(saturate(dot(V, -H)), power) * scale; float3 I = (VdotH) * thickness; return lightColor * I * tint; } #if _MAX2LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y; } #elif _MAX3LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } #else inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } #endif #if _MAX3LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #elif _MAX2LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #else #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##3 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv3.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #endif half2 BlendNormal2(half2 base, half2 blend) { return normalize(half3(base.xy + blend.xy, 1)).xy; } half3 BlendOverlay(half3 base, half3 blend) { return (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))); } half3 BlendMult2X(half3 base, half3 blend) { return (base * (blend * 2)); } half3 BlendLighterColor(half3 s, half3 d) { return (s.x + s.y + s.z > d.x + d.y + d.z) ? s : d; } #if _SURFACENORMALS #define HALF_EPS 4.8828125e-4 // 2^-11, machine epsilon: 1 + EPS = 1 (half of the ULP for 1.0f) void ConstructSurfaceGradientTBN(Input i) { float3x3 tbn = GetTBN(i); float3 t = tbn[0]; float3 b = tbn[1]; float3 n = tbn[2]; surfNormal = n;//mul(GetWorldToObjectMatrix(), float4(n, 1)).xyz; surfTangent = t;//mul(GetWorldToObjectMatrix(), float4(t, 1)).xyz; surfBitangent = b;//cross(surfNormal, surfTangent); float renormFactor = 1.0 / length(surfNormal); surfNormal *= renormFactor; surfTangent *= renormFactor; surfBitangent *= renormFactor; } half3 SurfaceGradientFromTBN(half2 deriv) { return deriv.x * surfTangent + deriv.y * surfBitangent; } // Input: vM is tangent space normal in [-1;1]. // Output: convert vM to a derivative. half2 TspaceNormalToDerivative(half3 vM) { const half scale = 1.0/128.0; // Ensure vM delivers a positive third component using abs() and // constrain vM.z so the range of the derivative is [-128; 128]. const half3 vMa = abs(vM); const half z_ma = max(vMa.z, scale*max(vMa.x, vMa.y)); return -half2(vM.x, vM.y)/z_ma; } // Used to produce a surface gradient from the gradient of a volume // bump function such as 3D Perlin noise. Equation 2 in [Mik10]. half3 SurfgradFromVolumeGradient(half3 grad) { return grad - dot(surfNormal, grad) * surfNormal; } half3 SurfgradFromTriplanarProjection(half3 pN, half2 xPlaneTN, half2 yPlaneTN, half2 zPlaneTN) { const half w0 = pN.x; const half w1 = pN.y; const half w2 = pN.z; // X-plane tangent normal to gradient derivative xPlaneTN = xPlaneTN * 2.0 - 1.0; half xPlaneRcpZ = rsqrt(max(1 - dot(xPlaneTN.x, xPlaneTN.x) - dot(xPlaneTN.y, xPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_xplane = xPlaneTN * -xPlaneRcpZ; // Y-plane tangent normal to gradient derivative yPlaneTN = yPlaneTN * 2.0 - 1.0; half yPlaneRcpZ = rsqrt(max(1 - dot(yPlaneTN.x, yPlaneTN.x) - dot(yPlaneTN.y, yPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_yplane = yPlaneTN * -yPlaneRcpZ; // Z-plane tangent normal to gradient derivative zPlaneTN = zPlaneTN * 2.0 - 1.0; half zPlaneRcpZ = rsqrt(max(1 - dot(zPlaneTN.x, zPlaneTN.x) - dot(zPlaneTN.y, zPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_zplane = zPlaneTN * -zPlaneRcpZ; // Assume deriv xplane, deriv yplane, and deriv zplane are // sampled using (z,y), (x,z), and (x,y), respectively. // Positive scales of the lookup coordinate will work // as well, but for negative scales the derivative components // will need to be negated accordingly. float3 grad = float3(w2*d_zplane.x + w1*d_yplane.x, w2*d_zplane.y + w0*d_xplane.y, w0*d_xplane.x + w1*d_yplane.y); return SurfgradFromVolumeGradient(grad); } half3 ConvertNormalToGradient(half3 normal) { half2 deriv = TspaceNormalToDerivative(normal); return SurfaceGradientFromTBN(deriv); } half3 ConvertNormal2ToGradient(half2 packedNormal) { half2 tNormal = packedNormal; half rcpZ = rsqrt(max(1 - dot(tNormal.x, tNormal.x) - dot(tNormal.y, tNormal.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 deriv = tNormal * -rcpZ; return SurfaceGradientFromTBN(deriv); } half3 ResolveNormalFromSurfaceGradient(half3 gradient) { return normalize(surfNormal - gradient); } #endif // _SURFACENORMALS void BlendNormalPerTex(inout RawSamples o, half2 noise, float4 fades) { #if _SURFACENORMALS float3 grad = ConvertNormal2ToGradient(noise.xy); o.surf0 += grad * fades.x; o.surf1 += grad * fades.y; #if !_MAX2LAYER o.surf2 += grad * fades.z; #endif #if !_MAX2LAYER && !_MAX3LAYER o.surf3 += grad * fades.w; #endif #else o.normSAO0.xy = lerp(o.normSAO0.xy, BlendNormal2(o.normSAO0.xy, noise.xy), fades.x); o.normSAO1.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #if !_MAX2LAYER o.normSAO2.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO2.xy, noise.xy), fades.y); #endif #if !_MAX2LAYER && !_MAX3LAYER o.normSAO3.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #endif #endif } half3 BlendNormal3(half3 n1, half3 n2) { n1 += float3( 0, 0, 1); n2 *= float3(-1, -1, 1); return n1*dot(n1, n2) / n1.z - n2; } half2 TransformTriplanarNormal(Input IN, float3x3 t2w, half3 axisSign, half3 absVertNormal, half3 pN, half2 a0, half2 a1, half2 a2) { a0 = a0 * 2 - 1; a1 = a1 * 2 - 1; a2 = a2 * 2 - 1; a0.x *= axisSign.x; a1.x *= axisSign.y; a2.x *= axisSign.z; half3 n0 = half3(a0.xy, 1); half3 n1 = half3(a1.xy, 1); half3 n2 = half3(a2.xy, 1); float3 wn = IN.worldNormal; n0 = BlendNormal3(half3(wn.zy, absVertNormal.x), n0); n1 = BlendNormal3(half3(wn.xz, absVertNormal.y), n1 * float3(-1, 1, 1)); n2 = BlendNormal3(half3(wn.xy, absVertNormal.z), n2); n0.z *= axisSign.x; n1.z *= axisSign.y; n2.z *= -axisSign.z; half3 worldNormal = (n0.zyx * pN.x + n1.xzy * pN.y + n2.xyz * pN.z); return mul(t2w, worldNormal).xy; } // funcs inline half MSLuminance(half3 rgb) { #ifdef UNITY_COLORSPACE_GAMMA return dot(rgb, half3(0.22, 0.707, 0.071)); #else return dot(rgb, half3(0.0396819152, 0.458021790, 0.00609653955)); #endif } float2 Hash2D( float2 x ) { float2 k = float2( 0.3183099, 0.3678794 ); x = x*k + k.yx; return -1.0 + 2.0*frac( 16.0 * k*frac( x.x*x.y*(x.x+x.y)) ); } float Noise2D(float2 p ) { float2 i = floor( p ); float2 f = frac( p ); float2 u = f*f*(3.0-2.0*f); return lerp( lerp( dot( Hash2D( i + float2(0.0,0.0) ), f - float2(0.0,0.0) ), dot( Hash2D( i + float2(1.0,0.0) ), f - float2(1.0,0.0) ), u.x), lerp( dot( Hash2D( i + float2(0.0,1.0) ), f - float2(0.0,1.0) ), dot( Hash2D( i + float2(1.0,1.0) ), f - float2(1.0,1.0) ), u.x), u.y); } float FBM2D(float2 uv) { float f = 0.5000*Noise2D( uv ); uv *= 2.01; f += 0.2500*Noise2D( uv ); uv *= 1.96; f += 0.1250*Noise2D( uv ); return f; } float3 Hash3D( float3 p ) { p = float3( dot(p,float3(127.1,311.7, 74.7)), dot(p,float3(269.5,183.3,246.1)), dot(p,float3(113.5,271.9,124.6))); return -1.0 + 2.0*frac(sin(p)*437.5453123); } float Noise3D( float3 p ) { float3 i = floor( p ); float3 f = frac( p ); float3 u = f*f*(3.0-2.0*f); return lerp( lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,0.0) ), f - float3(0.0,0.0,0.0) ), dot( Hash3D( i + float3(1.0,0.0,0.0) ), f - float3(1.0,0.0,0.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,0.0) ), f - float3(0.0,1.0,0.0) ), dot( Hash3D( i + float3(1.0,1.0,0.0) ), f - float3(1.0,1.0,0.0) ), u.x), u.y), lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,1.0) ), f - float3(0.0,0.0,1.0) ), dot( Hash3D( i + float3(1.0,0.0,1.0) ), f - float3(1.0,0.0,1.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,1.0) ), f - float3(0.0,1.0,1.0) ), dot( Hash3D( i + float3(1.0,1.0,1.0) ), f - float3(1.0,1.0,1.0) ), u.x), u.y), u.z ); } float FBM3D(float3 uv) { float f = 0.5000*Noise3D( uv ); uv *= 2.01; f += 0.2500*Noise3D( uv ); uv *= 1.96; f += 0.1250*Noise3D( uv ); return f; } float GetSaturation(float3 c) { float mi = min(min(c.x, c.y), c.z); float ma = max(max(c.x, c.y), c.z); return (ma - mi)/(ma + 1e-7); } // Better Color Lerp, does not have darkening issue float3 BetterColorLerp(float3 a, float3 b, float x) { float3 ic = lerp(a, b, x) + float3(1e-6,0.0,0.0); float sd = abs(GetSaturation(ic) - lerp(GetSaturation(a), GetSaturation(b), x)); float3 dir = normalize(float3(2.0 * ic.x - ic.y - ic.z, 2.0 * ic.y - ic.x - ic.z, 2.0 * ic.z - ic.y - ic.x)); float lgt = dot(float3(1.0, 1.0, 1.0), ic); float ff = dot(dir, normalize(ic)); const float dsp_str = 1.5; ic += dsp_str * dir * sd * ff * lgt; return saturate(ic); } half4 ComputeWeights(half4 iWeights, half h0, half h1, half h2, half h3, half contrast) { #if _DISABLEHEIGHTBLENDING return iWeights; #else // compute weight with height map //half4 weights = half4(iWeights.x * h0, iWeights.y * h1, iWeights.z * h2, iWeights.w * h3); half4 weights = half4(iWeights.x * max(h0,0.001), iWeights.y * max(h1,0.001), iWeights.z * max(h2,0.001), iWeights.w * max(h3,0.001)); // Contrast weights half maxWeight = max(max(weights.x, max(weights.y, weights.z)), weights.w); half transition = max(contrast * maxWeight, 0.0001); half threshold = maxWeight - transition; half scale = 1.0 / transition; weights = saturate((weights - threshold) * scale); weights = TotalOne(weights); return weights; #endif } half HeightBlend(half h1, half h2, half slope, half contrast) { #if _DISABLEHEIGHTBLENDING return slope; #else h2 = 1 - h2; half tween = saturate((slope - min(h1, h2)) / max(abs(h1 - h2), 0.001)); half blend = saturate( ( tween - (1-contrast) ) / max(contrast, 0.001)); return blend; #endif } #if _MAX4TEXTURES #define TEXCOUNT 4 #elif _MAX8TEXTURES #define TEXCOUNT 8 #elif _MAX12TEXTURES #define TEXCOUNT 12 #elif _MAX20TEXTURES #define TEXCOUNT 20 #elif _MAX24TEXTURES #define TEXCOUNT 24 #elif _MAX28TEXTURES #define TEXCOUNT 28 #elif _MAX32TEXTURES #define TEXCOUNT 32 #else #define TEXCOUNT 16 #endif #if _DECAL_SPLAT void DoMergeDecalSplats(half4 iWeights, half4 iIndexes, inout half4 indexes, inout half4 weights) { for (int i = 0; i < 4; ++i) { half w = iWeights[i]; half index = iIndexes[i]; if (w > weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = index; } else if (w > weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = index; } else if (w > weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = index; } else if (w > weights[3]) { weights[3] = w; indexes[3] = index; } } } #endif void Setup(out half4 weights, float2 uv, out Config config, half4 w0, half4 w1, half4 w2, half4 w3, half4 w4, half4 w5, half4 w6, half4 w7, float3 worldPos, DecalOutput decalOutput) { config = (Config)0; half4 indexes = 0; config.uv = uv; #if _WORLDUV uv = worldPos.xz; #endif #if _DISABLESPLATMAPS float2 scaledUV = uv; #else float2 scaledUV = uv * _UVScale.xy + _UVScale.zw; #endif // if only 4 textures, and blending 4 textures, skip this whole thing.. // this saves about 25% of the ALU of the base shader on low end. However if // we rely on sorted texture weights (distance resampling) we have to sort.. float4 defaultIndexes = float4(0,1,2,3); #if _MESHSUBARRAY defaultIndexes = _MeshSubArrayIndexes; #endif #if _MESHSUBARRAY && !_DECAL_SPLAT || (_MAX4TEXTURES && !_MAX3LAYER && !_MAX2LAYER && !_DISTANCERESAMPLE && !_POM && !_DECAL_SPLAT) weights = w0; config.uv0 = float3(scaledUV, defaultIndexes.x); config.uv1 = float3(scaledUV, defaultIndexes.y); config.uv2 = float3(scaledUV, defaultIndexes.z); config.uv3 = float3(scaledUV, defaultIndexes.w); return; #endif #if _DISABLESPLATMAPS weights = float4(1,0,0,0); return; #else half splats[TEXCOUNT]; splats[0] = w0.x; splats[1] = w0.y; splats[2] = w0.z; splats[3] = w0.w; #if !_MAX4TEXTURES splats[4] = w1.x; splats[5] = w1.y; splats[6] = w1.z; splats[7] = w1.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES splats[8] = w2.x; splats[9] = w2.y; splats[10] = w2.z; splats[11] = w2.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES splats[12] = w3.x; splats[13] = w3.y; splats[14] = w3.z; splats[15] = w3.w; #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[16] = w4.x; splats[17] = w4.y; splats[18] = w4.z; splats[19] = w4.w; #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[20] = w5.x; splats[21] = w5.y; splats[22] = w5.z; splats[23] = w5.w; #endif #if _MAX28TEXTURES || _MAX32TEXTURES splats[24] = w6.x; splats[25] = w6.y; splats[26] = w6.z; splats[27] = w6.w; #endif #if _MAX32TEXTURES splats[28] = w7.x; splats[29] = w7.y; splats[30] = w7.z; splats[31] = w7.w; #endif weights[0] = 0; weights[1] = 0; weights[2] = 0; weights[3] = 0; indexes[0] = 0; indexes[1] = 0; indexes[2] = 0; indexes[3] = 0; int i = 0; for (i = 0; i < TEXCOUNT; ++i) { half w = splats[i]; if (w >= weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = i; } else if (w >= weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = i; } else if (w >= weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = i; } else if (w >= weights[3]) { weights[3] = w; indexes[3] = i; } } // NaN Prevention if (weights.x <= 0) weights = float4(1, 0, 0, 0); #if _DECAL_SPLAT DoMergeDecalSplats(decalOutput.Weights, decalOutput.Indexes, weights, indexes); #endif // clamp and renormalize #if _MAX2LAYER weights.zw = 0; weights.xy = TotalOne(weights.xy); #elif _MAX3LAYER weights.w = 0; weights.xyz = TotalOne(weights.xyz); #elif !_DISABLEHEIGHTBLENDING || _NORMALIZEWEIGHTS // prevents black when painting, which the unity shader does not prevent. weights = normalize(weights); #endif config.uv0 = float3(scaledUV, indexes.x); config.uv1 = float3(scaledUV, indexes.y); config.uv2 = float3(scaledUV, indexes.z); config.uv3 = float3(scaledUV, indexes.w); #endif //_DISABLESPLATMAPS } float3 HeightToNormal(float height, float3 worldPos) { float3 dx = ddx(worldPos); float3 dy = ddy(worldPos); float3 crossX = cross(float3(0,1,0), dx); float3 crossY = cross(float3(0,1,0), dy); float3 d = abs(dot(crossY, dx)); float3 n = ((((height + ddx(height)) - height) * crossY) + (((height + ddy(height)) - height) * crossX)) * sign(d); n.z *= -1; return normalize((d * float3(0,1,0)) - n).xzy; } float ComputeMipLevel(float2 uv, float2 textureSize) { uv *= textureSize; float2 dx_vtc = ddx(uv); float2 dy_vtc = ddy(uv); float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); return 0.5 * log2(delta_max_sqr); } inline half2 UnpackNormal2(half4 packednormal) { return packednormal.wy * 2 - 1; } half3 TriplanarHBlend(half h0, half h1, half h2, half3 pN, half contrast) { half3 blend = pN / dot(pN, half3(1,1,1)); float3 heights = float3(h0, h1, h2) + (blend * 3.0); half height_start = max(max(heights.x, heights.y), heights.z) - contrast; half3 h = max(heights - height_start.xxx, half3(0,0,0)); blend = h / dot(h, half3(1,1,1)); return blend; } void ClearAllButAlbedo(inout MicroSplatLayer o, half3 display) { o.Albedo = display.rgb; o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } void ClearAllButAlbedo(inout MicroSplatLayer o, half display) { o.Albedo = half3(display, display, display); o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } half MicroShadow(float3 lightDir, half3 normal, half ao, half strength) { half shadow = saturate(abs(dot(normal, lightDir)) + (ao * ao * 2.0) - 1.0); return 1 - ((1-shadow) * strength); } void DoDebugOutput(inout MicroSplatLayer l) { #if _DEBUG_OUTPUT_ALBEDO ClearAllButAlbedo(l, l.Albedo); #elif _DEBUG_OUTPUT_NORMAL // oh unit shader compiler normal stripping, how I hate you so.. // must multiply by albedo to stop the normal from being white. Why, fuck knows? ClearAllButAlbedo(l, float3(l.Normal.xy * 0.5 + 0.5, l.Normal.z * saturate(l.Albedo.z+1))); #elif _DEBUG_OUTPUT_SMOOTHNESS ClearAllButAlbedo(l, l.Smoothness.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_METAL ClearAllButAlbedo(l, l.Metallic.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_AO ClearAllButAlbedo(l, l.Occlusion.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_EMISSION ClearAllButAlbedo(l, l.Emission * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_HEIGHT ClearAllButAlbedo(l, l.Height.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_SPECULAR && _USESPECULARWORKFLOW ClearAllButAlbedo(l, l.Specular * saturate(l.Albedo.z+1)); #elif _DEBUG_BRANCHCOUNT_WEIGHT ClearAllButAlbedo(l, _branchWeightCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TRIPLANAR ClearAllButAlbedo(l, _branchTriplanarCount / 24 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_CLUSTER ClearAllButAlbedo(l, _branchClusterCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_OTHER ClearAllButAlbedo(l, _branchOtherCount / 8 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TOTAL l.Albedo.r = _branchWeightCount / 12; l.Albedo.g = _branchTriplanarCount / 24; l.Albedo.b = _branchClusterCount / 12; ClearAllButAlbedo(l, (l.Albedo.r + l.Albedo.g + l.Albedo.b + (_branchOtherCount / 8)) / 4); #elif _DEBUG_OUTPUT_MICROSHADOWS ClearAllButAlbedo(l,l.Albedo); #elif _DEBUG_SAMPLECOUNT float sdisp = (float)_sampleCount / max(_SampleCountDiv, 1); half3 sdcolor = float3(sdisp, sdisp > 1 ? 1 : 0, 0); ClearAllButAlbedo(l, sdcolor * saturate(l.Albedo.z + 1)); #elif _DEBUG_PROCLAYERS ClearAllButAlbedo(l, (float)_procLayerCount / (float)_PCLayerCount * saturate(l.Albedo.z + 1)); #endif } // abstraction around sampler mode #if _USELODMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_LOD(tex, sampler##tex, u, l.x) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u, l.x) #elif _USEGRADMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_GRAD(tex, sampler##tex, u, l.xy, l.zw) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY_GRAD(tex, ss, u.xy, u.z, l.xy, l.zw) #else #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, u.xy, u.z) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u.xy, y.z) #endif #define MICROSPLAT_SAMPLE_DIFFUSE(u, cl, l) MICROSPLAT_SAMPLE(_Diffuse, u, l) #define MICROSPLAT_SAMPLE_EMIS(u, cl, l) MICROSPLAT_SAMPLE(_EmissiveMetal, u, l) #define MICROSPLAT_SAMPLE_DIFFUSE_LOD(u, cl, l) UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, u, l) #if _PACKINGHQ #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) half4(MICROSPLAT_SAMPLE(_NormalSAO, u, l).ga, MICROSPLAT_SAMPLE(_SmoothAO, u, l).ga).brag #else #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) MICROSPLAT_SAMPLE(_NormalSAO, u, l) #endif #if _USESPECULARWORKFLOW #define MICROSPLAT_SAMPLE_SPECULAR(u, cl, l) MICROSPLAT_SAMPLE(_Specular, u, l) #endif struct SimpleTriplanarConfig { float3 pn; float2 uv0; float2 uv1; float2 uv2; }; void PrepSimpleTriplanarConfig(inout SimpleTriplanarConfig tc, float3 worldPos, float3 normal, float contrast) { tc.pn = pow(abs(normal), contrast); tc.pn = tc.pn / (tc.pn.x + tc.pn.y + tc.pn.z); half3 axisSign = sign(normal); tc.uv0 = worldPos.zy * axisSign.x; tc.uv1 = worldPos.xz * axisSign.y; tc.uv2 = worldPos.xy * axisSign.z; } #define SimpleTriplanarSample(tex, tc, scale) (SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv0 * scale) * tc.pn.x + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv1 * scale) * tc.pn.y + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv2 * scale) * tc.pn.z) #define SimpleTriplanarSampleLOD(tex, tc, scale, lod) (SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv0 * scale, lod) * tc.pn.x + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv1 * scale, lod) * tc.pn.y + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv2 * scale, lod) * tc.pn.z) #define SimpleTriplanarSampleGrad(tex, tc, scale) (SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv0 * scale, ddx(tc.uv0) * scale, ddy(tc.uv0) * scale) * tc.pn.x + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv1 * scale, ddx(tc.uv1) * scale, ddy(tc.uv1) * scale) * tc.pn.y + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv2 * scale, ddx(tc.uv2) * scale, ddy(tc.uv2) * scale) * tc.pn.z) inline half3 MicroSplatDiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity) { specColor = lerp (half3(0,0,0), albedo, metallic); oneMinusReflectivity = (1-metallic); return albedo * oneMinusReflectivity; } Input DescToInput(ShaderData IN) { Input s = (Input)0; s.shaderData = IN; s.TBN = IN.TBNMatrix; s.worldNormal = IN.worldSpaceNormal; s.worldPos = IN.worldSpacePosition; s.viewDir = IN.tangentSpaceViewDir; s.uv_Control0 = IN.texcoord0.xy; s.worldUpVector = float3(0,1,0); s.worldHeight = IN.worldSpacePosition.y; #if _PLANETVECTORS float3 rwp = mul(_PQSToLocal, float4(IN.worldSpacePosition, 1)); s.worldHeight = distance(rwp, float3(0,0,0)); s.worldUpVector = normalize(rwp); #endif #if _MICROMESH && _MESHUV2 s.uv2_Diffuse = IN.texcoord1.xy; #endif #if _MEGASPLAT UnpackMegaSplat(s, IN); #endif #if _MICROVERTEXMESH || _MICRODIGGERMESH UnpackVertexWorkflow(s, IN); #endif #if _PLANETVECTORS DoPlanetDataInputCopy(s, IN); #endif return s; } void SampleAlbedo(inout Config config, inout TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half4 contrasts = _Contrast.xxxx; #if _PERTEXTRIPLANARCONTRAST SAMPLE_PER_TEX(ptc, 9.5, config, half4(1,0.5,0,0)); contrasts = half4(ptc0.y, ptc1.y, ptc2.y, ptc3.y); #endif #if _PERTEXTRIPLANAR SAMPLE_PER_TEX(pttri, 9.5, config, half4(0,0,0,0)); #endif { // For per-texture triplanar, we modify the view based blending factor of the triplanar // such that you get a pure blend of either top down projection, or with the top down projection // removed and renormalized. This causes dynamic flow control optimizations to kick in and avoid // the extra texture samples while keeping the code simple. Yay.. // We also only have to do this in the Albedo, because the pN values will be adjusted after the // albedo is sampled, causing future samples to use this data. #if _PERTEXTRIPLANAR if (pttri0.x > 0.66) { tc.pN0 = half3(0,1,0); } else if (pttri0.x > 0.33) { tc.pN0.y = 0; tc.pN0.xz = TotalOne(tc.pN0.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } half3 bf = tc.pN0; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN0, contrasts.x); tc.pN0 = bf; #endif s.albedo0 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } MSBRANCH(weights.y) { #if _PERTEXTRIPLANAR if (pttri1.x > 0.66) { tc.pN1 = half3(0,1,0); } else if (pttri1.x > 0.33) { tc.pN1.y = 0; tc.pN1.xz = TotalOne(tc.pN1.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { COUNTSAMPLE a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[2], config.cluster1, d2); } half3 bf = tc.pN1; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN1, contrasts.x); tc.pN1 = bf; #endif s.albedo1 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { #if _PERTEXTRIPLANAR if (pttri2.x > 0.66) { tc.pN2 = half3(0,1,0); } else if (pttri2.x > 0.33) { tc.pN2.y = 0; tc.pN2.xz = TotalOne(tc.pN2.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } half3 bf = tc.pN2; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN2, contrasts.x); tc.pN2 = bf; #endif s.albedo2 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { #if _PERTEXTRIPLANAR if (pttri3.x > 0.66) { tc.pN3 = half3(0,1,0); } else if (pttri3.x > 0.33) { tc.pN3.y = 0; tc.pN3.xz = TotalOne(tc.pN3.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } half3 bf = tc.pN3; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN3, contrasts.x); tc.pN3 = bf; #endif s.albedo3 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #else s.albedo0 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.albedo1 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.albedo2 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.albedo3 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #if _PERTEXHEIGHTOFFSET || _PERTEXHEIGHTCONTRAST SAMPLE_PER_TEX(ptHeight, 10.5, config, 1); #if _PERTEXHEIGHTOFFSET s.albedo0.a = saturate(s.albedo0.a + ptHeight0.b - 1); s.albedo1.a = saturate(s.albedo1.a + ptHeight1.b - 1); s.albedo2.a = saturate(s.albedo2.a + ptHeight2.b - 1); s.albedo3.a = saturate(s.albedo3.a + ptHeight3.b - 1); #endif #if _PERTEXHEIGHTCONTRAST s.albedo0.a = saturate(pow(s.albedo0.a + 0.5, abs(ptHeight0.a)) - 0.5); s.albedo1.a = saturate(pow(s.albedo1.a + 0.5, abs(ptHeight1.a)) - 0.5); s.albedo2.a = saturate(pow(s.albedo2.a + 0.5, abs(ptHeight2.a)) - 0.5); s.albedo3.a = saturate(pow(s.albedo3.a + 0.5, abs(ptHeight3.a)) - 0.5); #endif #endif } void SampleNormal(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _NONORMALMAP || _AUTONORMAL s.normSAO0 = half4(0,0, 0, 1); s.normSAO1 = half4(0,0, 0, 1); s.normSAO2 = half4(0,0, 0, 1); s.normSAO3 = half4(0,0, 0, 1); return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half3 absVertNormal = abs(tc.IN.worldNormal); float3x3 t2w = tc.IN.TBN; { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[0], config.cluster0, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[1], config.cluster0, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[2], config.cluster0, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf0 = SurfgradFromTriplanarProjection(tc.pN0, a0.xy, a1.xy, a2.xy); #else s.normSAO0.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN0, a0.xy, a1.xy, a2.xy); #endif s.normSAO0.zw = a0.zw * tc.pN0.x + a1.zw * tc.pN0.y + a2.zw * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[0], config.cluster1, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[1], config.cluster1, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[2], config.cluster1, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf1 = SurfgradFromTriplanarProjection(tc.pN1, a0.xy, a1.xy, a2.xy); #else s.normSAO1.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN1, a0.xy, a1.xy, a2.xy); #endif s.normSAO1.zw = a0.zw * tc.pN1.x + a1.zw * tc.pN1.y + a2.zw * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[0], config.cluster2, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[1], config.cluster2, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[2], config.cluster2, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf2 = SurfgradFromTriplanarProjection(tc.pN2, a0.xy, a1.xy, a2.xy); #else s.normSAO2.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN2, a0.xy, a1.xy, a2.xy); #endif s.normSAO2.zw = a0.zw * tc.pN2.x + a1.zw * tc.pN2.y + a2.zw * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[0], config.cluster3, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[1], config.cluster3, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[2], config.cluster3, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf3 = SurfgradFromTriplanarProjection(tc.pN3, a0.xy, a1.xy, a2.xy); #else s.normSAO3.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN3, a0.xy, a1.xy, a2.xy); #endif s.normSAO3.zw = a0.zw * tc.pN3.x + a1.zw * tc.pN3.y + a2.zw * tc.pN3.z; } #endif #else s.normSAO0 = MICROSPLAT_SAMPLE_NORMAL(config.uv0, config.cluster0, mipLevel).agrb; COUNTSAMPLE s.normSAO0.xy = s.normSAO0.xy * 2 - 1; #if _SURFACENORMALS s.surf0 = ConvertNormal2ToGradient(s.normSAO0.xy); #endif MSBRANCH(weights.y) { s.normSAO1 = MICROSPLAT_SAMPLE_NORMAL(config.uv1, config.cluster1, mipLevel).agrb; COUNTSAMPLE s.normSAO1.xy = s.normSAO1.xy * 2 - 1; #if _SURFACENORMALS s.surf1 = ConvertNormal2ToGradient(s.normSAO1.xy); #endif } #if !_MAX2LAYER MSBRANCH(weights.z) { s.normSAO2 = MICROSPLAT_SAMPLE_NORMAL(config.uv2, config.cluster2, mipLevel).agrb; COUNTSAMPLE s.normSAO2.xy = s.normSAO2.xy * 2 - 1; #if _SURFACENORMALS s.surf2 = ConvertNormal2ToGradient(s.normSAO2.xy); #endif } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.normSAO3 = MICROSPLAT_SAMPLE_NORMAL(config.uv3, config.cluster3, mipLevel).agrb; COUNTSAMPLE s.normSAO3.xy = s.normSAO3.xy * 2 - 1; #if _SURFACENORMALS s.surf3 = ConvertNormal2ToGradient(s.normSAO3.xy); #endif } #endif #endif } void SampleEmis(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USEEMISSIVEMETAL #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.emisMetal0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.emisMetal1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.emisMetal2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.emisMetal3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.emisMetal0 = MICROSPLAT_SAMPLE_EMIS(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.emisMetal1 = MICROSPLAT_SAMPLE_EMIS(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.emisMetal2 = MICROSPLAT_SAMPLE_EMIS(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.emisMetal3 = MICROSPLAT_SAMPLE_EMIS(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } void SampleSpecular(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USESPECULARWORKFLOW #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.specular0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.specular1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.specular2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.specular3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.specular0 = MICROSPLAT_SAMPLE_SPECULAR(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.specular1 = MICROSPLAT_SAMPLE_SPECULAR(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.specular2 = MICROSPLAT_SAMPLE_SPECULAR(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.specular3 = MICROSPLAT_SAMPLE_SPECULAR(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } MicroSplatLayer Sample(Input i, half4 weights, inout Config config, float camDist, float3 worldNormalVertex, DecalOutput decalOutput) { MicroSplatLayer o = (MicroSplatLayer)0; UNITY_INITIALIZE_OUTPUT(MicroSplatLayer,o); RawSamples samples = (RawSamples)0; InitRawSamples(samples); half4 albedo = 0; half4 normSAO = half4(0,0,0,1); half3 surfGrad = half3(0,0,0); half4 emisMetal = 0; half3 specular = 0; float worldHeight = i.worldPos.y; float3 upVector = float3(0,1,0); #if _GLOBALTINT || _GLOBALNORMALS || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _GLOBALSPECULAR float globalSlopeFilter = 1; #if _GLOBALSLOPEFILTER float2 gfilterUV = float2(1 - saturate(dot(worldNormalVertex, upVector) * 0.5 + 0.49), 0.5); globalSlopeFilter = SAMPLE_TEXTURE2D(_GlobalSlopeTex, sampler_Diffuse, gfilterUV).a; #endif #endif // declare outside of branchy areas.. half4 fxLevels = half4(0,0,0,0); half burnLevel = 0; half wetLevel = 0; half3 waterNormalFoam = half3(0, 0, 0); half porosity = 0.4; float streamFoam = 1.0f; half pud = 0; half snowCover = 0; half SSSThickness = 0; half3 SSSTint = half3(1,1,1); float traxBuffer = 0; float3 traxNormal = 0; float2 noiseUV = 0; #if _SPLATFADE MSBRANCHOTHER(1 - saturate(camDist - _SplatFade.y)) { #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE || _SNOWFOOTSTEPS traxBuffer = SampleTraxBuffer(i.worldPos, worldNormalVertex, traxNormal); #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA #if _MICROMESH fxLevels = SampleFXLevels(InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, config.uv), wetLevel, burnLevel, traxBuffer); #elif _MICROVERTEXMESH || _MICRODIGGERMESH || _MEGASPLAT fxLevels = ProcessFXLevels(i.fx, traxBuffer); #else fxLevels = SampleFXLevels(config.uv, wetLevel, burnLevel, traxBuffer); #endif #endif #if _DECAL fxLevels = max(fxLevels, decalOutput.fxLevels); #endif TriplanarConfig tc = (TriplanarConfig)0; UNITY_INITIALIZE_OUTPUT(TriplanarConfig,tc); MIPFORMAT albedoLOD = INITMIPFORMAT MIPFORMAT normalLOD = INITMIPFORMAT MIPFORMAT emisLOD = INITMIPFORMAT MIPFORMAT specLOD = INITMIPFORMAT MIPFORMAT origAlbedoLOD = INITMIPFORMAT; #if _TRIPLANAR && !_DISABLESPLATMAPS PrepTriplanar(i.shaderData.texcoord0, worldNormalVertex, i.worldPos, config, tc, weights, albedoLOD, normalLOD, emisLOD, origAlbedoLOD); tc.IN = i; #endif #if !_TRIPLANAR && !_DISABLESPLATMAPS #if _USELODMIP albedoLOD = ComputeMipLevel(config.uv0.xy, _Diffuse_TexelSize.zw); normalLOD = ComputeMipLevel(config.uv0.xy, _NormalSAO_TexelSize.zw); #if _USEEMISSIVEMETAL emisLOD = ComputeMipLevel(config.uv0.xy, _EmissiveMetal_TexelSize.zw); #endif #if _USESPECULARWORKFLOW specLOD = ComputeMipLevel(config.uv0.xy, _Specular_TexelSize.zw);; #endif #elif _USEGRADMIP albedoLOD = float4(ddx(config.uv0.xy), ddy(config.uv0.xy)); normalLOD = albedoLOD; #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #endif origAlbedoLOD = albedoLOD; #endif #if _PERTEXCURVEWEIGHT SAMPLE_PER_TEX(ptCurveWeight, 19.5, config, half4(0.5,1,1,1)); weights.x = lerp(smoothstep(0.5 - ptCurveWeight0.r, 0.5 + ptCurveWeight0.r, weights.x), weights.x, ptCurveWeight0.r*2); weights.y = lerp(smoothstep(0.5 - ptCurveWeight1.r, 0.5 + ptCurveWeight1.r, weights.y), weights.y, ptCurveWeight1.r*2); weights.z = lerp(smoothstep(0.5 - ptCurveWeight2.r, 0.5 + ptCurveWeight2.r, weights.z), weights.z, ptCurveWeight2.r*2); weights.w = lerp(smoothstep(0.5 - ptCurveWeight3.r, 0.5 + ptCurveWeight3.r, weights.w), weights.w, ptCurveWeight3.r*2); weights = TotalOne(weights); #endif // uvScale before anything #if _PERTEXUVSCALEOFFSET && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); config.uv0.xy = config.uv0.xy * ptUVScale0.rg + ptUVScale0.ba; config.uv1.xy = config.uv1.xy * ptUVScale1.rg + ptUVScale1.ba; #if !_MAX2LAYER config.uv2.xy = config.uv2.xy * ptUVScale2.rg + ptUVScale2.ba; #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = config.uv3.xy * ptUVScale3.rg + ptUVScale3.ba; #endif // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = albedoLOD * ptUVScale0.rgrg * weights.x + albedoLOD * ptUVScale1.rgrg * weights.y + albedoLOD * ptUVScale2.rgrg * weights.z + albedoLOD * ptUVScale3.rgrg * weights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #if _PERTEXUVROTATION && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVRot, 16.5, config, half4(0,0,0,0)); config.uv0.xy = RotateUV(config.uv0.xy, ptUVRot0.x); config.uv1.xy = RotateUV(config.uv1.xy, ptUVRot1.x); #if !_MAX2LAYER config.uv2.xy = RotateUV(config.uv2.xy, ptUVRot2.x); #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = RotateUV(config.uv3.xy, ptUVRot0.x); #endif #endif o.Alpha = 1; #if _POM && !_DISABLESPLATMAPS DoPOM(i, config, tc, albedoLOD, weights, camDist, worldNormalVertex); #endif SampleAlbedo(config, tc, samples, albedoLOD, weights); #if _NOISEHEIGHT ApplyNoiseHeight(samples, config.uv, config, i.worldPos, worldNormalVertex); #endif #if _STREAMS || (_PARALLAX && !_DISABLESPLATMAPS) half earlyHeight = BlendWeights(samples.albedo0.w, samples.albedo1.w, samples.albedo2.w, samples.albedo3.w, weights); #endif #if _STREAMS waterNormalFoam = GetWaterNormal(i, config.uv, worldNormalVertex); DoStreamRefract(config, tc, waterNormalFoam, fxLevels.b, earlyHeight); #endif #if _PARALLAX && !_DISABLESPLATMAPS DoParallax(i, earlyHeight, config, tc, samples, weights, camDist); #endif // Blend results #if _PERTEXINTERPCONTRAST && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptContrasts, 1.5, config, 0.5); half4 contrast = 0.5; contrast.x = ptContrasts0.a; contrast.y = ptContrasts1.a; #if !_MAX2LAYER contrast.z = ptContrasts2.a; #endif #if !_MAX3LAYER || !_MAX2LAYER contrast.w = ptContrasts3.a; #endif contrast = clamp(contrast + _Contrast, 0.0001, 1.0); half cnt = contrast.x * weights.x + contrast.y * weights.y + contrast.z * weights.z + contrast.w * weights.w; half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, cnt); #else half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, _Contrast); #endif #if _HYBRIDHEIGHTBLEND heightWeights = lerp(heightWeights, TotalOne(weights), saturate(camDist/max(1.0, _HybridHeightBlendDistance))); #endif // rescale derivatives after height weighting. Basically, in gradmip mode we blend the mip levels, // but this is before height mapping is sampled, so reblending them after alpha will make sure the other // channels (normal, etc) are sharper, which likely matters most.. #if _PERTEXUVSCALEOFFSET && !_DISABLESPLATMAPS #if _TRIPLANAR #if _USEGRADMIP SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); albedoLOD.d0 = origAlbedoLOD.d0 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d0 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d0 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d0 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d1 = origAlbedoLOD.d1 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d1 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d1 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d1 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d2 = origAlbedoLOD.d2 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d2 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d2 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d2 * ptUVScale3.xyxy * heightWeights.w; normalLOD.d0 = albedoLOD.d0; normalLOD.d1 = albedoLOD.d1; normalLOD.d2 = albedoLOD.d2; #if _USEEMISSIVEMETAL emisLOD.d0 = albedoLOD.d0; emisLOD.d1 = albedoLOD.d1; emisLOD.d2 = albedoLOD.d2; #endif #endif // gradmip #else // not triplanar // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = origAlbedoLOD * ptUVScale0.rgrg * heightWeights.x + origAlbedoLOD * ptUVScale1.rgrg * heightWeights.y + origAlbedoLOD * ptUVScale2.rgrg * heightWeights.z + origAlbedoLOD * ptUVScale3.rgrg * heightWeights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #endif #if _PARALLAX || _STREAMS SampleAlbedo(config, tc, samples, albedoLOD, heightWeights); #endif SampleNormal(config, tc, samples, normalLOD, heightWeights); #if _USEEMISSIVEMETAL SampleEmis(config, tc, samples, emisLOD, heightWeights); #endif #if _USESPECULARWORKFLOW SampleSpecular(config, tc, samples, specLOD, heightWeights); #endif #if _DISTANCERESAMPLE && !_DISABLESPLATMAPS DistanceResample(samples, config, tc, camDist, i.viewDir, fxLevels, albedoLOD, i.worldPos, heightWeights, worldNormalVertex); #endif #if _STARREACHFORMAT samples.normSAO0.w = length(samples.normSAO0.xy); samples.normSAO1.w = length(samples.normSAO1.xy); samples.normSAO2.w = length(samples.normSAO2.xy); samples.normSAO3.w = length(samples.normSAO3.xy); #endif // PerTexture sampling goes here, passing the samples structure #if _PERTEXMICROSHADOWS || _PERTEXFUZZYSHADE SAMPLE_PER_TEX(ptFuzz, 17.5, config, half4(0, 0, 1, 1)); #endif #if _PERTEXMICROSHADOWS #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) { half3 lightDir = GetGlobalLightDirTS(i); half4 microShadows = half4(1,1,1,1); microShadows.x = MicroShadow(lightDir, half3(samples.normSAO0.xy, 1), samples.normSAO0.a, ptFuzz0.a); microShadows.y = MicroShadow(lightDir, half3(samples.normSAO1.xy, 1), samples.normSAO1.a, ptFuzz1.a); microShadows.z = MicroShadow(lightDir, half3(samples.normSAO2.xy, 1), samples.normSAO2.a, ptFuzz2.a); microShadows.w = MicroShadow(lightDir, half3(samples.normSAO3.xy, 1), samples.normSAO3.a, ptFuzz3.a); samples.normSAO0.a *= microShadows.x; samples.normSAO1.a *= microShadows.y; #if !_MAX2LAYER samples.normSAO2.a *= microShadows.z; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a *= microShadows.w; #endif #if _DEBUG_OUTPUT_MICROSHADOWS o.Albedo = BlendWeights(microShadows.x, microShadows.y, microShadows.z, microShadows.a, heightWeights); return o; #endif } #endif #endif // _PERTEXMICROSHADOWS #if _PERTEXFUZZYSHADE samples.albedo0.rgb = FuzzyShade(samples.albedo0.rgb, half3(samples.normSAO0.rg, 1), ptFuzz0.r, ptFuzz0.g, ptFuzz0.b, i.viewDir); samples.albedo1.rgb = FuzzyShade(samples.albedo1.rgb, half3(samples.normSAO1.rg, 1), ptFuzz1.r, ptFuzz1.g, ptFuzz1.b, i.viewDir); #if !_MAX2LAYER samples.albedo2.rgb = FuzzyShade(samples.albedo2.rgb, half3(samples.normSAO2.rg, 1), ptFuzz2.r, ptFuzz2.g, ptFuzz2.b, i.viewDir); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = FuzzyShade(samples.albedo3.rgb, half3(samples.normSAO3.rg, 1), ptFuzz3.r, ptFuzz3.g, ptFuzz3.b, i.viewDir); #endif #endif #if _PERTEXSATURATION && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptSaturattion, 9.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = lerp(MSLuminance(samples.albedo0.rgb), samples.albedo0.rgb, ptSaturattion0.a); samples.albedo1.rgb = lerp(MSLuminance(samples.albedo1.rgb), samples.albedo1.rgb, ptSaturattion1.a); #if !_MAX2LAYER samples.albedo2.rgb = lerp(MSLuminance(samples.albedo2.rgb), samples.albedo2.rgb, ptSaturattion2.a); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = lerp(MSLuminance(samples.albedo3.rgb), samples.albedo3.rgb, ptSaturattion3.a); #endif #endif #if _PERTEXTINT && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptTints, 1.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb *= ptTints0.rgb; samples.albedo1.rgb *= ptTints1.rgb; #if !_MAX2LAYER samples.albedo2.rgb *= ptTints2.rgb; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb *= ptTints3.rgb; #endif #endif #if _PCHEIGHTGRADIENT || _PCHEIGHTHSV || _PCSLOPEGRADIENT || _PCSLOPEHSV ProceduralGradients(i, samples, config, worldHeight, worldNormalVertex); #endif #if _WETNESS || _PUDDLES || _STREAMS porosity = _GlobalPorosity; #endif #if _PERTEXCOLORINTENSITY SAMPLE_PER_TEX(ptCI, 23.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = saturate(samples.albedo0.rgb * (1 + ptCI0.rrr)); samples.albedo1.rgb = saturate(samples.albedo1.rgb * (1 + ptCI1.rrr)); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb * (1 + ptCI2.rrr)); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb * (1 + ptCI3.rrr)); #endif #endif #if (_PERTEXBRIGHTNESS || _PERTEXCONTRAST || _PERTEXPOROSITY || _PERTEXFOAM) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptBC, 3.5, config, half4(1, 1, 1, 1)); #if _PERTEXCONTRAST samples.albedo0.rgb = saturate(((samples.albedo0.rgb - 0.5) * ptBC0.g) + 0.5); samples.albedo1.rgb = saturate(((samples.albedo1.rgb - 0.5) * ptBC1.g) + 0.5); #if !_MAX2LAYER samples.albedo2.rgb = saturate(((samples.albedo2.rgb - 0.5) * ptBC2.g) + 0.5); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(((samples.albedo3.rgb - 0.5) * ptBC3.g) + 0.5); #endif #endif #if _PERTEXBRIGHTNESS samples.albedo0.rgb = saturate(samples.albedo0.rgb + ptBC0.rrr); samples.albedo1.rgb = saturate(samples.albedo1.rgb + ptBC1.rrr); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb + ptBC2.rrr); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb + ptBC3.rrr); #endif #endif #if _PERTEXPOROSITY porosity = BlendWeights(ptBC0.b, ptBC1.b, ptBC2.b, ptBC3.b, heightWeights); #endif #if _PERTEXFOAM streamFoam = BlendWeights(ptBC0.a, ptBC1.a, ptBC2.a, ptBC3.a, heightWeights); #endif #endif #if (_PERTEXNORMSTR || _PERTEXAOSTR || _PERTEXSMOOTHSTR || _PERTEXMETALLIC) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(perTexMatSettings, 2.5, config, half4(1.0, 1.0, 1.0, 0.0)); #endif #if _PERTEXNORMSTR && !_DISABLESPLATMAPS #if _SURFACENORMALS samples.surf0 *= perTexMatSettings0.r; samples.surf1 *= perTexMatSettings1.r; samples.surf2 *= perTexMatSettings2.r; samples.surf3 *= perTexMatSettings3.r; #else samples.normSAO0.xy *= perTexMatSettings0.r; samples.normSAO1.xy *= perTexMatSettings1.r; samples.normSAO2.xy *= perTexMatSettings2.r; samples.normSAO3.xy *= perTexMatSettings3.r; #endif #endif #if _PERTEXAOSTR && !_DISABLESPLATMAPS samples.normSAO0.a = pow(abs(samples.normSAO0.a), perTexMatSettings0.b); samples.normSAO1.a = pow(abs(samples.normSAO1.a), perTexMatSettings1.b); #if !_MAX2LAYER samples.normSAO2.a = pow(abs(samples.normSAO2.a), perTexMatSettings2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a = pow(abs(samples.normSAO3.a), perTexMatSettings3.b); #endif #endif #if _PERTEXSMOOTHSTR && !_DISABLESPLATMAPS samples.normSAO0.b += perTexMatSettings0.g; samples.normSAO1.b += perTexMatSettings1.g; samples.normSAO0.b = saturate(samples.normSAO0.b); samples.normSAO1.b = saturate(samples.normSAO1.b); #if !_MAX2LAYER samples.normSAO2.b += perTexMatSettings2.g; samples.normSAO2.b = saturate(samples.normSAO2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.b += perTexMatSettings3.g; samples.normSAO3.b = saturate(samples.normSAO3.b); #endif #endif #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) #if _PERTEXSSS { SAMPLE_PER_TEX(ptSSS, 18.5, config, half4(1, 1, 1, 1)); // tint, thickness half4 vals = ptSSS0 * heightWeights.x + ptSSS1 * heightWeights.y + ptSSS2 * heightWeights.z + ptSSS3 * heightWeights.w; SSSThickness = vals.a; SSSTint = vals.rgb; } #endif #endif #if _PERTEXRIMLIGHT { SAMPLE_PER_TEX(ptRimA, 26.5, config, half4(1, 1, 1, 1)); SAMPLE_PER_TEX(ptRimB, 27.5, config, half4(1, 1, 1, 0)); samples.emisMetal0.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO0.xy, 1))), max(0.0001, ptRimA0.g)) * ptRimB0.rgb * ptRimB0.a; samples.emisMetal1.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO1.xy, 1))), max(0.0001, ptRimA1.g)) * ptRimB1.rgb * ptRimB1.a; samples.emisMetal2.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO2.xy, 1))), max(0.0001, ptRimA2.g)) * ptRimB2.rgb * ptRimB2.a; samples.emisMetal3.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO3.xy, 1))), max(0.0001, ptRimA3.g)) * ptRimB3.rgb * ptRimB3.a; } #endif #if (((_DETAILNOISE && _PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && _PERTEXDISTANCENOISESTRENGTH)) || (_NORMALNOISE && _PERTEXNORMALNOISESTRENGTH)) && !_DISABLESPLATMAPS ApplyDetailDistanceNoisePerTex(samples, config, camDist, i.worldPos, worldNormalVertex); #endif #if _GLOBALNOISEUV // noise defaults so that a value of 1, 1 is 4 pixels in size and moves the uvs by 1 pixel max. #if _CUSTOMSPLATTEXTURES noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #else noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE ApplyTrax(samples, config, i.worldPos, traxBuffer, traxNormal); #endif #if (_ANTITILEARRAYDETAIL || _ANTITILEARRAYDISTANCE || _ANTITILEARRAYNORMAL) && !_DISABLESPLATMAPS ApplyAntiTilePerTex(samples, config, camDist, i.worldPos, worldNormalVertex, heightWeights); #endif #if _GEOMAP && !_DISABLESPLATMAPS GeoTexturePerTex(samples, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _GLOBALTINT && _PERTEXGLOBALTINTSTRENGTH && !_DISABLESPLATMAPS GlobalTintTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALNORMALS && _PERTEXGLOBALNORMALSTRENGTH && !_DISABLESPLATMAPS GlobalNormalTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && _PERTEXGLOBALSAOMSTRENGTH && !_DISABLESPLATMAPS GlobalSAOMTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && _PERTEXGLOBALEMISSTRENGTH && !_DISABLESPLATMAPS GlobalEmisTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && _PERTEXGLOBALSPECULARSTRENGTH && !_DISABLESPLATMAPS && _USESPECULARWORKFLOW GlobalSpecularTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _PERTEXMETALLIC && !_DISABLESPLATMAPS half metallic = BlendWeights(perTexMatSettings0.a, perTexMatSettings1.a, perTexMatSettings2.a, perTexMatSettings3.a, heightWeights); o.Metallic = metallic; #endif #if _GLITTER && !_DISABLESPLATMAPS DoGlitter(i, samples, config, camDist, worldNormalVertex, i.worldPos); #endif // Blend em.. #if _DISABLESPLATMAPS // If we don't sample from the _Diffuse, then the shader compiler will strip the sampler on // some platforms, which will cause everything to break. So we sample from the lowest mip // and saturate to 1 to keep the cost minimal. Annoying, but the compiler removes the texture // and sampler, even though the sampler is still used. albedo = saturate(UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, float3(0,0,0), 12) + 1); albedo.a = 0.5; // make height something we can blend with for the combined mesh mode, since it still height blends. normSAO = half4(0,0,0,1); #else albedo = BlendWeights(samples.albedo0, samples.albedo1, samples.albedo2, samples.albedo3, heightWeights); normSAO = BlendWeights(samples.normSAO0, samples.normSAO1, samples.normSAO2, samples.normSAO3, heightWeights); #if _SURFACENORMALS surfGrad = BlendWeights(samples.surf0, samples.surf1, samples.surf2, samples.surf3, heightWeights); #endif #if (_USEEMISSIVEMETAL || _PERTEXRIMLIGHT) && !_DISABLESPLATMAPS emisMetal = BlendWeights(samples.emisMetal0, samples.emisMetal1, samples.emisMetal2, samples.emisMetal3, heightWeights); #endif #if _USESPECULARWORKFLOW && !_DISABLESPLATMAPS specular = BlendWeights(samples.specular0, samples.specular1, samples.specular2, samples.specular3, heightWeights); #endif #if _PERTEXOUTLINECOLOR SAMPLE_PER_TEX(ptOutlineColor, 28.5, config, half4(0.5, 0.5, 0.5, 1)); half4 outlineColor = BlendWeights(ptOutlineColor0, ptOutlineColor1, ptOutlineColor2, ptOutlineColor3, heightWeights); half4 tstr = saturate(abs(heightWeights - 0.5) * 2); half transitionBlend = min(min(min(tstr.x, tstr.y), tstr.z), tstr.w); albedo.rgb = lerp(albedo.rgb * outlineColor.rgb * 2, albedo.rgb, outlineColor.a * transitionBlend); #endif #endif #if _MESHOVERLAYSPLATS || _MESHCOMBINED o.Alpha = 1.0; if (config.uv0.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.x; else if (config.uv1.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.y; else if (config.uv2.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.z; else if (config.uv3.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.w; #endif // effects which don't require per texture adjustments and are part of the splats sample go here. // Often, as an optimization, you can compute the non-per tex version of above effects here.. #if ((_DETAILNOISE && !_PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && !_PERTEXDISTANCENOISESTRENGTH) || (_NORMALNOISE && !_PERTEXNORMALNOISESTRENGTH)) ApplyDetailDistanceNoise(albedo.rgb, normSAO, surfGrad, config, camDist, i.worldPos, worldNormalVertex); #endif #if _SPLATFADE } #endif #if _SPLATFADE float2 sfDX = ddx(config.uv * _UVScale); float2 sfDY = ddy(config.uv * _UVScale); MSBRANCHOTHER(camDist - _SplatFade.x) { float falloff = saturate(InverseLerp(_SplatFade.x, _SplatFade.y, camDist)); half4 sfalb = SAMPLE_TEXTURE2D_ARRAY_GRAD(_Diffuse, sampler_Diffuse, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY); COUNTSAMPLE albedo.rgb = lerp(albedo.rgb, sfalb.rgb, falloff); #if !_NONORMALMAP && !_AUTONORMAL half4 sfnormSAO = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalSAO, sampler_NormalSAO, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY).agrb; COUNTSAMPLE sfnormSAO.xy = sfnormSAO.xy * 2 - 1; normSAO = lerp(normSAO, sfnormSAO, falloff); #if _SURFACENORMALS surfGrad = lerp(surfGrad, ConvertNormal2ToGradient(sfnormSAO.xy), falloff); #endif #endif } #endif #if _AUTONORMAL float3 autoNormal = HeightToNormal(albedo.a * _AutoNormalHeightScale, i.worldPos); normSAO.xy = autoNormal; normSAO.z = 0; normSAO.w = (autoNormal.z * autoNormal.z); #endif #if _MESHCOMBINED SampleMeshCombined(albedo, normSAO, surfGrad, emisMetal, specular, o.Alpha, SSSThickness, SSSTint, config, heightWeights); #endif #if _ISOBJECTSHADER SampleObjectShader(i, albedo, normSAO, surfGrad, emisMetal, specular, config); #endif #if _GEOMAP GeoTexture(albedo.rgb, normSAO, surfGrad, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _SCATTER ApplyScatter( #if _MEGASPLAT config, #endif i, albedo, normSAO, surfGrad, config.uv, camDist); #endif #if _DECAL DoDecalBlend(decalOutput, albedo, normSAO, surfGrad, emisMetal, i.uv_Control0); #endif #if _GLOBALTINT && !_PERTEXGLOBALTINTSTRENGTH GlobalTintTexture(albedo.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif #if _VSGRASSMAP VSGrassTexture(albedo.rgb, config, camDist); #endif #if _GLOBALNORMALS && !_PERTEXGLOBALNORMALSTRENGTH GlobalNormalTexture(normSAO, surfGrad, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && !_PERTEXGLOBALSAOMSTRENGTH GlobalSAOMTexture(normSAO, emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && !_PERTEXGLOBALEMISSTRENGTH GlobalEmisTexture(emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && !_PERTEXGLOBALSPECULARSTRENGTH && _USESPECULARWORKFLOW GlobalSpecularTexture(specular.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif o.Albedo = albedo.rgb; o.Height = albedo.a; #if _NONORMALMAP o.Normal = half3(0,0,1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #elif _SURFACENORMALS o.Normal = ResolveNormalFromSurfaceGradient(surfGrad); o.Normal = mul(GetTBN(i), o.Normal); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #else o.Normal = half3(normSAO.xy, 1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #endif #if _USEEMISSIVEMETAL || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _PERTEXRIMLIGHT #if _USEEMISSIVEMETAL emisMetal.rgb *= _EmissiveMult; #endif o.Emission += emisMetal.rgb; o.Metallic = emisMetal.a; #endif #if _USESPECULARWORKFLOW o.Specular = specular; #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA pud = DoStreams(i, o, fxLevels, config.uv, porosity, waterNormalFoam, worldNormalVertex, streamFoam, wetLevel, burnLevel, i.worldPos); #endif #if _SNOW snowCover = DoSnow(i, o, config.uv, WorldNormalVector(i, o.Normal), worldNormalVertex, i.worldPos, pud, porosity, camDist, config, weights, SSSTint, SSSThickness, traxBuffer, traxNormal); #endif #if _PERTEXSSS || _MESHCOMBINEDUSESSS || (_SNOW && _SNOWSSS) { half3 worldView = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); o.Emission += ComputeSSS(i, worldView, WorldNormalVector(i, o.Normal), SSSTint, SSSThickness, _SSSDistance, _SSSScale, _SSSPower); } #endif #if _SNOWGLITTER DoSnowGlitter(i, config, o, camDist, worldNormalVertex, snowCover); #endif #if _WINDPARTICULATE || _SNOWPARTICULATE DoWindParticulate(i, o, config, weights, camDist, worldNormalVertex, snowCover); #endif o.Normal.z = sqrt(1 - saturate(dot(o.Normal.xy, o.Normal.xy))); #if _SPECULARFADE { float specFade = saturate((i.worldPos.y - _SpecularFades.x) / max(_SpecularFades.y - _SpecularFades.x, 0.0001)); o.Metallic *= specFade; o.Smoothness *= specFade; } #endif #if _VSSHADOWMAP VSShadowTexture(o, i, config, camDist); #endif #if _TOONWIREFRAME ToonWireframe(config.uv, o.Albedo, camDist); #endif #if _SEETHROUGHSHADER SeethroughShader(o.Albedo, o.Emission, o.Alpha, i.worldPos, o.Normal, i.worldNormal); #endif #if _DEBUG_TRAXBUFFER ClearAllButAlbedo(o, half3(traxBuffer, 0, 0) * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMALVERTEX ClearAllButAlbedo(o, worldNormalVertex * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMAL ClearAllButAlbedo(o, WorldNormalVector(i, o.Normal) * saturate(o.Albedo.z+1)); #endif #if _DEBUG_MEGABARY && _MEGASPLAT o.Albedo = i.baryWeights.xyz; #endif return o; } void SampleSplats(float2 controlUV, inout half4 w0, inout half4 w1, inout half4 w2, inout half4 w3, inout half4 w4, inout half4 w5, inout half4 w6, inout half4 w7) { #if _CUSTOMSPLATTEXTURES #if !_MICROMESH controlUV = (controlUV * (_CustomControl0_TexelSize.zw - 1.0f) + 0.5f) * _CustomControl0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_CustomControl0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_CustomControl1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_CustomControl2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_CustomControl3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_CustomControl4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_CustomControl5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_CustomControl6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_CustomControl7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #else #if !_MICROMESH controlUV = (controlUV * (_Control0_TexelSize.zw - 1.0f) + 0.5f) * _Control0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_Control0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_Control1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_Control2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_Control3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_Control4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_Control5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_Control6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_Control7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #endif } MicroSplatLayer SurfImpl(Input i, float3 worldNormalVertex) { #if _MEGANOUV i.uv_Control0 = i.worldPos.xz; #endif float camDist = distance(_WorldSpaceCameraPos, i.worldPos); #if _FORCELOCALSPACE worldNormalVertex = mul((float3x3)GetWorldToObjectMatrix(), worldNormalVertex).xyz; i.worldPos = i.worldPos - mul(GetObjectToWorldMatrix(), float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _ORIGINSHIFT i.worldPos = i.worldPos + mul(_GlobalOriginMTX, float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _DEBUG_USE_TOPOLOGY i.worldPos = SAMPLE_TEXTURE2D(_DebugWorldPos, sampler_Diffuse, i.uv_Control0); worldNormalVertex = SAMPLE_TEXTURE2D(_DebugWorldNormal, sampler_Diffuse, i.uv_Control0); i.worldHeight = i.worldPos.y; #endif #if _ALPHABELOWHEIGHT && !_TBDISABLEALPHAHOLES ClipWaterLevel(i.worldPos); #endif #if !_TBDISABLEALPHAHOLES && defined(_ALPHATEST_ON) // UNITY 2019.3 holes ClipHoles(i.uv_Control0); #endif float2 origUV = i.uv_Control0; #if _MICROMESH && _MESHUV2 float2 controlUV = i.uv2_Diffuse; #else float2 controlUV = i.uv_Control0; #endif #if _MICROMESH controlUV = InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, controlUV); #endif half4 weights = half4(1,0,0,0); Config config = (Config)0; UNITY_INITIALIZE_OUTPUT(Config,config); config.uv = origUV; DecalOutput decalOutput = (DecalOutput)0; #if _DECAL decalOutput = DoDecals(i.uv_Control0, i.worldPos, camDist, worldNormalVertex); #endif #if _SURFACENORMALS // Initialize the surface gradient basis vectors ConstructSurfaceGradientTBN(i); #endif #if _SPLATFADE MSBRANCHOTHER(_SplatFade.y - camDist) #endif // _SPLATFADE { #if !_DISABLESPLATMAPS // Sample the splat data, from textures or vertices, and setup the config.. #if _MICRODIGGERMESH DiggerSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLAT MegaSplatVertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLATTEXTURE MegaSplatTextureSetup(controlUV, weights, origUV, config, i.worldPos, decalOutput); #elif _MICROVERTEXMESH VertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif !_PROCEDURALTEXTURE || _PROCEDURALBLENDSPLATS half4 w0 = 0; half4 w1 = 0; half4 w2 = 0; half4 w3 = 0; half4 w4 = 0; half4 w5 = 0; half4 w6 = 0; half4 w7 = 0; SampleSplats(controlUV, w0, w1, w2, w3, w4, w5, w6, w7); Setup(weights, origUV, config, w0, w1, w2, w3, w4, w5, w6, w7, i.worldPos, decalOutput); #endif #if _PROCEDURALTEXTURE float3 procNormal = worldNormalVertex; float3 worldPos = i.worldPos; ProceduralSetup(i, worldPos, i.worldHeight, procNormal, i.worldUpVector, weights, origUV, config, ddx(origUV), ddy(origUV), ddx(worldPos), ddy(worldPos), decalOutput); #endif #else // _DISABLESPLATMAPS Setup(weights, origUV, config, half4(1,0,0,0), 0, 0, 0, 0, 0, 0, 0, i.worldPos, decalOutput); #endif #if _SLOPETEXTURE SlopeTexture(config, weights, worldNormalVertex); #endif } // _SPLATFADE else case #if _TOONFLATTEXTURE float2 quv = floor(origUV * _ToonTerrainSize); float2 fuv = frac(origUV * _ToonTerrainSize); #if !_TOONFLATTEXTUREQUAD quv = Hash2D((fuv.x > fuv.y) ? quv : quv * 0.333); #endif float2 uvq = quv / _ToonTerrainSize; config.uv0.xy = uvq; config.uv1.xy = uvq; config.uv2.xy = uvq; config.uv3.xy = uvq; #endif #if (_TEXTURECLUSTER2 || _TEXTURECLUSTER3) && !_DISABLESPLATMAPS PrepClusters(origUV, config, i.worldPos, worldNormalVertex); #endif #if (_ALPHAHOLE || _ALPHAHOLETEXTURE) && !_DISABLESPLATMAPS && !_TBDISABLEALPHAHOLES ClipAlphaHole(config, weights); #endif MicroSplatLayer l = Sample(i, weights, config, camDist, worldNormalVertex, decalOutput); // On windows, sometimes the shared samplers gets stripped, so we have to do this crap. // We sample from the lowest mip, so it shouldn't cost much, but still, I hate this, wtf.. float stripVal = saturate(SAMPLE_TEXTURE2D_LOD(_Diffuse, sampler_Diffuse, config.uv0, 11).r + 2); stripVal *= saturate(SAMPLE_TEXTURE2D_LOD(_NormalSAO, sampler_NormalSAO, config.uv0, 11).r + 2); l.Albedo *= stripVal; l.Normal *= stripVal; #if _PROCEDURALTEXTURE ProceduralTextureDebugOutput(l, weights, config); #endif return l; } float4 ConstructTerrainTangent(float3 normal, float3 positiveZ) { // Consider a flat terrain. It should have tangent be (1, 0, 0) and bitangent be (0, 0, 1) as the UV of the terrain grid mesh is a scale of the world XZ position. // In CreateTangentToWorld function (in SpaceTransform.hlsl), it is cross(normal, tangent) * sgn for the bitangent vector. // It is not true in a left-handed coordinate system for the terrain bitangent, if we provide 1 as the tangent.w. It would produce (0, 0, -1) instead of (0, 0, 1). // Also terrain's tangent calculation was wrong in a left handed system because cross((0,0,1), terrainNormalOS) points to the wrong direction as negative X. // Therefore all the 4 xyzw components of the tangent needs to be flipped to correct the tangent frame. // (See TerrainLitData.hlsl - GetSurfaceAndBuiltinData) float3 tangent = normalize(cross(normal, positiveZ)); return float4(tangent, -1); } void TerrainInstancing(inout float4 vertex, inout float3 normal, inout float2 uv) { #if _MICROTERRAIN && defined(UNITY_INSTANCING_ENABLED) && !_TERRAINBLENDABLESHADER float2 patchVertex = vertex.xy; float4 instanceData = UNITY_ACCESS_INSTANCED_PROP(Terrain, _TerrainPatchInstanceData); float2 sampleCoords = (patchVertex.xy + instanceData.xy) * instanceData.z; // (xy + float2(xBase,yBase)) * skipScale uv = sampleCoords * _TerrainHeightmapRecipSize.zw; float2 sampleUV = (uv / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleUV, 0)); vertex.xz = sampleCoords * _TerrainHeightmapScale.xz; vertex.y = height * _TerrainHeightmapScale.y; normal = float3(0, 1, 0); #endif } void ApplyMeshModification(inout VertexData input) { #if _MICROTERRAIN && !_TERRAINBLENDABLESHADER float2 uv = input.texcoord0.xy; TerrainInstancing(input.vertex, input.normal, uv); input.texcoord0.xy = uv; #endif #if _PERPIXNORMAL && !_TERRAINBLENDABLESHADER input.normal = float3(0,1,0); #endif #if _MICROVERSEPREVIEW float4 recipSize = _TerrainHeightmapTexture_TexelSize; recipSize.zw = (1.0f / (_TerrainHeightmapTexture_TexelSize.zw-1)); float2 sampleCoords = (input.texcoord0.xy / recipSize.zw + 0.5f) * recipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleCoords, 0)); input.vertex.xyz += float3(0,1,0) * height * _TerrainHeight * 2; #endif } // called by the template, so we can remove tangent from VertexData void ApplyTerrainTangent(inout VertexToPixel input) { #if (_MICROTERRAIN || _PERPIXNORMAL) && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif // digger meshes ain't got no tangent either.. #if _MICRODIGGERMESH && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif } void ModifyVertex(inout VertexData v, inout ExtraV2F d) { ApplyMeshModification(v); #if _MICROVERTEXMESH || _MICRODIGGERMESH EncodeVertexWorkflow(v, d); #elif _MEGASPLAT EncodeMegaSplatVertex(v, d); #endif } void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d) { #if _MICROVERSEPREVIEW v.vertex.y = OffsetVertex(v, d).y; #elif _TESSDISTANCE || _TESSEDGE v.vertex.xyz += OffsetVertex(v, d); #endif } float3 GetTessFactors () { #if _TESSEDGE return float3(_TessData1.x, _TessData1.w, 0); #endif #if _TESSDISTANCE return float3(_TessData2.x, _TessData2.y, _TessData1.x); #endif return 0; } void SurfaceFunction(inout Surface o, inout ShaderData d) { float3 worldNormalVertex = d.worldSpaceNormal; #if _MICROVERSEPREVIEW float2 sampleCoords = d.texcoord0.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif (defined(UNITY_INSTANCING_ENABLED) && _MICROTERRAIN && !_TERRAINBLENDABLESHADER) float2 sampleCoords = (d.texcoord0.xy / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif _PERPIXNORMAL && (_MICROTERRAIN || _MICROMESHTERRAIN) && !_TERRAINBLENDABLESHADER float2 sampleCoords = (d.texcoord0.xy * _PerPixelNormal_TexelSize.zw + 0.5f) * _PerPixelNormal_TexelSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_PerPixelNormal, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomTangent, geomNormal)) * -1; #if _MICROMESHTERRAIN geomBitangent *= -1; #endif worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #endif #if _TOONPOLYEDGE FlatShade(d); #endif Input i = DescToInput(d); #if _SRPTERRAINBLEND MicroSplatLayer l = BlendWithTerrain(d); #if _DEBUG_WORLDNORMAL ClearAllButAlbedo(l, normalize(TangentToWorldSpace(d, l.Normal)) * saturate(l.Albedo.z+1)); #endif #else MicroSplatLayer l = SurfImpl(i, worldNormalVertex); #endif DoDebugOutput(l); o.Albedo = l.Albedo; o.Normal = l.Normal; o.Smoothness = l.Smoothness; o.Occlusion = l.Occlusion; o.Metallic = l.Metallic; o.Emission = l.Emission; #if _USESPECULARWORKFLOW o.Specular = l.Specular; #endif o.Height = l.Height; o.Alpha = l.Alpha; } // SHADERDESC ShaderData CreateShaderData(VertexToPixel i) { ShaderData d = (ShaderData)0; d.worldSpacePosition = i.worldPos; d.worldSpaceNormal = i.worldNormal; d.worldSpaceTangent = i.worldTangent.xyz; float3 bitangent = cross(i.worldTangent.xyz, i.worldNormal) * i.worldTangent.w * -1; d.TBNMatrix = float3x3(d.worldSpaceTangent, bitangent, d.worldSpaceNormal); d.worldSpaceViewDir = normalize(_WorldSpaceCameraPos - i.worldPos); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); d.texcoord0 = i.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER d.texcoord1 = i.texcoord1; // d.texcoord2 = i.texcoord2; #endif // d.texcoord3 = i.texcoord3; // d.vertexColor = i.vertexColor; // these rarely get used, so we back transform them. Usually will be stripped. #if _HDRP // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(GetCameraRelativePositionWS(i.worldPos), 1)); #else // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(i.worldPos, 1)); #endif // d.localSpaceNormal = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldNormal)); // d.localSpaceTangent = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldTangent.xyz)); // d.screenPos = i.screenPos; // d.screenUV = i.screenPos.xy / i.screenPos.w; // d.extraV2F0 = i.extraV2F0; // d.extraV2F1 = i.extraV2F1; // d.extraV2F2 = i.extraV2F2; // d.extraV2F3 = i.extraV2F3; // d.extraV2F4 = i.extraV2F4; // d.extraV2F5 = i.extraV2F5; // d.extraV2F6 = i.extraV2F6; // d.extraV2F7 = i.extraV2F7; return d; } // CHAINS void ChainModifyVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; ModifyVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainModifyTessellatedVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; // d.extraV2F0 = v2p.extraV2F0; // d.extraV2F1 = v2p.extraV2F1; // d.extraV2F2 = v2p.extraV2F2; // d.extraV2F3 = v2p.extraV2F3; // d.extraV2F4 = v2p.extraV2F4; // d.extraV2F5 = v2p.extraV2F5; // d.extraV2F6 = v2p.extraV2F6; // d.extraV2F7 = v2p.extraV2F7; ModifyTessellatedVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainFinalColorForward(inout Surface l, inout ShaderData d, inout half4 color) { } void ChainFinalGBufferStandard(inout Surface s, inout ShaderData d, inout half4 GBuffer0, inout half4 GBuffer1, inout half4 GBuffer2, inout half4 outEmission, inout half4 outShadowMask) { } #if _PASSSHADOW float3 _LightDirection; float3 _LightPosition; #endif // vertex shader VertexToPixel Vert (VertexData v) { VertexToPixel o = (VertexToPixel)0; UNITY_SETUP_INSTANCE_ID(v); UNITY_TRANSFER_INSTANCE_ID(v, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #if !_TESSELLATION_ON ChainModifyVertex(v, o); #endif o.texcoord0 = v.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.texcoord1 = v.texcoord1; // o.texcoord2 = v.texcoord2; #endif // o.texcoord3 = v.texcoord3; // o.vertexColor = v.vertexColor; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz); o.worldPos = TransformObjectToWorld(v.vertex.xyz); o.worldNormal = TransformObjectToWorldNormal(v.normal); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float2 uv1 = v.texcoord1.xy; float2 uv2 = v.texcoord2.xy; o.worldTangent = float4(TransformObjectToWorldDir(v.tangent.xyz), v.tangent.w); #else float2 uv1 = v.texcoord0.xy; float2 uv2 = uv1; #endif // MS Only ApplyTerrainTangent(o); #if _PASSSHADOW #if _CASTING_PUNCTUAL_LIGHT_SHADOW float3 lightDirectionWS = normalize(_LightPosition - o.worldPos); #else float3 lightDirectionWS = _LightDirection; #endif // Define shadow pass specific clip position for Universal o.pos = TransformWorldToHClip(ApplyShadowBias(o.worldPos, o.worldNormal, lightDirectionWS)); #if UNITY_REVERSED_Z o.pos.z = min(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #else o.pos.z = max(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #endif #elif _PASSMETA #if _MICROTERRAIN o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), v.texcoord0.xy, v.texcoord0.xy, unity_LightmapST, unity_DynamicLightmapST); #else o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), uv1, uv2, unity_LightmapST, unity_DynamicLightmapST); #endif #else o.pos = TransformWorldToHClip(o.worldPos); #endif // o.screenPos = ComputeScreenPos(o.pos, _ProjectionParams.x); #if _PASSFORWARD || _PASSGBUFFER #if _MICROTERRAIN OUTPUT_LIGHTMAP_UV(v.texcoord0.xy, unity_LightmapST, o.lightmapUV); #else OUTPUT_LIGHTMAP_UV(uv1, unity_LightmapST, o.lightmapUV); #endif OUTPUT_SH(o.worldNormal, o.sh); #if defined(DYNAMICLIGHTMAP_ON) o.dynamicLightmapUV.xy = uv2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; #endif #endif #ifdef VARYINGS_NEED_FOG_AND_VERTEX_LIGHT half fogFactor = 0; #if defined(_FOG_FRAGMENT) fogFactor = ComputeFogFactor(o.pos.z); #endif #if _BAKEDLIT half3 vertexLight = VertexLighting(o.worldPos, o.worldNormal); o.fogFactorAndVertexLight = half4(fogFactor, vertexLight); #else o.fogFactorAndVertexLight = half4(fogFactor, 0,0,0); #endif #endif #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) o.shadowCoord = GetShadowCoord(vertexInput); #endif return o; } // fragment shader half4 Frag (VertexToPixel IN #ifdef _DEPTHOFFSET_ON , out float outputDepth : SV_Depth #endif ) : SV_Target { UNITY_SETUP_INSTANCE_ID(IN); ShaderData d = CreateShaderData(IN); Surface l = (Surface)0; #ifdef _DEPTHOFFSET_ON l.outputDepth = outputDepth; #endif l.Albedo = half3(0.5, 0.5, 0.5); l.Normal = float3(0,0,1); l.Occlusion = 1; l.Alpha = 1; SurfaceFunction(l, d); #ifdef _DEPTHOFFSET_ON outputDepth = l.outputDepth; #endif return 0; } ENDHLSL } Pass { Name "DepthOnly" Tags { "LightMode" = "DepthOnly" } // Render State Blend One Zero, One Zero Cull Back ZTest LEqual ZWrite On ColorMask 0 HLSLPROGRAM #pragma vertex Vert #pragma fragment Frag #define SHADERPASS SHADERPASS_DEPTHNORMALSONLY #define _PASSDEPTH 1 #define _PASSDEPTHNORMALS 1 #pragma target 3.5 #pragma prefer_hlslcc gles #pragma exclude_renderers d3d11_9x #pragma multi_compile_instancing #pragma multi_compile _ DOTS_INSTANCING_ON #pragma multi_compile_local _ _ALPHATEST_ON #define _MICROSPLAT 1 #define _MICROPOLARISMESH 1 #define _USEGRADMIP 1 #define _PERTEXUVSCALEOFFSET 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _MSRENDERLOOP_UNITYLD 1 #define _MSRENDERLOOP_UNITYURP2020 1 #define _MSRENDERLOOP_UNITYURP2021 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _URP 1 // Includes #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Version.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl" #include "Packages/com.unity.shadergraph/ShaderGraphLibrary/ShaderVariablesFunctions.hlsl" #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, float3x3(d.worldSpaceTangent, cross(d.worldSpaceTangent, d.worldSpaceNormal), d.worldSpaceNormal)) #define UnityObjectToWorldNormal(normal) mul(GetObjectToWorldMatrix(), normal) #define _WorldSpaceLightPos0 _MainLightPosition #define UNITY_DECLARE_TEX2D(name) TEXTURE2D(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2D_NOSAMPLER(name) TEXTURE2D(name); #define UNITY_DECLARE_TEX2DARRAY(name) TEXTURE2D_ARRAY(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2DARRAY_NOSAMPLER(name) TEXTURE2D_ARRAY(name); #define UNITY_SAMPLE_TEX2DARRAY(tex,coord) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, coord.xy, coord.z) #define UNITY_SAMPLE_TEX2DARRAY_LOD(tex,coord,lod) SAMPLE_TEXTURE2D_ARRAY_LOD(tex, sampler##tex, coord.xy, coord.z, lod) #define UNITY_SAMPLE_TEX2D(tex, coord) SAMPLE_TEXTURE2D(tex, sampler##tex, coord) #define UNITY_SAMPLE_TEX2D_SAMPLER(tex, samp, coord) SAMPLE_TEXTURE2D(tex, sampler##samp, coord) #if defined(UNITY_COMPILER_HLSL) #define UNITY_INITIALIZE_OUTPUT(type,name) name = (type)0; #else #define UNITY_INITIALIZE_OUTPUT(type,name) #endif #define sampler2D_float sampler2D #define sampler2D_half sampler2D // data across stages, stripped like the above. struct VertexToPixel { float4 pos : SV_POSITION; float3 worldPos : TEXCOORD0; float3 worldNormal : TEXCOORD1; float4 worldTangent : TEXCOORD2; float4 texcoord0 : TEXCCOORD3; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 texcoord1 : TEXCCOORD4; // float4 texcoord2 : TEXCCOORD5; #endif // float4 texcoord3 : TEXCCOORD6; // float4 screenPos : TEXCOORD7; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD13; // float4 extraV2F1 : TEXCOORD14; // float4 extraV2F2 : TEXCOORD15; // float4 extraV2F3 : TEXCOORD16; // float4 extraV2F4 : TEXCOORD17; // float4 extraV2F5 : TEXCOORD18; // float4 extraV2F6 : TEXCOORD19; // float4 extraV2F7 : TEXCOORD20; #if defined(LIGHTMAP_ON) float2 lightmapUV : TEXCOORD8; #endif #if defined(DYNAMICLIGHTMAP_ON) float2 dynamicLightmapUV : TEXCOORD9; #endif #if !defined(LIGHTMAP_ON) float3 sh : TEXCOORD10; #endif float4 fogFactorAndVertexLight : TEXCOORD11; float4 shadowCoord : TEXCOORD12; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : CUSTOM_INSTANCE_ID; #endif #if (defined(UNITY_STEREO_MULTIVIEW_ENABLED)) || (defined(UNITY_STEREO_INSTANCING_ENABLED) && (defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE))) uint stereoTargetEyeIndexAsBlendIdx0 : BLENDINDICES0; #endif #if (defined(UNITY_STEREO_INSTANCING_ENABLED)) uint stereoTargetEyeIndexAsRTArrayIdx : SV_RenderTargetArrayIndex; #endif #if defined(SHADER_STAGE_FRAGMENT) && defined(VARYINGS_NEED_CULLFACE) FRONT_FACE_TYPE cullFace : FRONT_FACE_SEMANTIC; #endif }; // TEMPLATE_SHARED // data describing the user output of a pixel struct Surface { half3 Albedo; half Height; half3 Normal; half Smoothness; half3 Emission; half Metallic; half3 Specular; half Occlusion; half Alpha; // HDRP Only half SpecularOcclusion; half SubsurfaceMask; half Thickness; half CoatMask; half Anisotropy; half IridescenceMask; half IridescenceThickness; }; // data the user might need, this will grow to be big. But easy to strip struct ShaderData { float3 localSpacePosition; float3 localSpaceNormal; float3 localSpaceTangent; float3 worldSpacePosition; float3 worldSpaceNormal; float3 worldSpaceTangent; float3 worldSpaceViewDir; float3 tangentSpaceViewDir; float4 texcoord0; float4 texcoord1; float4 texcoord2; float4 texcoord3; float2 screenUV; float4 screenPos; float4 vertexColor; float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; float3x3 TBNMatrix; }; struct VertexData { #if SHADER_TARGET > 30 && _PLANETCOMPUTE // // uint vertexID : SV_VertexID; #endif float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD4; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD5; // Add Precomputed Velocity (Alembic computes velocities on runtime side). #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID }; struct TessVertex { float4 vertex : INTERNALTESSPOS; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD4; // float4 extraV2F1 : TEXCOORD5; // float4 extraV2F2 : TEXCOORD6; // float4 extraV2F3 : TEXCOORD7; // float4 extraV2F4 : TEXCOORD8; // float4 extraV2F5 : TEXCOORD9; // float4 extraV2F6 : TEXCOORD10; // float4 extraV2F7 : TEXCOORD11; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD12; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD13; #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; struct ExtraV2F { float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; }; float3 WorldToTangentSpace(ShaderData d, float3 normal) { return mul(d.TBNMatrix, normal); } float3 TangentToWorldSpace(ShaderData d, float3 normal) { return mul(normal, d.TBNMatrix); } // in this case, make standard more like SRPs, because we can't fix // GetWorldToObjectMatrix() in HDRP, since it already does macro-fu there #if _STANDARD float3 TransformWorldToObject(float3 p) { return mul(GetWorldToObjectMatrix(), float4(p, 1)); }; float3 TransformObjectToWorld(float3 p) { return mul(GetObjectToWorldMatrix(), float4(p, 1)); }; float4 TransformWorldToObject(float4 p) { return mul(GetWorldToObjectMatrix(), p); }; float4 TransformObjectToWorld(float4 p) { return mul(GetObjectToWorldMatrix(), p); }; float4x4 GetWorldToObjectMatrix() { return GetWorldToObjectMatrix(); } float4x4 GetObjectToWorldMatrix() { return GetObjectToWorldMatrix(); } #endif float3 GetCameraWorldPosition() { #if _HDRP return GetCameraRelativePositionWS(_WorldSpaceCameraPos); #else return _WorldSpaceCameraPos; #endif } #if _HDRP half3 UnpackNormalmapRGorAG(half4 packednormal) { // This do the trick packednormal.x *= packednormal.w; half3 normal; normal.xy = packednormal.xy * 2 - 1; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } half3 UnpackNormal(half4 packednormal) { #if defined(UNITY_NO_DXT5nm) return packednormal.xyz * 2 - 1; #else return UnpackNormalmapRGorAG(packednormal); #endif } #endif #if _HDRP || _URP half3 UnpackScaleNormal(half4 packednormal, half scale) { #ifndef UNITY_NO_DXT5nm // Unpack normal as DXT5nm (1, y, 1, x) or BC5 (x, y, 0, 1) // Note neutral texture like "bump" is (0, 0, 1, 1) to work with both plain RGB normal and DXT5nm/BC5 packednormal.x *= packednormal.w; #endif half3 normal; normal.xy = (packednormal.xy * 2 - 1) * scale; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } #endif void GetSun(out float3 lightDir, out float3 color) { lightDir = float3(0.5, 0.5, 0); color = 1; #if _HDRP if (_DirectionalLightCount > 0) { DirectionalLightData light = _DirectionalLightDatas[0]; lightDir = -light.forward.xyz; color = light.color; } #elif _STANDARD lightDir = normalize(_WorldSpaceLightPos0.xyz); color = _LightColor0.rgb; #elif _URP Light light = GetMainLight(); lightDir = light.direction; color = light.color; #endif } CBUFFER_START(UnityPerMaterial) #if _MESHSUBARRAY half4 _MeshSubArrayIndexes; #endif float4 _Diffuse_TexelSize; float4 _NormalSAO_TexelSize; #if _HYBRIDHEIGHTBLEND float _HybridHeightBlendDistance; #endif #if _PACKINGHQ float4 _SmoothAO_TexelSize; #endif #ifdef _ALPHATEST_ON float4 _TerrainHolesTexture_TexelSize; #endif #if _USESPECULARWORKFLOW float4 _Specular_TexelSize; #endif #if _USEEMISSIVEMETAL float4 _EmissiveMetal_TexelSize; #endif #if _USEEMISSIVEMETAL half _EmissiveMult; #endif #if _AUTONORMAL half _AutoNormalHeightScale; #endif float4 _UVScale; // scale and offset half _Contrast; #if _VSSHADOWMAP float4 gVSSunDirection; #endif #if _FORCELOCALSPACE && _PLANETVECTORS float4x4 _PQSToLocal; #endif #if _ORIGINSHIFT float4x4 _GlobalOriginMTX; #endif float4 _Control0_TexelSize; #if _CUSTOMSPLATTEXTURES float4 _CustomControl0_TexelSize; #endif float4 _PerPixelNormal_TexelSize; #if _CONTROLNOISEUV || _GLOBALNOISEUV float2 _NoiseUVParams; #endif float4 _PerTexProps_TexelSize; #if _SURFACENORMALS float3 surfTangent; float3 surfBitangent; float3 surfNormal; #endif CBUFFER_END #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, data.TBN) // In Unity 2020.3LTS, Unity will spew tons of errors about missing this sampler in // URP, even though it shouldn't be required. TEXTURE2D(_MainTex); // globals, outside of CBuffer, but used by more than one module float3 _gGlitterLightDir; float3 _gGlitterLightWorldPos; half3 _gGlitterLightColor; #if (_MICROTERRAIN || _MICROMESHTERRAIN) 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) float4 _TerrainNormalmapTexture_TexelSize; #endif #if (_MICROTERRAIN || _MICROMESHTERRAIN) TEXTURE2D(_TerrainHeightmapTexture); float4 _TerrainHeightmapTexture_TexelSize; TEXTURE2D(_TerrainNormalmapTexture); #endif UNITY_INSTANCING_BUFFER_START(Terrain) UNITY_DEFINE_INSTANCED_PROP(float4, _TerrainPatchInstanceData) // float4(xBase, yBase, skipScale, ~) UNITY_INSTANCING_BUFFER_END(Terrain) // dynamic branching helpers, for regular and aggressive branching // debug mode shows how many samples using branching will save us. // // These macros are always used instead of the UNITY_BRANCH macro // to maintain debug displays and allow branching to be disabled // on as granular level as we want. #if _BRANCHSAMPLES #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; if (w > 0) #else #define MSBRANCH(w) UNITY_BRANCH if (w > 0) #endif #else #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; #else #define MSBRANCH(w) #endif #endif #if _BRANCHSAMPLESAGR #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER ||_DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; if (w > 0.001) #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; if (w > 0.001) #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; if (w > 0.001) #else #define MSBRANCHTRIPLANAR(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHCLUSTER(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHOTHER(w) UNITY_BRANCH if (w > 0.001) #endif #else #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER || _DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; #else #define MSBRANCHTRIPLANAR(w) #define MSBRANCHCLUSTER(w) #define MSBRANCHOTHER(w) #endif #endif #if _DEBUG_SAMPLECOUNT int _sampleCount; #define COUNTSAMPLE { _sampleCount++; } #else #define COUNTSAMPLE #endif #if _DEBUG_PROCLAYERS int _procLayerCount; #define COUNTPROCLAYER { _procLayerCount++; } #else #define COUNTPROCLAYER #endif #if _DEBUG_USE_TOPOLOGY TEXTURE2D(_DebugWorldPos); TEXTURE2D(_DebugWorldNormal); #endif // splat UNITY_DECLARE_TEX2DARRAY(_Diffuse); UNITY_DECLARE_TEX2DARRAY(_NormalSAO); #if _CONTROLNOISEUV || _GLOBALNOISEUV TEXTURE2D(_NoiseUV); #endif #if _PACKINGHQ UNITY_DECLARE_TEX2DARRAY(_SmoothAO); #endif #if _USESPECULARWORKFLOW UNITY_DECLARE_TEX2DARRAY(_Specular); #endif #if _USEEMISSIVEMETAL UNITY_DECLARE_TEX2DARRAY(_EmissiveMetal); #endif TEXTURE2D(_PerPixelNormal); SamplerState shared_linear_clamp_sampler; SamplerState shared_point_clamp_sampler; TEXTURE2D(_Control0); #if _CUSTOMSPLATTEXTURES TEXTURE2D(_CustomControl0); #if !_MAX4TEXTURES TEXTURE2D(_CustomControl1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_CustomControl2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_CustomControl3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl6); #endif #if _MAX32TEXTURES TEXTURE2D(_CustomControl7); #endif #else #if !_MAX4TEXTURES TEXTURE2D(_Control1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_Control2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_Control3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control6); #endif #if _MAX32TEXTURES TEXTURE2D(_Control7); #endif #endif TEXTURE2D_FLOAT(_PerTexProps); struct DecalLayer { float3 uv; float2 dx; float2 dy; int decalIndex; bool dynamic; }; struct DecalOutput { DecalLayer l0; DecalLayer l1; DecalLayer l2; DecalLayer l3; half4 Weights; half4 Indexes; half4 fxLevels; }; struct TriGradMipFormat { float4 d0; float4 d1; float4 d2; }; float InverseLerp(float x, float y, float v) { return (v-x)/max(y-x, 0.001); } float2 InverseLerp(float2 x, float2 y, float2 v) { return (v-x)/max(y-x, float2(0.001, 0.001)); } float3 InverseLerp(float3 x, float3 y, float3 v) { return (v-x)/max(y-x, float3(0.001, 0.001, 0.001)); } float4 InverseLerp(float4 x, float4 y, float4 v) { return (v-x)/max(y-x, float4(0.001, 0.001, 0.001, 0.001)); } // 2019.3 holes #ifdef _ALPHATEST_ON TEXTURE2D(_TerrainHolesTexture); void ClipHoles(float2 uv) { float hole = SAMPLE_TEXTURE2D(_TerrainHolesTexture, shared_linear_clamp_sampler, uv).r; COUNTSAMPLE clip(hole < 0.5f ? -1 : 1); } #endif #if _TRIPLANAR #if _USEGRADMIP #define MIPFORMAT TriGradMipFormat #define INITMIPFORMAT (TriGradMipFormat)0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float3 #define INITMIPFORMAT 0; #define MIPFROMATRAW float3 #endif #else #if _USEGRADMIP #define MIPFORMAT float4 #define INITMIPFORMAT 0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float #define INITMIPFORMAT 0; #define MIPFROMATRAW float #endif #endif float2 TotalOne(float2 v) { return v * (1.0 / max(v.x + v.y, 0.001)); } float3 TotalOne(float3 v) { return v * (1.0 / max(v.x + v.y + v.z, 0.001)); } float4 TotalOne(float4 v) { return v * (1.0 / max(v.x + v.y + v.z + v.w, 0.001)); } float2 RotateUV(float2 uv, float amt) { uv -=0.5; float s = sin ( amt); float c = cos ( amt ); float2x2 mtx = float2x2( c, -s, s, c); mtx *= 0.5; mtx += 0.5; mtx = mtx * 2-1; uv = mul ( uv, mtx ); uv += 0.5; return uv; } float4 DecodeToFloat4(float v) { uint vi = (uint)(v * (256.0f * 256.0f * 256.0f * 256.0f)); int ex = (int)(vi / (256 * 256 * 256) % 256); int ey = (int)((vi / (256 * 256)) % 256); int ez = (int)((vi / (256)) % 256); int ew = (int)(vi % 256); float4 e = float4(ex / 255.0, ey / 255.0, ez / 255.0, ew / 255.0); return e; } struct Input { ShaderData shaderData; float2 uv_Control0; float2 uv2_Diffuse; float worldHeight; float3 worldUpVector; float3 viewDir; float3 worldPos; float3 worldNormal; float4 color; float3x3 TBN; // vertex/digger workflow data half4 w0; half4 w1; half4 w2; half4 w3; half4 w4; half4 w5; half4 w6; // megasplat data half4 layer0; half4 layer1; half3 baryWeights; half4 scatter0; half4 scatter1; // wetness, puddles, streams, lava from vertex or megasplat half4 fx; // snow min, snow max half4 fx2; }; struct TriplanarConfig { float3x3 uv0; float3x3 uv1; float3x3 uv2; float3x3 uv3; half3 pN; half3 pN0; half3 pN1; half3 pN2; half3 pN3; half3 axisSign; Input IN; }; struct Config { float2 uv; float3 uv0; float3 uv1; float3 uv2; float3 uv3; half4 cluster0; half4 cluster1; half4 cluster2; half4 cluster3; }; struct MicroSplatLayer { half3 Albedo; half3 Normal; half Smoothness; half Occlusion; half Metallic; half Height; half3 Emission; #if _USESPECULARWORKFLOW half3 Specular; #endif half Alpha; }; // raw, unblended samples from arrays struct RawSamples { half4 albedo0; half4 albedo1; half4 albedo2; half4 albedo3; #if _SURFACENORMALS half3 surf0; half3 surf1; half3 surf2; half3 surf3; #endif half4 normSAO0; half4 normSAO1; half4 normSAO2; half4 normSAO3; #if _USEEMISSIVEMETAL || _GLOBALEMIS || _GLOBALSMOOTHAOMETAL || _PERTEXSSS || _PERTEXRIMLIGHT half4 emisMetal0; half4 emisMetal1; half4 emisMetal2; half4 emisMetal3; #endif #if _USESPECULARWORKFLOW half3 specular0; half3 specular1; half3 specular2; half3 specular3; #endif }; void InitRawSamples(inout RawSamples s) { s.normSAO0 = half4(0,0,0,1); s.normSAO1 = half4(0,0,0,1); s.normSAO2 = half4(0,0,0,1); s.normSAO3 = half4(0,0,0,1); #if _SURFACENORMALS s.surf0 = half3(0,0,1); s.surf1 = half3(0,0,1); s.surf2 = half3(0,0,1); s.surf3 = half3(0,0,1); #endif } float3 GetGlobalLightDir(Input i) { float3 lightDir = float3(1,0,0); #if _HDRP || PASS_DEFERRED lightDir = normalize(_gGlitterLightDir.xyz); #elif _URP lightDir = GetMainLight().direction; #else #ifndef USING_DIRECTIONAL_LIGHT lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); #else lightDir = normalize(_WorldSpaceLightPos0.xyz); #endif #endif return lightDir; } float3x3 GetTBN(Input i) { return i.TBN; } float3 GetGlobalLightDirTS(Input i) { float3 lightDirWS = GetGlobalLightDir(i); return mul(GetTBN(i), lightDirWS); } half3 GetGlobalLightColor() { #if _HDRP || PASS_DEFERRED return _gGlitterLightColor; #elif _URP return (GetMainLight().color); #else return _LightColor0.rgb; #endif } half3 FuzzyShade(half3 color, half3 normal, half coreMult, half edgeMult, half power, float3 viewDir) { half dt = saturate(dot(viewDir, normal)); half dark = 1.0 - (coreMult * dt); half edge = pow(1-dt, power) * edgeMult; return color * (dark + edge); } half3 ComputeSSS(Input i, float3 V, float3 N, half3 tint, half thickness, half distortion, half scale, half power) { float3 L = GetGlobalLightDir(i); half3 lightColor = GetGlobalLightColor(); float3 H = normalize(L + N * distortion); float VdotH = pow(saturate(dot(V, -H)), power) * scale; float3 I = (VdotH) * thickness; return lightColor * I * tint; } #if _MAX2LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y; } #elif _MAX3LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } #else inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } #endif #if _MAX3LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #elif _MAX2LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #else #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##3 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv3.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #endif half2 BlendNormal2(half2 base, half2 blend) { return normalize(half3(base.xy + blend.xy, 1)).xy; } half3 BlendOverlay(half3 base, half3 blend) { return (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))); } half3 BlendMult2X(half3 base, half3 blend) { return (base * (blend * 2)); } half3 BlendLighterColor(half3 s, half3 d) { return (s.x + s.y + s.z > d.x + d.y + d.z) ? s : d; } #if _SURFACENORMALS #define HALF_EPS 4.8828125e-4 // 2^-11, machine epsilon: 1 + EPS = 1 (half of the ULP for 1.0f) void ConstructSurfaceGradientTBN(Input i) { float3x3 tbn = GetTBN(i); float3 t = tbn[0]; float3 b = tbn[1]; float3 n = tbn[2]; surfNormal = n;//mul(GetWorldToObjectMatrix(), float4(n, 1)).xyz; surfTangent = t;//mul(GetWorldToObjectMatrix(), float4(t, 1)).xyz; surfBitangent = b;//cross(surfNormal, surfTangent); float renormFactor = 1.0 / length(surfNormal); surfNormal *= renormFactor; surfTangent *= renormFactor; surfBitangent *= renormFactor; } half3 SurfaceGradientFromTBN(half2 deriv) { return deriv.x * surfTangent + deriv.y * surfBitangent; } // Input: vM is tangent space normal in [-1;1]. // Output: convert vM to a derivative. half2 TspaceNormalToDerivative(half3 vM) { const half scale = 1.0/128.0; // Ensure vM delivers a positive third component using abs() and // constrain vM.z so the range of the derivative is [-128; 128]. const half3 vMa = abs(vM); const half z_ma = max(vMa.z, scale*max(vMa.x, vMa.y)); return -half2(vM.x, vM.y)/z_ma; } // Used to produce a surface gradient from the gradient of a volume // bump function such as 3D Perlin noise. Equation 2 in [Mik10]. half3 SurfgradFromVolumeGradient(half3 grad) { return grad - dot(surfNormal, grad) * surfNormal; } half3 SurfgradFromTriplanarProjection(half3 pN, half2 xPlaneTN, half2 yPlaneTN, half2 zPlaneTN) { const half w0 = pN.x; const half w1 = pN.y; const half w2 = pN.z; // X-plane tangent normal to gradient derivative xPlaneTN = xPlaneTN * 2.0 - 1.0; half xPlaneRcpZ = rsqrt(max(1 - dot(xPlaneTN.x, xPlaneTN.x) - dot(xPlaneTN.y, xPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_xplane = xPlaneTN * -xPlaneRcpZ; // Y-plane tangent normal to gradient derivative yPlaneTN = yPlaneTN * 2.0 - 1.0; half yPlaneRcpZ = rsqrt(max(1 - dot(yPlaneTN.x, yPlaneTN.x) - dot(yPlaneTN.y, yPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_yplane = yPlaneTN * -yPlaneRcpZ; // Z-plane tangent normal to gradient derivative zPlaneTN = zPlaneTN * 2.0 - 1.0; half zPlaneRcpZ = rsqrt(max(1 - dot(zPlaneTN.x, zPlaneTN.x) - dot(zPlaneTN.y, zPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_zplane = zPlaneTN * -zPlaneRcpZ; // Assume deriv xplane, deriv yplane, and deriv zplane are // sampled using (z,y), (x,z), and (x,y), respectively. // Positive scales of the lookup coordinate will work // as well, but for negative scales the derivative components // will need to be negated accordingly. float3 grad = float3(w2*d_zplane.x + w1*d_yplane.x, w2*d_zplane.y + w0*d_xplane.y, w0*d_xplane.x + w1*d_yplane.y); return SurfgradFromVolumeGradient(grad); } half3 ConvertNormalToGradient(half3 normal) { half2 deriv = TspaceNormalToDerivative(normal); return SurfaceGradientFromTBN(deriv); } half3 ConvertNormal2ToGradient(half2 packedNormal) { half2 tNormal = packedNormal; half rcpZ = rsqrt(max(1 - dot(tNormal.x, tNormal.x) - dot(tNormal.y, tNormal.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 deriv = tNormal * -rcpZ; return SurfaceGradientFromTBN(deriv); } half3 ResolveNormalFromSurfaceGradient(half3 gradient) { return normalize(surfNormal - gradient); } #endif // _SURFACENORMALS void BlendNormalPerTex(inout RawSamples o, half2 noise, float4 fades) { #if _SURFACENORMALS float3 grad = ConvertNormal2ToGradient(noise.xy); o.surf0 += grad * fades.x; o.surf1 += grad * fades.y; #if !_MAX2LAYER o.surf2 += grad * fades.z; #endif #if !_MAX2LAYER && !_MAX3LAYER o.surf3 += grad * fades.w; #endif #else o.normSAO0.xy = lerp(o.normSAO0.xy, BlendNormal2(o.normSAO0.xy, noise.xy), fades.x); o.normSAO1.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #if !_MAX2LAYER o.normSAO2.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO2.xy, noise.xy), fades.y); #endif #if !_MAX2LAYER && !_MAX3LAYER o.normSAO3.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #endif #endif } half3 BlendNormal3(half3 n1, half3 n2) { n1 += float3( 0, 0, 1); n2 *= float3(-1, -1, 1); return n1*dot(n1, n2) / n1.z - n2; } half2 TransformTriplanarNormal(Input IN, float3x3 t2w, half3 axisSign, half3 absVertNormal, half3 pN, half2 a0, half2 a1, half2 a2) { a0 = a0 * 2 - 1; a1 = a1 * 2 - 1; a2 = a2 * 2 - 1; a0.x *= axisSign.x; a1.x *= axisSign.y; a2.x *= axisSign.z; half3 n0 = half3(a0.xy, 1); half3 n1 = half3(a1.xy, 1); half3 n2 = half3(a2.xy, 1); float3 wn = IN.worldNormal; n0 = BlendNormal3(half3(wn.zy, absVertNormal.x), n0); n1 = BlendNormal3(half3(wn.xz, absVertNormal.y), n1 * float3(-1, 1, 1)); n2 = BlendNormal3(half3(wn.xy, absVertNormal.z), n2); n0.z *= axisSign.x; n1.z *= axisSign.y; n2.z *= -axisSign.z; half3 worldNormal = (n0.zyx * pN.x + n1.xzy * pN.y + n2.xyz * pN.z); return mul(t2w, worldNormal).xy; } // funcs inline half MSLuminance(half3 rgb) { #ifdef UNITY_COLORSPACE_GAMMA return dot(rgb, half3(0.22, 0.707, 0.071)); #else return dot(rgb, half3(0.0396819152, 0.458021790, 0.00609653955)); #endif } float2 Hash2D( float2 x ) { float2 k = float2( 0.3183099, 0.3678794 ); x = x*k + k.yx; return -1.0 + 2.0*frac( 16.0 * k*frac( x.x*x.y*(x.x+x.y)) ); } float Noise2D(float2 p ) { float2 i = floor( p ); float2 f = frac( p ); float2 u = f*f*(3.0-2.0*f); return lerp( lerp( dot( Hash2D( i + float2(0.0,0.0) ), f - float2(0.0,0.0) ), dot( Hash2D( i + float2(1.0,0.0) ), f - float2(1.0,0.0) ), u.x), lerp( dot( Hash2D( i + float2(0.0,1.0) ), f - float2(0.0,1.0) ), dot( Hash2D( i + float2(1.0,1.0) ), f - float2(1.0,1.0) ), u.x), u.y); } float FBM2D(float2 uv) { float f = 0.5000*Noise2D( uv ); uv *= 2.01; f += 0.2500*Noise2D( uv ); uv *= 1.96; f += 0.1250*Noise2D( uv ); return f; } float3 Hash3D( float3 p ) { p = float3( dot(p,float3(127.1,311.7, 74.7)), dot(p,float3(269.5,183.3,246.1)), dot(p,float3(113.5,271.9,124.6))); return -1.0 + 2.0*frac(sin(p)*437.5453123); } float Noise3D( float3 p ) { float3 i = floor( p ); float3 f = frac( p ); float3 u = f*f*(3.0-2.0*f); return lerp( lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,0.0) ), f - float3(0.0,0.0,0.0) ), dot( Hash3D( i + float3(1.0,0.0,0.0) ), f - float3(1.0,0.0,0.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,0.0) ), f - float3(0.0,1.0,0.0) ), dot( Hash3D( i + float3(1.0,1.0,0.0) ), f - float3(1.0,1.0,0.0) ), u.x), u.y), lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,1.0) ), f - float3(0.0,0.0,1.0) ), dot( Hash3D( i + float3(1.0,0.0,1.0) ), f - float3(1.0,0.0,1.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,1.0) ), f - float3(0.0,1.0,1.0) ), dot( Hash3D( i + float3(1.0,1.0,1.0) ), f - float3(1.0,1.0,1.0) ), u.x), u.y), u.z ); } float FBM3D(float3 uv) { float f = 0.5000*Noise3D( uv ); uv *= 2.01; f += 0.2500*Noise3D( uv ); uv *= 1.96; f += 0.1250*Noise3D( uv ); return f; } float GetSaturation(float3 c) { float mi = min(min(c.x, c.y), c.z); float ma = max(max(c.x, c.y), c.z); return (ma - mi)/(ma + 1e-7); } // Better Color Lerp, does not have darkening issue float3 BetterColorLerp(float3 a, float3 b, float x) { float3 ic = lerp(a, b, x) + float3(1e-6,0.0,0.0); float sd = abs(GetSaturation(ic) - lerp(GetSaturation(a), GetSaturation(b), x)); float3 dir = normalize(float3(2.0 * ic.x - ic.y - ic.z, 2.0 * ic.y - ic.x - ic.z, 2.0 * ic.z - ic.y - ic.x)); float lgt = dot(float3(1.0, 1.0, 1.0), ic); float ff = dot(dir, normalize(ic)); const float dsp_str = 1.5; ic += dsp_str * dir * sd * ff * lgt; return saturate(ic); } half4 ComputeWeights(half4 iWeights, half h0, half h1, half h2, half h3, half contrast) { #if _DISABLEHEIGHTBLENDING return iWeights; #else // compute weight with height map //half4 weights = half4(iWeights.x * h0, iWeights.y * h1, iWeights.z * h2, iWeights.w * h3); half4 weights = half4(iWeights.x * max(h0,0.001), iWeights.y * max(h1,0.001), iWeights.z * max(h2,0.001), iWeights.w * max(h3,0.001)); // Contrast weights half maxWeight = max(max(weights.x, max(weights.y, weights.z)), weights.w); half transition = max(contrast * maxWeight, 0.0001); half threshold = maxWeight - transition; half scale = 1.0 / transition; weights = saturate((weights - threshold) * scale); weights = TotalOne(weights); return weights; #endif } half HeightBlend(half h1, half h2, half slope, half contrast) { #if _DISABLEHEIGHTBLENDING return slope; #else h2 = 1 - h2; half tween = saturate((slope - min(h1, h2)) / max(abs(h1 - h2), 0.001)); half blend = saturate( ( tween - (1-contrast) ) / max(contrast, 0.001)); return blend; #endif } #if _MAX4TEXTURES #define TEXCOUNT 4 #elif _MAX8TEXTURES #define TEXCOUNT 8 #elif _MAX12TEXTURES #define TEXCOUNT 12 #elif _MAX20TEXTURES #define TEXCOUNT 20 #elif _MAX24TEXTURES #define TEXCOUNT 24 #elif _MAX28TEXTURES #define TEXCOUNT 28 #elif _MAX32TEXTURES #define TEXCOUNT 32 #else #define TEXCOUNT 16 #endif #if _DECAL_SPLAT void DoMergeDecalSplats(half4 iWeights, half4 iIndexes, inout half4 indexes, inout half4 weights) { for (int i = 0; i < 4; ++i) { half w = iWeights[i]; half index = iIndexes[i]; if (w > weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = index; } else if (w > weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = index; } else if (w > weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = index; } else if (w > weights[3]) { weights[3] = w; indexes[3] = index; } } } #endif void Setup(out half4 weights, float2 uv, out Config config, half4 w0, half4 w1, half4 w2, half4 w3, half4 w4, half4 w5, half4 w6, half4 w7, float3 worldPos, DecalOutput decalOutput) { config = (Config)0; half4 indexes = 0; config.uv = uv; #if _WORLDUV uv = worldPos.xz; #endif #if _DISABLESPLATMAPS float2 scaledUV = uv; #else float2 scaledUV = uv * _UVScale.xy + _UVScale.zw; #endif // if only 4 textures, and blending 4 textures, skip this whole thing.. // this saves about 25% of the ALU of the base shader on low end. However if // we rely on sorted texture weights (distance resampling) we have to sort.. float4 defaultIndexes = float4(0,1,2,3); #if _MESHSUBARRAY defaultIndexes = _MeshSubArrayIndexes; #endif #if _MESHSUBARRAY && !_DECAL_SPLAT || (_MAX4TEXTURES && !_MAX3LAYER && !_MAX2LAYER && !_DISTANCERESAMPLE && !_POM && !_DECAL_SPLAT) weights = w0; config.uv0 = float3(scaledUV, defaultIndexes.x); config.uv1 = float3(scaledUV, defaultIndexes.y); config.uv2 = float3(scaledUV, defaultIndexes.z); config.uv3 = float3(scaledUV, defaultIndexes.w); return; #endif #if _DISABLESPLATMAPS weights = float4(1,0,0,0); return; #else half splats[TEXCOUNT]; splats[0] = w0.x; splats[1] = w0.y; splats[2] = w0.z; splats[3] = w0.w; #if !_MAX4TEXTURES splats[4] = w1.x; splats[5] = w1.y; splats[6] = w1.z; splats[7] = w1.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES splats[8] = w2.x; splats[9] = w2.y; splats[10] = w2.z; splats[11] = w2.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES splats[12] = w3.x; splats[13] = w3.y; splats[14] = w3.z; splats[15] = w3.w; #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[16] = w4.x; splats[17] = w4.y; splats[18] = w4.z; splats[19] = w4.w; #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[20] = w5.x; splats[21] = w5.y; splats[22] = w5.z; splats[23] = w5.w; #endif #if _MAX28TEXTURES || _MAX32TEXTURES splats[24] = w6.x; splats[25] = w6.y; splats[26] = w6.z; splats[27] = w6.w; #endif #if _MAX32TEXTURES splats[28] = w7.x; splats[29] = w7.y; splats[30] = w7.z; splats[31] = w7.w; #endif weights[0] = 0; weights[1] = 0; weights[2] = 0; weights[3] = 0; indexes[0] = 0; indexes[1] = 0; indexes[2] = 0; indexes[3] = 0; int i = 0; for (i = 0; i < TEXCOUNT; ++i) { half w = splats[i]; if (w >= weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = i; } else if (w >= weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = i; } else if (w >= weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = i; } else if (w >= weights[3]) { weights[3] = w; indexes[3] = i; } } // NaN Prevention if (weights.x <= 0) weights = float4(1, 0, 0, 0); #if _DECAL_SPLAT DoMergeDecalSplats(decalOutput.Weights, decalOutput.Indexes, weights, indexes); #endif // clamp and renormalize #if _MAX2LAYER weights.zw = 0; weights.xy = TotalOne(weights.xy); #elif _MAX3LAYER weights.w = 0; weights.xyz = TotalOne(weights.xyz); #elif !_DISABLEHEIGHTBLENDING || _NORMALIZEWEIGHTS // prevents black when painting, which the unity shader does not prevent. weights = normalize(weights); #endif config.uv0 = float3(scaledUV, indexes.x); config.uv1 = float3(scaledUV, indexes.y); config.uv2 = float3(scaledUV, indexes.z); config.uv3 = float3(scaledUV, indexes.w); #endif //_DISABLESPLATMAPS } float3 HeightToNormal(float height, float3 worldPos) { float3 dx = ddx(worldPos); float3 dy = ddy(worldPos); float3 crossX = cross(float3(0,1,0), dx); float3 crossY = cross(float3(0,1,0), dy); float3 d = abs(dot(crossY, dx)); float3 n = ((((height + ddx(height)) - height) * crossY) + (((height + ddy(height)) - height) * crossX)) * sign(d); n.z *= -1; return normalize((d * float3(0,1,0)) - n).xzy; } float ComputeMipLevel(float2 uv, float2 textureSize) { uv *= textureSize; float2 dx_vtc = ddx(uv); float2 dy_vtc = ddy(uv); float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); return 0.5 * log2(delta_max_sqr); } inline half2 UnpackNormal2(half4 packednormal) { return packednormal.wy * 2 - 1; } half3 TriplanarHBlend(half h0, half h1, half h2, half3 pN, half contrast) { half3 blend = pN / dot(pN, half3(1,1,1)); float3 heights = float3(h0, h1, h2) + (blend * 3.0); half height_start = max(max(heights.x, heights.y), heights.z) - contrast; half3 h = max(heights - height_start.xxx, half3(0,0,0)); blend = h / dot(h, half3(1,1,1)); return blend; } void ClearAllButAlbedo(inout MicroSplatLayer o, half3 display) { o.Albedo = display.rgb; o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } void ClearAllButAlbedo(inout MicroSplatLayer o, half display) { o.Albedo = half3(display, display, display); o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } half MicroShadow(float3 lightDir, half3 normal, half ao, half strength) { half shadow = saturate(abs(dot(normal, lightDir)) + (ao * ao * 2.0) - 1.0); return 1 - ((1-shadow) * strength); } void DoDebugOutput(inout MicroSplatLayer l) { #if _DEBUG_OUTPUT_ALBEDO ClearAllButAlbedo(l, l.Albedo); #elif _DEBUG_OUTPUT_NORMAL // oh unit shader compiler normal stripping, how I hate you so.. // must multiply by albedo to stop the normal from being white. Why, fuck knows? ClearAllButAlbedo(l, float3(l.Normal.xy * 0.5 + 0.5, l.Normal.z * saturate(l.Albedo.z+1))); #elif _DEBUG_OUTPUT_SMOOTHNESS ClearAllButAlbedo(l, l.Smoothness.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_METAL ClearAllButAlbedo(l, l.Metallic.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_AO ClearAllButAlbedo(l, l.Occlusion.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_EMISSION ClearAllButAlbedo(l, l.Emission * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_HEIGHT ClearAllButAlbedo(l, l.Height.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_SPECULAR && _USESPECULARWORKFLOW ClearAllButAlbedo(l, l.Specular * saturate(l.Albedo.z+1)); #elif _DEBUG_BRANCHCOUNT_WEIGHT ClearAllButAlbedo(l, _branchWeightCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TRIPLANAR ClearAllButAlbedo(l, _branchTriplanarCount / 24 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_CLUSTER ClearAllButAlbedo(l, _branchClusterCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_OTHER ClearAllButAlbedo(l, _branchOtherCount / 8 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TOTAL l.Albedo.r = _branchWeightCount / 12; l.Albedo.g = _branchTriplanarCount / 24; l.Albedo.b = _branchClusterCount / 12; ClearAllButAlbedo(l, (l.Albedo.r + l.Albedo.g + l.Albedo.b + (_branchOtherCount / 8)) / 4); #elif _DEBUG_OUTPUT_MICROSHADOWS ClearAllButAlbedo(l,l.Albedo); #elif _DEBUG_SAMPLECOUNT float sdisp = (float)_sampleCount / max(_SampleCountDiv, 1); half3 sdcolor = float3(sdisp, sdisp > 1 ? 1 : 0, 0); ClearAllButAlbedo(l, sdcolor * saturate(l.Albedo.z + 1)); #elif _DEBUG_PROCLAYERS ClearAllButAlbedo(l, (float)_procLayerCount / (float)_PCLayerCount * saturate(l.Albedo.z + 1)); #endif } // abstraction around sampler mode #if _USELODMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_LOD(tex, sampler##tex, u, l.x) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u, l.x) #elif _USEGRADMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_GRAD(tex, sampler##tex, u, l.xy, l.zw) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY_GRAD(tex, ss, u.xy, u.z, l.xy, l.zw) #else #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, u.xy, u.z) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u.xy, y.z) #endif #define MICROSPLAT_SAMPLE_DIFFUSE(u, cl, l) MICROSPLAT_SAMPLE(_Diffuse, u, l) #define MICROSPLAT_SAMPLE_EMIS(u, cl, l) MICROSPLAT_SAMPLE(_EmissiveMetal, u, l) #define MICROSPLAT_SAMPLE_DIFFUSE_LOD(u, cl, l) UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, u, l) #if _PACKINGHQ #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) half4(MICROSPLAT_SAMPLE(_NormalSAO, u, l).ga, MICROSPLAT_SAMPLE(_SmoothAO, u, l).ga).brag #else #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) MICROSPLAT_SAMPLE(_NormalSAO, u, l) #endif #if _USESPECULARWORKFLOW #define MICROSPLAT_SAMPLE_SPECULAR(u, cl, l) MICROSPLAT_SAMPLE(_Specular, u, l) #endif struct SimpleTriplanarConfig { float3 pn; float2 uv0; float2 uv1; float2 uv2; }; void PrepSimpleTriplanarConfig(inout SimpleTriplanarConfig tc, float3 worldPos, float3 normal, float contrast) { tc.pn = pow(abs(normal), contrast); tc.pn = tc.pn / (tc.pn.x + tc.pn.y + tc.pn.z); half3 axisSign = sign(normal); tc.uv0 = worldPos.zy * axisSign.x; tc.uv1 = worldPos.xz * axisSign.y; tc.uv2 = worldPos.xy * axisSign.z; } #define SimpleTriplanarSample(tex, tc, scale) (SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv0 * scale) * tc.pn.x + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv1 * scale) * tc.pn.y + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv2 * scale) * tc.pn.z) #define SimpleTriplanarSampleLOD(tex, tc, scale, lod) (SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv0 * scale, lod) * tc.pn.x + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv1 * scale, lod) * tc.pn.y + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv2 * scale, lod) * tc.pn.z) #define SimpleTriplanarSampleGrad(tex, tc, scale) (SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv0 * scale, ddx(tc.uv0) * scale, ddy(tc.uv0) * scale) * tc.pn.x + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv1 * scale, ddx(tc.uv1) * scale, ddy(tc.uv1) * scale) * tc.pn.y + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv2 * scale, ddx(tc.uv2) * scale, ddy(tc.uv2) * scale) * tc.pn.z) inline half3 MicroSplatDiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity) { specColor = lerp (half3(0,0,0), albedo, metallic); oneMinusReflectivity = (1-metallic); return albedo * oneMinusReflectivity; } Input DescToInput(ShaderData IN) { Input s = (Input)0; s.shaderData = IN; s.TBN = IN.TBNMatrix; s.worldNormal = IN.worldSpaceNormal; s.worldPos = IN.worldSpacePosition; s.viewDir = IN.tangentSpaceViewDir; s.uv_Control0 = IN.texcoord0.xy; s.worldUpVector = float3(0,1,0); s.worldHeight = IN.worldSpacePosition.y; #if _PLANETVECTORS float3 rwp = mul(_PQSToLocal, float4(IN.worldSpacePosition, 1)); s.worldHeight = distance(rwp, float3(0,0,0)); s.worldUpVector = normalize(rwp); #endif #if _MICROMESH && _MESHUV2 s.uv2_Diffuse = IN.texcoord1.xy; #endif #if _MEGASPLAT UnpackMegaSplat(s, IN); #endif #if _MICROVERTEXMESH || _MICRODIGGERMESH UnpackVertexWorkflow(s, IN); #endif #if _PLANETVECTORS DoPlanetDataInputCopy(s, IN); #endif return s; } void SampleAlbedo(inout Config config, inout TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half4 contrasts = _Contrast.xxxx; #if _PERTEXTRIPLANARCONTRAST SAMPLE_PER_TEX(ptc, 9.5, config, half4(1,0.5,0,0)); contrasts = half4(ptc0.y, ptc1.y, ptc2.y, ptc3.y); #endif #if _PERTEXTRIPLANAR SAMPLE_PER_TEX(pttri, 9.5, config, half4(0,0,0,0)); #endif { // For per-texture triplanar, we modify the view based blending factor of the triplanar // such that you get a pure blend of either top down projection, or with the top down projection // removed and renormalized. This causes dynamic flow control optimizations to kick in and avoid // the extra texture samples while keeping the code simple. Yay.. // We also only have to do this in the Albedo, because the pN values will be adjusted after the // albedo is sampled, causing future samples to use this data. #if _PERTEXTRIPLANAR if (pttri0.x > 0.66) { tc.pN0 = half3(0,1,0); } else if (pttri0.x > 0.33) { tc.pN0.y = 0; tc.pN0.xz = TotalOne(tc.pN0.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } half3 bf = tc.pN0; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN0, contrasts.x); tc.pN0 = bf; #endif s.albedo0 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } MSBRANCH(weights.y) { #if _PERTEXTRIPLANAR if (pttri1.x > 0.66) { tc.pN1 = half3(0,1,0); } else if (pttri1.x > 0.33) { tc.pN1.y = 0; tc.pN1.xz = TotalOne(tc.pN1.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { COUNTSAMPLE a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[2], config.cluster1, d2); } half3 bf = tc.pN1; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN1, contrasts.x); tc.pN1 = bf; #endif s.albedo1 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { #if _PERTEXTRIPLANAR if (pttri2.x > 0.66) { tc.pN2 = half3(0,1,0); } else if (pttri2.x > 0.33) { tc.pN2.y = 0; tc.pN2.xz = TotalOne(tc.pN2.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } half3 bf = tc.pN2; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN2, contrasts.x); tc.pN2 = bf; #endif s.albedo2 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { #if _PERTEXTRIPLANAR if (pttri3.x > 0.66) { tc.pN3 = half3(0,1,0); } else if (pttri3.x > 0.33) { tc.pN3.y = 0; tc.pN3.xz = TotalOne(tc.pN3.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } half3 bf = tc.pN3; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN3, contrasts.x); tc.pN3 = bf; #endif s.albedo3 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #else s.albedo0 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.albedo1 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.albedo2 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.albedo3 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #if _PERTEXHEIGHTOFFSET || _PERTEXHEIGHTCONTRAST SAMPLE_PER_TEX(ptHeight, 10.5, config, 1); #if _PERTEXHEIGHTOFFSET s.albedo0.a = saturate(s.albedo0.a + ptHeight0.b - 1); s.albedo1.a = saturate(s.albedo1.a + ptHeight1.b - 1); s.albedo2.a = saturate(s.albedo2.a + ptHeight2.b - 1); s.albedo3.a = saturate(s.albedo3.a + ptHeight3.b - 1); #endif #if _PERTEXHEIGHTCONTRAST s.albedo0.a = saturate(pow(s.albedo0.a + 0.5, abs(ptHeight0.a)) - 0.5); s.albedo1.a = saturate(pow(s.albedo1.a + 0.5, abs(ptHeight1.a)) - 0.5); s.albedo2.a = saturate(pow(s.albedo2.a + 0.5, abs(ptHeight2.a)) - 0.5); s.albedo3.a = saturate(pow(s.albedo3.a + 0.5, abs(ptHeight3.a)) - 0.5); #endif #endif } void SampleNormal(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _NONORMALMAP || _AUTONORMAL s.normSAO0 = half4(0,0, 0, 1); s.normSAO1 = half4(0,0, 0, 1); s.normSAO2 = half4(0,0, 0, 1); s.normSAO3 = half4(0,0, 0, 1); return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half3 absVertNormal = abs(tc.IN.worldNormal); float3x3 t2w = tc.IN.TBN; { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[0], config.cluster0, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[1], config.cluster0, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[2], config.cluster0, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf0 = SurfgradFromTriplanarProjection(tc.pN0, a0.xy, a1.xy, a2.xy); #else s.normSAO0.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN0, a0.xy, a1.xy, a2.xy); #endif s.normSAO0.zw = a0.zw * tc.pN0.x + a1.zw * tc.pN0.y + a2.zw * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[0], config.cluster1, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[1], config.cluster1, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[2], config.cluster1, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf1 = SurfgradFromTriplanarProjection(tc.pN1, a0.xy, a1.xy, a2.xy); #else s.normSAO1.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN1, a0.xy, a1.xy, a2.xy); #endif s.normSAO1.zw = a0.zw * tc.pN1.x + a1.zw * tc.pN1.y + a2.zw * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[0], config.cluster2, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[1], config.cluster2, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[2], config.cluster2, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf2 = SurfgradFromTriplanarProjection(tc.pN2, a0.xy, a1.xy, a2.xy); #else s.normSAO2.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN2, a0.xy, a1.xy, a2.xy); #endif s.normSAO2.zw = a0.zw * tc.pN2.x + a1.zw * tc.pN2.y + a2.zw * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[0], config.cluster3, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[1], config.cluster3, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[2], config.cluster3, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf3 = SurfgradFromTriplanarProjection(tc.pN3, a0.xy, a1.xy, a2.xy); #else s.normSAO3.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN3, a0.xy, a1.xy, a2.xy); #endif s.normSAO3.zw = a0.zw * tc.pN3.x + a1.zw * tc.pN3.y + a2.zw * tc.pN3.z; } #endif #else s.normSAO0 = MICROSPLAT_SAMPLE_NORMAL(config.uv0, config.cluster0, mipLevel).agrb; COUNTSAMPLE s.normSAO0.xy = s.normSAO0.xy * 2 - 1; #if _SURFACENORMALS s.surf0 = ConvertNormal2ToGradient(s.normSAO0.xy); #endif MSBRANCH(weights.y) { s.normSAO1 = MICROSPLAT_SAMPLE_NORMAL(config.uv1, config.cluster1, mipLevel).agrb; COUNTSAMPLE s.normSAO1.xy = s.normSAO1.xy * 2 - 1; #if _SURFACENORMALS s.surf1 = ConvertNormal2ToGradient(s.normSAO1.xy); #endif } #if !_MAX2LAYER MSBRANCH(weights.z) { s.normSAO2 = MICROSPLAT_SAMPLE_NORMAL(config.uv2, config.cluster2, mipLevel).agrb; COUNTSAMPLE s.normSAO2.xy = s.normSAO2.xy * 2 - 1; #if _SURFACENORMALS s.surf2 = ConvertNormal2ToGradient(s.normSAO2.xy); #endif } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.normSAO3 = MICROSPLAT_SAMPLE_NORMAL(config.uv3, config.cluster3, mipLevel).agrb; COUNTSAMPLE s.normSAO3.xy = s.normSAO3.xy * 2 - 1; #if _SURFACENORMALS s.surf3 = ConvertNormal2ToGradient(s.normSAO3.xy); #endif } #endif #endif } void SampleEmis(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USEEMISSIVEMETAL #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.emisMetal0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.emisMetal1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.emisMetal2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.emisMetal3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.emisMetal0 = MICROSPLAT_SAMPLE_EMIS(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.emisMetal1 = MICROSPLAT_SAMPLE_EMIS(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.emisMetal2 = MICROSPLAT_SAMPLE_EMIS(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.emisMetal3 = MICROSPLAT_SAMPLE_EMIS(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } void SampleSpecular(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USESPECULARWORKFLOW #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.specular0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.specular1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.specular2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.specular3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.specular0 = MICROSPLAT_SAMPLE_SPECULAR(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.specular1 = MICROSPLAT_SAMPLE_SPECULAR(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.specular2 = MICROSPLAT_SAMPLE_SPECULAR(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.specular3 = MICROSPLAT_SAMPLE_SPECULAR(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } MicroSplatLayer Sample(Input i, half4 weights, inout Config config, float camDist, float3 worldNormalVertex, DecalOutput decalOutput) { MicroSplatLayer o = (MicroSplatLayer)0; UNITY_INITIALIZE_OUTPUT(MicroSplatLayer,o); RawSamples samples = (RawSamples)0; InitRawSamples(samples); half4 albedo = 0; half4 normSAO = half4(0,0,0,1); half3 surfGrad = half3(0,0,0); half4 emisMetal = 0; half3 specular = 0; float worldHeight = i.worldPos.y; float3 upVector = float3(0,1,0); #if _GLOBALTINT || _GLOBALNORMALS || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _GLOBALSPECULAR float globalSlopeFilter = 1; #if _GLOBALSLOPEFILTER float2 gfilterUV = float2(1 - saturate(dot(worldNormalVertex, upVector) * 0.5 + 0.49), 0.5); globalSlopeFilter = SAMPLE_TEXTURE2D(_GlobalSlopeTex, sampler_Diffuse, gfilterUV).a; #endif #endif // declare outside of branchy areas.. half4 fxLevels = half4(0,0,0,0); half burnLevel = 0; half wetLevel = 0; half3 waterNormalFoam = half3(0, 0, 0); half porosity = 0.4; float streamFoam = 1.0f; half pud = 0; half snowCover = 0; half SSSThickness = 0; half3 SSSTint = half3(1,1,1); float traxBuffer = 0; float3 traxNormal = 0; float2 noiseUV = 0; #if _SPLATFADE MSBRANCHOTHER(1 - saturate(camDist - _SplatFade.y)) { #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE || _SNOWFOOTSTEPS traxBuffer = SampleTraxBuffer(i.worldPos, worldNormalVertex, traxNormal); #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA #if _MICROMESH fxLevels = SampleFXLevels(InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, config.uv), wetLevel, burnLevel, traxBuffer); #elif _MICROVERTEXMESH || _MICRODIGGERMESH || _MEGASPLAT fxLevels = ProcessFXLevels(i.fx, traxBuffer); #else fxLevels = SampleFXLevels(config.uv, wetLevel, burnLevel, traxBuffer); #endif #endif #if _DECAL fxLevels = max(fxLevels, decalOutput.fxLevels); #endif TriplanarConfig tc = (TriplanarConfig)0; UNITY_INITIALIZE_OUTPUT(TriplanarConfig,tc); MIPFORMAT albedoLOD = INITMIPFORMAT MIPFORMAT normalLOD = INITMIPFORMAT MIPFORMAT emisLOD = INITMIPFORMAT MIPFORMAT specLOD = INITMIPFORMAT MIPFORMAT origAlbedoLOD = INITMIPFORMAT; #if _TRIPLANAR && !_DISABLESPLATMAPS PrepTriplanar(i.shaderData.texcoord0, worldNormalVertex, i.worldPos, config, tc, weights, albedoLOD, normalLOD, emisLOD, origAlbedoLOD); tc.IN = i; #endif #if !_TRIPLANAR && !_DISABLESPLATMAPS #if _USELODMIP albedoLOD = ComputeMipLevel(config.uv0.xy, _Diffuse_TexelSize.zw); normalLOD = ComputeMipLevel(config.uv0.xy, _NormalSAO_TexelSize.zw); #if _USEEMISSIVEMETAL emisLOD = ComputeMipLevel(config.uv0.xy, _EmissiveMetal_TexelSize.zw); #endif #if _USESPECULARWORKFLOW specLOD = ComputeMipLevel(config.uv0.xy, _Specular_TexelSize.zw);; #endif #elif _USEGRADMIP albedoLOD = float4(ddx(config.uv0.xy), ddy(config.uv0.xy)); normalLOD = albedoLOD; #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #endif origAlbedoLOD = albedoLOD; #endif #if _PERTEXCURVEWEIGHT SAMPLE_PER_TEX(ptCurveWeight, 19.5, config, half4(0.5,1,1,1)); weights.x = lerp(smoothstep(0.5 - ptCurveWeight0.r, 0.5 + ptCurveWeight0.r, weights.x), weights.x, ptCurveWeight0.r*2); weights.y = lerp(smoothstep(0.5 - ptCurveWeight1.r, 0.5 + ptCurveWeight1.r, weights.y), weights.y, ptCurveWeight1.r*2); weights.z = lerp(smoothstep(0.5 - ptCurveWeight2.r, 0.5 + ptCurveWeight2.r, weights.z), weights.z, ptCurveWeight2.r*2); weights.w = lerp(smoothstep(0.5 - ptCurveWeight3.r, 0.5 + ptCurveWeight3.r, weights.w), weights.w, ptCurveWeight3.r*2); weights = TotalOne(weights); #endif // uvScale before anything #if _PERTEXUVSCALEOFFSET && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); config.uv0.xy = config.uv0.xy * ptUVScale0.rg + ptUVScale0.ba; config.uv1.xy = config.uv1.xy * ptUVScale1.rg + ptUVScale1.ba; #if !_MAX2LAYER config.uv2.xy = config.uv2.xy * ptUVScale2.rg + ptUVScale2.ba; #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = config.uv3.xy * ptUVScale3.rg + ptUVScale3.ba; #endif // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = albedoLOD * ptUVScale0.rgrg * weights.x + albedoLOD * ptUVScale1.rgrg * weights.y + albedoLOD * ptUVScale2.rgrg * weights.z + albedoLOD * ptUVScale3.rgrg * weights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #if _PERTEXUVROTATION && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVRot, 16.5, config, half4(0,0,0,0)); config.uv0.xy = RotateUV(config.uv0.xy, ptUVRot0.x); config.uv1.xy = RotateUV(config.uv1.xy, ptUVRot1.x); #if !_MAX2LAYER config.uv2.xy = RotateUV(config.uv2.xy, ptUVRot2.x); #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = RotateUV(config.uv3.xy, ptUVRot0.x); #endif #endif o.Alpha = 1; #if _POM && !_DISABLESPLATMAPS DoPOM(i, config, tc, albedoLOD, weights, camDist, worldNormalVertex); #endif SampleAlbedo(config, tc, samples, albedoLOD, weights); #if _NOISEHEIGHT ApplyNoiseHeight(samples, config.uv, config, i.worldPos, worldNormalVertex); #endif #if _STREAMS || (_PARALLAX && !_DISABLESPLATMAPS) half earlyHeight = BlendWeights(samples.albedo0.w, samples.albedo1.w, samples.albedo2.w, samples.albedo3.w, weights); #endif #if _STREAMS waterNormalFoam = GetWaterNormal(i, config.uv, worldNormalVertex); DoStreamRefract(config, tc, waterNormalFoam, fxLevels.b, earlyHeight); #endif #if _PARALLAX && !_DISABLESPLATMAPS DoParallax(i, earlyHeight, config, tc, samples, weights, camDist); #endif // Blend results #if _PERTEXINTERPCONTRAST && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptContrasts, 1.5, config, 0.5); half4 contrast = 0.5; contrast.x = ptContrasts0.a; contrast.y = ptContrasts1.a; #if !_MAX2LAYER contrast.z = ptContrasts2.a; #endif #if !_MAX3LAYER || !_MAX2LAYER contrast.w = ptContrasts3.a; #endif contrast = clamp(contrast + _Contrast, 0.0001, 1.0); half cnt = contrast.x * weights.x + contrast.y * weights.y + contrast.z * weights.z + contrast.w * weights.w; half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, cnt); #else half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, _Contrast); #endif #if _HYBRIDHEIGHTBLEND heightWeights = lerp(heightWeights, TotalOne(weights), saturate(camDist/max(1.0, _HybridHeightBlendDistance))); #endif // rescale derivatives after height weighting. Basically, in gradmip mode we blend the mip levels, // but this is before height mapping is sampled, so reblending them after alpha will make sure the other // channels (normal, etc) are sharper, which likely matters most.. #if _PERTEXUVSCALEOFFSET && !_DISABLESPLATMAPS #if _TRIPLANAR #if _USEGRADMIP SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); albedoLOD.d0 = origAlbedoLOD.d0 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d0 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d0 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d0 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d1 = origAlbedoLOD.d1 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d1 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d1 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d1 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d2 = origAlbedoLOD.d2 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d2 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d2 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d2 * ptUVScale3.xyxy * heightWeights.w; normalLOD.d0 = albedoLOD.d0; normalLOD.d1 = albedoLOD.d1; normalLOD.d2 = albedoLOD.d2; #if _USEEMISSIVEMETAL emisLOD.d0 = albedoLOD.d0; emisLOD.d1 = albedoLOD.d1; emisLOD.d2 = albedoLOD.d2; #endif #endif // gradmip #else // not triplanar // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = origAlbedoLOD * ptUVScale0.rgrg * heightWeights.x + origAlbedoLOD * ptUVScale1.rgrg * heightWeights.y + origAlbedoLOD * ptUVScale2.rgrg * heightWeights.z + origAlbedoLOD * ptUVScale3.rgrg * heightWeights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #endif #if _PARALLAX || _STREAMS SampleAlbedo(config, tc, samples, albedoLOD, heightWeights); #endif SampleNormal(config, tc, samples, normalLOD, heightWeights); #if _USEEMISSIVEMETAL SampleEmis(config, tc, samples, emisLOD, heightWeights); #endif #if _USESPECULARWORKFLOW SampleSpecular(config, tc, samples, specLOD, heightWeights); #endif #if _DISTANCERESAMPLE && !_DISABLESPLATMAPS DistanceResample(samples, config, tc, camDist, i.viewDir, fxLevels, albedoLOD, i.worldPos, heightWeights, worldNormalVertex); #endif #if _STARREACHFORMAT samples.normSAO0.w = length(samples.normSAO0.xy); samples.normSAO1.w = length(samples.normSAO1.xy); samples.normSAO2.w = length(samples.normSAO2.xy); samples.normSAO3.w = length(samples.normSAO3.xy); #endif // PerTexture sampling goes here, passing the samples structure #if _PERTEXMICROSHADOWS || _PERTEXFUZZYSHADE SAMPLE_PER_TEX(ptFuzz, 17.5, config, half4(0, 0, 1, 1)); #endif #if _PERTEXMICROSHADOWS #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) { half3 lightDir = GetGlobalLightDirTS(i); half4 microShadows = half4(1,1,1,1); microShadows.x = MicroShadow(lightDir, half3(samples.normSAO0.xy, 1), samples.normSAO0.a, ptFuzz0.a); microShadows.y = MicroShadow(lightDir, half3(samples.normSAO1.xy, 1), samples.normSAO1.a, ptFuzz1.a); microShadows.z = MicroShadow(lightDir, half3(samples.normSAO2.xy, 1), samples.normSAO2.a, ptFuzz2.a); microShadows.w = MicroShadow(lightDir, half3(samples.normSAO3.xy, 1), samples.normSAO3.a, ptFuzz3.a); samples.normSAO0.a *= microShadows.x; samples.normSAO1.a *= microShadows.y; #if !_MAX2LAYER samples.normSAO2.a *= microShadows.z; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a *= microShadows.w; #endif #if _DEBUG_OUTPUT_MICROSHADOWS o.Albedo = BlendWeights(microShadows.x, microShadows.y, microShadows.z, microShadows.a, heightWeights); return o; #endif } #endif #endif // _PERTEXMICROSHADOWS #if _PERTEXFUZZYSHADE samples.albedo0.rgb = FuzzyShade(samples.albedo0.rgb, half3(samples.normSAO0.rg, 1), ptFuzz0.r, ptFuzz0.g, ptFuzz0.b, i.viewDir); samples.albedo1.rgb = FuzzyShade(samples.albedo1.rgb, half3(samples.normSAO1.rg, 1), ptFuzz1.r, ptFuzz1.g, ptFuzz1.b, i.viewDir); #if !_MAX2LAYER samples.albedo2.rgb = FuzzyShade(samples.albedo2.rgb, half3(samples.normSAO2.rg, 1), ptFuzz2.r, ptFuzz2.g, ptFuzz2.b, i.viewDir); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = FuzzyShade(samples.albedo3.rgb, half3(samples.normSAO3.rg, 1), ptFuzz3.r, ptFuzz3.g, ptFuzz3.b, i.viewDir); #endif #endif #if _PERTEXSATURATION && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptSaturattion, 9.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = lerp(MSLuminance(samples.albedo0.rgb), samples.albedo0.rgb, ptSaturattion0.a); samples.albedo1.rgb = lerp(MSLuminance(samples.albedo1.rgb), samples.albedo1.rgb, ptSaturattion1.a); #if !_MAX2LAYER samples.albedo2.rgb = lerp(MSLuminance(samples.albedo2.rgb), samples.albedo2.rgb, ptSaturattion2.a); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = lerp(MSLuminance(samples.albedo3.rgb), samples.albedo3.rgb, ptSaturattion3.a); #endif #endif #if _PERTEXTINT && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptTints, 1.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb *= ptTints0.rgb; samples.albedo1.rgb *= ptTints1.rgb; #if !_MAX2LAYER samples.albedo2.rgb *= ptTints2.rgb; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb *= ptTints3.rgb; #endif #endif #if _PCHEIGHTGRADIENT || _PCHEIGHTHSV || _PCSLOPEGRADIENT || _PCSLOPEHSV ProceduralGradients(i, samples, config, worldHeight, worldNormalVertex); #endif #if _WETNESS || _PUDDLES || _STREAMS porosity = _GlobalPorosity; #endif #if _PERTEXCOLORINTENSITY SAMPLE_PER_TEX(ptCI, 23.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = saturate(samples.albedo0.rgb * (1 + ptCI0.rrr)); samples.albedo1.rgb = saturate(samples.albedo1.rgb * (1 + ptCI1.rrr)); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb * (1 + ptCI2.rrr)); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb * (1 + ptCI3.rrr)); #endif #endif #if (_PERTEXBRIGHTNESS || _PERTEXCONTRAST || _PERTEXPOROSITY || _PERTEXFOAM) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptBC, 3.5, config, half4(1, 1, 1, 1)); #if _PERTEXCONTRAST samples.albedo0.rgb = saturate(((samples.albedo0.rgb - 0.5) * ptBC0.g) + 0.5); samples.albedo1.rgb = saturate(((samples.albedo1.rgb - 0.5) * ptBC1.g) + 0.5); #if !_MAX2LAYER samples.albedo2.rgb = saturate(((samples.albedo2.rgb - 0.5) * ptBC2.g) + 0.5); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(((samples.albedo3.rgb - 0.5) * ptBC3.g) + 0.5); #endif #endif #if _PERTEXBRIGHTNESS samples.albedo0.rgb = saturate(samples.albedo0.rgb + ptBC0.rrr); samples.albedo1.rgb = saturate(samples.albedo1.rgb + ptBC1.rrr); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb + ptBC2.rrr); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb + ptBC3.rrr); #endif #endif #if _PERTEXPOROSITY porosity = BlendWeights(ptBC0.b, ptBC1.b, ptBC2.b, ptBC3.b, heightWeights); #endif #if _PERTEXFOAM streamFoam = BlendWeights(ptBC0.a, ptBC1.a, ptBC2.a, ptBC3.a, heightWeights); #endif #endif #if (_PERTEXNORMSTR || _PERTEXAOSTR || _PERTEXSMOOTHSTR || _PERTEXMETALLIC) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(perTexMatSettings, 2.5, config, half4(1.0, 1.0, 1.0, 0.0)); #endif #if _PERTEXNORMSTR && !_DISABLESPLATMAPS #if _SURFACENORMALS samples.surf0 *= perTexMatSettings0.r; samples.surf1 *= perTexMatSettings1.r; samples.surf2 *= perTexMatSettings2.r; samples.surf3 *= perTexMatSettings3.r; #else samples.normSAO0.xy *= perTexMatSettings0.r; samples.normSAO1.xy *= perTexMatSettings1.r; samples.normSAO2.xy *= perTexMatSettings2.r; samples.normSAO3.xy *= perTexMatSettings3.r; #endif #endif #if _PERTEXAOSTR && !_DISABLESPLATMAPS samples.normSAO0.a = pow(abs(samples.normSAO0.a), perTexMatSettings0.b); samples.normSAO1.a = pow(abs(samples.normSAO1.a), perTexMatSettings1.b); #if !_MAX2LAYER samples.normSAO2.a = pow(abs(samples.normSAO2.a), perTexMatSettings2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a = pow(abs(samples.normSAO3.a), perTexMatSettings3.b); #endif #endif #if _PERTEXSMOOTHSTR && !_DISABLESPLATMAPS samples.normSAO0.b += perTexMatSettings0.g; samples.normSAO1.b += perTexMatSettings1.g; samples.normSAO0.b = saturate(samples.normSAO0.b); samples.normSAO1.b = saturate(samples.normSAO1.b); #if !_MAX2LAYER samples.normSAO2.b += perTexMatSettings2.g; samples.normSAO2.b = saturate(samples.normSAO2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.b += perTexMatSettings3.g; samples.normSAO3.b = saturate(samples.normSAO3.b); #endif #endif #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) #if _PERTEXSSS { SAMPLE_PER_TEX(ptSSS, 18.5, config, half4(1, 1, 1, 1)); // tint, thickness half4 vals = ptSSS0 * heightWeights.x + ptSSS1 * heightWeights.y + ptSSS2 * heightWeights.z + ptSSS3 * heightWeights.w; SSSThickness = vals.a; SSSTint = vals.rgb; } #endif #endif #if _PERTEXRIMLIGHT { SAMPLE_PER_TEX(ptRimA, 26.5, config, half4(1, 1, 1, 1)); SAMPLE_PER_TEX(ptRimB, 27.5, config, half4(1, 1, 1, 0)); samples.emisMetal0.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO0.xy, 1))), max(0.0001, ptRimA0.g)) * ptRimB0.rgb * ptRimB0.a; samples.emisMetal1.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO1.xy, 1))), max(0.0001, ptRimA1.g)) * ptRimB1.rgb * ptRimB1.a; samples.emisMetal2.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO2.xy, 1))), max(0.0001, ptRimA2.g)) * ptRimB2.rgb * ptRimB2.a; samples.emisMetal3.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO3.xy, 1))), max(0.0001, ptRimA3.g)) * ptRimB3.rgb * ptRimB3.a; } #endif #if (((_DETAILNOISE && _PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && _PERTEXDISTANCENOISESTRENGTH)) || (_NORMALNOISE && _PERTEXNORMALNOISESTRENGTH)) && !_DISABLESPLATMAPS ApplyDetailDistanceNoisePerTex(samples, config, camDist, i.worldPos, worldNormalVertex); #endif #if _GLOBALNOISEUV // noise defaults so that a value of 1, 1 is 4 pixels in size and moves the uvs by 1 pixel max. #if _CUSTOMSPLATTEXTURES noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #else noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE ApplyTrax(samples, config, i.worldPos, traxBuffer, traxNormal); #endif #if (_ANTITILEARRAYDETAIL || _ANTITILEARRAYDISTANCE || _ANTITILEARRAYNORMAL) && !_DISABLESPLATMAPS ApplyAntiTilePerTex(samples, config, camDist, i.worldPos, worldNormalVertex, heightWeights); #endif #if _GEOMAP && !_DISABLESPLATMAPS GeoTexturePerTex(samples, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _GLOBALTINT && _PERTEXGLOBALTINTSTRENGTH && !_DISABLESPLATMAPS GlobalTintTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALNORMALS && _PERTEXGLOBALNORMALSTRENGTH && !_DISABLESPLATMAPS GlobalNormalTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && _PERTEXGLOBALSAOMSTRENGTH && !_DISABLESPLATMAPS GlobalSAOMTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && _PERTEXGLOBALEMISSTRENGTH && !_DISABLESPLATMAPS GlobalEmisTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && _PERTEXGLOBALSPECULARSTRENGTH && !_DISABLESPLATMAPS && _USESPECULARWORKFLOW GlobalSpecularTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _PERTEXMETALLIC && !_DISABLESPLATMAPS half metallic = BlendWeights(perTexMatSettings0.a, perTexMatSettings1.a, perTexMatSettings2.a, perTexMatSettings3.a, heightWeights); o.Metallic = metallic; #endif #if _GLITTER && !_DISABLESPLATMAPS DoGlitter(i, samples, config, camDist, worldNormalVertex, i.worldPos); #endif // Blend em.. #if _DISABLESPLATMAPS // If we don't sample from the _Diffuse, then the shader compiler will strip the sampler on // some platforms, which will cause everything to break. So we sample from the lowest mip // and saturate to 1 to keep the cost minimal. Annoying, but the compiler removes the texture // and sampler, even though the sampler is still used. albedo = saturate(UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, float3(0,0,0), 12) + 1); albedo.a = 0.5; // make height something we can blend with for the combined mesh mode, since it still height blends. normSAO = half4(0,0,0,1); #else albedo = BlendWeights(samples.albedo0, samples.albedo1, samples.albedo2, samples.albedo3, heightWeights); normSAO = BlendWeights(samples.normSAO0, samples.normSAO1, samples.normSAO2, samples.normSAO3, heightWeights); #if _SURFACENORMALS surfGrad = BlendWeights(samples.surf0, samples.surf1, samples.surf2, samples.surf3, heightWeights); #endif #if (_USEEMISSIVEMETAL || _PERTEXRIMLIGHT) && !_DISABLESPLATMAPS emisMetal = BlendWeights(samples.emisMetal0, samples.emisMetal1, samples.emisMetal2, samples.emisMetal3, heightWeights); #endif #if _USESPECULARWORKFLOW && !_DISABLESPLATMAPS specular = BlendWeights(samples.specular0, samples.specular1, samples.specular2, samples.specular3, heightWeights); #endif #if _PERTEXOUTLINECOLOR SAMPLE_PER_TEX(ptOutlineColor, 28.5, config, half4(0.5, 0.5, 0.5, 1)); half4 outlineColor = BlendWeights(ptOutlineColor0, ptOutlineColor1, ptOutlineColor2, ptOutlineColor3, heightWeights); half4 tstr = saturate(abs(heightWeights - 0.5) * 2); half transitionBlend = min(min(min(tstr.x, tstr.y), tstr.z), tstr.w); albedo.rgb = lerp(albedo.rgb * outlineColor.rgb * 2, albedo.rgb, outlineColor.a * transitionBlend); #endif #endif #if _MESHOVERLAYSPLATS || _MESHCOMBINED o.Alpha = 1.0; if (config.uv0.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.x; else if (config.uv1.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.y; else if (config.uv2.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.z; else if (config.uv3.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.w; #endif // effects which don't require per texture adjustments and are part of the splats sample go here. // Often, as an optimization, you can compute the non-per tex version of above effects here.. #if ((_DETAILNOISE && !_PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && !_PERTEXDISTANCENOISESTRENGTH) || (_NORMALNOISE && !_PERTEXNORMALNOISESTRENGTH)) ApplyDetailDistanceNoise(albedo.rgb, normSAO, surfGrad, config, camDist, i.worldPos, worldNormalVertex); #endif #if _SPLATFADE } #endif #if _SPLATFADE float2 sfDX = ddx(config.uv * _UVScale); float2 sfDY = ddy(config.uv * _UVScale); MSBRANCHOTHER(camDist - _SplatFade.x) { float falloff = saturate(InverseLerp(_SplatFade.x, _SplatFade.y, camDist)); half4 sfalb = SAMPLE_TEXTURE2D_ARRAY_GRAD(_Diffuse, sampler_Diffuse, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY); COUNTSAMPLE albedo.rgb = lerp(albedo.rgb, sfalb.rgb, falloff); #if !_NONORMALMAP && !_AUTONORMAL half4 sfnormSAO = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalSAO, sampler_NormalSAO, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY).agrb; COUNTSAMPLE sfnormSAO.xy = sfnormSAO.xy * 2 - 1; normSAO = lerp(normSAO, sfnormSAO, falloff); #if _SURFACENORMALS surfGrad = lerp(surfGrad, ConvertNormal2ToGradient(sfnormSAO.xy), falloff); #endif #endif } #endif #if _AUTONORMAL float3 autoNormal = HeightToNormal(albedo.a * _AutoNormalHeightScale, i.worldPos); normSAO.xy = autoNormal; normSAO.z = 0; normSAO.w = (autoNormal.z * autoNormal.z); #endif #if _MESHCOMBINED SampleMeshCombined(albedo, normSAO, surfGrad, emisMetal, specular, o.Alpha, SSSThickness, SSSTint, config, heightWeights); #endif #if _ISOBJECTSHADER SampleObjectShader(i, albedo, normSAO, surfGrad, emisMetal, specular, config); #endif #if _GEOMAP GeoTexture(albedo.rgb, normSAO, surfGrad, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _SCATTER ApplyScatter( #if _MEGASPLAT config, #endif i, albedo, normSAO, surfGrad, config.uv, camDist); #endif #if _DECAL DoDecalBlend(decalOutput, albedo, normSAO, surfGrad, emisMetal, i.uv_Control0); #endif #if _GLOBALTINT && !_PERTEXGLOBALTINTSTRENGTH GlobalTintTexture(albedo.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif #if _VSGRASSMAP VSGrassTexture(albedo.rgb, config, camDist); #endif #if _GLOBALNORMALS && !_PERTEXGLOBALNORMALSTRENGTH GlobalNormalTexture(normSAO, surfGrad, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && !_PERTEXGLOBALSAOMSTRENGTH GlobalSAOMTexture(normSAO, emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && !_PERTEXGLOBALEMISSTRENGTH GlobalEmisTexture(emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && !_PERTEXGLOBALSPECULARSTRENGTH && _USESPECULARWORKFLOW GlobalSpecularTexture(specular.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif o.Albedo = albedo.rgb; o.Height = albedo.a; #if _NONORMALMAP o.Normal = half3(0,0,1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #elif _SURFACENORMALS o.Normal = ResolveNormalFromSurfaceGradient(surfGrad); o.Normal = mul(GetTBN(i), o.Normal); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #else o.Normal = half3(normSAO.xy, 1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #endif #if _USEEMISSIVEMETAL || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _PERTEXRIMLIGHT #if _USEEMISSIVEMETAL emisMetal.rgb *= _EmissiveMult; #endif o.Emission += emisMetal.rgb; o.Metallic = emisMetal.a; #endif #if _USESPECULARWORKFLOW o.Specular = specular; #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA pud = DoStreams(i, o, fxLevels, config.uv, porosity, waterNormalFoam, worldNormalVertex, streamFoam, wetLevel, burnLevel, i.worldPos); #endif #if _SNOW snowCover = DoSnow(i, o, config.uv, WorldNormalVector(i, o.Normal), worldNormalVertex, i.worldPos, pud, porosity, camDist, config, weights, SSSTint, SSSThickness, traxBuffer, traxNormal); #endif #if _PERTEXSSS || _MESHCOMBINEDUSESSS || (_SNOW && _SNOWSSS) { half3 worldView = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); o.Emission += ComputeSSS(i, worldView, WorldNormalVector(i, o.Normal), SSSTint, SSSThickness, _SSSDistance, _SSSScale, _SSSPower); } #endif #if _SNOWGLITTER DoSnowGlitter(i, config, o, camDist, worldNormalVertex, snowCover); #endif #if _WINDPARTICULATE || _SNOWPARTICULATE DoWindParticulate(i, o, config, weights, camDist, worldNormalVertex, snowCover); #endif o.Normal.z = sqrt(1 - saturate(dot(o.Normal.xy, o.Normal.xy))); #if _SPECULARFADE { float specFade = saturate((i.worldPos.y - _SpecularFades.x) / max(_SpecularFades.y - _SpecularFades.x, 0.0001)); o.Metallic *= specFade; o.Smoothness *= specFade; } #endif #if _VSSHADOWMAP VSShadowTexture(o, i, config, camDist); #endif #if _TOONWIREFRAME ToonWireframe(config.uv, o.Albedo, camDist); #endif #if _SEETHROUGHSHADER SeethroughShader(o.Albedo, o.Emission, o.Alpha, i.worldPos, o.Normal, i.worldNormal); #endif #if _DEBUG_TRAXBUFFER ClearAllButAlbedo(o, half3(traxBuffer, 0, 0) * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMALVERTEX ClearAllButAlbedo(o, worldNormalVertex * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMAL ClearAllButAlbedo(o, WorldNormalVector(i, o.Normal) * saturate(o.Albedo.z+1)); #endif #if _DEBUG_MEGABARY && _MEGASPLAT o.Albedo = i.baryWeights.xyz; #endif return o; } void SampleSplats(float2 controlUV, inout half4 w0, inout half4 w1, inout half4 w2, inout half4 w3, inout half4 w4, inout half4 w5, inout half4 w6, inout half4 w7) { #if _CUSTOMSPLATTEXTURES #if !_MICROMESH controlUV = (controlUV * (_CustomControl0_TexelSize.zw - 1.0f) + 0.5f) * _CustomControl0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_CustomControl0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_CustomControl1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_CustomControl2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_CustomControl3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_CustomControl4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_CustomControl5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_CustomControl6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_CustomControl7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #else #if !_MICROMESH controlUV = (controlUV * (_Control0_TexelSize.zw - 1.0f) + 0.5f) * _Control0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_Control0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_Control1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_Control2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_Control3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_Control4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_Control5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_Control6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_Control7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #endif } MicroSplatLayer SurfImpl(Input i, float3 worldNormalVertex) { #if _MEGANOUV i.uv_Control0 = i.worldPos.xz; #endif float camDist = distance(_WorldSpaceCameraPos, i.worldPos); #if _FORCELOCALSPACE worldNormalVertex = mul((float3x3)GetWorldToObjectMatrix(), worldNormalVertex).xyz; i.worldPos = i.worldPos - mul(GetObjectToWorldMatrix(), float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _ORIGINSHIFT i.worldPos = i.worldPos + mul(_GlobalOriginMTX, float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _DEBUG_USE_TOPOLOGY i.worldPos = SAMPLE_TEXTURE2D(_DebugWorldPos, sampler_Diffuse, i.uv_Control0); worldNormalVertex = SAMPLE_TEXTURE2D(_DebugWorldNormal, sampler_Diffuse, i.uv_Control0); i.worldHeight = i.worldPos.y; #endif #if _ALPHABELOWHEIGHT && !_TBDISABLEALPHAHOLES ClipWaterLevel(i.worldPos); #endif #if !_TBDISABLEALPHAHOLES && defined(_ALPHATEST_ON) // UNITY 2019.3 holes ClipHoles(i.uv_Control0); #endif float2 origUV = i.uv_Control0; #if _MICROMESH && _MESHUV2 float2 controlUV = i.uv2_Diffuse; #else float2 controlUV = i.uv_Control0; #endif #if _MICROMESH controlUV = InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, controlUV); #endif half4 weights = half4(1,0,0,0); Config config = (Config)0; UNITY_INITIALIZE_OUTPUT(Config,config); config.uv = origUV; DecalOutput decalOutput = (DecalOutput)0; #if _DECAL decalOutput = DoDecals(i.uv_Control0, i.worldPos, camDist, worldNormalVertex); #endif #if _SURFACENORMALS // Initialize the surface gradient basis vectors ConstructSurfaceGradientTBN(i); #endif #if _SPLATFADE MSBRANCHOTHER(_SplatFade.y - camDist) #endif // _SPLATFADE { #if !_DISABLESPLATMAPS // Sample the splat data, from textures or vertices, and setup the config.. #if _MICRODIGGERMESH DiggerSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLAT MegaSplatVertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLATTEXTURE MegaSplatTextureSetup(controlUV, weights, origUV, config, i.worldPos, decalOutput); #elif _MICROVERTEXMESH VertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif !_PROCEDURALTEXTURE || _PROCEDURALBLENDSPLATS half4 w0 = 0; half4 w1 = 0; half4 w2 = 0; half4 w3 = 0; half4 w4 = 0; half4 w5 = 0; half4 w6 = 0; half4 w7 = 0; SampleSplats(controlUV, w0, w1, w2, w3, w4, w5, w6, w7); Setup(weights, origUV, config, w0, w1, w2, w3, w4, w5, w6, w7, i.worldPos, decalOutput); #endif #if _PROCEDURALTEXTURE float3 procNormal = worldNormalVertex; float3 worldPos = i.worldPos; ProceduralSetup(i, worldPos, i.worldHeight, procNormal, i.worldUpVector, weights, origUV, config, ddx(origUV), ddy(origUV), ddx(worldPos), ddy(worldPos), decalOutput); #endif #else // _DISABLESPLATMAPS Setup(weights, origUV, config, half4(1,0,0,0), 0, 0, 0, 0, 0, 0, 0, i.worldPos, decalOutput); #endif #if _SLOPETEXTURE SlopeTexture(config, weights, worldNormalVertex); #endif } // _SPLATFADE else case #if _TOONFLATTEXTURE float2 quv = floor(origUV * _ToonTerrainSize); float2 fuv = frac(origUV * _ToonTerrainSize); #if !_TOONFLATTEXTUREQUAD quv = Hash2D((fuv.x > fuv.y) ? quv : quv * 0.333); #endif float2 uvq = quv / _ToonTerrainSize; config.uv0.xy = uvq; config.uv1.xy = uvq; config.uv2.xy = uvq; config.uv3.xy = uvq; #endif #if (_TEXTURECLUSTER2 || _TEXTURECLUSTER3) && !_DISABLESPLATMAPS PrepClusters(origUV, config, i.worldPos, worldNormalVertex); #endif #if (_ALPHAHOLE || _ALPHAHOLETEXTURE) && !_DISABLESPLATMAPS && !_TBDISABLEALPHAHOLES ClipAlphaHole(config, weights); #endif MicroSplatLayer l = Sample(i, weights, config, camDist, worldNormalVertex, decalOutput); // On windows, sometimes the shared samplers gets stripped, so we have to do this crap. // We sample from the lowest mip, so it shouldn't cost much, but still, I hate this, wtf.. float stripVal = saturate(SAMPLE_TEXTURE2D_LOD(_Diffuse, sampler_Diffuse, config.uv0, 11).r + 2); stripVal *= saturate(SAMPLE_TEXTURE2D_LOD(_NormalSAO, sampler_NormalSAO, config.uv0, 11).r + 2); l.Albedo *= stripVal; l.Normal *= stripVal; #if _PROCEDURALTEXTURE ProceduralTextureDebugOutput(l, weights, config); #endif return l; } float4 ConstructTerrainTangent(float3 normal, float3 positiveZ) { // Consider a flat terrain. It should have tangent be (1, 0, 0) and bitangent be (0, 0, 1) as the UV of the terrain grid mesh is a scale of the world XZ position. // In CreateTangentToWorld function (in SpaceTransform.hlsl), it is cross(normal, tangent) * sgn for the bitangent vector. // It is not true in a left-handed coordinate system for the terrain bitangent, if we provide 1 as the tangent.w. It would produce (0, 0, -1) instead of (0, 0, 1). // Also terrain's tangent calculation was wrong in a left handed system because cross((0,0,1), terrainNormalOS) points to the wrong direction as negative X. // Therefore all the 4 xyzw components of the tangent needs to be flipped to correct the tangent frame. // (See TerrainLitData.hlsl - GetSurfaceAndBuiltinData) float3 tangent = normalize(cross(normal, positiveZ)); return float4(tangent, -1); } void TerrainInstancing(inout float4 vertex, inout float3 normal, inout float2 uv) { #if _MICROTERRAIN && defined(UNITY_INSTANCING_ENABLED) && !_TERRAINBLENDABLESHADER float2 patchVertex = vertex.xy; float4 instanceData = UNITY_ACCESS_INSTANCED_PROP(Terrain, _TerrainPatchInstanceData); float2 sampleCoords = (patchVertex.xy + instanceData.xy) * instanceData.z; // (xy + float2(xBase,yBase)) * skipScale uv = sampleCoords * _TerrainHeightmapRecipSize.zw; float2 sampleUV = (uv / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleUV, 0)); vertex.xz = sampleCoords * _TerrainHeightmapScale.xz; vertex.y = height * _TerrainHeightmapScale.y; normal = float3(0, 1, 0); #endif } void ApplyMeshModification(inout VertexData input) { #if _MICROTERRAIN && !_TERRAINBLENDABLESHADER float2 uv = input.texcoord0.xy; TerrainInstancing(input.vertex, input.normal, uv); input.texcoord0.xy = uv; #endif #if _PERPIXNORMAL && !_TERRAINBLENDABLESHADER input.normal = float3(0,1,0); #endif #if _MICROVERSEPREVIEW float4 recipSize = _TerrainHeightmapTexture_TexelSize; recipSize.zw = (1.0f / (_TerrainHeightmapTexture_TexelSize.zw-1)); float2 sampleCoords = (input.texcoord0.xy / recipSize.zw + 0.5f) * recipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleCoords, 0)); input.vertex.xyz += float3(0,1,0) * height * _TerrainHeight * 2; #endif } // called by the template, so we can remove tangent from VertexData void ApplyTerrainTangent(inout VertexToPixel input) { #if (_MICROTERRAIN || _PERPIXNORMAL) && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif // digger meshes ain't got no tangent either.. #if _MICRODIGGERMESH && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif } void ModifyVertex(inout VertexData v, inout ExtraV2F d) { ApplyMeshModification(v); #if _MICROVERTEXMESH || _MICRODIGGERMESH EncodeVertexWorkflow(v, d); #elif _MEGASPLAT EncodeMegaSplatVertex(v, d); #endif } void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d) { #if _MICROVERSEPREVIEW v.vertex.y = OffsetVertex(v, d).y; #elif _TESSDISTANCE || _TESSEDGE v.vertex.xyz += OffsetVertex(v, d); #endif } float3 GetTessFactors () { #if _TESSEDGE return float3(_TessData1.x, _TessData1.w, 0); #endif #if _TESSDISTANCE return float3(_TessData2.x, _TessData2.y, _TessData1.x); #endif return 0; } void SurfaceFunction(inout Surface o, inout ShaderData d) { float3 worldNormalVertex = d.worldSpaceNormal; #if _MICROVERSEPREVIEW float2 sampleCoords = d.texcoord0.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif (defined(UNITY_INSTANCING_ENABLED) && _MICROTERRAIN && !_TERRAINBLENDABLESHADER) float2 sampleCoords = (d.texcoord0.xy / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif _PERPIXNORMAL && (_MICROTERRAIN || _MICROMESHTERRAIN) && !_TERRAINBLENDABLESHADER float2 sampleCoords = (d.texcoord0.xy * _PerPixelNormal_TexelSize.zw + 0.5f) * _PerPixelNormal_TexelSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_PerPixelNormal, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomTangent, geomNormal)) * -1; #if _MICROMESHTERRAIN geomBitangent *= -1; #endif worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #endif #if _TOONPOLYEDGE FlatShade(d); #endif Input i = DescToInput(d); #if _SRPTERRAINBLEND MicroSplatLayer l = BlendWithTerrain(d); #if _DEBUG_WORLDNORMAL ClearAllButAlbedo(l, normalize(TangentToWorldSpace(d, l.Normal)) * saturate(l.Albedo.z+1)); #endif #else MicroSplatLayer l = SurfImpl(i, worldNormalVertex); #endif DoDebugOutput(l); o.Albedo = l.Albedo; o.Normal = l.Normal; o.Smoothness = l.Smoothness; o.Occlusion = l.Occlusion; o.Metallic = l.Metallic; o.Emission = l.Emission; #if _USESPECULARWORKFLOW o.Specular = l.Specular; #endif o.Height = l.Height; o.Alpha = l.Alpha; } // SHADERDESC ShaderData CreateShaderData(VertexToPixel i) { ShaderData d = (ShaderData)0; d.worldSpacePosition = i.worldPos; d.worldSpaceNormal = i.worldNormal; d.worldSpaceTangent = i.worldTangent.xyz; float3 bitangent = cross(i.worldTangent.xyz, i.worldNormal) * i.worldTangent.w * -1; d.TBNMatrix = float3x3(d.worldSpaceTangent, bitangent, d.worldSpaceNormal); d.worldSpaceViewDir = normalize(_WorldSpaceCameraPos - i.worldPos); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); d.texcoord0 = i.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER d.texcoord1 = i.texcoord1; // d.texcoord2 = i.texcoord2; #endif // d.texcoord3 = i.texcoord3; // d.vertexColor = i.vertexColor; // these rarely get used, so we back transform them. Usually will be stripped. #if _HDRP // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(GetCameraRelativePositionWS(i.worldPos), 1)); #else // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(i.worldPos, 1)); #endif // d.localSpaceNormal = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldNormal)); // d.localSpaceTangent = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldTangent.xyz)); // d.screenPos = i.screenPos; // d.screenUV = i.screenPos.xy / i.screenPos.w; // d.extraV2F0 = i.extraV2F0; // d.extraV2F1 = i.extraV2F1; // d.extraV2F2 = i.extraV2F2; // d.extraV2F3 = i.extraV2F3; // d.extraV2F4 = i.extraV2F4; // d.extraV2F5 = i.extraV2F5; // d.extraV2F6 = i.extraV2F6; // d.extraV2F7 = i.extraV2F7; return d; } // CHAINS void ChainModifyVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; ModifyVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainModifyTessellatedVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; // d.extraV2F0 = v2p.extraV2F0; // d.extraV2F1 = v2p.extraV2F1; // d.extraV2F2 = v2p.extraV2F2; // d.extraV2F3 = v2p.extraV2F3; // d.extraV2F4 = v2p.extraV2F4; // d.extraV2F5 = v2p.extraV2F5; // d.extraV2F6 = v2p.extraV2F6; // d.extraV2F7 = v2p.extraV2F7; ModifyTessellatedVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainFinalColorForward(inout Surface l, inout ShaderData d, inout half4 color) { } void ChainFinalGBufferStandard(inout Surface s, inout ShaderData d, inout half4 GBuffer0, inout half4 GBuffer1, inout half4 GBuffer2, inout half4 outEmission, inout half4 outShadowMask) { } #if _PASSSHADOW float3 _LightDirection; float3 _LightPosition; #endif // vertex shader VertexToPixel Vert (VertexData v) { VertexToPixel o = (VertexToPixel)0; UNITY_SETUP_INSTANCE_ID(v); UNITY_TRANSFER_INSTANCE_ID(v, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #if !_TESSELLATION_ON ChainModifyVertex(v, o); #endif o.texcoord0 = v.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.texcoord1 = v.texcoord1; // o.texcoord2 = v.texcoord2; #endif // o.texcoord3 = v.texcoord3; // o.vertexColor = v.vertexColor; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz); o.worldPos = TransformObjectToWorld(v.vertex.xyz); o.worldNormal = TransformObjectToWorldNormal(v.normal); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float2 uv1 = v.texcoord1.xy; float2 uv2 = v.texcoord2.xy; o.worldTangent = float4(TransformObjectToWorldDir(v.tangent.xyz), v.tangent.w); #else float2 uv1 = v.texcoord0.xy; float2 uv2 = uv1; #endif // MS Only ApplyTerrainTangent(o); #if _PASSSHADOW #if _CASTING_PUNCTUAL_LIGHT_SHADOW float3 lightDirectionWS = normalize(_LightPosition - o.worldPos); #else float3 lightDirectionWS = _LightDirection; #endif // Define shadow pass specific clip position for Universal o.pos = TransformWorldToHClip(ApplyShadowBias(o.worldPos, o.worldNormal, lightDirectionWS)); #if UNITY_REVERSED_Z o.pos.z = min(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #else o.pos.z = max(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #endif #elif _PASSMETA #if _MICROTERRAIN o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), v.texcoord0.xy, v.texcoord0.xy, unity_LightmapST, unity_DynamicLightmapST); #else o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), uv1, uv2, unity_LightmapST, unity_DynamicLightmapST); #endif #else o.pos = TransformWorldToHClip(o.worldPos); #endif // o.screenPos = ComputeScreenPos(o.pos, _ProjectionParams.x); #if _PASSFORWARD || _PASSGBUFFER #if _MICROTERRAIN OUTPUT_LIGHTMAP_UV(v.texcoord0.xy, unity_LightmapST, o.lightmapUV); #else OUTPUT_LIGHTMAP_UV(uv1, unity_LightmapST, o.lightmapUV); #endif OUTPUT_SH(o.worldNormal, o.sh); #if defined(DYNAMICLIGHTMAP_ON) o.dynamicLightmapUV.xy = uv2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; #endif #endif #ifdef VARYINGS_NEED_FOG_AND_VERTEX_LIGHT half fogFactor = 0; #if defined(_FOG_FRAGMENT) fogFactor = ComputeFogFactor(o.pos.z); #endif #if _BAKEDLIT half3 vertexLight = VertexLighting(o.worldPos, o.worldNormal); o.fogFactorAndVertexLight = half4(fogFactor, vertexLight); #else o.fogFactorAndVertexLight = half4(fogFactor, 0,0,0); #endif #endif #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) o.shadowCoord = GetShadowCoord(vertexInput); #endif return o; } // fragment shader half4 Frag (VertexToPixel IN #ifdef _DEPTHOFFSET_ON , out float outputDepth : SV_Depth #endif ) : SV_Target { UNITY_SETUP_INSTANCE_ID(IN); UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN); ShaderData d = CreateShaderData(IN); Surface l = (Surface)0; #ifdef _DEPTHOFFSET_ON l.outputDepth = outputDepth; #endif l.Albedo = half3(0.5, 0.5, 0.5); l.Normal = float3(0,0,1); l.Occlusion = 1; l.Alpha = 1; SurfaceFunction(l, d); #ifdef _DEPTHOFFSET_ON outputDepth = l.outputDepth; #endif return 0; } ENDHLSL } Pass { Name "Meta" Tags { "LightMode" = "Meta" } // Render State Cull Off HLSLPROGRAM #pragma vertex Vert #pragma fragment Frag #pragma target 3.5 #pragma prefer_hlslcc gles #pragma exclude_renderers d3d11_9x #pragma multi_compile_local _ _ALPHATEST_ON #define SHADERPASS SHADERPASS_META #define _PASSMETA 1 #define _MICROSPLAT 1 #define _MICROPOLARISMESH 1 #define _USEGRADMIP 1 #define _PERTEXUVSCALEOFFSET 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _MSRENDERLOOP_UNITYLD 1 #define _MSRENDERLOOP_UNITYURP2020 1 #define _MSRENDERLOOP_UNITYURP2021 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _URP 1 // Includes #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Version.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/MetaInput.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl" #include "Packages/com.unity.shadergraph/ShaderGraphLibrary/ShaderVariablesFunctions.hlsl" #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, float3x3(d.worldSpaceTangent, cross(d.worldSpaceTangent, d.worldSpaceNormal), d.worldSpaceNormal)) #define UnityObjectToWorldNormal(normal) mul(GetObjectToWorldMatrix(), normal) #define _WorldSpaceLightPos0 _MainLightPosition #define UNITY_DECLARE_TEX2D(name) TEXTURE2D(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2D_NOSAMPLER(name) TEXTURE2D(name); #define UNITY_DECLARE_TEX2DARRAY(name) TEXTURE2D_ARRAY(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2DARRAY_NOSAMPLER(name) TEXTURE2D_ARRAY(name); #define UNITY_SAMPLE_TEX2DARRAY(tex,coord) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, coord.xy, coord.z) #define UNITY_SAMPLE_TEX2DARRAY_LOD(tex,coord,lod) SAMPLE_TEXTURE2D_ARRAY_LOD(tex, sampler##tex, coord.xy, coord.z, lod) #define UNITY_SAMPLE_TEX2D(tex, coord) SAMPLE_TEXTURE2D(tex, sampler##tex, coord) #define UNITY_SAMPLE_TEX2D_SAMPLER(tex, samp, coord) SAMPLE_TEXTURE2D(tex, sampler##samp, coord) #if defined(UNITY_COMPILER_HLSL) #define UNITY_INITIALIZE_OUTPUT(type,name) name = (type)0; #else #define UNITY_INITIALIZE_OUTPUT(type,name) #endif #define sampler2D_float sampler2D #define sampler2D_half sampler2D // data across stages, stripped like the above. struct VertexToPixel { float4 pos : SV_POSITION; float3 worldPos : TEXCOORD0; float3 worldNormal : TEXCOORD1; float4 worldTangent : TEXCOORD2; float4 texcoord0 : TEXCCOORD3; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 texcoord1 : TEXCCOORD4; // float4 texcoord2 : TEXCCOORD5; #endif // float4 texcoord3 : TEXCCOORD6; // float4 screenPos : TEXCOORD7; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD13; // float4 extraV2F1 : TEXCOORD14; // float4 extraV2F2 : TEXCOORD15; // float4 extraV2F3 : TEXCOORD16; // float4 extraV2F4 : TEXCOORD17; // float4 extraV2F5 : TEXCOORD18; // float4 extraV2F6 : TEXCOORD19; // float4 extraV2F7 : TEXCOORD20; #if defined(LIGHTMAP_ON) float2 lightmapUV : TEXCOORD8; #endif #if defined(DYNAMICLIGHTMAP_ON) float2 dynamicLightmapUV : TEXCOORD9; #endif #if !defined(LIGHTMAP_ON) float3 sh : TEXCOORD10; #endif float4 fogFactorAndVertexLight : TEXCOORD11; float4 shadowCoord : TEXCOORD12; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : CUSTOM_INSTANCE_ID; #endif #if (defined(UNITY_STEREO_MULTIVIEW_ENABLED)) || (defined(UNITY_STEREO_INSTANCING_ENABLED) && (defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE))) uint stereoTargetEyeIndexAsBlendIdx0 : BLENDINDICES0; #endif #if (defined(UNITY_STEREO_INSTANCING_ENABLED)) uint stereoTargetEyeIndexAsRTArrayIdx : SV_RenderTargetArrayIndex; #endif #if defined(SHADER_STAGE_FRAGMENT) && defined(VARYINGS_NEED_CULLFACE) FRONT_FACE_TYPE cullFace : FRONT_FACE_SEMANTIC; #endif }; // TEMPLATE_SHARED // data describing the user output of a pixel struct Surface { half3 Albedo; half Height; half3 Normal; half Smoothness; half3 Emission; half Metallic; half3 Specular; half Occlusion; half Alpha; // HDRP Only half SpecularOcclusion; half SubsurfaceMask; half Thickness; half CoatMask; half Anisotropy; half IridescenceMask; half IridescenceThickness; }; // data the user might need, this will grow to be big. But easy to strip struct ShaderData { float3 localSpacePosition; float3 localSpaceNormal; float3 localSpaceTangent; float3 worldSpacePosition; float3 worldSpaceNormal; float3 worldSpaceTangent; float3 worldSpaceViewDir; float3 tangentSpaceViewDir; float4 texcoord0; float4 texcoord1; float4 texcoord2; float4 texcoord3; float2 screenUV; float4 screenPos; float4 vertexColor; float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; float3x3 TBNMatrix; }; struct VertexData { #if SHADER_TARGET > 30 && _PLANETCOMPUTE // // uint vertexID : SV_VertexID; #endif float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD4; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD5; // Add Precomputed Velocity (Alembic computes velocities on runtime side). #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID }; struct TessVertex { float4 vertex : INTERNALTESSPOS; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD4; // float4 extraV2F1 : TEXCOORD5; // float4 extraV2F2 : TEXCOORD6; // float4 extraV2F3 : TEXCOORD7; // float4 extraV2F4 : TEXCOORD8; // float4 extraV2F5 : TEXCOORD9; // float4 extraV2F6 : TEXCOORD10; // float4 extraV2F7 : TEXCOORD11; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD12; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD13; #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; struct ExtraV2F { float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; }; float3 WorldToTangentSpace(ShaderData d, float3 normal) { return mul(d.TBNMatrix, normal); } float3 TangentToWorldSpace(ShaderData d, float3 normal) { return mul(normal, d.TBNMatrix); } // in this case, make standard more like SRPs, because we can't fix // GetWorldToObjectMatrix() in HDRP, since it already does macro-fu there #if _STANDARD float3 TransformWorldToObject(float3 p) { return mul(GetWorldToObjectMatrix(), float4(p, 1)); }; float3 TransformObjectToWorld(float3 p) { return mul(GetObjectToWorldMatrix(), float4(p, 1)); }; float4 TransformWorldToObject(float4 p) { return mul(GetWorldToObjectMatrix(), p); }; float4 TransformObjectToWorld(float4 p) { return mul(GetObjectToWorldMatrix(), p); }; float4x4 GetWorldToObjectMatrix() { return GetWorldToObjectMatrix(); } float4x4 GetObjectToWorldMatrix() { return GetObjectToWorldMatrix(); } #endif float3 GetCameraWorldPosition() { #if _HDRP return GetCameraRelativePositionWS(_WorldSpaceCameraPos); #else return _WorldSpaceCameraPos; #endif } #if _HDRP half3 UnpackNormalmapRGorAG(half4 packednormal) { // This do the trick packednormal.x *= packednormal.w; half3 normal; normal.xy = packednormal.xy * 2 - 1; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } half3 UnpackNormal(half4 packednormal) { #if defined(UNITY_NO_DXT5nm) return packednormal.xyz * 2 - 1; #else return UnpackNormalmapRGorAG(packednormal); #endif } #endif #if _HDRP || _URP half3 UnpackScaleNormal(half4 packednormal, half scale) { #ifndef UNITY_NO_DXT5nm // Unpack normal as DXT5nm (1, y, 1, x) or BC5 (x, y, 0, 1) // Note neutral texture like "bump" is (0, 0, 1, 1) to work with both plain RGB normal and DXT5nm/BC5 packednormal.x *= packednormal.w; #endif half3 normal; normal.xy = (packednormal.xy * 2 - 1) * scale; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } #endif void GetSun(out float3 lightDir, out float3 color) { lightDir = float3(0.5, 0.5, 0); color = 1; #if _HDRP if (_DirectionalLightCount > 0) { DirectionalLightData light = _DirectionalLightDatas[0]; lightDir = -light.forward.xyz; color = light.color; } #elif _STANDARD lightDir = normalize(_WorldSpaceLightPos0.xyz); color = _LightColor0.rgb; #elif _URP Light light = GetMainLight(); lightDir = light.direction; color = light.color; #endif } CBUFFER_START(UnityPerMaterial) #if _MESHSUBARRAY half4 _MeshSubArrayIndexes; #endif float4 _Diffuse_TexelSize; float4 _NormalSAO_TexelSize; #if _HYBRIDHEIGHTBLEND float _HybridHeightBlendDistance; #endif #if _PACKINGHQ float4 _SmoothAO_TexelSize; #endif #ifdef _ALPHATEST_ON float4 _TerrainHolesTexture_TexelSize; #endif #if _USESPECULARWORKFLOW float4 _Specular_TexelSize; #endif #if _USEEMISSIVEMETAL float4 _EmissiveMetal_TexelSize; #endif #if _USEEMISSIVEMETAL half _EmissiveMult; #endif #if _AUTONORMAL half _AutoNormalHeightScale; #endif float4 _UVScale; // scale and offset half _Contrast; #if _VSSHADOWMAP float4 gVSSunDirection; #endif #if _FORCELOCALSPACE && _PLANETVECTORS float4x4 _PQSToLocal; #endif #if _ORIGINSHIFT float4x4 _GlobalOriginMTX; #endif float4 _Control0_TexelSize; #if _CUSTOMSPLATTEXTURES float4 _CustomControl0_TexelSize; #endif float4 _PerPixelNormal_TexelSize; #if _CONTROLNOISEUV || _GLOBALNOISEUV float2 _NoiseUVParams; #endif float4 _PerTexProps_TexelSize; #if _SURFACENORMALS float3 surfTangent; float3 surfBitangent; float3 surfNormal; #endif CBUFFER_END #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, data.TBN) // In Unity 2020.3LTS, Unity will spew tons of errors about missing this sampler in // URP, even though it shouldn't be required. TEXTURE2D(_MainTex); // globals, outside of CBuffer, but used by more than one module float3 _gGlitterLightDir; float3 _gGlitterLightWorldPos; half3 _gGlitterLightColor; #if (_MICROTERRAIN || _MICROMESHTERRAIN) 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) float4 _TerrainNormalmapTexture_TexelSize; #endif #if (_MICROTERRAIN || _MICROMESHTERRAIN) TEXTURE2D(_TerrainHeightmapTexture); float4 _TerrainHeightmapTexture_TexelSize; TEXTURE2D(_TerrainNormalmapTexture); #endif UNITY_INSTANCING_BUFFER_START(Terrain) UNITY_DEFINE_INSTANCED_PROP(float4, _TerrainPatchInstanceData) // float4(xBase, yBase, skipScale, ~) UNITY_INSTANCING_BUFFER_END(Terrain) // dynamic branching helpers, for regular and aggressive branching // debug mode shows how many samples using branching will save us. // // These macros are always used instead of the UNITY_BRANCH macro // to maintain debug displays and allow branching to be disabled // on as granular level as we want. #if _BRANCHSAMPLES #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; if (w > 0) #else #define MSBRANCH(w) UNITY_BRANCH if (w > 0) #endif #else #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; #else #define MSBRANCH(w) #endif #endif #if _BRANCHSAMPLESAGR #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER ||_DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; if (w > 0.001) #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; if (w > 0.001) #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; if (w > 0.001) #else #define MSBRANCHTRIPLANAR(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHCLUSTER(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHOTHER(w) UNITY_BRANCH if (w > 0.001) #endif #else #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER || _DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; #else #define MSBRANCHTRIPLANAR(w) #define MSBRANCHCLUSTER(w) #define MSBRANCHOTHER(w) #endif #endif #if _DEBUG_SAMPLECOUNT int _sampleCount; #define COUNTSAMPLE { _sampleCount++; } #else #define COUNTSAMPLE #endif #if _DEBUG_PROCLAYERS int _procLayerCount; #define COUNTPROCLAYER { _procLayerCount++; } #else #define COUNTPROCLAYER #endif #if _DEBUG_USE_TOPOLOGY TEXTURE2D(_DebugWorldPos); TEXTURE2D(_DebugWorldNormal); #endif // splat UNITY_DECLARE_TEX2DARRAY(_Diffuse); UNITY_DECLARE_TEX2DARRAY(_NormalSAO); #if _CONTROLNOISEUV || _GLOBALNOISEUV TEXTURE2D(_NoiseUV); #endif #if _PACKINGHQ UNITY_DECLARE_TEX2DARRAY(_SmoothAO); #endif #if _USESPECULARWORKFLOW UNITY_DECLARE_TEX2DARRAY(_Specular); #endif #if _USEEMISSIVEMETAL UNITY_DECLARE_TEX2DARRAY(_EmissiveMetal); #endif TEXTURE2D(_PerPixelNormal); SamplerState shared_linear_clamp_sampler; SamplerState shared_point_clamp_sampler; TEXTURE2D(_Control0); #if _CUSTOMSPLATTEXTURES TEXTURE2D(_CustomControl0); #if !_MAX4TEXTURES TEXTURE2D(_CustomControl1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_CustomControl2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_CustomControl3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl6); #endif #if _MAX32TEXTURES TEXTURE2D(_CustomControl7); #endif #else #if !_MAX4TEXTURES TEXTURE2D(_Control1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_Control2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_Control3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control6); #endif #if _MAX32TEXTURES TEXTURE2D(_Control7); #endif #endif TEXTURE2D_FLOAT(_PerTexProps); struct DecalLayer { float3 uv; float2 dx; float2 dy; int decalIndex; bool dynamic; }; struct DecalOutput { DecalLayer l0; DecalLayer l1; DecalLayer l2; DecalLayer l3; half4 Weights; half4 Indexes; half4 fxLevels; }; struct TriGradMipFormat { float4 d0; float4 d1; float4 d2; }; float InverseLerp(float x, float y, float v) { return (v-x)/max(y-x, 0.001); } float2 InverseLerp(float2 x, float2 y, float2 v) { return (v-x)/max(y-x, float2(0.001, 0.001)); } float3 InverseLerp(float3 x, float3 y, float3 v) { return (v-x)/max(y-x, float3(0.001, 0.001, 0.001)); } float4 InverseLerp(float4 x, float4 y, float4 v) { return (v-x)/max(y-x, float4(0.001, 0.001, 0.001, 0.001)); } // 2019.3 holes #ifdef _ALPHATEST_ON TEXTURE2D(_TerrainHolesTexture); void ClipHoles(float2 uv) { float hole = SAMPLE_TEXTURE2D(_TerrainHolesTexture, shared_linear_clamp_sampler, uv).r; COUNTSAMPLE clip(hole < 0.5f ? -1 : 1); } #endif #if _TRIPLANAR #if _USEGRADMIP #define MIPFORMAT TriGradMipFormat #define INITMIPFORMAT (TriGradMipFormat)0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float3 #define INITMIPFORMAT 0; #define MIPFROMATRAW float3 #endif #else #if _USEGRADMIP #define MIPFORMAT float4 #define INITMIPFORMAT 0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float #define INITMIPFORMAT 0; #define MIPFROMATRAW float #endif #endif float2 TotalOne(float2 v) { return v * (1.0 / max(v.x + v.y, 0.001)); } float3 TotalOne(float3 v) { return v * (1.0 / max(v.x + v.y + v.z, 0.001)); } float4 TotalOne(float4 v) { return v * (1.0 / max(v.x + v.y + v.z + v.w, 0.001)); } float2 RotateUV(float2 uv, float amt) { uv -=0.5; float s = sin ( amt); float c = cos ( amt ); float2x2 mtx = float2x2( c, -s, s, c); mtx *= 0.5; mtx += 0.5; mtx = mtx * 2-1; uv = mul ( uv, mtx ); uv += 0.5; return uv; } float4 DecodeToFloat4(float v) { uint vi = (uint)(v * (256.0f * 256.0f * 256.0f * 256.0f)); int ex = (int)(vi / (256 * 256 * 256) % 256); int ey = (int)((vi / (256 * 256)) % 256); int ez = (int)((vi / (256)) % 256); int ew = (int)(vi % 256); float4 e = float4(ex / 255.0, ey / 255.0, ez / 255.0, ew / 255.0); return e; } struct Input { ShaderData shaderData; float2 uv_Control0; float2 uv2_Diffuse; float worldHeight; float3 worldUpVector; float3 viewDir; float3 worldPos; float3 worldNormal; float4 color; float3x3 TBN; // vertex/digger workflow data half4 w0; half4 w1; half4 w2; half4 w3; half4 w4; half4 w5; half4 w6; // megasplat data half4 layer0; half4 layer1; half3 baryWeights; half4 scatter0; half4 scatter1; // wetness, puddles, streams, lava from vertex or megasplat half4 fx; // snow min, snow max half4 fx2; }; struct TriplanarConfig { float3x3 uv0; float3x3 uv1; float3x3 uv2; float3x3 uv3; half3 pN; half3 pN0; half3 pN1; half3 pN2; half3 pN3; half3 axisSign; Input IN; }; struct Config { float2 uv; float3 uv0; float3 uv1; float3 uv2; float3 uv3; half4 cluster0; half4 cluster1; half4 cluster2; half4 cluster3; }; struct MicroSplatLayer { half3 Albedo; half3 Normal; half Smoothness; half Occlusion; half Metallic; half Height; half3 Emission; #if _USESPECULARWORKFLOW half3 Specular; #endif half Alpha; }; // raw, unblended samples from arrays struct RawSamples { half4 albedo0; half4 albedo1; half4 albedo2; half4 albedo3; #if _SURFACENORMALS half3 surf0; half3 surf1; half3 surf2; half3 surf3; #endif half4 normSAO0; half4 normSAO1; half4 normSAO2; half4 normSAO3; #if _USEEMISSIVEMETAL || _GLOBALEMIS || _GLOBALSMOOTHAOMETAL || _PERTEXSSS || _PERTEXRIMLIGHT half4 emisMetal0; half4 emisMetal1; half4 emisMetal2; half4 emisMetal3; #endif #if _USESPECULARWORKFLOW half3 specular0; half3 specular1; half3 specular2; half3 specular3; #endif }; void InitRawSamples(inout RawSamples s) { s.normSAO0 = half4(0,0,0,1); s.normSAO1 = half4(0,0,0,1); s.normSAO2 = half4(0,0,0,1); s.normSAO3 = half4(0,0,0,1); #if _SURFACENORMALS s.surf0 = half3(0,0,1); s.surf1 = half3(0,0,1); s.surf2 = half3(0,0,1); s.surf3 = half3(0,0,1); #endif } float3 GetGlobalLightDir(Input i) { float3 lightDir = float3(1,0,0); #if _HDRP || PASS_DEFERRED lightDir = normalize(_gGlitterLightDir.xyz); #elif _URP lightDir = GetMainLight().direction; #else #ifndef USING_DIRECTIONAL_LIGHT lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); #else lightDir = normalize(_WorldSpaceLightPos0.xyz); #endif #endif return lightDir; } float3x3 GetTBN(Input i) { return i.TBN; } float3 GetGlobalLightDirTS(Input i) { float3 lightDirWS = GetGlobalLightDir(i); return mul(GetTBN(i), lightDirWS); } half3 GetGlobalLightColor() { #if _HDRP || PASS_DEFERRED return _gGlitterLightColor; #elif _URP return (GetMainLight().color); #else return _LightColor0.rgb; #endif } half3 FuzzyShade(half3 color, half3 normal, half coreMult, half edgeMult, half power, float3 viewDir) { half dt = saturate(dot(viewDir, normal)); half dark = 1.0 - (coreMult * dt); half edge = pow(1-dt, power) * edgeMult; return color * (dark + edge); } half3 ComputeSSS(Input i, float3 V, float3 N, half3 tint, half thickness, half distortion, half scale, half power) { float3 L = GetGlobalLightDir(i); half3 lightColor = GetGlobalLightColor(); float3 H = normalize(L + N * distortion); float VdotH = pow(saturate(dot(V, -H)), power) * scale; float3 I = (VdotH) * thickness; return lightColor * I * tint; } #if _MAX2LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y; } #elif _MAX3LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } #else inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } #endif #if _MAX3LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #elif _MAX2LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #else #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##3 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv3.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #endif half2 BlendNormal2(half2 base, half2 blend) { return normalize(half3(base.xy + blend.xy, 1)).xy; } half3 BlendOverlay(half3 base, half3 blend) { return (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))); } half3 BlendMult2X(half3 base, half3 blend) { return (base * (blend * 2)); } half3 BlendLighterColor(half3 s, half3 d) { return (s.x + s.y + s.z > d.x + d.y + d.z) ? s : d; } #if _SURFACENORMALS #define HALF_EPS 4.8828125e-4 // 2^-11, machine epsilon: 1 + EPS = 1 (half of the ULP for 1.0f) void ConstructSurfaceGradientTBN(Input i) { float3x3 tbn = GetTBN(i); float3 t = tbn[0]; float3 b = tbn[1]; float3 n = tbn[2]; surfNormal = n;//mul(GetWorldToObjectMatrix(), float4(n, 1)).xyz; surfTangent = t;//mul(GetWorldToObjectMatrix(), float4(t, 1)).xyz; surfBitangent = b;//cross(surfNormal, surfTangent); float renormFactor = 1.0 / length(surfNormal); surfNormal *= renormFactor; surfTangent *= renormFactor; surfBitangent *= renormFactor; } half3 SurfaceGradientFromTBN(half2 deriv) { return deriv.x * surfTangent + deriv.y * surfBitangent; } // Input: vM is tangent space normal in [-1;1]. // Output: convert vM to a derivative. half2 TspaceNormalToDerivative(half3 vM) { const half scale = 1.0/128.0; // Ensure vM delivers a positive third component using abs() and // constrain vM.z so the range of the derivative is [-128; 128]. const half3 vMa = abs(vM); const half z_ma = max(vMa.z, scale*max(vMa.x, vMa.y)); return -half2(vM.x, vM.y)/z_ma; } // Used to produce a surface gradient from the gradient of a volume // bump function such as 3D Perlin noise. Equation 2 in [Mik10]. half3 SurfgradFromVolumeGradient(half3 grad) { return grad - dot(surfNormal, grad) * surfNormal; } half3 SurfgradFromTriplanarProjection(half3 pN, half2 xPlaneTN, half2 yPlaneTN, half2 zPlaneTN) { const half w0 = pN.x; const half w1 = pN.y; const half w2 = pN.z; // X-plane tangent normal to gradient derivative xPlaneTN = xPlaneTN * 2.0 - 1.0; half xPlaneRcpZ = rsqrt(max(1 - dot(xPlaneTN.x, xPlaneTN.x) - dot(xPlaneTN.y, xPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_xplane = xPlaneTN * -xPlaneRcpZ; // Y-plane tangent normal to gradient derivative yPlaneTN = yPlaneTN * 2.0 - 1.0; half yPlaneRcpZ = rsqrt(max(1 - dot(yPlaneTN.x, yPlaneTN.x) - dot(yPlaneTN.y, yPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_yplane = yPlaneTN * -yPlaneRcpZ; // Z-plane tangent normal to gradient derivative zPlaneTN = zPlaneTN * 2.0 - 1.0; half zPlaneRcpZ = rsqrt(max(1 - dot(zPlaneTN.x, zPlaneTN.x) - dot(zPlaneTN.y, zPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_zplane = zPlaneTN * -zPlaneRcpZ; // Assume deriv xplane, deriv yplane, and deriv zplane are // sampled using (z,y), (x,z), and (x,y), respectively. // Positive scales of the lookup coordinate will work // as well, but for negative scales the derivative components // will need to be negated accordingly. float3 grad = float3(w2*d_zplane.x + w1*d_yplane.x, w2*d_zplane.y + w0*d_xplane.y, w0*d_xplane.x + w1*d_yplane.y); return SurfgradFromVolumeGradient(grad); } half3 ConvertNormalToGradient(half3 normal) { half2 deriv = TspaceNormalToDerivative(normal); return SurfaceGradientFromTBN(deriv); } half3 ConvertNormal2ToGradient(half2 packedNormal) { half2 tNormal = packedNormal; half rcpZ = rsqrt(max(1 - dot(tNormal.x, tNormal.x) - dot(tNormal.y, tNormal.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 deriv = tNormal * -rcpZ; return SurfaceGradientFromTBN(deriv); } half3 ResolveNormalFromSurfaceGradient(half3 gradient) { return normalize(surfNormal - gradient); } #endif // _SURFACENORMALS void BlendNormalPerTex(inout RawSamples o, half2 noise, float4 fades) { #if _SURFACENORMALS float3 grad = ConvertNormal2ToGradient(noise.xy); o.surf0 += grad * fades.x; o.surf1 += grad * fades.y; #if !_MAX2LAYER o.surf2 += grad * fades.z; #endif #if !_MAX2LAYER && !_MAX3LAYER o.surf3 += grad * fades.w; #endif #else o.normSAO0.xy = lerp(o.normSAO0.xy, BlendNormal2(o.normSAO0.xy, noise.xy), fades.x); o.normSAO1.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #if !_MAX2LAYER o.normSAO2.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO2.xy, noise.xy), fades.y); #endif #if !_MAX2LAYER && !_MAX3LAYER o.normSAO3.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #endif #endif } half3 BlendNormal3(half3 n1, half3 n2) { n1 += float3( 0, 0, 1); n2 *= float3(-1, -1, 1); return n1*dot(n1, n2) / n1.z - n2; } half2 TransformTriplanarNormal(Input IN, float3x3 t2w, half3 axisSign, half3 absVertNormal, half3 pN, half2 a0, half2 a1, half2 a2) { a0 = a0 * 2 - 1; a1 = a1 * 2 - 1; a2 = a2 * 2 - 1; a0.x *= axisSign.x; a1.x *= axisSign.y; a2.x *= axisSign.z; half3 n0 = half3(a0.xy, 1); half3 n1 = half3(a1.xy, 1); half3 n2 = half3(a2.xy, 1); float3 wn = IN.worldNormal; n0 = BlendNormal3(half3(wn.zy, absVertNormal.x), n0); n1 = BlendNormal3(half3(wn.xz, absVertNormal.y), n1 * float3(-1, 1, 1)); n2 = BlendNormal3(half3(wn.xy, absVertNormal.z), n2); n0.z *= axisSign.x; n1.z *= axisSign.y; n2.z *= -axisSign.z; half3 worldNormal = (n0.zyx * pN.x + n1.xzy * pN.y + n2.xyz * pN.z); return mul(t2w, worldNormal).xy; } // funcs inline half MSLuminance(half3 rgb) { #ifdef UNITY_COLORSPACE_GAMMA return dot(rgb, half3(0.22, 0.707, 0.071)); #else return dot(rgb, half3(0.0396819152, 0.458021790, 0.00609653955)); #endif } float2 Hash2D( float2 x ) { float2 k = float2( 0.3183099, 0.3678794 ); x = x*k + k.yx; return -1.0 + 2.0*frac( 16.0 * k*frac( x.x*x.y*(x.x+x.y)) ); } float Noise2D(float2 p ) { float2 i = floor( p ); float2 f = frac( p ); float2 u = f*f*(3.0-2.0*f); return lerp( lerp( dot( Hash2D( i + float2(0.0,0.0) ), f - float2(0.0,0.0) ), dot( Hash2D( i + float2(1.0,0.0) ), f - float2(1.0,0.0) ), u.x), lerp( dot( Hash2D( i + float2(0.0,1.0) ), f - float2(0.0,1.0) ), dot( Hash2D( i + float2(1.0,1.0) ), f - float2(1.0,1.0) ), u.x), u.y); } float FBM2D(float2 uv) { float f = 0.5000*Noise2D( uv ); uv *= 2.01; f += 0.2500*Noise2D( uv ); uv *= 1.96; f += 0.1250*Noise2D( uv ); return f; } float3 Hash3D( float3 p ) { p = float3( dot(p,float3(127.1,311.7, 74.7)), dot(p,float3(269.5,183.3,246.1)), dot(p,float3(113.5,271.9,124.6))); return -1.0 + 2.0*frac(sin(p)*437.5453123); } float Noise3D( float3 p ) { float3 i = floor( p ); float3 f = frac( p ); float3 u = f*f*(3.0-2.0*f); return lerp( lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,0.0) ), f - float3(0.0,0.0,0.0) ), dot( Hash3D( i + float3(1.0,0.0,0.0) ), f - float3(1.0,0.0,0.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,0.0) ), f - float3(0.0,1.0,0.0) ), dot( Hash3D( i + float3(1.0,1.0,0.0) ), f - float3(1.0,1.0,0.0) ), u.x), u.y), lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,1.0) ), f - float3(0.0,0.0,1.0) ), dot( Hash3D( i + float3(1.0,0.0,1.0) ), f - float3(1.0,0.0,1.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,1.0) ), f - float3(0.0,1.0,1.0) ), dot( Hash3D( i + float3(1.0,1.0,1.0) ), f - float3(1.0,1.0,1.0) ), u.x), u.y), u.z ); } float FBM3D(float3 uv) { float f = 0.5000*Noise3D( uv ); uv *= 2.01; f += 0.2500*Noise3D( uv ); uv *= 1.96; f += 0.1250*Noise3D( uv ); return f; } float GetSaturation(float3 c) { float mi = min(min(c.x, c.y), c.z); float ma = max(max(c.x, c.y), c.z); return (ma - mi)/(ma + 1e-7); } // Better Color Lerp, does not have darkening issue float3 BetterColorLerp(float3 a, float3 b, float x) { float3 ic = lerp(a, b, x) + float3(1e-6,0.0,0.0); float sd = abs(GetSaturation(ic) - lerp(GetSaturation(a), GetSaturation(b), x)); float3 dir = normalize(float3(2.0 * ic.x - ic.y - ic.z, 2.0 * ic.y - ic.x - ic.z, 2.0 * ic.z - ic.y - ic.x)); float lgt = dot(float3(1.0, 1.0, 1.0), ic); float ff = dot(dir, normalize(ic)); const float dsp_str = 1.5; ic += dsp_str * dir * sd * ff * lgt; return saturate(ic); } half4 ComputeWeights(half4 iWeights, half h0, half h1, half h2, half h3, half contrast) { #if _DISABLEHEIGHTBLENDING return iWeights; #else // compute weight with height map //half4 weights = half4(iWeights.x * h0, iWeights.y * h1, iWeights.z * h2, iWeights.w * h3); half4 weights = half4(iWeights.x * max(h0,0.001), iWeights.y * max(h1,0.001), iWeights.z * max(h2,0.001), iWeights.w * max(h3,0.001)); // Contrast weights half maxWeight = max(max(weights.x, max(weights.y, weights.z)), weights.w); half transition = max(contrast * maxWeight, 0.0001); half threshold = maxWeight - transition; half scale = 1.0 / transition; weights = saturate((weights - threshold) * scale); weights = TotalOne(weights); return weights; #endif } half HeightBlend(half h1, half h2, half slope, half contrast) { #if _DISABLEHEIGHTBLENDING return slope; #else h2 = 1 - h2; half tween = saturate((slope - min(h1, h2)) / max(abs(h1 - h2), 0.001)); half blend = saturate( ( tween - (1-contrast) ) / max(contrast, 0.001)); return blend; #endif } #if _MAX4TEXTURES #define TEXCOUNT 4 #elif _MAX8TEXTURES #define TEXCOUNT 8 #elif _MAX12TEXTURES #define TEXCOUNT 12 #elif _MAX20TEXTURES #define TEXCOUNT 20 #elif _MAX24TEXTURES #define TEXCOUNT 24 #elif _MAX28TEXTURES #define TEXCOUNT 28 #elif _MAX32TEXTURES #define TEXCOUNT 32 #else #define TEXCOUNT 16 #endif #if _DECAL_SPLAT void DoMergeDecalSplats(half4 iWeights, half4 iIndexes, inout half4 indexes, inout half4 weights) { for (int i = 0; i < 4; ++i) { half w = iWeights[i]; half index = iIndexes[i]; if (w > weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = index; } else if (w > weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = index; } else if (w > weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = index; } else if (w > weights[3]) { weights[3] = w; indexes[3] = index; } } } #endif void Setup(out half4 weights, float2 uv, out Config config, half4 w0, half4 w1, half4 w2, half4 w3, half4 w4, half4 w5, half4 w6, half4 w7, float3 worldPos, DecalOutput decalOutput) { config = (Config)0; half4 indexes = 0; config.uv = uv; #if _WORLDUV uv = worldPos.xz; #endif #if _DISABLESPLATMAPS float2 scaledUV = uv; #else float2 scaledUV = uv * _UVScale.xy + _UVScale.zw; #endif // if only 4 textures, and blending 4 textures, skip this whole thing.. // this saves about 25% of the ALU of the base shader on low end. However if // we rely on sorted texture weights (distance resampling) we have to sort.. float4 defaultIndexes = float4(0,1,2,3); #if _MESHSUBARRAY defaultIndexes = _MeshSubArrayIndexes; #endif #if _MESHSUBARRAY && !_DECAL_SPLAT || (_MAX4TEXTURES && !_MAX3LAYER && !_MAX2LAYER && !_DISTANCERESAMPLE && !_POM && !_DECAL_SPLAT) weights = w0; config.uv0 = float3(scaledUV, defaultIndexes.x); config.uv1 = float3(scaledUV, defaultIndexes.y); config.uv2 = float3(scaledUV, defaultIndexes.z); config.uv3 = float3(scaledUV, defaultIndexes.w); return; #endif #if _DISABLESPLATMAPS weights = float4(1,0,0,0); return; #else half splats[TEXCOUNT]; splats[0] = w0.x; splats[1] = w0.y; splats[2] = w0.z; splats[3] = w0.w; #if !_MAX4TEXTURES splats[4] = w1.x; splats[5] = w1.y; splats[6] = w1.z; splats[7] = w1.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES splats[8] = w2.x; splats[9] = w2.y; splats[10] = w2.z; splats[11] = w2.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES splats[12] = w3.x; splats[13] = w3.y; splats[14] = w3.z; splats[15] = w3.w; #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[16] = w4.x; splats[17] = w4.y; splats[18] = w4.z; splats[19] = w4.w; #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[20] = w5.x; splats[21] = w5.y; splats[22] = w5.z; splats[23] = w5.w; #endif #if _MAX28TEXTURES || _MAX32TEXTURES splats[24] = w6.x; splats[25] = w6.y; splats[26] = w6.z; splats[27] = w6.w; #endif #if _MAX32TEXTURES splats[28] = w7.x; splats[29] = w7.y; splats[30] = w7.z; splats[31] = w7.w; #endif weights[0] = 0; weights[1] = 0; weights[2] = 0; weights[3] = 0; indexes[0] = 0; indexes[1] = 0; indexes[2] = 0; indexes[3] = 0; int i = 0; for (i = 0; i < TEXCOUNT; ++i) { half w = splats[i]; if (w >= weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = i; } else if (w >= weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = i; } else if (w >= weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = i; } else if (w >= weights[3]) { weights[3] = w; indexes[3] = i; } } // NaN Prevention if (weights.x <= 0) weights = float4(1, 0, 0, 0); #if _DECAL_SPLAT DoMergeDecalSplats(decalOutput.Weights, decalOutput.Indexes, weights, indexes); #endif // clamp and renormalize #if _MAX2LAYER weights.zw = 0; weights.xy = TotalOne(weights.xy); #elif _MAX3LAYER weights.w = 0; weights.xyz = TotalOne(weights.xyz); #elif !_DISABLEHEIGHTBLENDING || _NORMALIZEWEIGHTS // prevents black when painting, which the unity shader does not prevent. weights = normalize(weights); #endif config.uv0 = float3(scaledUV, indexes.x); config.uv1 = float3(scaledUV, indexes.y); config.uv2 = float3(scaledUV, indexes.z); config.uv3 = float3(scaledUV, indexes.w); #endif //_DISABLESPLATMAPS } float3 HeightToNormal(float height, float3 worldPos) { float3 dx = ddx(worldPos); float3 dy = ddy(worldPos); float3 crossX = cross(float3(0,1,0), dx); float3 crossY = cross(float3(0,1,0), dy); float3 d = abs(dot(crossY, dx)); float3 n = ((((height + ddx(height)) - height) * crossY) + (((height + ddy(height)) - height) * crossX)) * sign(d); n.z *= -1; return normalize((d * float3(0,1,0)) - n).xzy; } float ComputeMipLevel(float2 uv, float2 textureSize) { uv *= textureSize; float2 dx_vtc = ddx(uv); float2 dy_vtc = ddy(uv); float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); return 0.5 * log2(delta_max_sqr); } inline half2 UnpackNormal2(half4 packednormal) { return packednormal.wy * 2 - 1; } half3 TriplanarHBlend(half h0, half h1, half h2, half3 pN, half contrast) { half3 blend = pN / dot(pN, half3(1,1,1)); float3 heights = float3(h0, h1, h2) + (blend * 3.0); half height_start = max(max(heights.x, heights.y), heights.z) - contrast; half3 h = max(heights - height_start.xxx, half3(0,0,0)); blend = h / dot(h, half3(1,1,1)); return blend; } void ClearAllButAlbedo(inout MicroSplatLayer o, half3 display) { o.Albedo = display.rgb; o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } void ClearAllButAlbedo(inout MicroSplatLayer o, half display) { o.Albedo = half3(display, display, display); o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } half MicroShadow(float3 lightDir, half3 normal, half ao, half strength) { half shadow = saturate(abs(dot(normal, lightDir)) + (ao * ao * 2.0) - 1.0); return 1 - ((1-shadow) * strength); } void DoDebugOutput(inout MicroSplatLayer l) { #if _DEBUG_OUTPUT_ALBEDO ClearAllButAlbedo(l, l.Albedo); #elif _DEBUG_OUTPUT_NORMAL // oh unit shader compiler normal stripping, how I hate you so.. // must multiply by albedo to stop the normal from being white. Why, fuck knows? ClearAllButAlbedo(l, float3(l.Normal.xy * 0.5 + 0.5, l.Normal.z * saturate(l.Albedo.z+1))); #elif _DEBUG_OUTPUT_SMOOTHNESS ClearAllButAlbedo(l, l.Smoothness.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_METAL ClearAllButAlbedo(l, l.Metallic.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_AO ClearAllButAlbedo(l, l.Occlusion.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_EMISSION ClearAllButAlbedo(l, l.Emission * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_HEIGHT ClearAllButAlbedo(l, l.Height.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_SPECULAR && _USESPECULARWORKFLOW ClearAllButAlbedo(l, l.Specular * saturate(l.Albedo.z+1)); #elif _DEBUG_BRANCHCOUNT_WEIGHT ClearAllButAlbedo(l, _branchWeightCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TRIPLANAR ClearAllButAlbedo(l, _branchTriplanarCount / 24 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_CLUSTER ClearAllButAlbedo(l, _branchClusterCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_OTHER ClearAllButAlbedo(l, _branchOtherCount / 8 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TOTAL l.Albedo.r = _branchWeightCount / 12; l.Albedo.g = _branchTriplanarCount / 24; l.Albedo.b = _branchClusterCount / 12; ClearAllButAlbedo(l, (l.Albedo.r + l.Albedo.g + l.Albedo.b + (_branchOtherCount / 8)) / 4); #elif _DEBUG_OUTPUT_MICROSHADOWS ClearAllButAlbedo(l,l.Albedo); #elif _DEBUG_SAMPLECOUNT float sdisp = (float)_sampleCount / max(_SampleCountDiv, 1); half3 sdcolor = float3(sdisp, sdisp > 1 ? 1 : 0, 0); ClearAllButAlbedo(l, sdcolor * saturate(l.Albedo.z + 1)); #elif _DEBUG_PROCLAYERS ClearAllButAlbedo(l, (float)_procLayerCount / (float)_PCLayerCount * saturate(l.Albedo.z + 1)); #endif } // abstraction around sampler mode #if _USELODMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_LOD(tex, sampler##tex, u, l.x) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u, l.x) #elif _USEGRADMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_GRAD(tex, sampler##tex, u, l.xy, l.zw) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY_GRAD(tex, ss, u.xy, u.z, l.xy, l.zw) #else #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, u.xy, u.z) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u.xy, y.z) #endif #define MICROSPLAT_SAMPLE_DIFFUSE(u, cl, l) MICROSPLAT_SAMPLE(_Diffuse, u, l) #define MICROSPLAT_SAMPLE_EMIS(u, cl, l) MICROSPLAT_SAMPLE(_EmissiveMetal, u, l) #define MICROSPLAT_SAMPLE_DIFFUSE_LOD(u, cl, l) UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, u, l) #if _PACKINGHQ #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) half4(MICROSPLAT_SAMPLE(_NormalSAO, u, l).ga, MICROSPLAT_SAMPLE(_SmoothAO, u, l).ga).brag #else #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) MICROSPLAT_SAMPLE(_NormalSAO, u, l) #endif #if _USESPECULARWORKFLOW #define MICROSPLAT_SAMPLE_SPECULAR(u, cl, l) MICROSPLAT_SAMPLE(_Specular, u, l) #endif struct SimpleTriplanarConfig { float3 pn; float2 uv0; float2 uv1; float2 uv2; }; void PrepSimpleTriplanarConfig(inout SimpleTriplanarConfig tc, float3 worldPos, float3 normal, float contrast) { tc.pn = pow(abs(normal), contrast); tc.pn = tc.pn / (tc.pn.x + tc.pn.y + tc.pn.z); half3 axisSign = sign(normal); tc.uv0 = worldPos.zy * axisSign.x; tc.uv1 = worldPos.xz * axisSign.y; tc.uv2 = worldPos.xy * axisSign.z; } #define SimpleTriplanarSample(tex, tc, scale) (SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv0 * scale) * tc.pn.x + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv1 * scale) * tc.pn.y + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv2 * scale) * tc.pn.z) #define SimpleTriplanarSampleLOD(tex, tc, scale, lod) (SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv0 * scale, lod) * tc.pn.x + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv1 * scale, lod) * tc.pn.y + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv2 * scale, lod) * tc.pn.z) #define SimpleTriplanarSampleGrad(tex, tc, scale) (SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv0 * scale, ddx(tc.uv0) * scale, ddy(tc.uv0) * scale) * tc.pn.x + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv1 * scale, ddx(tc.uv1) * scale, ddy(tc.uv1) * scale) * tc.pn.y + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv2 * scale, ddx(tc.uv2) * scale, ddy(tc.uv2) * scale) * tc.pn.z) inline half3 MicroSplatDiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity) { specColor = lerp (half3(0,0,0), albedo, metallic); oneMinusReflectivity = (1-metallic); return albedo * oneMinusReflectivity; } Input DescToInput(ShaderData IN) { Input s = (Input)0; s.shaderData = IN; s.TBN = IN.TBNMatrix; s.worldNormal = IN.worldSpaceNormal; s.worldPos = IN.worldSpacePosition; s.viewDir = IN.tangentSpaceViewDir; s.uv_Control0 = IN.texcoord0.xy; s.worldUpVector = float3(0,1,0); s.worldHeight = IN.worldSpacePosition.y; #if _PLANETVECTORS float3 rwp = mul(_PQSToLocal, float4(IN.worldSpacePosition, 1)); s.worldHeight = distance(rwp, float3(0,0,0)); s.worldUpVector = normalize(rwp); #endif #if _MICROMESH && _MESHUV2 s.uv2_Diffuse = IN.texcoord1.xy; #endif #if _MEGASPLAT UnpackMegaSplat(s, IN); #endif #if _MICROVERTEXMESH || _MICRODIGGERMESH UnpackVertexWorkflow(s, IN); #endif #if _PLANETVECTORS DoPlanetDataInputCopy(s, IN); #endif return s; } void SampleAlbedo(inout Config config, inout TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half4 contrasts = _Contrast.xxxx; #if _PERTEXTRIPLANARCONTRAST SAMPLE_PER_TEX(ptc, 9.5, config, half4(1,0.5,0,0)); contrasts = half4(ptc0.y, ptc1.y, ptc2.y, ptc3.y); #endif #if _PERTEXTRIPLANAR SAMPLE_PER_TEX(pttri, 9.5, config, half4(0,0,0,0)); #endif { // For per-texture triplanar, we modify the view based blending factor of the triplanar // such that you get a pure blend of either top down projection, or with the top down projection // removed and renormalized. This causes dynamic flow control optimizations to kick in and avoid // the extra texture samples while keeping the code simple. Yay.. // We also only have to do this in the Albedo, because the pN values will be adjusted after the // albedo is sampled, causing future samples to use this data. #if _PERTEXTRIPLANAR if (pttri0.x > 0.66) { tc.pN0 = half3(0,1,0); } else if (pttri0.x > 0.33) { tc.pN0.y = 0; tc.pN0.xz = TotalOne(tc.pN0.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } half3 bf = tc.pN0; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN0, contrasts.x); tc.pN0 = bf; #endif s.albedo0 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } MSBRANCH(weights.y) { #if _PERTEXTRIPLANAR if (pttri1.x > 0.66) { tc.pN1 = half3(0,1,0); } else if (pttri1.x > 0.33) { tc.pN1.y = 0; tc.pN1.xz = TotalOne(tc.pN1.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { COUNTSAMPLE a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[2], config.cluster1, d2); } half3 bf = tc.pN1; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN1, contrasts.x); tc.pN1 = bf; #endif s.albedo1 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { #if _PERTEXTRIPLANAR if (pttri2.x > 0.66) { tc.pN2 = half3(0,1,0); } else if (pttri2.x > 0.33) { tc.pN2.y = 0; tc.pN2.xz = TotalOne(tc.pN2.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } half3 bf = tc.pN2; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN2, contrasts.x); tc.pN2 = bf; #endif s.albedo2 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { #if _PERTEXTRIPLANAR if (pttri3.x > 0.66) { tc.pN3 = half3(0,1,0); } else if (pttri3.x > 0.33) { tc.pN3.y = 0; tc.pN3.xz = TotalOne(tc.pN3.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } half3 bf = tc.pN3; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN3, contrasts.x); tc.pN3 = bf; #endif s.albedo3 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #else s.albedo0 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.albedo1 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.albedo2 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.albedo3 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #if _PERTEXHEIGHTOFFSET || _PERTEXHEIGHTCONTRAST SAMPLE_PER_TEX(ptHeight, 10.5, config, 1); #if _PERTEXHEIGHTOFFSET s.albedo0.a = saturate(s.albedo0.a + ptHeight0.b - 1); s.albedo1.a = saturate(s.albedo1.a + ptHeight1.b - 1); s.albedo2.a = saturate(s.albedo2.a + ptHeight2.b - 1); s.albedo3.a = saturate(s.albedo3.a + ptHeight3.b - 1); #endif #if _PERTEXHEIGHTCONTRAST s.albedo0.a = saturate(pow(s.albedo0.a + 0.5, abs(ptHeight0.a)) - 0.5); s.albedo1.a = saturate(pow(s.albedo1.a + 0.5, abs(ptHeight1.a)) - 0.5); s.albedo2.a = saturate(pow(s.albedo2.a + 0.5, abs(ptHeight2.a)) - 0.5); s.albedo3.a = saturate(pow(s.albedo3.a + 0.5, abs(ptHeight3.a)) - 0.5); #endif #endif } void SampleNormal(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _NONORMALMAP || _AUTONORMAL s.normSAO0 = half4(0,0, 0, 1); s.normSAO1 = half4(0,0, 0, 1); s.normSAO2 = half4(0,0, 0, 1); s.normSAO3 = half4(0,0, 0, 1); return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half3 absVertNormal = abs(tc.IN.worldNormal); float3x3 t2w = tc.IN.TBN; { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[0], config.cluster0, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[1], config.cluster0, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[2], config.cluster0, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf0 = SurfgradFromTriplanarProjection(tc.pN0, a0.xy, a1.xy, a2.xy); #else s.normSAO0.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN0, a0.xy, a1.xy, a2.xy); #endif s.normSAO0.zw = a0.zw * tc.pN0.x + a1.zw * tc.pN0.y + a2.zw * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[0], config.cluster1, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[1], config.cluster1, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[2], config.cluster1, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf1 = SurfgradFromTriplanarProjection(tc.pN1, a0.xy, a1.xy, a2.xy); #else s.normSAO1.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN1, a0.xy, a1.xy, a2.xy); #endif s.normSAO1.zw = a0.zw * tc.pN1.x + a1.zw * tc.pN1.y + a2.zw * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[0], config.cluster2, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[1], config.cluster2, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[2], config.cluster2, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf2 = SurfgradFromTriplanarProjection(tc.pN2, a0.xy, a1.xy, a2.xy); #else s.normSAO2.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN2, a0.xy, a1.xy, a2.xy); #endif s.normSAO2.zw = a0.zw * tc.pN2.x + a1.zw * tc.pN2.y + a2.zw * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[0], config.cluster3, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[1], config.cluster3, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[2], config.cluster3, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf3 = SurfgradFromTriplanarProjection(tc.pN3, a0.xy, a1.xy, a2.xy); #else s.normSAO3.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN3, a0.xy, a1.xy, a2.xy); #endif s.normSAO3.zw = a0.zw * tc.pN3.x + a1.zw * tc.pN3.y + a2.zw * tc.pN3.z; } #endif #else s.normSAO0 = MICROSPLAT_SAMPLE_NORMAL(config.uv0, config.cluster0, mipLevel).agrb; COUNTSAMPLE s.normSAO0.xy = s.normSAO0.xy * 2 - 1; #if _SURFACENORMALS s.surf0 = ConvertNormal2ToGradient(s.normSAO0.xy); #endif MSBRANCH(weights.y) { s.normSAO1 = MICROSPLAT_SAMPLE_NORMAL(config.uv1, config.cluster1, mipLevel).agrb; COUNTSAMPLE s.normSAO1.xy = s.normSAO1.xy * 2 - 1; #if _SURFACENORMALS s.surf1 = ConvertNormal2ToGradient(s.normSAO1.xy); #endif } #if !_MAX2LAYER MSBRANCH(weights.z) { s.normSAO2 = MICROSPLAT_SAMPLE_NORMAL(config.uv2, config.cluster2, mipLevel).agrb; COUNTSAMPLE s.normSAO2.xy = s.normSAO2.xy * 2 - 1; #if _SURFACENORMALS s.surf2 = ConvertNormal2ToGradient(s.normSAO2.xy); #endif } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.normSAO3 = MICROSPLAT_SAMPLE_NORMAL(config.uv3, config.cluster3, mipLevel).agrb; COUNTSAMPLE s.normSAO3.xy = s.normSAO3.xy * 2 - 1; #if _SURFACENORMALS s.surf3 = ConvertNormal2ToGradient(s.normSAO3.xy); #endif } #endif #endif } void SampleEmis(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USEEMISSIVEMETAL #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.emisMetal0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.emisMetal1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.emisMetal2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.emisMetal3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.emisMetal0 = MICROSPLAT_SAMPLE_EMIS(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.emisMetal1 = MICROSPLAT_SAMPLE_EMIS(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.emisMetal2 = MICROSPLAT_SAMPLE_EMIS(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.emisMetal3 = MICROSPLAT_SAMPLE_EMIS(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } void SampleSpecular(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USESPECULARWORKFLOW #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.specular0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.specular1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.specular2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.specular3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.specular0 = MICROSPLAT_SAMPLE_SPECULAR(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.specular1 = MICROSPLAT_SAMPLE_SPECULAR(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.specular2 = MICROSPLAT_SAMPLE_SPECULAR(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.specular3 = MICROSPLAT_SAMPLE_SPECULAR(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } MicroSplatLayer Sample(Input i, half4 weights, inout Config config, float camDist, float3 worldNormalVertex, DecalOutput decalOutput) { MicroSplatLayer o = (MicroSplatLayer)0; UNITY_INITIALIZE_OUTPUT(MicroSplatLayer,o); RawSamples samples = (RawSamples)0; InitRawSamples(samples); half4 albedo = 0; half4 normSAO = half4(0,0,0,1); half3 surfGrad = half3(0,0,0); half4 emisMetal = 0; half3 specular = 0; float worldHeight = i.worldPos.y; float3 upVector = float3(0,1,0); #if _GLOBALTINT || _GLOBALNORMALS || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _GLOBALSPECULAR float globalSlopeFilter = 1; #if _GLOBALSLOPEFILTER float2 gfilterUV = float2(1 - saturate(dot(worldNormalVertex, upVector) * 0.5 + 0.49), 0.5); globalSlopeFilter = SAMPLE_TEXTURE2D(_GlobalSlopeTex, sampler_Diffuse, gfilterUV).a; #endif #endif // declare outside of branchy areas.. half4 fxLevels = half4(0,0,0,0); half burnLevel = 0; half wetLevel = 0; half3 waterNormalFoam = half3(0, 0, 0); half porosity = 0.4; float streamFoam = 1.0f; half pud = 0; half snowCover = 0; half SSSThickness = 0; half3 SSSTint = half3(1,1,1); float traxBuffer = 0; float3 traxNormal = 0; float2 noiseUV = 0; #if _SPLATFADE MSBRANCHOTHER(1 - saturate(camDist - _SplatFade.y)) { #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE || _SNOWFOOTSTEPS traxBuffer = SampleTraxBuffer(i.worldPos, worldNormalVertex, traxNormal); #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA #if _MICROMESH fxLevels = SampleFXLevels(InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, config.uv), wetLevel, burnLevel, traxBuffer); #elif _MICROVERTEXMESH || _MICRODIGGERMESH || _MEGASPLAT fxLevels = ProcessFXLevels(i.fx, traxBuffer); #else fxLevels = SampleFXLevels(config.uv, wetLevel, burnLevel, traxBuffer); #endif #endif #if _DECAL fxLevels = max(fxLevels, decalOutput.fxLevels); #endif TriplanarConfig tc = (TriplanarConfig)0; UNITY_INITIALIZE_OUTPUT(TriplanarConfig,tc); MIPFORMAT albedoLOD = INITMIPFORMAT MIPFORMAT normalLOD = INITMIPFORMAT MIPFORMAT emisLOD = INITMIPFORMAT MIPFORMAT specLOD = INITMIPFORMAT MIPFORMAT origAlbedoLOD = INITMIPFORMAT; #if _TRIPLANAR && !_DISABLESPLATMAPS PrepTriplanar(i.shaderData.texcoord0, worldNormalVertex, i.worldPos, config, tc, weights, albedoLOD, normalLOD, emisLOD, origAlbedoLOD); tc.IN = i; #endif #if !_TRIPLANAR && !_DISABLESPLATMAPS #if _USELODMIP albedoLOD = ComputeMipLevel(config.uv0.xy, _Diffuse_TexelSize.zw); normalLOD = ComputeMipLevel(config.uv0.xy, _NormalSAO_TexelSize.zw); #if _USEEMISSIVEMETAL emisLOD = ComputeMipLevel(config.uv0.xy, _EmissiveMetal_TexelSize.zw); #endif #if _USESPECULARWORKFLOW specLOD = ComputeMipLevel(config.uv0.xy, _Specular_TexelSize.zw);; #endif #elif _USEGRADMIP albedoLOD = float4(ddx(config.uv0.xy), ddy(config.uv0.xy)); normalLOD = albedoLOD; #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #endif origAlbedoLOD = albedoLOD; #endif #if _PERTEXCURVEWEIGHT SAMPLE_PER_TEX(ptCurveWeight, 19.5, config, half4(0.5,1,1,1)); weights.x = lerp(smoothstep(0.5 - ptCurveWeight0.r, 0.5 + ptCurveWeight0.r, weights.x), weights.x, ptCurveWeight0.r*2); weights.y = lerp(smoothstep(0.5 - ptCurveWeight1.r, 0.5 + ptCurveWeight1.r, weights.y), weights.y, ptCurveWeight1.r*2); weights.z = lerp(smoothstep(0.5 - ptCurveWeight2.r, 0.5 + ptCurveWeight2.r, weights.z), weights.z, ptCurveWeight2.r*2); weights.w = lerp(smoothstep(0.5 - ptCurveWeight3.r, 0.5 + ptCurveWeight3.r, weights.w), weights.w, ptCurveWeight3.r*2); weights = TotalOne(weights); #endif // uvScale before anything #if _PERTEXUVSCALEOFFSET && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); config.uv0.xy = config.uv0.xy * ptUVScale0.rg + ptUVScale0.ba; config.uv1.xy = config.uv1.xy * ptUVScale1.rg + ptUVScale1.ba; #if !_MAX2LAYER config.uv2.xy = config.uv2.xy * ptUVScale2.rg + ptUVScale2.ba; #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = config.uv3.xy * ptUVScale3.rg + ptUVScale3.ba; #endif // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = albedoLOD * ptUVScale0.rgrg * weights.x + albedoLOD * ptUVScale1.rgrg * weights.y + albedoLOD * ptUVScale2.rgrg * weights.z + albedoLOD * ptUVScale3.rgrg * weights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #if _PERTEXUVROTATION && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVRot, 16.5, config, half4(0,0,0,0)); config.uv0.xy = RotateUV(config.uv0.xy, ptUVRot0.x); config.uv1.xy = RotateUV(config.uv1.xy, ptUVRot1.x); #if !_MAX2LAYER config.uv2.xy = RotateUV(config.uv2.xy, ptUVRot2.x); #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = RotateUV(config.uv3.xy, ptUVRot0.x); #endif #endif o.Alpha = 1; #if _POM && !_DISABLESPLATMAPS DoPOM(i, config, tc, albedoLOD, weights, camDist, worldNormalVertex); #endif SampleAlbedo(config, tc, samples, albedoLOD, weights); #if _NOISEHEIGHT ApplyNoiseHeight(samples, config.uv, config, i.worldPos, worldNormalVertex); #endif #if _STREAMS || (_PARALLAX && !_DISABLESPLATMAPS) half earlyHeight = BlendWeights(samples.albedo0.w, samples.albedo1.w, samples.albedo2.w, samples.albedo3.w, weights); #endif #if _STREAMS waterNormalFoam = GetWaterNormal(i, config.uv, worldNormalVertex); DoStreamRefract(config, tc, waterNormalFoam, fxLevels.b, earlyHeight); #endif #if _PARALLAX && !_DISABLESPLATMAPS DoParallax(i, earlyHeight, config, tc, samples, weights, camDist); #endif // Blend results #if _PERTEXINTERPCONTRAST && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptContrasts, 1.5, config, 0.5); half4 contrast = 0.5; contrast.x = ptContrasts0.a; contrast.y = ptContrasts1.a; #if !_MAX2LAYER contrast.z = ptContrasts2.a; #endif #if !_MAX3LAYER || !_MAX2LAYER contrast.w = ptContrasts3.a; #endif contrast = clamp(contrast + _Contrast, 0.0001, 1.0); half cnt = contrast.x * weights.x + contrast.y * weights.y + contrast.z * weights.z + contrast.w * weights.w; half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, cnt); #else half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, _Contrast); #endif #if _HYBRIDHEIGHTBLEND heightWeights = lerp(heightWeights, TotalOne(weights), saturate(camDist/max(1.0, _HybridHeightBlendDistance))); #endif // rescale derivatives after height weighting. Basically, in gradmip mode we blend the mip levels, // but this is before height mapping is sampled, so reblending them after alpha will make sure the other // channels (normal, etc) are sharper, which likely matters most.. #if _PERTEXUVSCALEOFFSET && !_DISABLESPLATMAPS #if _TRIPLANAR #if _USEGRADMIP SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); albedoLOD.d0 = origAlbedoLOD.d0 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d0 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d0 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d0 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d1 = origAlbedoLOD.d1 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d1 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d1 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d1 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d2 = origAlbedoLOD.d2 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d2 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d2 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d2 * ptUVScale3.xyxy * heightWeights.w; normalLOD.d0 = albedoLOD.d0; normalLOD.d1 = albedoLOD.d1; normalLOD.d2 = albedoLOD.d2; #if _USEEMISSIVEMETAL emisLOD.d0 = albedoLOD.d0; emisLOD.d1 = albedoLOD.d1; emisLOD.d2 = albedoLOD.d2; #endif #endif // gradmip #else // not triplanar // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = origAlbedoLOD * ptUVScale0.rgrg * heightWeights.x + origAlbedoLOD * ptUVScale1.rgrg * heightWeights.y + origAlbedoLOD * ptUVScale2.rgrg * heightWeights.z + origAlbedoLOD * ptUVScale3.rgrg * heightWeights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #endif #if _PARALLAX || _STREAMS SampleAlbedo(config, tc, samples, albedoLOD, heightWeights); #endif SampleNormal(config, tc, samples, normalLOD, heightWeights); #if _USEEMISSIVEMETAL SampleEmis(config, tc, samples, emisLOD, heightWeights); #endif #if _USESPECULARWORKFLOW SampleSpecular(config, tc, samples, specLOD, heightWeights); #endif #if _DISTANCERESAMPLE && !_DISABLESPLATMAPS DistanceResample(samples, config, tc, camDist, i.viewDir, fxLevels, albedoLOD, i.worldPos, heightWeights, worldNormalVertex); #endif #if _STARREACHFORMAT samples.normSAO0.w = length(samples.normSAO0.xy); samples.normSAO1.w = length(samples.normSAO1.xy); samples.normSAO2.w = length(samples.normSAO2.xy); samples.normSAO3.w = length(samples.normSAO3.xy); #endif // PerTexture sampling goes here, passing the samples structure #if _PERTEXMICROSHADOWS || _PERTEXFUZZYSHADE SAMPLE_PER_TEX(ptFuzz, 17.5, config, half4(0, 0, 1, 1)); #endif #if _PERTEXMICROSHADOWS #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) { half3 lightDir = GetGlobalLightDirTS(i); half4 microShadows = half4(1,1,1,1); microShadows.x = MicroShadow(lightDir, half3(samples.normSAO0.xy, 1), samples.normSAO0.a, ptFuzz0.a); microShadows.y = MicroShadow(lightDir, half3(samples.normSAO1.xy, 1), samples.normSAO1.a, ptFuzz1.a); microShadows.z = MicroShadow(lightDir, half3(samples.normSAO2.xy, 1), samples.normSAO2.a, ptFuzz2.a); microShadows.w = MicroShadow(lightDir, half3(samples.normSAO3.xy, 1), samples.normSAO3.a, ptFuzz3.a); samples.normSAO0.a *= microShadows.x; samples.normSAO1.a *= microShadows.y; #if !_MAX2LAYER samples.normSAO2.a *= microShadows.z; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a *= microShadows.w; #endif #if _DEBUG_OUTPUT_MICROSHADOWS o.Albedo = BlendWeights(microShadows.x, microShadows.y, microShadows.z, microShadows.a, heightWeights); return o; #endif } #endif #endif // _PERTEXMICROSHADOWS #if _PERTEXFUZZYSHADE samples.albedo0.rgb = FuzzyShade(samples.albedo0.rgb, half3(samples.normSAO0.rg, 1), ptFuzz0.r, ptFuzz0.g, ptFuzz0.b, i.viewDir); samples.albedo1.rgb = FuzzyShade(samples.albedo1.rgb, half3(samples.normSAO1.rg, 1), ptFuzz1.r, ptFuzz1.g, ptFuzz1.b, i.viewDir); #if !_MAX2LAYER samples.albedo2.rgb = FuzzyShade(samples.albedo2.rgb, half3(samples.normSAO2.rg, 1), ptFuzz2.r, ptFuzz2.g, ptFuzz2.b, i.viewDir); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = FuzzyShade(samples.albedo3.rgb, half3(samples.normSAO3.rg, 1), ptFuzz3.r, ptFuzz3.g, ptFuzz3.b, i.viewDir); #endif #endif #if _PERTEXSATURATION && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptSaturattion, 9.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = lerp(MSLuminance(samples.albedo0.rgb), samples.albedo0.rgb, ptSaturattion0.a); samples.albedo1.rgb = lerp(MSLuminance(samples.albedo1.rgb), samples.albedo1.rgb, ptSaturattion1.a); #if !_MAX2LAYER samples.albedo2.rgb = lerp(MSLuminance(samples.albedo2.rgb), samples.albedo2.rgb, ptSaturattion2.a); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = lerp(MSLuminance(samples.albedo3.rgb), samples.albedo3.rgb, ptSaturattion3.a); #endif #endif #if _PERTEXTINT && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptTints, 1.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb *= ptTints0.rgb; samples.albedo1.rgb *= ptTints1.rgb; #if !_MAX2LAYER samples.albedo2.rgb *= ptTints2.rgb; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb *= ptTints3.rgb; #endif #endif #if _PCHEIGHTGRADIENT || _PCHEIGHTHSV || _PCSLOPEGRADIENT || _PCSLOPEHSV ProceduralGradients(i, samples, config, worldHeight, worldNormalVertex); #endif #if _WETNESS || _PUDDLES || _STREAMS porosity = _GlobalPorosity; #endif #if _PERTEXCOLORINTENSITY SAMPLE_PER_TEX(ptCI, 23.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = saturate(samples.albedo0.rgb * (1 + ptCI0.rrr)); samples.albedo1.rgb = saturate(samples.albedo1.rgb * (1 + ptCI1.rrr)); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb * (1 + ptCI2.rrr)); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb * (1 + ptCI3.rrr)); #endif #endif #if (_PERTEXBRIGHTNESS || _PERTEXCONTRAST || _PERTEXPOROSITY || _PERTEXFOAM) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptBC, 3.5, config, half4(1, 1, 1, 1)); #if _PERTEXCONTRAST samples.albedo0.rgb = saturate(((samples.albedo0.rgb - 0.5) * ptBC0.g) + 0.5); samples.albedo1.rgb = saturate(((samples.albedo1.rgb - 0.5) * ptBC1.g) + 0.5); #if !_MAX2LAYER samples.albedo2.rgb = saturate(((samples.albedo2.rgb - 0.5) * ptBC2.g) + 0.5); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(((samples.albedo3.rgb - 0.5) * ptBC3.g) + 0.5); #endif #endif #if _PERTEXBRIGHTNESS samples.albedo0.rgb = saturate(samples.albedo0.rgb + ptBC0.rrr); samples.albedo1.rgb = saturate(samples.albedo1.rgb + ptBC1.rrr); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb + ptBC2.rrr); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb + ptBC3.rrr); #endif #endif #if _PERTEXPOROSITY porosity = BlendWeights(ptBC0.b, ptBC1.b, ptBC2.b, ptBC3.b, heightWeights); #endif #if _PERTEXFOAM streamFoam = BlendWeights(ptBC0.a, ptBC1.a, ptBC2.a, ptBC3.a, heightWeights); #endif #endif #if (_PERTEXNORMSTR || _PERTEXAOSTR || _PERTEXSMOOTHSTR || _PERTEXMETALLIC) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(perTexMatSettings, 2.5, config, half4(1.0, 1.0, 1.0, 0.0)); #endif #if _PERTEXNORMSTR && !_DISABLESPLATMAPS #if _SURFACENORMALS samples.surf0 *= perTexMatSettings0.r; samples.surf1 *= perTexMatSettings1.r; samples.surf2 *= perTexMatSettings2.r; samples.surf3 *= perTexMatSettings3.r; #else samples.normSAO0.xy *= perTexMatSettings0.r; samples.normSAO1.xy *= perTexMatSettings1.r; samples.normSAO2.xy *= perTexMatSettings2.r; samples.normSAO3.xy *= perTexMatSettings3.r; #endif #endif #if _PERTEXAOSTR && !_DISABLESPLATMAPS samples.normSAO0.a = pow(abs(samples.normSAO0.a), perTexMatSettings0.b); samples.normSAO1.a = pow(abs(samples.normSAO1.a), perTexMatSettings1.b); #if !_MAX2LAYER samples.normSAO2.a = pow(abs(samples.normSAO2.a), perTexMatSettings2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a = pow(abs(samples.normSAO3.a), perTexMatSettings3.b); #endif #endif #if _PERTEXSMOOTHSTR && !_DISABLESPLATMAPS samples.normSAO0.b += perTexMatSettings0.g; samples.normSAO1.b += perTexMatSettings1.g; samples.normSAO0.b = saturate(samples.normSAO0.b); samples.normSAO1.b = saturate(samples.normSAO1.b); #if !_MAX2LAYER samples.normSAO2.b += perTexMatSettings2.g; samples.normSAO2.b = saturate(samples.normSAO2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.b += perTexMatSettings3.g; samples.normSAO3.b = saturate(samples.normSAO3.b); #endif #endif #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) #if _PERTEXSSS { SAMPLE_PER_TEX(ptSSS, 18.5, config, half4(1, 1, 1, 1)); // tint, thickness half4 vals = ptSSS0 * heightWeights.x + ptSSS1 * heightWeights.y + ptSSS2 * heightWeights.z + ptSSS3 * heightWeights.w; SSSThickness = vals.a; SSSTint = vals.rgb; } #endif #endif #if _PERTEXRIMLIGHT { SAMPLE_PER_TEX(ptRimA, 26.5, config, half4(1, 1, 1, 1)); SAMPLE_PER_TEX(ptRimB, 27.5, config, half4(1, 1, 1, 0)); samples.emisMetal0.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO0.xy, 1))), max(0.0001, ptRimA0.g)) * ptRimB0.rgb * ptRimB0.a; samples.emisMetal1.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO1.xy, 1))), max(0.0001, ptRimA1.g)) * ptRimB1.rgb * ptRimB1.a; samples.emisMetal2.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO2.xy, 1))), max(0.0001, ptRimA2.g)) * ptRimB2.rgb * ptRimB2.a; samples.emisMetal3.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO3.xy, 1))), max(0.0001, ptRimA3.g)) * ptRimB3.rgb * ptRimB3.a; } #endif #if (((_DETAILNOISE && _PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && _PERTEXDISTANCENOISESTRENGTH)) || (_NORMALNOISE && _PERTEXNORMALNOISESTRENGTH)) && !_DISABLESPLATMAPS ApplyDetailDistanceNoisePerTex(samples, config, camDist, i.worldPos, worldNormalVertex); #endif #if _GLOBALNOISEUV // noise defaults so that a value of 1, 1 is 4 pixels in size and moves the uvs by 1 pixel max. #if _CUSTOMSPLATTEXTURES noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #else noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE ApplyTrax(samples, config, i.worldPos, traxBuffer, traxNormal); #endif #if (_ANTITILEARRAYDETAIL || _ANTITILEARRAYDISTANCE || _ANTITILEARRAYNORMAL) && !_DISABLESPLATMAPS ApplyAntiTilePerTex(samples, config, camDist, i.worldPos, worldNormalVertex, heightWeights); #endif #if _GEOMAP && !_DISABLESPLATMAPS GeoTexturePerTex(samples, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _GLOBALTINT && _PERTEXGLOBALTINTSTRENGTH && !_DISABLESPLATMAPS GlobalTintTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALNORMALS && _PERTEXGLOBALNORMALSTRENGTH && !_DISABLESPLATMAPS GlobalNormalTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && _PERTEXGLOBALSAOMSTRENGTH && !_DISABLESPLATMAPS GlobalSAOMTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && _PERTEXGLOBALEMISSTRENGTH && !_DISABLESPLATMAPS GlobalEmisTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && _PERTEXGLOBALSPECULARSTRENGTH && !_DISABLESPLATMAPS && _USESPECULARWORKFLOW GlobalSpecularTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _PERTEXMETALLIC && !_DISABLESPLATMAPS half metallic = BlendWeights(perTexMatSettings0.a, perTexMatSettings1.a, perTexMatSettings2.a, perTexMatSettings3.a, heightWeights); o.Metallic = metallic; #endif #if _GLITTER && !_DISABLESPLATMAPS DoGlitter(i, samples, config, camDist, worldNormalVertex, i.worldPos); #endif // Blend em.. #if _DISABLESPLATMAPS // If we don't sample from the _Diffuse, then the shader compiler will strip the sampler on // some platforms, which will cause everything to break. So we sample from the lowest mip // and saturate to 1 to keep the cost minimal. Annoying, but the compiler removes the texture // and sampler, even though the sampler is still used. albedo = saturate(UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, float3(0,0,0), 12) + 1); albedo.a = 0.5; // make height something we can blend with for the combined mesh mode, since it still height blends. normSAO = half4(0,0,0,1); #else albedo = BlendWeights(samples.albedo0, samples.albedo1, samples.albedo2, samples.albedo3, heightWeights); normSAO = BlendWeights(samples.normSAO0, samples.normSAO1, samples.normSAO2, samples.normSAO3, heightWeights); #if _SURFACENORMALS surfGrad = BlendWeights(samples.surf0, samples.surf1, samples.surf2, samples.surf3, heightWeights); #endif #if (_USEEMISSIVEMETAL || _PERTEXRIMLIGHT) && !_DISABLESPLATMAPS emisMetal = BlendWeights(samples.emisMetal0, samples.emisMetal1, samples.emisMetal2, samples.emisMetal3, heightWeights); #endif #if _USESPECULARWORKFLOW && !_DISABLESPLATMAPS specular = BlendWeights(samples.specular0, samples.specular1, samples.specular2, samples.specular3, heightWeights); #endif #if _PERTEXOUTLINECOLOR SAMPLE_PER_TEX(ptOutlineColor, 28.5, config, half4(0.5, 0.5, 0.5, 1)); half4 outlineColor = BlendWeights(ptOutlineColor0, ptOutlineColor1, ptOutlineColor2, ptOutlineColor3, heightWeights); half4 tstr = saturate(abs(heightWeights - 0.5) * 2); half transitionBlend = min(min(min(tstr.x, tstr.y), tstr.z), tstr.w); albedo.rgb = lerp(albedo.rgb * outlineColor.rgb * 2, albedo.rgb, outlineColor.a * transitionBlend); #endif #endif #if _MESHOVERLAYSPLATS || _MESHCOMBINED o.Alpha = 1.0; if (config.uv0.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.x; else if (config.uv1.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.y; else if (config.uv2.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.z; else if (config.uv3.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.w; #endif // effects which don't require per texture adjustments and are part of the splats sample go here. // Often, as an optimization, you can compute the non-per tex version of above effects here.. #if ((_DETAILNOISE && !_PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && !_PERTEXDISTANCENOISESTRENGTH) || (_NORMALNOISE && !_PERTEXNORMALNOISESTRENGTH)) ApplyDetailDistanceNoise(albedo.rgb, normSAO, surfGrad, config, camDist, i.worldPos, worldNormalVertex); #endif #if _SPLATFADE } #endif #if _SPLATFADE float2 sfDX = ddx(config.uv * _UVScale); float2 sfDY = ddy(config.uv * _UVScale); MSBRANCHOTHER(camDist - _SplatFade.x) { float falloff = saturate(InverseLerp(_SplatFade.x, _SplatFade.y, camDist)); half4 sfalb = SAMPLE_TEXTURE2D_ARRAY_GRAD(_Diffuse, sampler_Diffuse, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY); COUNTSAMPLE albedo.rgb = lerp(albedo.rgb, sfalb.rgb, falloff); #if !_NONORMALMAP && !_AUTONORMAL half4 sfnormSAO = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalSAO, sampler_NormalSAO, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY).agrb; COUNTSAMPLE sfnormSAO.xy = sfnormSAO.xy * 2 - 1; normSAO = lerp(normSAO, sfnormSAO, falloff); #if _SURFACENORMALS surfGrad = lerp(surfGrad, ConvertNormal2ToGradient(sfnormSAO.xy), falloff); #endif #endif } #endif #if _AUTONORMAL float3 autoNormal = HeightToNormal(albedo.a * _AutoNormalHeightScale, i.worldPos); normSAO.xy = autoNormal; normSAO.z = 0; normSAO.w = (autoNormal.z * autoNormal.z); #endif #if _MESHCOMBINED SampleMeshCombined(albedo, normSAO, surfGrad, emisMetal, specular, o.Alpha, SSSThickness, SSSTint, config, heightWeights); #endif #if _ISOBJECTSHADER SampleObjectShader(i, albedo, normSAO, surfGrad, emisMetal, specular, config); #endif #if _GEOMAP GeoTexture(albedo.rgb, normSAO, surfGrad, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _SCATTER ApplyScatter( #if _MEGASPLAT config, #endif i, albedo, normSAO, surfGrad, config.uv, camDist); #endif #if _DECAL DoDecalBlend(decalOutput, albedo, normSAO, surfGrad, emisMetal, i.uv_Control0); #endif #if _GLOBALTINT && !_PERTEXGLOBALTINTSTRENGTH GlobalTintTexture(albedo.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif #if _VSGRASSMAP VSGrassTexture(albedo.rgb, config, camDist); #endif #if _GLOBALNORMALS && !_PERTEXGLOBALNORMALSTRENGTH GlobalNormalTexture(normSAO, surfGrad, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && !_PERTEXGLOBALSAOMSTRENGTH GlobalSAOMTexture(normSAO, emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && !_PERTEXGLOBALEMISSTRENGTH GlobalEmisTexture(emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && !_PERTEXGLOBALSPECULARSTRENGTH && _USESPECULARWORKFLOW GlobalSpecularTexture(specular.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif o.Albedo = albedo.rgb; o.Height = albedo.a; #if _NONORMALMAP o.Normal = half3(0,0,1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #elif _SURFACENORMALS o.Normal = ResolveNormalFromSurfaceGradient(surfGrad); o.Normal = mul(GetTBN(i), o.Normal); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #else o.Normal = half3(normSAO.xy, 1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #endif #if _USEEMISSIVEMETAL || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _PERTEXRIMLIGHT #if _USEEMISSIVEMETAL emisMetal.rgb *= _EmissiveMult; #endif o.Emission += emisMetal.rgb; o.Metallic = emisMetal.a; #endif #if _USESPECULARWORKFLOW o.Specular = specular; #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA pud = DoStreams(i, o, fxLevels, config.uv, porosity, waterNormalFoam, worldNormalVertex, streamFoam, wetLevel, burnLevel, i.worldPos); #endif #if _SNOW snowCover = DoSnow(i, o, config.uv, WorldNormalVector(i, o.Normal), worldNormalVertex, i.worldPos, pud, porosity, camDist, config, weights, SSSTint, SSSThickness, traxBuffer, traxNormal); #endif #if _PERTEXSSS || _MESHCOMBINEDUSESSS || (_SNOW && _SNOWSSS) { half3 worldView = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); o.Emission += ComputeSSS(i, worldView, WorldNormalVector(i, o.Normal), SSSTint, SSSThickness, _SSSDistance, _SSSScale, _SSSPower); } #endif #if _SNOWGLITTER DoSnowGlitter(i, config, o, camDist, worldNormalVertex, snowCover); #endif #if _WINDPARTICULATE || _SNOWPARTICULATE DoWindParticulate(i, o, config, weights, camDist, worldNormalVertex, snowCover); #endif o.Normal.z = sqrt(1 - saturate(dot(o.Normal.xy, o.Normal.xy))); #if _SPECULARFADE { float specFade = saturate((i.worldPos.y - _SpecularFades.x) / max(_SpecularFades.y - _SpecularFades.x, 0.0001)); o.Metallic *= specFade; o.Smoothness *= specFade; } #endif #if _VSSHADOWMAP VSShadowTexture(o, i, config, camDist); #endif #if _TOONWIREFRAME ToonWireframe(config.uv, o.Albedo, camDist); #endif #if _SEETHROUGHSHADER SeethroughShader(o.Albedo, o.Emission, o.Alpha, i.worldPos, o.Normal, i.worldNormal); #endif #if _DEBUG_TRAXBUFFER ClearAllButAlbedo(o, half3(traxBuffer, 0, 0) * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMALVERTEX ClearAllButAlbedo(o, worldNormalVertex * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMAL ClearAllButAlbedo(o, WorldNormalVector(i, o.Normal) * saturate(o.Albedo.z+1)); #endif #if _DEBUG_MEGABARY && _MEGASPLAT o.Albedo = i.baryWeights.xyz; #endif return o; } void SampleSplats(float2 controlUV, inout half4 w0, inout half4 w1, inout half4 w2, inout half4 w3, inout half4 w4, inout half4 w5, inout half4 w6, inout half4 w7) { #if _CUSTOMSPLATTEXTURES #if !_MICROMESH controlUV = (controlUV * (_CustomControl0_TexelSize.zw - 1.0f) + 0.5f) * _CustomControl0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_CustomControl0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_CustomControl1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_CustomControl2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_CustomControl3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_CustomControl4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_CustomControl5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_CustomControl6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_CustomControl7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #else #if !_MICROMESH controlUV = (controlUV * (_Control0_TexelSize.zw - 1.0f) + 0.5f) * _Control0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_Control0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_Control1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_Control2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_Control3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_Control4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_Control5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_Control6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_Control7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #endif } MicroSplatLayer SurfImpl(Input i, float3 worldNormalVertex) { #if _MEGANOUV i.uv_Control0 = i.worldPos.xz; #endif float camDist = distance(_WorldSpaceCameraPos, i.worldPos); #if _FORCELOCALSPACE worldNormalVertex = mul((float3x3)GetWorldToObjectMatrix(), worldNormalVertex).xyz; i.worldPos = i.worldPos - mul(GetObjectToWorldMatrix(), float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _ORIGINSHIFT i.worldPos = i.worldPos + mul(_GlobalOriginMTX, float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _DEBUG_USE_TOPOLOGY i.worldPos = SAMPLE_TEXTURE2D(_DebugWorldPos, sampler_Diffuse, i.uv_Control0); worldNormalVertex = SAMPLE_TEXTURE2D(_DebugWorldNormal, sampler_Diffuse, i.uv_Control0); i.worldHeight = i.worldPos.y; #endif #if _ALPHABELOWHEIGHT && !_TBDISABLEALPHAHOLES ClipWaterLevel(i.worldPos); #endif #if !_TBDISABLEALPHAHOLES && defined(_ALPHATEST_ON) // UNITY 2019.3 holes ClipHoles(i.uv_Control0); #endif float2 origUV = i.uv_Control0; #if _MICROMESH && _MESHUV2 float2 controlUV = i.uv2_Diffuse; #else float2 controlUV = i.uv_Control0; #endif #if _MICROMESH controlUV = InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, controlUV); #endif half4 weights = half4(1,0,0,0); Config config = (Config)0; UNITY_INITIALIZE_OUTPUT(Config,config); config.uv = origUV; DecalOutput decalOutput = (DecalOutput)0; #if _DECAL decalOutput = DoDecals(i.uv_Control0, i.worldPos, camDist, worldNormalVertex); #endif #if _SURFACENORMALS // Initialize the surface gradient basis vectors ConstructSurfaceGradientTBN(i); #endif #if _SPLATFADE MSBRANCHOTHER(_SplatFade.y - camDist) #endif // _SPLATFADE { #if !_DISABLESPLATMAPS // Sample the splat data, from textures or vertices, and setup the config.. #if _MICRODIGGERMESH DiggerSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLAT MegaSplatVertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLATTEXTURE MegaSplatTextureSetup(controlUV, weights, origUV, config, i.worldPos, decalOutput); #elif _MICROVERTEXMESH VertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif !_PROCEDURALTEXTURE || _PROCEDURALBLENDSPLATS half4 w0 = 0; half4 w1 = 0; half4 w2 = 0; half4 w3 = 0; half4 w4 = 0; half4 w5 = 0; half4 w6 = 0; half4 w7 = 0; SampleSplats(controlUV, w0, w1, w2, w3, w4, w5, w6, w7); Setup(weights, origUV, config, w0, w1, w2, w3, w4, w5, w6, w7, i.worldPos, decalOutput); #endif #if _PROCEDURALTEXTURE float3 procNormal = worldNormalVertex; float3 worldPos = i.worldPos; ProceduralSetup(i, worldPos, i.worldHeight, procNormal, i.worldUpVector, weights, origUV, config, ddx(origUV), ddy(origUV), ddx(worldPos), ddy(worldPos), decalOutput); #endif #else // _DISABLESPLATMAPS Setup(weights, origUV, config, half4(1,0,0,0), 0, 0, 0, 0, 0, 0, 0, i.worldPos, decalOutput); #endif #if _SLOPETEXTURE SlopeTexture(config, weights, worldNormalVertex); #endif } // _SPLATFADE else case #if _TOONFLATTEXTURE float2 quv = floor(origUV * _ToonTerrainSize); float2 fuv = frac(origUV * _ToonTerrainSize); #if !_TOONFLATTEXTUREQUAD quv = Hash2D((fuv.x > fuv.y) ? quv : quv * 0.333); #endif float2 uvq = quv / _ToonTerrainSize; config.uv0.xy = uvq; config.uv1.xy = uvq; config.uv2.xy = uvq; config.uv3.xy = uvq; #endif #if (_TEXTURECLUSTER2 || _TEXTURECLUSTER3) && !_DISABLESPLATMAPS PrepClusters(origUV, config, i.worldPos, worldNormalVertex); #endif #if (_ALPHAHOLE || _ALPHAHOLETEXTURE) && !_DISABLESPLATMAPS && !_TBDISABLEALPHAHOLES ClipAlphaHole(config, weights); #endif MicroSplatLayer l = Sample(i, weights, config, camDist, worldNormalVertex, decalOutput); // On windows, sometimes the shared samplers gets stripped, so we have to do this crap. // We sample from the lowest mip, so it shouldn't cost much, but still, I hate this, wtf.. float stripVal = saturate(SAMPLE_TEXTURE2D_LOD(_Diffuse, sampler_Diffuse, config.uv0, 11).r + 2); stripVal *= saturate(SAMPLE_TEXTURE2D_LOD(_NormalSAO, sampler_NormalSAO, config.uv0, 11).r + 2); l.Albedo *= stripVal; l.Normal *= stripVal; #if _PROCEDURALTEXTURE ProceduralTextureDebugOutput(l, weights, config); #endif return l; } float4 ConstructTerrainTangent(float3 normal, float3 positiveZ) { // Consider a flat terrain. It should have tangent be (1, 0, 0) and bitangent be (0, 0, 1) as the UV of the terrain grid mesh is a scale of the world XZ position. // In CreateTangentToWorld function (in SpaceTransform.hlsl), it is cross(normal, tangent) * sgn for the bitangent vector. // It is not true in a left-handed coordinate system for the terrain bitangent, if we provide 1 as the tangent.w. It would produce (0, 0, -1) instead of (0, 0, 1). // Also terrain's tangent calculation was wrong in a left handed system because cross((0,0,1), terrainNormalOS) points to the wrong direction as negative X. // Therefore all the 4 xyzw components of the tangent needs to be flipped to correct the tangent frame. // (See TerrainLitData.hlsl - GetSurfaceAndBuiltinData) float3 tangent = normalize(cross(normal, positiveZ)); return float4(tangent, -1); } void TerrainInstancing(inout float4 vertex, inout float3 normal, inout float2 uv) { #if _MICROTERRAIN && defined(UNITY_INSTANCING_ENABLED) && !_TERRAINBLENDABLESHADER float2 patchVertex = vertex.xy; float4 instanceData = UNITY_ACCESS_INSTANCED_PROP(Terrain, _TerrainPatchInstanceData); float2 sampleCoords = (patchVertex.xy + instanceData.xy) * instanceData.z; // (xy + float2(xBase,yBase)) * skipScale uv = sampleCoords * _TerrainHeightmapRecipSize.zw; float2 sampleUV = (uv / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleUV, 0)); vertex.xz = sampleCoords * _TerrainHeightmapScale.xz; vertex.y = height * _TerrainHeightmapScale.y; normal = float3(0, 1, 0); #endif } void ApplyMeshModification(inout VertexData input) { #if _MICROTERRAIN && !_TERRAINBLENDABLESHADER float2 uv = input.texcoord0.xy; TerrainInstancing(input.vertex, input.normal, uv); input.texcoord0.xy = uv; #endif #if _PERPIXNORMAL && !_TERRAINBLENDABLESHADER input.normal = float3(0,1,0); #endif #if _MICROVERSEPREVIEW float4 recipSize = _TerrainHeightmapTexture_TexelSize; recipSize.zw = (1.0f / (_TerrainHeightmapTexture_TexelSize.zw-1)); float2 sampleCoords = (input.texcoord0.xy / recipSize.zw + 0.5f) * recipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleCoords, 0)); input.vertex.xyz += float3(0,1,0) * height * _TerrainHeight * 2; #endif } // called by the template, so we can remove tangent from VertexData void ApplyTerrainTangent(inout VertexToPixel input) { #if (_MICROTERRAIN || _PERPIXNORMAL) && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif // digger meshes ain't got no tangent either.. #if _MICRODIGGERMESH && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif } void ModifyVertex(inout VertexData v, inout ExtraV2F d) { ApplyMeshModification(v); #if _MICROVERTEXMESH || _MICRODIGGERMESH EncodeVertexWorkflow(v, d); #elif _MEGASPLAT EncodeMegaSplatVertex(v, d); #endif } void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d) { #if _MICROVERSEPREVIEW v.vertex.y = OffsetVertex(v, d).y; #elif _TESSDISTANCE || _TESSEDGE v.vertex.xyz += OffsetVertex(v, d); #endif } float3 GetTessFactors () { #if _TESSEDGE return float3(_TessData1.x, _TessData1.w, 0); #endif #if _TESSDISTANCE return float3(_TessData2.x, _TessData2.y, _TessData1.x); #endif return 0; } void SurfaceFunction(inout Surface o, inout ShaderData d) { float3 worldNormalVertex = d.worldSpaceNormal; #if _MICROVERSEPREVIEW float2 sampleCoords = d.texcoord0.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif (defined(UNITY_INSTANCING_ENABLED) && _MICROTERRAIN && !_TERRAINBLENDABLESHADER) float2 sampleCoords = (d.texcoord0.xy / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif _PERPIXNORMAL && (_MICROTERRAIN || _MICROMESHTERRAIN) && !_TERRAINBLENDABLESHADER float2 sampleCoords = (d.texcoord0.xy * _PerPixelNormal_TexelSize.zw + 0.5f) * _PerPixelNormal_TexelSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_PerPixelNormal, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomTangent, geomNormal)) * -1; #if _MICROMESHTERRAIN geomBitangent *= -1; #endif worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #endif #if _TOONPOLYEDGE FlatShade(d); #endif Input i = DescToInput(d); #if _SRPTERRAINBLEND MicroSplatLayer l = BlendWithTerrain(d); #if _DEBUG_WORLDNORMAL ClearAllButAlbedo(l, normalize(TangentToWorldSpace(d, l.Normal)) * saturate(l.Albedo.z+1)); #endif #else MicroSplatLayer l = SurfImpl(i, worldNormalVertex); #endif DoDebugOutput(l); o.Albedo = l.Albedo; o.Normal = l.Normal; o.Smoothness = l.Smoothness; o.Occlusion = l.Occlusion; o.Metallic = l.Metallic; o.Emission = l.Emission; #if _USESPECULARWORKFLOW o.Specular = l.Specular; #endif o.Height = l.Height; o.Alpha = l.Alpha; } // SHADERDESC ShaderData CreateShaderData(VertexToPixel i) { ShaderData d = (ShaderData)0; d.worldSpacePosition = i.worldPos; d.worldSpaceNormal = i.worldNormal; d.worldSpaceTangent = i.worldTangent.xyz; float3 bitangent = cross(i.worldTangent.xyz, i.worldNormal) * i.worldTangent.w * -1; d.TBNMatrix = float3x3(d.worldSpaceTangent, bitangent, d.worldSpaceNormal); d.worldSpaceViewDir = normalize(_WorldSpaceCameraPos - i.worldPos); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); d.texcoord0 = i.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER d.texcoord1 = i.texcoord1; // d.texcoord2 = i.texcoord2; #endif // d.texcoord3 = i.texcoord3; // d.vertexColor = i.vertexColor; // these rarely get used, so we back transform them. Usually will be stripped. #if _HDRP // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(GetCameraRelativePositionWS(i.worldPos), 1)); #else // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(i.worldPos, 1)); #endif // d.localSpaceNormal = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldNormal)); // d.localSpaceTangent = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldTangent.xyz)); // d.screenPos = i.screenPos; // d.screenUV = i.screenPos.xy / i.screenPos.w; // d.extraV2F0 = i.extraV2F0; // d.extraV2F1 = i.extraV2F1; // d.extraV2F2 = i.extraV2F2; // d.extraV2F3 = i.extraV2F3; // d.extraV2F4 = i.extraV2F4; // d.extraV2F5 = i.extraV2F5; // d.extraV2F6 = i.extraV2F6; // d.extraV2F7 = i.extraV2F7; return d; } // CHAINS void ChainModifyVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; ModifyVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainModifyTessellatedVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; // d.extraV2F0 = v2p.extraV2F0; // d.extraV2F1 = v2p.extraV2F1; // d.extraV2F2 = v2p.extraV2F2; // d.extraV2F3 = v2p.extraV2F3; // d.extraV2F4 = v2p.extraV2F4; // d.extraV2F5 = v2p.extraV2F5; // d.extraV2F6 = v2p.extraV2F6; // d.extraV2F7 = v2p.extraV2F7; ModifyTessellatedVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainFinalColorForward(inout Surface l, inout ShaderData d, inout half4 color) { } void ChainFinalGBufferStandard(inout Surface s, inout ShaderData d, inout half4 GBuffer0, inout half4 GBuffer1, inout half4 GBuffer2, inout half4 outEmission, inout half4 outShadowMask) { } #if _PASSSHADOW float3 _LightDirection; float3 _LightPosition; #endif // vertex shader VertexToPixel Vert (VertexData v) { VertexToPixel o = (VertexToPixel)0; UNITY_SETUP_INSTANCE_ID(v); UNITY_TRANSFER_INSTANCE_ID(v, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #if !_TESSELLATION_ON ChainModifyVertex(v, o); #endif o.texcoord0 = v.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.texcoord1 = v.texcoord1; // o.texcoord2 = v.texcoord2; #endif // o.texcoord3 = v.texcoord3; // o.vertexColor = v.vertexColor; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz); o.worldPos = TransformObjectToWorld(v.vertex.xyz); o.worldNormal = TransformObjectToWorldNormal(v.normal); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float2 uv1 = v.texcoord1.xy; float2 uv2 = v.texcoord2.xy; o.worldTangent = float4(TransformObjectToWorldDir(v.tangent.xyz), v.tangent.w); #else float2 uv1 = v.texcoord0.xy; float2 uv2 = uv1; #endif // MS Only ApplyTerrainTangent(o); #if _PASSSHADOW #if _CASTING_PUNCTUAL_LIGHT_SHADOW float3 lightDirectionWS = normalize(_LightPosition - o.worldPos); #else float3 lightDirectionWS = _LightDirection; #endif // Define shadow pass specific clip position for Universal o.pos = TransformWorldToHClip(ApplyShadowBias(o.worldPos, o.worldNormal, lightDirectionWS)); #if UNITY_REVERSED_Z o.pos.z = min(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #else o.pos.z = max(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #endif #elif _PASSMETA #if _MICROTERRAIN o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), v.texcoord0.xy, v.texcoord0.xy, unity_LightmapST, unity_DynamicLightmapST); #else o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), uv1, uv2, unity_LightmapST, unity_DynamicLightmapST); #endif #else o.pos = TransformWorldToHClip(o.worldPos); #endif // o.screenPos = ComputeScreenPos(o.pos, _ProjectionParams.x); #if _PASSFORWARD || _PASSGBUFFER #if _MICROTERRAIN OUTPUT_LIGHTMAP_UV(v.texcoord0.xy, unity_LightmapST, o.lightmapUV); #else OUTPUT_LIGHTMAP_UV(uv1, unity_LightmapST, o.lightmapUV); #endif OUTPUT_SH(o.worldNormal, o.sh); #if defined(DYNAMICLIGHTMAP_ON) o.dynamicLightmapUV.xy = uv2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; #endif #endif #ifdef VARYINGS_NEED_FOG_AND_VERTEX_LIGHT half fogFactor = 0; #if defined(_FOG_FRAGMENT) fogFactor = ComputeFogFactor(o.pos.z); #endif #if _BAKEDLIT half3 vertexLight = VertexLighting(o.worldPos, o.worldNormal); o.fogFactorAndVertexLight = half4(fogFactor, vertexLight); #else o.fogFactorAndVertexLight = half4(fogFactor, 0,0,0); #endif #endif #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) o.shadowCoord = GetShadowCoord(vertexInput); #endif return o; } // fragment shader half4 Frag (VertexToPixel IN) : SV_Target { UNITY_SETUP_INSTANCE_ID(IN); ShaderData d = CreateShaderData(IN); Surface l = (Surface)0; l.Albedo = half3(0.5, 0.5, 0.5); l.Normal = float3(0,0,1); l.Occlusion = 1; l.Alpha = 1; SurfaceFunction(l, d); MetaInput metaInput = (MetaInput)0; metaInput.Albedo = l.Albedo; metaInput.Emission = l.Emission; return MetaFragment(metaInput); } ENDHLSL } Pass { Name "DepthNormals" Tags { "LightMode" = "DepthNormals" } // Render State Cull Back Blend One Zero ZTest LEqual ZWrite On HLSLPROGRAM #pragma vertex Vert #pragma fragment Frag #pragma target 3.5 #pragma prefer_hlslcc gles #pragma exclude_renderers d3d11_9x #pragma multi_compile_fog #pragma multi_compile_instancing #pragma multi_compile _ DOTS_INSTANCING_ON #pragma multi_compile_local _ _ALPHATEST_ON #pragma multi_compile_fragment _ _WRITE_RENDERING_LAYERS #define SHADERPASS SHADERPASS_DEPTHNORMALSONLY #define _PASSDEPTH 1 #define _PASSDEPTHNORMALS 1 #define _MICROSPLAT 1 #define _MICROPOLARISMESH 1 #define _USEGRADMIP 1 #define _PERTEXUVSCALEOFFSET 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _MSRENDERLOOP_UNITYLD 1 #define _MSRENDERLOOP_UNITYURP2020 1 #define _MSRENDERLOOP_UNITYURP2021 1 #define _MSRENDERLOOP_UNITYURP2022 1 #define _URP 1 // this has to be here or specular color will be ignored. Not in SG code #if _SIMPLELIT #define _SPECULAR_COLOR #endif // Includes #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Version.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl" #include "Packages/com.unity.shadergraph/ShaderGraphLibrary/ShaderVariablesFunctions.hlsl" #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, float3x3(d.worldSpaceTangent, cross(d.worldSpaceTangent, d.worldSpaceNormal), d.worldSpaceNormal)) #define UnityObjectToWorldNormal(normal) mul(GetObjectToWorldMatrix(), normal) #define _WorldSpaceLightPos0 _MainLightPosition #define UNITY_DECLARE_TEX2D(name) TEXTURE2D(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2D_NOSAMPLER(name) TEXTURE2D(name); #define UNITY_DECLARE_TEX2DARRAY(name) TEXTURE2D_ARRAY(name); SAMPLER(sampler##name); #define UNITY_DECLARE_TEX2DARRAY_NOSAMPLER(name) TEXTURE2D_ARRAY(name); #define UNITY_SAMPLE_TEX2DARRAY(tex,coord) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, coord.xy, coord.z) #define UNITY_SAMPLE_TEX2DARRAY_LOD(tex,coord,lod) SAMPLE_TEXTURE2D_ARRAY_LOD(tex, sampler##tex, coord.xy, coord.z, lod) #define UNITY_SAMPLE_TEX2D(tex, coord) SAMPLE_TEXTURE2D(tex, sampler##tex, coord) #define UNITY_SAMPLE_TEX2D_SAMPLER(tex, samp, coord) SAMPLE_TEXTURE2D(tex, sampler##samp, coord) #if defined(UNITY_COMPILER_HLSL) #define UNITY_INITIALIZE_OUTPUT(type,name) name = (type)0; #else #define UNITY_INITIALIZE_OUTPUT(type,name) #endif #define sampler2D_float sampler2D #define sampler2D_half sampler2D // data across stages, stripped like the above. struct VertexToPixel { float4 pos : SV_POSITION; float3 worldPos : TEXCOORD0; float3 worldNormal : TEXCOORD1; float4 worldTangent : TEXCOORD2; float4 texcoord0 : TEXCCOORD3; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 texcoord1 : TEXCCOORD4; // float4 texcoord2 : TEXCCOORD5; #endif // float4 texcoord3 : TEXCCOORD6; // float4 screenPos : TEXCOORD7; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD13; // float4 extraV2F1 : TEXCOORD14; // float4 extraV2F2 : TEXCOORD15; // float4 extraV2F3 : TEXCOORD16; // float4 extraV2F4 : TEXCOORD17; // float4 extraV2F5 : TEXCOORD18; // float4 extraV2F6 : TEXCOORD19; // float4 extraV2F7 : TEXCOORD20; #if defined(LIGHTMAP_ON) float2 lightmapUV : TEXCOORD8; #endif #if defined(DYNAMICLIGHTMAP_ON) float2 dynamicLightmapUV : TEXCOORD9; #endif #if !defined(LIGHTMAP_ON) float3 sh : TEXCOORD10; #endif float4 fogFactorAndVertexLight : TEXCOORD11; float4 shadowCoord : TEXCOORD12; #if UNITY_ANY_INSTANCING_ENABLED uint instanceID : CUSTOM_INSTANCE_ID; #endif #if (defined(UNITY_STEREO_MULTIVIEW_ENABLED)) || (defined(UNITY_STEREO_INSTANCING_ENABLED) && (defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE))) uint stereoTargetEyeIndexAsBlendIdx0 : BLENDINDICES0; #endif #if (defined(UNITY_STEREO_INSTANCING_ENABLED)) uint stereoTargetEyeIndexAsRTArrayIdx : SV_RenderTargetArrayIndex; #endif #if defined(SHADER_STAGE_FRAGMENT) && defined(VARYINGS_NEED_CULLFACE) FRONT_FACE_TYPE cullFace : FRONT_FACE_SEMANTIC; #endif }; // TEMPLATE_SHARED // data describing the user output of a pixel struct Surface { half3 Albedo; half Height; half3 Normal; half Smoothness; half3 Emission; half Metallic; half3 Specular; half Occlusion; half Alpha; // HDRP Only half SpecularOcclusion; half SubsurfaceMask; half Thickness; half CoatMask; half Anisotropy; half IridescenceMask; half IridescenceThickness; }; // data the user might need, this will grow to be big. But easy to strip struct ShaderData { float3 localSpacePosition; float3 localSpaceNormal; float3 localSpaceTangent; float3 worldSpacePosition; float3 worldSpaceNormal; float3 worldSpaceTangent; float3 worldSpaceViewDir; float3 tangentSpaceViewDir; float4 texcoord0; float4 texcoord1; float4 texcoord2; float4 texcoord3; float2 screenUV; float4 screenPos; float4 vertexColor; float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; float3x3 TBNMatrix; }; struct VertexData { #if SHADER_TARGET > 30 && _PLANETCOMPUTE // // uint vertexID : SV_VertexID; #endif float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD4; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD5; // Add Precomputed Velocity (Alembic computes velocities on runtime side). #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID }; struct TessVertex { float4 vertex : INTERNALTESSPOS; float3 normal : NORMAL; float4 texcoord0 : TEXCOORD0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float4 tangent : TANGENT; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; #endif // float4 texcoord3 : TEXCOORD3; // float4 vertexColor : COLOR; // float4 extraV2F0 : TEXCOORD4; // float4 extraV2F1 : TEXCOORD5; // float4 extraV2F2 : TEXCOORD6; // float4 extraV2F3 : TEXCOORD7; // float4 extraV2F4 : TEXCOORD8; // float4 extraV2F5 : TEXCOORD9; // float4 extraV2F6 : TEXCOORD10; // float4 extraV2F7 : TEXCOORD11; #if _HDRP && (_PASSMOTIONVECTOR || (_PASSFORWARD && defined(_WRITE_TRANSPARENT_MOTION_VECTOR))) float3 previousPositionOS : TEXCOORD12; // Contain previous transform position (in case of skinning for example) #if defined (_ADD_PRECOMPUTED_VELOCITY) float3 precomputedVelocity : TEXCOORD13; #endif #endif UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; struct ExtraV2F { float4 extraV2F0; float4 extraV2F1; float4 extraV2F2; float4 extraV2F3; float4 extraV2F4; float4 extraV2F5; float4 extraV2F6; float4 extraV2F7; }; float3 WorldToTangentSpace(ShaderData d, float3 normal) { return mul(d.TBNMatrix, normal); } float3 TangentToWorldSpace(ShaderData d, float3 normal) { return mul(normal, d.TBNMatrix); } // in this case, make standard more like SRPs, because we can't fix // GetWorldToObjectMatrix() in HDRP, since it already does macro-fu there #if _STANDARD float3 TransformWorldToObject(float3 p) { return mul(GetWorldToObjectMatrix(), float4(p, 1)); }; float3 TransformObjectToWorld(float3 p) { return mul(GetObjectToWorldMatrix(), float4(p, 1)); }; float4 TransformWorldToObject(float4 p) { return mul(GetWorldToObjectMatrix(), p); }; float4 TransformObjectToWorld(float4 p) { return mul(GetObjectToWorldMatrix(), p); }; float4x4 GetWorldToObjectMatrix() { return GetWorldToObjectMatrix(); } float4x4 GetObjectToWorldMatrix() { return GetObjectToWorldMatrix(); } #endif float3 GetCameraWorldPosition() { #if _HDRP return GetCameraRelativePositionWS(_WorldSpaceCameraPos); #else return _WorldSpaceCameraPos; #endif } #if _HDRP half3 UnpackNormalmapRGorAG(half4 packednormal) { // This do the trick packednormal.x *= packednormal.w; half3 normal; normal.xy = packednormal.xy * 2 - 1; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } half3 UnpackNormal(half4 packednormal) { #if defined(UNITY_NO_DXT5nm) return packednormal.xyz * 2 - 1; #else return UnpackNormalmapRGorAG(packednormal); #endif } #endif #if _HDRP || _URP half3 UnpackScaleNormal(half4 packednormal, half scale) { #ifndef UNITY_NO_DXT5nm // Unpack normal as DXT5nm (1, y, 1, x) or BC5 (x, y, 0, 1) // Note neutral texture like "bump" is (0, 0, 1, 1) to work with both plain RGB normal and DXT5nm/BC5 packednormal.x *= packednormal.w; #endif half3 normal; normal.xy = (packednormal.xy * 2 - 1) * scale; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); return normal; } #endif void GetSun(out float3 lightDir, out float3 color) { lightDir = float3(0.5, 0.5, 0); color = 1; #if _HDRP if (_DirectionalLightCount > 0) { DirectionalLightData light = _DirectionalLightDatas[0]; lightDir = -light.forward.xyz; color = light.color; } #elif _STANDARD lightDir = normalize(_WorldSpaceLightPos0.xyz); color = _LightColor0.rgb; #elif _URP Light light = GetMainLight(); lightDir = light.direction; color = light.color; #endif } CBUFFER_START(UnityPerMaterial) #if _MESHSUBARRAY half4 _MeshSubArrayIndexes; #endif float4 _Diffuse_TexelSize; float4 _NormalSAO_TexelSize; #if _HYBRIDHEIGHTBLEND float _HybridHeightBlendDistance; #endif #if _PACKINGHQ float4 _SmoothAO_TexelSize; #endif #ifdef _ALPHATEST_ON float4 _TerrainHolesTexture_TexelSize; #endif #if _USESPECULARWORKFLOW float4 _Specular_TexelSize; #endif #if _USEEMISSIVEMETAL float4 _EmissiveMetal_TexelSize; #endif #if _USEEMISSIVEMETAL half _EmissiveMult; #endif #if _AUTONORMAL half _AutoNormalHeightScale; #endif float4 _UVScale; // scale and offset half _Contrast; #if _VSSHADOWMAP float4 gVSSunDirection; #endif #if _FORCELOCALSPACE && _PLANETVECTORS float4x4 _PQSToLocal; #endif #if _ORIGINSHIFT float4x4 _GlobalOriginMTX; #endif float4 _Control0_TexelSize; #if _CUSTOMSPLATTEXTURES float4 _CustomControl0_TexelSize; #endif float4 _PerPixelNormal_TexelSize; #if _CONTROLNOISEUV || _GLOBALNOISEUV float2 _NoiseUVParams; #endif float4 _PerTexProps_TexelSize; #if _SURFACENORMALS float3 surfTangent; float3 surfBitangent; float3 surfNormal; #endif CBUFFER_END #undef WorldNormalVector #define WorldNormalVector(data, normal) mul(normal, data.TBN) // In Unity 2020.3LTS, Unity will spew tons of errors about missing this sampler in // URP, even though it shouldn't be required. TEXTURE2D(_MainTex); // globals, outside of CBuffer, but used by more than one module float3 _gGlitterLightDir; float3 _gGlitterLightWorldPos; half3 _gGlitterLightColor; #if (_MICROTERRAIN || _MICROMESHTERRAIN) 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) float4 _TerrainNormalmapTexture_TexelSize; #endif #if (_MICROTERRAIN || _MICROMESHTERRAIN) TEXTURE2D(_TerrainHeightmapTexture); float4 _TerrainHeightmapTexture_TexelSize; TEXTURE2D(_TerrainNormalmapTexture); #endif UNITY_INSTANCING_BUFFER_START(Terrain) UNITY_DEFINE_INSTANCED_PROP(float4, _TerrainPatchInstanceData) // float4(xBase, yBase, skipScale, ~) UNITY_INSTANCING_BUFFER_END(Terrain) // dynamic branching helpers, for regular and aggressive branching // debug mode shows how many samples using branching will save us. // // These macros are always used instead of the UNITY_BRANCH macro // to maintain debug displays and allow branching to be disabled // on as granular level as we want. #if _BRANCHSAMPLES #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; if (w > 0) #else #define MSBRANCH(w) UNITY_BRANCH if (w > 0) #endif #else #if _DEBUG_BRANCHCOUNT_WEIGHT || _DEBUG_BRANCHCOUNT_TOTAL float _branchWeightCount; #define MSBRANCH(w) if (w > 0) _branchWeightCount++; #else #define MSBRANCH(w) #endif #endif #if _BRANCHSAMPLESAGR #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER ||_DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; if (w > 0.001) #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; if (w > 0.001) #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; if (w > 0.001) #else #define MSBRANCHTRIPLANAR(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHCLUSTER(w) UNITY_BRANCH if (w > 0.001) #define MSBRANCHOTHER(w) UNITY_BRANCH if (w > 0.001) #endif #else #if _DEBUG_BRANCHCOUNT_TRIPLANAR || _DEBUG_BRANCHCOUNT_CLUSTER || _DEBUG_BRANCHCOUNT_OTHER || _DEBUG_BRANCHCOUNT_TOTAL float _branchTriplanarCount; float _branchClusterCount; float _branchOtherCount; #define MSBRANCHTRIPLANAR(w) if (w > 0.001) _branchTriplanarCount++; #define MSBRANCHCLUSTER(w) if (w > 0.001) _branchClusterCount++; #define MSBRANCHOTHER(w) if (w > 0.001) _branchOtherCount++; #else #define MSBRANCHTRIPLANAR(w) #define MSBRANCHCLUSTER(w) #define MSBRANCHOTHER(w) #endif #endif #if _DEBUG_SAMPLECOUNT int _sampleCount; #define COUNTSAMPLE { _sampleCount++; } #else #define COUNTSAMPLE #endif #if _DEBUG_PROCLAYERS int _procLayerCount; #define COUNTPROCLAYER { _procLayerCount++; } #else #define COUNTPROCLAYER #endif #if _DEBUG_USE_TOPOLOGY TEXTURE2D(_DebugWorldPos); TEXTURE2D(_DebugWorldNormal); #endif // splat UNITY_DECLARE_TEX2DARRAY(_Diffuse); UNITY_DECLARE_TEX2DARRAY(_NormalSAO); #if _CONTROLNOISEUV || _GLOBALNOISEUV TEXTURE2D(_NoiseUV); #endif #if _PACKINGHQ UNITY_DECLARE_TEX2DARRAY(_SmoothAO); #endif #if _USESPECULARWORKFLOW UNITY_DECLARE_TEX2DARRAY(_Specular); #endif #if _USEEMISSIVEMETAL UNITY_DECLARE_TEX2DARRAY(_EmissiveMetal); #endif TEXTURE2D(_PerPixelNormal); SamplerState shared_linear_clamp_sampler; SamplerState shared_point_clamp_sampler; TEXTURE2D(_Control0); #if _CUSTOMSPLATTEXTURES TEXTURE2D(_CustomControl0); #if !_MAX4TEXTURES TEXTURE2D(_CustomControl1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_CustomControl2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_CustomControl3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_CustomControl6); #endif #if _MAX32TEXTURES TEXTURE2D(_CustomControl7); #endif #else #if !_MAX4TEXTURES TEXTURE2D(_Control1); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES TEXTURE2D(_Control2); #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES TEXTURE2D(_Control3); #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control4); #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control5); #endif #if _MAX28TEXTURES || _MAX32TEXTURES TEXTURE2D(_Control6); #endif #if _MAX32TEXTURES TEXTURE2D(_Control7); #endif #endif TEXTURE2D_FLOAT(_PerTexProps); struct DecalLayer { float3 uv; float2 dx; float2 dy; int decalIndex; bool dynamic; }; struct DecalOutput { DecalLayer l0; DecalLayer l1; DecalLayer l2; DecalLayer l3; half4 Weights; half4 Indexes; half4 fxLevels; }; struct TriGradMipFormat { float4 d0; float4 d1; float4 d2; }; float InverseLerp(float x, float y, float v) { return (v-x)/max(y-x, 0.001); } float2 InverseLerp(float2 x, float2 y, float2 v) { return (v-x)/max(y-x, float2(0.001, 0.001)); } float3 InverseLerp(float3 x, float3 y, float3 v) { return (v-x)/max(y-x, float3(0.001, 0.001, 0.001)); } float4 InverseLerp(float4 x, float4 y, float4 v) { return (v-x)/max(y-x, float4(0.001, 0.001, 0.001, 0.001)); } // 2019.3 holes #ifdef _ALPHATEST_ON TEXTURE2D(_TerrainHolesTexture); void ClipHoles(float2 uv) { float hole = SAMPLE_TEXTURE2D(_TerrainHolesTexture, shared_linear_clamp_sampler, uv).r; COUNTSAMPLE clip(hole < 0.5f ? -1 : 1); } #endif #if _TRIPLANAR #if _USEGRADMIP #define MIPFORMAT TriGradMipFormat #define INITMIPFORMAT (TriGradMipFormat)0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float3 #define INITMIPFORMAT 0; #define MIPFROMATRAW float3 #endif #else #if _USEGRADMIP #define MIPFORMAT float4 #define INITMIPFORMAT 0; #define MIPFROMATRAW float4 #else #define MIPFORMAT float #define INITMIPFORMAT 0; #define MIPFROMATRAW float #endif #endif float2 TotalOne(float2 v) { return v * (1.0 / max(v.x + v.y, 0.001)); } float3 TotalOne(float3 v) { return v * (1.0 / max(v.x + v.y + v.z, 0.001)); } float4 TotalOne(float4 v) { return v * (1.0 / max(v.x + v.y + v.z + v.w, 0.001)); } float2 RotateUV(float2 uv, float amt) { uv -=0.5; float s = sin ( amt); float c = cos ( amt ); float2x2 mtx = float2x2( c, -s, s, c); mtx *= 0.5; mtx += 0.5; mtx = mtx * 2-1; uv = mul ( uv, mtx ); uv += 0.5; return uv; } float4 DecodeToFloat4(float v) { uint vi = (uint)(v * (256.0f * 256.0f * 256.0f * 256.0f)); int ex = (int)(vi / (256 * 256 * 256) % 256); int ey = (int)((vi / (256 * 256)) % 256); int ez = (int)((vi / (256)) % 256); int ew = (int)(vi % 256); float4 e = float4(ex / 255.0, ey / 255.0, ez / 255.0, ew / 255.0); return e; } struct Input { ShaderData shaderData; float2 uv_Control0; float2 uv2_Diffuse; float worldHeight; float3 worldUpVector; float3 viewDir; float3 worldPos; float3 worldNormal; float4 color; float3x3 TBN; // vertex/digger workflow data half4 w0; half4 w1; half4 w2; half4 w3; half4 w4; half4 w5; half4 w6; // megasplat data half4 layer0; half4 layer1; half3 baryWeights; half4 scatter0; half4 scatter1; // wetness, puddles, streams, lava from vertex or megasplat half4 fx; // snow min, snow max half4 fx2; }; struct TriplanarConfig { float3x3 uv0; float3x3 uv1; float3x3 uv2; float3x3 uv3; half3 pN; half3 pN0; half3 pN1; half3 pN2; half3 pN3; half3 axisSign; Input IN; }; struct Config { float2 uv; float3 uv0; float3 uv1; float3 uv2; float3 uv3; half4 cluster0; half4 cluster1; half4 cluster2; half4 cluster3; }; struct MicroSplatLayer { half3 Albedo; half3 Normal; half Smoothness; half Occlusion; half Metallic; half Height; half3 Emission; #if _USESPECULARWORKFLOW half3 Specular; #endif half Alpha; }; // raw, unblended samples from arrays struct RawSamples { half4 albedo0; half4 albedo1; half4 albedo2; half4 albedo3; #if _SURFACENORMALS half3 surf0; half3 surf1; half3 surf2; half3 surf3; #endif half4 normSAO0; half4 normSAO1; half4 normSAO2; half4 normSAO3; #if _USEEMISSIVEMETAL || _GLOBALEMIS || _GLOBALSMOOTHAOMETAL || _PERTEXSSS || _PERTEXRIMLIGHT half4 emisMetal0; half4 emisMetal1; half4 emisMetal2; half4 emisMetal3; #endif #if _USESPECULARWORKFLOW half3 specular0; half3 specular1; half3 specular2; half3 specular3; #endif }; void InitRawSamples(inout RawSamples s) { s.normSAO0 = half4(0,0,0,1); s.normSAO1 = half4(0,0,0,1); s.normSAO2 = half4(0,0,0,1); s.normSAO3 = half4(0,0,0,1); #if _SURFACENORMALS s.surf0 = half3(0,0,1); s.surf1 = half3(0,0,1); s.surf2 = half3(0,0,1); s.surf3 = half3(0,0,1); #endif } float3 GetGlobalLightDir(Input i) { float3 lightDir = float3(1,0,0); #if _HDRP || PASS_DEFERRED lightDir = normalize(_gGlitterLightDir.xyz); #elif _URP lightDir = GetMainLight().direction; #else #ifndef USING_DIRECTIONAL_LIGHT lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); #else lightDir = normalize(_WorldSpaceLightPos0.xyz); #endif #endif return lightDir; } float3x3 GetTBN(Input i) { return i.TBN; } float3 GetGlobalLightDirTS(Input i) { float3 lightDirWS = GetGlobalLightDir(i); return mul(GetTBN(i), lightDirWS); } half3 GetGlobalLightColor() { #if _HDRP || PASS_DEFERRED return _gGlitterLightColor; #elif _URP return (GetMainLight().color); #else return _LightColor0.rgb; #endif } half3 FuzzyShade(half3 color, half3 normal, half coreMult, half edgeMult, half power, float3 viewDir) { half dt = saturate(dot(viewDir, normal)); half dark = 1.0 - (coreMult * dt); half edge = pow(1-dt, power) * edgeMult; return color * (dark + edge); } half3 ComputeSSS(Input i, float3 V, float3 N, half3 tint, half thickness, half distortion, half scale, half power) { float3 L = GetGlobalLightDir(i); half3 lightColor = GetGlobalLightColor(); float3 H = normalize(L + N * distortion); float VdotH = pow(saturate(dot(V, -H)), power) * scale; float3 I = (VdotH) * thickness; return lightColor * I * tint; } #if _MAX2LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y; } #elif _MAX3LAYER inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z; } #else inline half BlendWeights(half s1, half s2, half s3, half s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half2 BlendWeights(half2 s1, half2 s2, half2 s3, half2 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half3 BlendWeights(half3 s1, half3 s2, half3 s3, half3 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } inline half4 BlendWeights(half4 s1, half4 s2, half4 s3, half4 s4, half4 w) { return s1 * w.x + s2 * w.y + s3 * w.z + s4 * w.w; } #endif #if _MAX3LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #elif _MAX2LAYER #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = defVal; \ half4 varName##1 = defVal; \ half4 varName##2 = defVal; \ half4 varName##3 = defVal; \ varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #else #define SAMPLE_PER_TEX(varName, pixel, config, defVal) \ half4 varName##0 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv0.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##1 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv1.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##2 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv2.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ half4 varName##3 = SAMPLE_TEXTURE2D_LOD(_PerTexProps, shared_point_clamp_sampler, float2(config.uv3.z*_PerTexProps_TexelSize.x, pixel*_PerTexProps_TexelSize.y), 0); \ #endif half2 BlendNormal2(half2 base, half2 blend) { return normalize(half3(base.xy + blend.xy, 1)).xy; } half3 BlendOverlay(half3 base, half3 blend) { return (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend))); } half3 BlendMult2X(half3 base, half3 blend) { return (base * (blend * 2)); } half3 BlendLighterColor(half3 s, half3 d) { return (s.x + s.y + s.z > d.x + d.y + d.z) ? s : d; } #if _SURFACENORMALS #define HALF_EPS 4.8828125e-4 // 2^-11, machine epsilon: 1 + EPS = 1 (half of the ULP for 1.0f) void ConstructSurfaceGradientTBN(Input i) { float3x3 tbn = GetTBN(i); float3 t = tbn[0]; float3 b = tbn[1]; float3 n = tbn[2]; surfNormal = n;//mul(GetWorldToObjectMatrix(), float4(n, 1)).xyz; surfTangent = t;//mul(GetWorldToObjectMatrix(), float4(t, 1)).xyz; surfBitangent = b;//cross(surfNormal, surfTangent); float renormFactor = 1.0 / length(surfNormal); surfNormal *= renormFactor; surfTangent *= renormFactor; surfBitangent *= renormFactor; } half3 SurfaceGradientFromTBN(half2 deriv) { return deriv.x * surfTangent + deriv.y * surfBitangent; } // Input: vM is tangent space normal in [-1;1]. // Output: convert vM to a derivative. half2 TspaceNormalToDerivative(half3 vM) { const half scale = 1.0/128.0; // Ensure vM delivers a positive third component using abs() and // constrain vM.z so the range of the derivative is [-128; 128]. const half3 vMa = abs(vM); const half z_ma = max(vMa.z, scale*max(vMa.x, vMa.y)); return -half2(vM.x, vM.y)/z_ma; } // Used to produce a surface gradient from the gradient of a volume // bump function such as 3D Perlin noise. Equation 2 in [Mik10]. half3 SurfgradFromVolumeGradient(half3 grad) { return grad - dot(surfNormal, grad) * surfNormal; } half3 SurfgradFromTriplanarProjection(half3 pN, half2 xPlaneTN, half2 yPlaneTN, half2 zPlaneTN) { const half w0 = pN.x; const half w1 = pN.y; const half w2 = pN.z; // X-plane tangent normal to gradient derivative xPlaneTN = xPlaneTN * 2.0 - 1.0; half xPlaneRcpZ = rsqrt(max(1 - dot(xPlaneTN.x, xPlaneTN.x) - dot(xPlaneTN.y, xPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_xplane = xPlaneTN * -xPlaneRcpZ; // Y-plane tangent normal to gradient derivative yPlaneTN = yPlaneTN * 2.0 - 1.0; half yPlaneRcpZ = rsqrt(max(1 - dot(yPlaneTN.x, yPlaneTN.x) - dot(yPlaneTN.y, yPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_yplane = yPlaneTN * -yPlaneRcpZ; // Z-plane tangent normal to gradient derivative zPlaneTN = zPlaneTN * 2.0 - 1.0; half zPlaneRcpZ = rsqrt(max(1 - dot(zPlaneTN.x, zPlaneTN.x) - dot(zPlaneTN.y, zPlaneTN.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 d_zplane = zPlaneTN * -zPlaneRcpZ; // Assume deriv xplane, deriv yplane, and deriv zplane are // sampled using (z,y), (x,z), and (x,y), respectively. // Positive scales of the lookup coordinate will work // as well, but for negative scales the derivative components // will need to be negated accordingly. float3 grad = float3(w2*d_zplane.x + w1*d_yplane.x, w2*d_zplane.y + w0*d_xplane.y, w0*d_xplane.x + w1*d_yplane.y); return SurfgradFromVolumeGradient(grad); } half3 ConvertNormalToGradient(half3 normal) { half2 deriv = TspaceNormalToDerivative(normal); return SurfaceGradientFromTBN(deriv); } half3 ConvertNormal2ToGradient(half2 packedNormal) { half2 tNormal = packedNormal; half rcpZ = rsqrt(max(1 - dot(tNormal.x, tNormal.x) - dot(tNormal.y, tNormal.y), dot(HALF_EPS, HALF_EPS))); // Clamp to avoid INF half2 deriv = tNormal * -rcpZ; return SurfaceGradientFromTBN(deriv); } half3 ResolveNormalFromSurfaceGradient(half3 gradient) { return normalize(surfNormal - gradient); } #endif // _SURFACENORMALS void BlendNormalPerTex(inout RawSamples o, half2 noise, float4 fades) { #if _SURFACENORMALS float3 grad = ConvertNormal2ToGradient(noise.xy); o.surf0 += grad * fades.x; o.surf1 += grad * fades.y; #if !_MAX2LAYER o.surf2 += grad * fades.z; #endif #if !_MAX2LAYER && !_MAX3LAYER o.surf3 += grad * fades.w; #endif #else o.normSAO0.xy = lerp(o.normSAO0.xy, BlendNormal2(o.normSAO0.xy, noise.xy), fades.x); o.normSAO1.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #if !_MAX2LAYER o.normSAO2.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO2.xy, noise.xy), fades.y); #endif #if !_MAX2LAYER && !_MAX3LAYER o.normSAO3.xy = lerp(o.normSAO1.xy, BlendNormal2(o.normSAO1.xy, noise.xy), fades.y); #endif #endif } half3 BlendNormal3(half3 n1, half3 n2) { n1 += float3( 0, 0, 1); n2 *= float3(-1, -1, 1); return n1*dot(n1, n2) / n1.z - n2; } half2 TransformTriplanarNormal(Input IN, float3x3 t2w, half3 axisSign, half3 absVertNormal, half3 pN, half2 a0, half2 a1, half2 a2) { a0 = a0 * 2 - 1; a1 = a1 * 2 - 1; a2 = a2 * 2 - 1; a0.x *= axisSign.x; a1.x *= axisSign.y; a2.x *= axisSign.z; half3 n0 = half3(a0.xy, 1); half3 n1 = half3(a1.xy, 1); half3 n2 = half3(a2.xy, 1); float3 wn = IN.worldNormal; n0 = BlendNormal3(half3(wn.zy, absVertNormal.x), n0); n1 = BlendNormal3(half3(wn.xz, absVertNormal.y), n1 * float3(-1, 1, 1)); n2 = BlendNormal3(half3(wn.xy, absVertNormal.z), n2); n0.z *= axisSign.x; n1.z *= axisSign.y; n2.z *= -axisSign.z; half3 worldNormal = (n0.zyx * pN.x + n1.xzy * pN.y + n2.xyz * pN.z); return mul(t2w, worldNormal).xy; } // funcs inline half MSLuminance(half3 rgb) { #ifdef UNITY_COLORSPACE_GAMMA return dot(rgb, half3(0.22, 0.707, 0.071)); #else return dot(rgb, half3(0.0396819152, 0.458021790, 0.00609653955)); #endif } float2 Hash2D( float2 x ) { float2 k = float2( 0.3183099, 0.3678794 ); x = x*k + k.yx; return -1.0 + 2.0*frac( 16.0 * k*frac( x.x*x.y*(x.x+x.y)) ); } float Noise2D(float2 p ) { float2 i = floor( p ); float2 f = frac( p ); float2 u = f*f*(3.0-2.0*f); return lerp( lerp( dot( Hash2D( i + float2(0.0,0.0) ), f - float2(0.0,0.0) ), dot( Hash2D( i + float2(1.0,0.0) ), f - float2(1.0,0.0) ), u.x), lerp( dot( Hash2D( i + float2(0.0,1.0) ), f - float2(0.0,1.0) ), dot( Hash2D( i + float2(1.0,1.0) ), f - float2(1.0,1.0) ), u.x), u.y); } float FBM2D(float2 uv) { float f = 0.5000*Noise2D( uv ); uv *= 2.01; f += 0.2500*Noise2D( uv ); uv *= 1.96; f += 0.1250*Noise2D( uv ); return f; } float3 Hash3D( float3 p ) { p = float3( dot(p,float3(127.1,311.7, 74.7)), dot(p,float3(269.5,183.3,246.1)), dot(p,float3(113.5,271.9,124.6))); return -1.0 + 2.0*frac(sin(p)*437.5453123); } float Noise3D( float3 p ) { float3 i = floor( p ); float3 f = frac( p ); float3 u = f*f*(3.0-2.0*f); return lerp( lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,0.0) ), f - float3(0.0,0.0,0.0) ), dot( Hash3D( i + float3(1.0,0.0,0.0) ), f - float3(1.0,0.0,0.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,0.0) ), f - float3(0.0,1.0,0.0) ), dot( Hash3D( i + float3(1.0,1.0,0.0) ), f - float3(1.0,1.0,0.0) ), u.x), u.y), lerp( lerp( dot( Hash3D( i + float3(0.0,0.0,1.0) ), f - float3(0.0,0.0,1.0) ), dot( Hash3D( i + float3(1.0,0.0,1.0) ), f - float3(1.0,0.0,1.0) ), u.x), lerp( dot( Hash3D( i + float3(0.0,1.0,1.0) ), f - float3(0.0,1.0,1.0) ), dot( Hash3D( i + float3(1.0,1.0,1.0) ), f - float3(1.0,1.0,1.0) ), u.x), u.y), u.z ); } float FBM3D(float3 uv) { float f = 0.5000*Noise3D( uv ); uv *= 2.01; f += 0.2500*Noise3D( uv ); uv *= 1.96; f += 0.1250*Noise3D( uv ); return f; } float GetSaturation(float3 c) { float mi = min(min(c.x, c.y), c.z); float ma = max(max(c.x, c.y), c.z); return (ma - mi)/(ma + 1e-7); } // Better Color Lerp, does not have darkening issue float3 BetterColorLerp(float3 a, float3 b, float x) { float3 ic = lerp(a, b, x) + float3(1e-6,0.0,0.0); float sd = abs(GetSaturation(ic) - lerp(GetSaturation(a), GetSaturation(b), x)); float3 dir = normalize(float3(2.0 * ic.x - ic.y - ic.z, 2.0 * ic.y - ic.x - ic.z, 2.0 * ic.z - ic.y - ic.x)); float lgt = dot(float3(1.0, 1.0, 1.0), ic); float ff = dot(dir, normalize(ic)); const float dsp_str = 1.5; ic += dsp_str * dir * sd * ff * lgt; return saturate(ic); } half4 ComputeWeights(half4 iWeights, half h0, half h1, half h2, half h3, half contrast) { #if _DISABLEHEIGHTBLENDING return iWeights; #else // compute weight with height map //half4 weights = half4(iWeights.x * h0, iWeights.y * h1, iWeights.z * h2, iWeights.w * h3); half4 weights = half4(iWeights.x * max(h0,0.001), iWeights.y * max(h1,0.001), iWeights.z * max(h2,0.001), iWeights.w * max(h3,0.001)); // Contrast weights half maxWeight = max(max(weights.x, max(weights.y, weights.z)), weights.w); half transition = max(contrast * maxWeight, 0.0001); half threshold = maxWeight - transition; half scale = 1.0 / transition; weights = saturate((weights - threshold) * scale); weights = TotalOne(weights); return weights; #endif } half HeightBlend(half h1, half h2, half slope, half contrast) { #if _DISABLEHEIGHTBLENDING return slope; #else h2 = 1 - h2; half tween = saturate((slope - min(h1, h2)) / max(abs(h1 - h2), 0.001)); half blend = saturate( ( tween - (1-contrast) ) / max(contrast, 0.001)); return blend; #endif } #if _MAX4TEXTURES #define TEXCOUNT 4 #elif _MAX8TEXTURES #define TEXCOUNT 8 #elif _MAX12TEXTURES #define TEXCOUNT 12 #elif _MAX20TEXTURES #define TEXCOUNT 20 #elif _MAX24TEXTURES #define TEXCOUNT 24 #elif _MAX28TEXTURES #define TEXCOUNT 28 #elif _MAX32TEXTURES #define TEXCOUNT 32 #else #define TEXCOUNT 16 #endif #if _DECAL_SPLAT void DoMergeDecalSplats(half4 iWeights, half4 iIndexes, inout half4 indexes, inout half4 weights) { for (int i = 0; i < 4; ++i) { half w = iWeights[i]; half index = iIndexes[i]; if (w > weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = index; } else if (w > weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = index; } else if (w > weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = index; } else if (w > weights[3]) { weights[3] = w; indexes[3] = index; } } } #endif void Setup(out half4 weights, float2 uv, out Config config, half4 w0, half4 w1, half4 w2, half4 w3, half4 w4, half4 w5, half4 w6, half4 w7, float3 worldPos, DecalOutput decalOutput) { config = (Config)0; half4 indexes = 0; config.uv = uv; #if _WORLDUV uv = worldPos.xz; #endif #if _DISABLESPLATMAPS float2 scaledUV = uv; #else float2 scaledUV = uv * _UVScale.xy + _UVScale.zw; #endif // if only 4 textures, and blending 4 textures, skip this whole thing.. // this saves about 25% of the ALU of the base shader on low end. However if // we rely on sorted texture weights (distance resampling) we have to sort.. float4 defaultIndexes = float4(0,1,2,3); #if _MESHSUBARRAY defaultIndexes = _MeshSubArrayIndexes; #endif #if _MESHSUBARRAY && !_DECAL_SPLAT || (_MAX4TEXTURES && !_MAX3LAYER && !_MAX2LAYER && !_DISTANCERESAMPLE && !_POM && !_DECAL_SPLAT) weights = w0; config.uv0 = float3(scaledUV, defaultIndexes.x); config.uv1 = float3(scaledUV, defaultIndexes.y); config.uv2 = float3(scaledUV, defaultIndexes.z); config.uv3 = float3(scaledUV, defaultIndexes.w); return; #endif #if _DISABLESPLATMAPS weights = float4(1,0,0,0); return; #else half splats[TEXCOUNT]; splats[0] = w0.x; splats[1] = w0.y; splats[2] = w0.z; splats[3] = w0.w; #if !_MAX4TEXTURES splats[4] = w1.x; splats[5] = w1.y; splats[6] = w1.z; splats[7] = w1.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES splats[8] = w2.x; splats[9] = w2.y; splats[10] = w2.z; splats[11] = w2.w; #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES splats[12] = w3.x; splats[13] = w3.y; splats[14] = w3.z; splats[15] = w3.w; #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[16] = w4.x; splats[17] = w4.y; splats[18] = w4.z; splats[19] = w4.w; #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES splats[20] = w5.x; splats[21] = w5.y; splats[22] = w5.z; splats[23] = w5.w; #endif #if _MAX28TEXTURES || _MAX32TEXTURES splats[24] = w6.x; splats[25] = w6.y; splats[26] = w6.z; splats[27] = w6.w; #endif #if _MAX32TEXTURES splats[28] = w7.x; splats[29] = w7.y; splats[30] = w7.z; splats[31] = w7.w; #endif weights[0] = 0; weights[1] = 0; weights[2] = 0; weights[3] = 0; indexes[0] = 0; indexes[1] = 0; indexes[2] = 0; indexes[3] = 0; int i = 0; for (i = 0; i < TEXCOUNT; ++i) { half w = splats[i]; if (w >= weights[0]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = weights[0]; indexes[1] = indexes[0]; weights[0] = w; indexes[0] = i; } else if (w >= weights[1]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = weights[1]; indexes[2] = indexes[1]; weights[1] = w; indexes[1] = i; } else if (w >= weights[2]) { weights[3] = weights[2]; indexes[3] = indexes[2]; weights[2] = w; indexes[2] = i; } else if (w >= weights[3]) { weights[3] = w; indexes[3] = i; } } // NaN Prevention if (weights.x <= 0) weights = float4(1, 0, 0, 0); #if _DECAL_SPLAT DoMergeDecalSplats(decalOutput.Weights, decalOutput.Indexes, weights, indexes); #endif // clamp and renormalize #if _MAX2LAYER weights.zw = 0; weights.xy = TotalOne(weights.xy); #elif _MAX3LAYER weights.w = 0; weights.xyz = TotalOne(weights.xyz); #elif !_DISABLEHEIGHTBLENDING || _NORMALIZEWEIGHTS // prevents black when painting, which the unity shader does not prevent. weights = normalize(weights); #endif config.uv0 = float3(scaledUV, indexes.x); config.uv1 = float3(scaledUV, indexes.y); config.uv2 = float3(scaledUV, indexes.z); config.uv3 = float3(scaledUV, indexes.w); #endif //_DISABLESPLATMAPS } float3 HeightToNormal(float height, float3 worldPos) { float3 dx = ddx(worldPos); float3 dy = ddy(worldPos); float3 crossX = cross(float3(0,1,0), dx); float3 crossY = cross(float3(0,1,0), dy); float3 d = abs(dot(crossY, dx)); float3 n = ((((height + ddx(height)) - height) * crossY) + (((height + ddy(height)) - height) * crossX)) * sign(d); n.z *= -1; return normalize((d * float3(0,1,0)) - n).xzy; } float ComputeMipLevel(float2 uv, float2 textureSize) { uv *= textureSize; float2 dx_vtc = ddx(uv); float2 dy_vtc = ddy(uv); float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); return 0.5 * log2(delta_max_sqr); } inline half2 UnpackNormal2(half4 packednormal) { return packednormal.wy * 2 - 1; } half3 TriplanarHBlend(half h0, half h1, half h2, half3 pN, half contrast) { half3 blend = pN / dot(pN, half3(1,1,1)); float3 heights = float3(h0, h1, h2) + (blend * 3.0); half height_start = max(max(heights.x, heights.y), heights.z) - contrast; half3 h = max(heights - height_start.xxx, half3(0,0,0)); blend = h / dot(h, half3(1,1,1)); return blend; } void ClearAllButAlbedo(inout MicroSplatLayer o, half3 display) { o.Albedo = display.rgb; o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } void ClearAllButAlbedo(inout MicroSplatLayer o, half display) { o.Albedo = half3(display, display, display); o.Normal = half3(0, 0, 1); o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; o.Metallic = 0; o.Height = 0; #if _USESPECULARWORKFLOW o.Specular = 0; #endif } half MicroShadow(float3 lightDir, half3 normal, half ao, half strength) { half shadow = saturate(abs(dot(normal, lightDir)) + (ao * ao * 2.0) - 1.0); return 1 - ((1-shadow) * strength); } void DoDebugOutput(inout MicroSplatLayer l) { #if _DEBUG_OUTPUT_ALBEDO ClearAllButAlbedo(l, l.Albedo); #elif _DEBUG_OUTPUT_NORMAL // oh unit shader compiler normal stripping, how I hate you so.. // must multiply by albedo to stop the normal from being white. Why, fuck knows? ClearAllButAlbedo(l, float3(l.Normal.xy * 0.5 + 0.5, l.Normal.z * saturate(l.Albedo.z+1))); #elif _DEBUG_OUTPUT_SMOOTHNESS ClearAllButAlbedo(l, l.Smoothness.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_METAL ClearAllButAlbedo(l, l.Metallic.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_AO ClearAllButAlbedo(l, l.Occlusion.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_EMISSION ClearAllButAlbedo(l, l.Emission * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_HEIGHT ClearAllButAlbedo(l, l.Height.xxx * saturate(l.Albedo.z+1)); #elif _DEBUG_OUTPUT_SPECULAR && _USESPECULARWORKFLOW ClearAllButAlbedo(l, l.Specular * saturate(l.Albedo.z+1)); #elif _DEBUG_BRANCHCOUNT_WEIGHT ClearAllButAlbedo(l, _branchWeightCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TRIPLANAR ClearAllButAlbedo(l, _branchTriplanarCount / 24 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_CLUSTER ClearAllButAlbedo(l, _branchClusterCount / 12 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_OTHER ClearAllButAlbedo(l, _branchOtherCount / 8 * saturate(l.Albedo.z + 1)); #elif _DEBUG_BRANCHCOUNT_TOTAL l.Albedo.r = _branchWeightCount / 12; l.Albedo.g = _branchTriplanarCount / 24; l.Albedo.b = _branchClusterCount / 12; ClearAllButAlbedo(l, (l.Albedo.r + l.Albedo.g + l.Albedo.b + (_branchOtherCount / 8)) / 4); #elif _DEBUG_OUTPUT_MICROSHADOWS ClearAllButAlbedo(l,l.Albedo); #elif _DEBUG_SAMPLECOUNT float sdisp = (float)_sampleCount / max(_SampleCountDiv, 1); half3 sdcolor = float3(sdisp, sdisp > 1 ? 1 : 0, 0); ClearAllButAlbedo(l, sdcolor * saturate(l.Albedo.z + 1)); #elif _DEBUG_PROCLAYERS ClearAllButAlbedo(l, (float)_procLayerCount / (float)_PCLayerCount * saturate(l.Albedo.z + 1)); #endif } // abstraction around sampler mode #if _USELODMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_LOD(tex, sampler##tex, u, l.x) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u, l.x) #elif _USEGRADMIP #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_GRAD(tex, sampler##tex, u, l.xy, l.zw) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY_GRAD(tex, ss, u.xy, u.z, l.xy, l.zw) #else #define MICROSPLAT_SAMPLE(tex, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, sampler##tex, u.xy, u.z) #define MICROSPLAT_SAMPLE_SAMPLER(tex, ss, u, l) SAMPLE_TEXTURE2D_ARRAY(tex, ss, u.xy, y.z) #endif #define MICROSPLAT_SAMPLE_DIFFUSE(u, cl, l) MICROSPLAT_SAMPLE(_Diffuse, u, l) #define MICROSPLAT_SAMPLE_EMIS(u, cl, l) MICROSPLAT_SAMPLE(_EmissiveMetal, u, l) #define MICROSPLAT_SAMPLE_DIFFUSE_LOD(u, cl, l) UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, u, l) #if _PACKINGHQ #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) half4(MICROSPLAT_SAMPLE(_NormalSAO, u, l).ga, MICROSPLAT_SAMPLE(_SmoothAO, u, l).ga).brag #else #define MICROSPLAT_SAMPLE_NORMAL(u, cl, l) MICROSPLAT_SAMPLE(_NormalSAO, u, l) #endif #if _USESPECULARWORKFLOW #define MICROSPLAT_SAMPLE_SPECULAR(u, cl, l) MICROSPLAT_SAMPLE(_Specular, u, l) #endif struct SimpleTriplanarConfig { float3 pn; float2 uv0; float2 uv1; float2 uv2; }; void PrepSimpleTriplanarConfig(inout SimpleTriplanarConfig tc, float3 worldPos, float3 normal, float contrast) { tc.pn = pow(abs(normal), contrast); tc.pn = tc.pn / (tc.pn.x + tc.pn.y + tc.pn.z); half3 axisSign = sign(normal); tc.uv0 = worldPos.zy * axisSign.x; tc.uv1 = worldPos.xz * axisSign.y; tc.uv2 = worldPos.xy * axisSign.z; } #define SimpleTriplanarSample(tex, tc, scale) (SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv0 * scale) * tc.pn.x + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv1 * scale) * tc.pn.y + SAMPLE_TEXTURE2D(tex, sampler_Diffuse, tc.uv2 * scale) * tc.pn.z) #define SimpleTriplanarSampleLOD(tex, tc, scale, lod) (SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv0 * scale, lod) * tc.pn.x + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv1 * scale, lod) * tc.pn.y + SAMPLE_TEXTURE2D_LOD(tex, sampler_Diffuse, tc.uv2 * scale, lod) * tc.pn.z) #define SimpleTriplanarSampleGrad(tex, tc, scale) (SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv0 * scale, ddx(tc.uv0) * scale, ddy(tc.uv0) * scale) * tc.pn.x + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv1 * scale, ddx(tc.uv1) * scale, ddy(tc.uv1) * scale) * tc.pn.y + SAMPLE_TEXTURE2D_GRAD(tex, sampler_Diffuse, tc.uv2 * scale, ddx(tc.uv2) * scale, ddy(tc.uv2) * scale) * tc.pn.z) inline half3 MicroSplatDiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity) { specColor = lerp (half3(0,0,0), albedo, metallic); oneMinusReflectivity = (1-metallic); return albedo * oneMinusReflectivity; } Input DescToInput(ShaderData IN) { Input s = (Input)0; s.shaderData = IN; s.TBN = IN.TBNMatrix; s.worldNormal = IN.worldSpaceNormal; s.worldPos = IN.worldSpacePosition; s.viewDir = IN.tangentSpaceViewDir; s.uv_Control0 = IN.texcoord0.xy; s.worldUpVector = float3(0,1,0); s.worldHeight = IN.worldSpacePosition.y; #if _PLANETVECTORS float3 rwp = mul(_PQSToLocal, float4(IN.worldSpacePosition, 1)); s.worldHeight = distance(rwp, float3(0,0,0)); s.worldUpVector = normalize(rwp); #endif #if _MICROMESH && _MESHUV2 s.uv2_Diffuse = IN.texcoord1.xy; #endif #if _MEGASPLAT UnpackMegaSplat(s, IN); #endif #if _MICROVERTEXMESH || _MICRODIGGERMESH UnpackVertexWorkflow(s, IN); #endif #if _PLANETVECTORS DoPlanetDataInputCopy(s, IN); #endif return s; } void SampleAlbedo(inout Config config, inout TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half4 contrasts = _Contrast.xxxx; #if _PERTEXTRIPLANARCONTRAST SAMPLE_PER_TEX(ptc, 9.5, config, half4(1,0.5,0,0)); contrasts = half4(ptc0.y, ptc1.y, ptc2.y, ptc3.y); #endif #if _PERTEXTRIPLANAR SAMPLE_PER_TEX(pttri, 9.5, config, half4(0,0,0,0)); #endif { // For per-texture triplanar, we modify the view based blending factor of the triplanar // such that you get a pure blend of either top down projection, or with the top down projection // removed and renormalized. This causes dynamic flow control optimizations to kick in and avoid // the extra texture samples while keeping the code simple. Yay.. // We also only have to do this in the Albedo, because the pN values will be adjusted after the // albedo is sampled, causing future samples to use this data. #if _PERTEXTRIPLANAR if (pttri0.x > 0.66) { tc.pN0 = half3(0,1,0); } else if (pttri0.x > 0.33) { tc.pN0.y = 0; tc.pN0.xz = TotalOne(tc.pN0.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } half3 bf = tc.pN0; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN0, contrasts.x); tc.pN0 = bf; #endif s.albedo0 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } MSBRANCH(weights.y) { #if _PERTEXTRIPLANAR if (pttri1.x > 0.66) { tc.pN1 = half3(0,1,0); } else if (pttri1.x > 0.33) { tc.pN1.y = 0; tc.pN1.xz = TotalOne(tc.pN1.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { COUNTSAMPLE a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv1[2], config.cluster1, d2); } half3 bf = tc.pN1; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN1, contrasts.x); tc.pN1 = bf; #endif s.albedo1 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { #if _PERTEXTRIPLANAR if (pttri2.x > 0.66) { tc.pN2 = half3(0,1,0); } else if (pttri2.x > 0.33) { tc.pN2.y = 0; tc.pN2.xz = TotalOne(tc.pN2.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } half3 bf = tc.pN2; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN2, contrasts.x); tc.pN2 = bf; #endif s.albedo2 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { #if _PERTEXTRIPLANAR if (pttri3.x > 0.66) { tc.pN3 = half3(0,1,0); } else if (pttri3.x > 0.33) { tc.pN3.y = 0; tc.pN3.xz = TotalOne(tc.pN3.xz); } #endif half4 a0 = half4(0,0,0,0); half4 a1 = half4(0,0,0,0); half4 a2 = half4(0,0,0,0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_DIFFUSE(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } half3 bf = tc.pN3; #if _TRIPLANARHEIGHTBLEND bf = TriplanarHBlend(a0.a, a1.a, a2.a, tc.pN3, contrasts.x); tc.pN3 = bf; #endif s.albedo3 = a0 * bf.x + a1 * bf.y + a2 * bf.z; } #endif #else s.albedo0 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.albedo1 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.albedo2 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.albedo3 = MICROSPLAT_SAMPLE_DIFFUSE(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #if _PERTEXHEIGHTOFFSET || _PERTEXHEIGHTCONTRAST SAMPLE_PER_TEX(ptHeight, 10.5, config, 1); #if _PERTEXHEIGHTOFFSET s.albedo0.a = saturate(s.albedo0.a + ptHeight0.b - 1); s.albedo1.a = saturate(s.albedo1.a + ptHeight1.b - 1); s.albedo2.a = saturate(s.albedo2.a + ptHeight2.b - 1); s.albedo3.a = saturate(s.albedo3.a + ptHeight3.b - 1); #endif #if _PERTEXHEIGHTCONTRAST s.albedo0.a = saturate(pow(s.albedo0.a + 0.5, abs(ptHeight0.a)) - 0.5); s.albedo1.a = saturate(pow(s.albedo1.a + 0.5, abs(ptHeight1.a)) - 0.5); s.albedo2.a = saturate(pow(s.albedo2.a + 0.5, abs(ptHeight2.a)) - 0.5); s.albedo3.a = saturate(pow(s.albedo3.a + 0.5, abs(ptHeight3.a)) - 0.5); #endif #endif } void SampleNormal(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _NONORMALMAP || _AUTONORMAL s.normSAO0 = half4(0,0, 0, 1); s.normSAO1 = half4(0,0, 0, 1); s.normSAO2 = half4(0,0, 0, 1); s.normSAO3 = half4(0,0, 0, 1); return; #endif #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif half3 absVertNormal = abs(tc.IN.worldNormal); float3x3 t2w = tc.IN.TBN; { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[0], config.cluster0, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[1], config.cluster0, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv0[2], config.cluster0, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf0 = SurfgradFromTriplanarProjection(tc.pN0, a0.xy, a1.xy, a2.xy); #else s.normSAO0.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN0, a0.xy, a1.xy, a2.xy); #endif s.normSAO0.zw = a0.zw * tc.pN0.x + a1.zw * tc.pN0.y + a2.zw * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[0], config.cluster1, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[1], config.cluster1, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv1[2], config.cluster1, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf1 = SurfgradFromTriplanarProjection(tc.pN1, a0.xy, a1.xy, a2.xy); #else s.normSAO1.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN1, a0.xy, a1.xy, a2.xy); #endif s.normSAO1.zw = a0.zw * tc.pN1.x + a1.zw * tc.pN1.y + a2.zw * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[0], config.cluster2, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[1], config.cluster2, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv2[2], config.cluster2, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf2 = SurfgradFromTriplanarProjection(tc.pN2, a0.xy, a1.xy, a2.xy); #else s.normSAO2.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN2, a0.xy, a1.xy, a2.xy); #endif s.normSAO2.zw = a0.zw * tc.pN2.x + a1.zw * tc.pN2.y + a2.zw * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0.5, 0.5, 0, 1); half4 a1 = half4(0.5, 0.5, 0, 1); half4 a2 = half4(0.5, 0.5, 0, 1); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[0], config.cluster3, d0).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[1], config.cluster3, d1).agrb; COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_NORMAL(tc.uv3[2], config.cluster3, d2).agrb; COUNTSAMPLE } #if _SURFACENORMALS s.surf3 = SurfgradFromTriplanarProjection(tc.pN3, a0.xy, a1.xy, a2.xy); #else s.normSAO3.xy = TransformTriplanarNormal(tc.IN, t2w, tc.axisSign, absVertNormal, tc.pN3, a0.xy, a1.xy, a2.xy); #endif s.normSAO3.zw = a0.zw * tc.pN3.x + a1.zw * tc.pN3.y + a2.zw * tc.pN3.z; } #endif #else s.normSAO0 = MICROSPLAT_SAMPLE_NORMAL(config.uv0, config.cluster0, mipLevel).agrb; COUNTSAMPLE s.normSAO0.xy = s.normSAO0.xy * 2 - 1; #if _SURFACENORMALS s.surf0 = ConvertNormal2ToGradient(s.normSAO0.xy); #endif MSBRANCH(weights.y) { s.normSAO1 = MICROSPLAT_SAMPLE_NORMAL(config.uv1, config.cluster1, mipLevel).agrb; COUNTSAMPLE s.normSAO1.xy = s.normSAO1.xy * 2 - 1; #if _SURFACENORMALS s.surf1 = ConvertNormal2ToGradient(s.normSAO1.xy); #endif } #if !_MAX2LAYER MSBRANCH(weights.z) { s.normSAO2 = MICROSPLAT_SAMPLE_NORMAL(config.uv2, config.cluster2, mipLevel).agrb; COUNTSAMPLE s.normSAO2.xy = s.normSAO2.xy * 2 - 1; #if _SURFACENORMALS s.surf2 = ConvertNormal2ToGradient(s.normSAO2.xy); #endif } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.normSAO3 = MICROSPLAT_SAMPLE_NORMAL(config.uv3, config.cluster3, mipLevel).agrb; COUNTSAMPLE s.normSAO3.xy = s.normSAO3.xy * 2 - 1; #if _SURFACENORMALS s.surf3 = ConvertNormal2ToGradient(s.normSAO3.xy); #endif } #endif #endif } void SampleEmis(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USEEMISSIVEMETAL #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.emisMetal0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.emisMetal1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.emisMetal2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_EMIS(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.emisMetal3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.emisMetal0 = MICROSPLAT_SAMPLE_EMIS(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.emisMetal1 = MICROSPLAT_SAMPLE_EMIS(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.emisMetal2 = MICROSPLAT_SAMPLE_EMIS(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.emisMetal3 = MICROSPLAT_SAMPLE_EMIS(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } void SampleSpecular(Config config, TriplanarConfig tc, inout RawSamples s, MIPFORMAT mipLevel, half4 weights) { #if _DISABLESPLATMAPS return; #endif #if _USESPECULARWORKFLOW #if _TRIPLANAR #if _USEGRADMIP float4 d0 = mipLevel.d0; float4 d1 = mipLevel.d1; float4 d2 = mipLevel.d2; #elif _USELODMIP float d0 = mipLevel.x; float d1 = mipLevel.y; float d2 = mipLevel.z; #else MIPFORMAT d0 = mipLevel; MIPFORMAT d1 = mipLevel; MIPFORMAT d2 = mipLevel; #endif { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN0.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[0], config.cluster0, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[1], config.cluster0, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN0.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv0[2], config.cluster0, d2); COUNTSAMPLE } s.specular0 = a0 * tc.pN0.x + a1 * tc.pN0.y + a2 * tc.pN0.z; } MSBRANCH(weights.y) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN1.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[0], config.cluster1, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[1], config.cluster1, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN1.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv1[2], config.cluster1, d2); COUNTSAMPLE } s.specular1 = a0 * tc.pN1.x + a1 * tc.pN1.y + a2 * tc.pN1.z; } #if !_MAX2LAYER MSBRANCH(weights.z) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN2.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[0], config.cluster2, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[1], config.cluster2, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN2.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv2[2], config.cluster2, d2); COUNTSAMPLE } s.specular2 = a0 * tc.pN2.x + a1 * tc.pN2.y + a2 * tc.pN2.z; } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { half4 a0 = half4(0, 0, 0, 0); half4 a1 = half4(0, 0, 0, 0); half4 a2 = half4(0, 0, 0, 0); MSBRANCHTRIPLANAR(tc.pN3.x) { a0 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[0], config.cluster3, d0); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.y) { a1 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[1], config.cluster3, d1); COUNTSAMPLE } MSBRANCHTRIPLANAR(tc.pN3.z) { a2 = MICROSPLAT_SAMPLE_SPECULAR(tc.uv3[2], config.cluster3, d2); COUNTSAMPLE } s.specular3 = a0 * tc.pN3.x + a1 * tc.pN3.y + a2 * tc.pN3.z; } #endif #else s.specular0 = MICROSPLAT_SAMPLE_SPECULAR(config.uv0, config.cluster0, mipLevel); COUNTSAMPLE MSBRANCH(weights.y) { s.specular1 = MICROSPLAT_SAMPLE_SPECULAR(config.uv1, config.cluster1, mipLevel); COUNTSAMPLE } #if !_MAX2LAYER MSBRANCH(weights.z) { s.specular2 = MICROSPLAT_SAMPLE_SPECULAR(config.uv2, config.cluster2, mipLevel); COUNTSAMPLE } #endif #if !_MAX3LAYER || !_MAX2LAYER MSBRANCH(weights.w) { s.specular3 = MICROSPLAT_SAMPLE_SPECULAR(config.uv3, config.cluster3, mipLevel); COUNTSAMPLE } #endif #endif #endif } MicroSplatLayer Sample(Input i, half4 weights, inout Config config, float camDist, float3 worldNormalVertex, DecalOutput decalOutput) { MicroSplatLayer o = (MicroSplatLayer)0; UNITY_INITIALIZE_OUTPUT(MicroSplatLayer,o); RawSamples samples = (RawSamples)0; InitRawSamples(samples); half4 albedo = 0; half4 normSAO = half4(0,0,0,1); half3 surfGrad = half3(0,0,0); half4 emisMetal = 0; half3 specular = 0; float worldHeight = i.worldPos.y; float3 upVector = float3(0,1,0); #if _GLOBALTINT || _GLOBALNORMALS || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _GLOBALSPECULAR float globalSlopeFilter = 1; #if _GLOBALSLOPEFILTER float2 gfilterUV = float2(1 - saturate(dot(worldNormalVertex, upVector) * 0.5 + 0.49), 0.5); globalSlopeFilter = SAMPLE_TEXTURE2D(_GlobalSlopeTex, sampler_Diffuse, gfilterUV).a; #endif #endif // declare outside of branchy areas.. half4 fxLevels = half4(0,0,0,0); half burnLevel = 0; half wetLevel = 0; half3 waterNormalFoam = half3(0, 0, 0); half porosity = 0.4; float streamFoam = 1.0f; half pud = 0; half snowCover = 0; half SSSThickness = 0; half3 SSSTint = half3(1,1,1); float traxBuffer = 0; float3 traxNormal = 0; float2 noiseUV = 0; #if _SPLATFADE MSBRANCHOTHER(1 - saturate(camDist - _SplatFade.y)) { #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE || _SNOWFOOTSTEPS traxBuffer = SampleTraxBuffer(i.worldPos, worldNormalVertex, traxNormal); #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA #if _MICROMESH fxLevels = SampleFXLevels(InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, config.uv), wetLevel, burnLevel, traxBuffer); #elif _MICROVERTEXMESH || _MICRODIGGERMESH || _MEGASPLAT fxLevels = ProcessFXLevels(i.fx, traxBuffer); #else fxLevels = SampleFXLevels(config.uv, wetLevel, burnLevel, traxBuffer); #endif #endif #if _DECAL fxLevels = max(fxLevels, decalOutput.fxLevels); #endif TriplanarConfig tc = (TriplanarConfig)0; UNITY_INITIALIZE_OUTPUT(TriplanarConfig,tc); MIPFORMAT albedoLOD = INITMIPFORMAT MIPFORMAT normalLOD = INITMIPFORMAT MIPFORMAT emisLOD = INITMIPFORMAT MIPFORMAT specLOD = INITMIPFORMAT MIPFORMAT origAlbedoLOD = INITMIPFORMAT; #if _TRIPLANAR && !_DISABLESPLATMAPS PrepTriplanar(i.shaderData.texcoord0, worldNormalVertex, i.worldPos, config, tc, weights, albedoLOD, normalLOD, emisLOD, origAlbedoLOD); tc.IN = i; #endif #if !_TRIPLANAR && !_DISABLESPLATMAPS #if _USELODMIP albedoLOD = ComputeMipLevel(config.uv0.xy, _Diffuse_TexelSize.zw); normalLOD = ComputeMipLevel(config.uv0.xy, _NormalSAO_TexelSize.zw); #if _USEEMISSIVEMETAL emisLOD = ComputeMipLevel(config.uv0.xy, _EmissiveMetal_TexelSize.zw); #endif #if _USESPECULARWORKFLOW specLOD = ComputeMipLevel(config.uv0.xy, _Specular_TexelSize.zw);; #endif #elif _USEGRADMIP albedoLOD = float4(ddx(config.uv0.xy), ddy(config.uv0.xy)); normalLOD = albedoLOD; #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #endif origAlbedoLOD = albedoLOD; #endif #if _PERTEXCURVEWEIGHT SAMPLE_PER_TEX(ptCurveWeight, 19.5, config, half4(0.5,1,1,1)); weights.x = lerp(smoothstep(0.5 - ptCurveWeight0.r, 0.5 + ptCurveWeight0.r, weights.x), weights.x, ptCurveWeight0.r*2); weights.y = lerp(smoothstep(0.5 - ptCurveWeight1.r, 0.5 + ptCurveWeight1.r, weights.y), weights.y, ptCurveWeight1.r*2); weights.z = lerp(smoothstep(0.5 - ptCurveWeight2.r, 0.5 + ptCurveWeight2.r, weights.z), weights.z, ptCurveWeight2.r*2); weights.w = lerp(smoothstep(0.5 - ptCurveWeight3.r, 0.5 + ptCurveWeight3.r, weights.w), weights.w, ptCurveWeight3.r*2); weights = TotalOne(weights); #endif // uvScale before anything #if _PERTEXUVSCALEOFFSET && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); config.uv0.xy = config.uv0.xy * ptUVScale0.rg + ptUVScale0.ba; config.uv1.xy = config.uv1.xy * ptUVScale1.rg + ptUVScale1.ba; #if !_MAX2LAYER config.uv2.xy = config.uv2.xy * ptUVScale2.rg + ptUVScale2.ba; #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = config.uv3.xy * ptUVScale3.rg + ptUVScale3.ba; #endif // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = albedoLOD * ptUVScale0.rgrg * weights.x + albedoLOD * ptUVScale1.rgrg * weights.y + albedoLOD * ptUVScale2.rgrg * weights.z + albedoLOD * ptUVScale3.rgrg * weights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #if _PERTEXUVROTATION && !_TRIPLANAR && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptUVRot, 16.5, config, half4(0,0,0,0)); config.uv0.xy = RotateUV(config.uv0.xy, ptUVRot0.x); config.uv1.xy = RotateUV(config.uv1.xy, ptUVRot1.x); #if !_MAX2LAYER config.uv2.xy = RotateUV(config.uv2.xy, ptUVRot2.x); #endif #if !_MAX3LAYER || !_MAX2LAYER config.uv3.xy = RotateUV(config.uv3.xy, ptUVRot0.x); #endif #endif o.Alpha = 1; #if _POM && !_DISABLESPLATMAPS DoPOM(i, config, tc, albedoLOD, weights, camDist, worldNormalVertex); #endif SampleAlbedo(config, tc, samples, albedoLOD, weights); #if _NOISEHEIGHT ApplyNoiseHeight(samples, config.uv, config, i.worldPos, worldNormalVertex); #endif #if _STREAMS || (_PARALLAX && !_DISABLESPLATMAPS) half earlyHeight = BlendWeights(samples.albedo0.w, samples.albedo1.w, samples.albedo2.w, samples.albedo3.w, weights); #endif #if _STREAMS waterNormalFoam = GetWaterNormal(i, config.uv, worldNormalVertex); DoStreamRefract(config, tc, waterNormalFoam, fxLevels.b, earlyHeight); #endif #if _PARALLAX && !_DISABLESPLATMAPS DoParallax(i, earlyHeight, config, tc, samples, weights, camDist); #endif // Blend results #if _PERTEXINTERPCONTRAST && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptContrasts, 1.5, config, 0.5); half4 contrast = 0.5; contrast.x = ptContrasts0.a; contrast.y = ptContrasts1.a; #if !_MAX2LAYER contrast.z = ptContrasts2.a; #endif #if !_MAX3LAYER || !_MAX2LAYER contrast.w = ptContrasts3.a; #endif contrast = clamp(contrast + _Contrast, 0.0001, 1.0); half cnt = contrast.x * weights.x + contrast.y * weights.y + contrast.z * weights.z + contrast.w * weights.w; half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, cnt); #else half4 heightWeights = ComputeWeights(weights, samples.albedo0.a, samples.albedo1.a, samples.albedo2.a, samples.albedo3.a, _Contrast); #endif #if _HYBRIDHEIGHTBLEND heightWeights = lerp(heightWeights, TotalOne(weights), saturate(camDist/max(1.0, _HybridHeightBlendDistance))); #endif // rescale derivatives after height weighting. Basically, in gradmip mode we blend the mip levels, // but this is before height mapping is sampled, so reblending them after alpha will make sure the other // channels (normal, etc) are sharper, which likely matters most.. #if _PERTEXUVSCALEOFFSET && !_DISABLESPLATMAPS #if _TRIPLANAR #if _USEGRADMIP SAMPLE_PER_TEX(ptUVScale, 0.5, config, half4(1,1,0,0)); albedoLOD.d0 = origAlbedoLOD.d0 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d0 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d0 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d0 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d1 = origAlbedoLOD.d1 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d1 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d1 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d1 * ptUVScale3.xyxy * heightWeights.w; albedoLOD.d2 = origAlbedoLOD.d2 * ptUVScale0.xyxy * heightWeights.x + origAlbedoLOD.d2 * ptUVScale1.xyxy * heightWeights.y + origAlbedoLOD.d2 * ptUVScale2.xyxy * heightWeights.z + origAlbedoLOD.d2 * ptUVScale3.xyxy * heightWeights.w; normalLOD.d0 = albedoLOD.d0; normalLOD.d1 = albedoLOD.d1; normalLOD.d2 = albedoLOD.d2; #if _USEEMISSIVEMETAL emisLOD.d0 = albedoLOD.d0; emisLOD.d1 = albedoLOD.d1; emisLOD.d2 = albedoLOD.d2; #endif #endif // gradmip #else // not triplanar // fix for pertex uv scale using gradient sampler and weight blended derivatives #if _USEGRADMIP albedoLOD = origAlbedoLOD * ptUVScale0.rgrg * heightWeights.x + origAlbedoLOD * ptUVScale1.rgrg * heightWeights.y + origAlbedoLOD * ptUVScale2.rgrg * heightWeights.z + origAlbedoLOD * ptUVScale3.rgrg * heightWeights.w; normalLOD = albedoLOD; #if _USEEMISSIVEMETAL emisLOD = albedoLOD; #endif #if _USESPECULARWORKFLOW specLOD = albedoLOD; #endif #endif #endif #endif #if _PARALLAX || _STREAMS SampleAlbedo(config, tc, samples, albedoLOD, heightWeights); #endif SampleNormal(config, tc, samples, normalLOD, heightWeights); #if _USEEMISSIVEMETAL SampleEmis(config, tc, samples, emisLOD, heightWeights); #endif #if _USESPECULARWORKFLOW SampleSpecular(config, tc, samples, specLOD, heightWeights); #endif #if _DISTANCERESAMPLE && !_DISABLESPLATMAPS DistanceResample(samples, config, tc, camDist, i.viewDir, fxLevels, albedoLOD, i.worldPos, heightWeights, worldNormalVertex); #endif #if _STARREACHFORMAT samples.normSAO0.w = length(samples.normSAO0.xy); samples.normSAO1.w = length(samples.normSAO1.xy); samples.normSAO2.w = length(samples.normSAO2.xy); samples.normSAO3.w = length(samples.normSAO3.xy); #endif // PerTexture sampling goes here, passing the samples structure #if _PERTEXMICROSHADOWS || _PERTEXFUZZYSHADE SAMPLE_PER_TEX(ptFuzz, 17.5, config, half4(0, 0, 1, 1)); #endif #if _PERTEXMICROSHADOWS #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) { half3 lightDir = GetGlobalLightDirTS(i); half4 microShadows = half4(1,1,1,1); microShadows.x = MicroShadow(lightDir, half3(samples.normSAO0.xy, 1), samples.normSAO0.a, ptFuzz0.a); microShadows.y = MicroShadow(lightDir, half3(samples.normSAO1.xy, 1), samples.normSAO1.a, ptFuzz1.a); microShadows.z = MicroShadow(lightDir, half3(samples.normSAO2.xy, 1), samples.normSAO2.a, ptFuzz2.a); microShadows.w = MicroShadow(lightDir, half3(samples.normSAO3.xy, 1), samples.normSAO3.a, ptFuzz3.a); samples.normSAO0.a *= microShadows.x; samples.normSAO1.a *= microShadows.y; #if !_MAX2LAYER samples.normSAO2.a *= microShadows.z; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a *= microShadows.w; #endif #if _DEBUG_OUTPUT_MICROSHADOWS o.Albedo = BlendWeights(microShadows.x, microShadows.y, microShadows.z, microShadows.a, heightWeights); return o; #endif } #endif #endif // _PERTEXMICROSHADOWS #if _PERTEXFUZZYSHADE samples.albedo0.rgb = FuzzyShade(samples.albedo0.rgb, half3(samples.normSAO0.rg, 1), ptFuzz0.r, ptFuzz0.g, ptFuzz0.b, i.viewDir); samples.albedo1.rgb = FuzzyShade(samples.albedo1.rgb, half3(samples.normSAO1.rg, 1), ptFuzz1.r, ptFuzz1.g, ptFuzz1.b, i.viewDir); #if !_MAX2LAYER samples.albedo2.rgb = FuzzyShade(samples.albedo2.rgb, half3(samples.normSAO2.rg, 1), ptFuzz2.r, ptFuzz2.g, ptFuzz2.b, i.viewDir); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = FuzzyShade(samples.albedo3.rgb, half3(samples.normSAO3.rg, 1), ptFuzz3.r, ptFuzz3.g, ptFuzz3.b, i.viewDir); #endif #endif #if _PERTEXSATURATION && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptSaturattion, 9.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = lerp(MSLuminance(samples.albedo0.rgb), samples.albedo0.rgb, ptSaturattion0.a); samples.albedo1.rgb = lerp(MSLuminance(samples.albedo1.rgb), samples.albedo1.rgb, ptSaturattion1.a); #if !_MAX2LAYER samples.albedo2.rgb = lerp(MSLuminance(samples.albedo2.rgb), samples.albedo2.rgb, ptSaturattion2.a); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = lerp(MSLuminance(samples.albedo3.rgb), samples.albedo3.rgb, ptSaturattion3.a); #endif #endif #if _PERTEXTINT && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptTints, 1.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb *= ptTints0.rgb; samples.albedo1.rgb *= ptTints1.rgb; #if !_MAX2LAYER samples.albedo2.rgb *= ptTints2.rgb; #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb *= ptTints3.rgb; #endif #endif #if _PCHEIGHTGRADIENT || _PCHEIGHTHSV || _PCSLOPEGRADIENT || _PCSLOPEHSV ProceduralGradients(i, samples, config, worldHeight, worldNormalVertex); #endif #if _WETNESS || _PUDDLES || _STREAMS porosity = _GlobalPorosity; #endif #if _PERTEXCOLORINTENSITY SAMPLE_PER_TEX(ptCI, 23.5, config, half4(1, 1, 1, 1)); samples.albedo0.rgb = saturate(samples.albedo0.rgb * (1 + ptCI0.rrr)); samples.albedo1.rgb = saturate(samples.albedo1.rgb * (1 + ptCI1.rrr)); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb * (1 + ptCI2.rrr)); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb * (1 + ptCI3.rrr)); #endif #endif #if (_PERTEXBRIGHTNESS || _PERTEXCONTRAST || _PERTEXPOROSITY || _PERTEXFOAM) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(ptBC, 3.5, config, half4(1, 1, 1, 1)); #if _PERTEXCONTRAST samples.albedo0.rgb = saturate(((samples.albedo0.rgb - 0.5) * ptBC0.g) + 0.5); samples.albedo1.rgb = saturate(((samples.albedo1.rgb - 0.5) * ptBC1.g) + 0.5); #if !_MAX2LAYER samples.albedo2.rgb = saturate(((samples.albedo2.rgb - 0.5) * ptBC2.g) + 0.5); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(((samples.albedo3.rgb - 0.5) * ptBC3.g) + 0.5); #endif #endif #if _PERTEXBRIGHTNESS samples.albedo0.rgb = saturate(samples.albedo0.rgb + ptBC0.rrr); samples.albedo1.rgb = saturate(samples.albedo1.rgb + ptBC1.rrr); #if !_MAX2LAYER samples.albedo2.rgb = saturate(samples.albedo2.rgb + ptBC2.rrr); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.albedo3.rgb = saturate(samples.albedo3.rgb + ptBC3.rrr); #endif #endif #if _PERTEXPOROSITY porosity = BlendWeights(ptBC0.b, ptBC1.b, ptBC2.b, ptBC3.b, heightWeights); #endif #if _PERTEXFOAM streamFoam = BlendWeights(ptBC0.a, ptBC1.a, ptBC2.a, ptBC3.a, heightWeights); #endif #endif #if (_PERTEXNORMSTR || _PERTEXAOSTR || _PERTEXSMOOTHSTR || _PERTEXMETALLIC) && !_DISABLESPLATMAPS SAMPLE_PER_TEX(perTexMatSettings, 2.5, config, half4(1.0, 1.0, 1.0, 0.0)); #endif #if _PERTEXNORMSTR && !_DISABLESPLATMAPS #if _SURFACENORMALS samples.surf0 *= perTexMatSettings0.r; samples.surf1 *= perTexMatSettings1.r; samples.surf2 *= perTexMatSettings2.r; samples.surf3 *= perTexMatSettings3.r; #else samples.normSAO0.xy *= perTexMatSettings0.r; samples.normSAO1.xy *= perTexMatSettings1.r; samples.normSAO2.xy *= perTexMatSettings2.r; samples.normSAO3.xy *= perTexMatSettings3.r; #endif #endif #if _PERTEXAOSTR && !_DISABLESPLATMAPS samples.normSAO0.a = pow(abs(samples.normSAO0.a), perTexMatSettings0.b); samples.normSAO1.a = pow(abs(samples.normSAO1.a), perTexMatSettings1.b); #if !_MAX2LAYER samples.normSAO2.a = pow(abs(samples.normSAO2.a), perTexMatSettings2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.a = pow(abs(samples.normSAO3.a), perTexMatSettings3.b); #endif #endif #if _PERTEXSMOOTHSTR && !_DISABLESPLATMAPS samples.normSAO0.b += perTexMatSettings0.g; samples.normSAO1.b += perTexMatSettings1.g; samples.normSAO0.b = saturate(samples.normSAO0.b); samples.normSAO1.b = saturate(samples.normSAO1.b); #if !_MAX2LAYER samples.normSAO2.b += perTexMatSettings2.g; samples.normSAO2.b = saturate(samples.normSAO2.b); #endif #if !_MAX3LAYER || !_MAX2LAYER samples.normSAO3.b += perTexMatSettings3.g; samples.normSAO3.b = saturate(samples.normSAO3.b); #endif #endif #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_DEFERRED) || (defined(_URP) && defined(_PASSFORWARD) || _HDRP) #if _PERTEXSSS { SAMPLE_PER_TEX(ptSSS, 18.5, config, half4(1, 1, 1, 1)); // tint, thickness half4 vals = ptSSS0 * heightWeights.x + ptSSS1 * heightWeights.y + ptSSS2 * heightWeights.z + ptSSS3 * heightWeights.w; SSSThickness = vals.a; SSSTint = vals.rgb; } #endif #endif #if _PERTEXRIMLIGHT { SAMPLE_PER_TEX(ptRimA, 26.5, config, half4(1, 1, 1, 1)); SAMPLE_PER_TEX(ptRimB, 27.5, config, half4(1, 1, 1, 0)); samples.emisMetal0.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO0.xy, 1))), max(0.0001, ptRimA0.g)) * ptRimB0.rgb * ptRimB0.a; samples.emisMetal1.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO1.xy, 1))), max(0.0001, ptRimA1.g)) * ptRimB1.rgb * ptRimB1.a; samples.emisMetal2.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO2.xy, 1))), max(0.0001, ptRimA2.g)) * ptRimB2.rgb * ptRimB2.a; samples.emisMetal3.rgb += pow(1.0 - saturate(dot(i.viewDir, float3(samples.normSAO3.xy, 1))), max(0.0001, ptRimA3.g)) * ptRimB3.rgb * ptRimB3.a; } #endif #if (((_DETAILNOISE && _PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && _PERTEXDISTANCENOISESTRENGTH)) || (_NORMALNOISE && _PERTEXNORMALNOISESTRENGTH)) && !_DISABLESPLATMAPS ApplyDetailDistanceNoisePerTex(samples, config, camDist, i.worldPos, worldNormalVertex); #endif #if _GLOBALNOISEUV // noise defaults so that a value of 1, 1 is 4 pixels in size and moves the uvs by 1 pixel max. #if _CUSTOMSPLATTEXTURES noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #else noiseUV = (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, config.uv * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif #endif #if _TRAXSINGLE || _TRAXARRAY || _TRAXNOTEXTURE ApplyTrax(samples, config, i.worldPos, traxBuffer, traxNormal); #endif #if (_ANTITILEARRAYDETAIL || _ANTITILEARRAYDISTANCE || _ANTITILEARRAYNORMAL) && !_DISABLESPLATMAPS ApplyAntiTilePerTex(samples, config, camDist, i.worldPos, worldNormalVertex, heightWeights); #endif #if _GEOMAP && !_DISABLESPLATMAPS GeoTexturePerTex(samples, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _GLOBALTINT && _PERTEXGLOBALTINTSTRENGTH && !_DISABLESPLATMAPS GlobalTintTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALNORMALS && _PERTEXGLOBALNORMALSTRENGTH && !_DISABLESPLATMAPS GlobalNormalTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && _PERTEXGLOBALSAOMSTRENGTH && !_DISABLESPLATMAPS GlobalSAOMTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && _PERTEXGLOBALEMISSTRENGTH && !_DISABLESPLATMAPS GlobalEmisTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && _PERTEXGLOBALSPECULARSTRENGTH && !_DISABLESPLATMAPS && _USESPECULARWORKFLOW GlobalSpecularTexturePerTex(samples, config, camDist, globalSlopeFilter, noiseUV); #endif #if _PERTEXMETALLIC && !_DISABLESPLATMAPS half metallic = BlendWeights(perTexMatSettings0.a, perTexMatSettings1.a, perTexMatSettings2.a, perTexMatSettings3.a, heightWeights); o.Metallic = metallic; #endif #if _GLITTER && !_DISABLESPLATMAPS DoGlitter(i, samples, config, camDist, worldNormalVertex, i.worldPos); #endif // Blend em.. #if _DISABLESPLATMAPS // If we don't sample from the _Diffuse, then the shader compiler will strip the sampler on // some platforms, which will cause everything to break. So we sample from the lowest mip // and saturate to 1 to keep the cost minimal. Annoying, but the compiler removes the texture // and sampler, even though the sampler is still used. albedo = saturate(UNITY_SAMPLE_TEX2DARRAY_LOD(_Diffuse, float3(0,0,0), 12) + 1); albedo.a = 0.5; // make height something we can blend with for the combined mesh mode, since it still height blends. normSAO = half4(0,0,0,1); #else albedo = BlendWeights(samples.albedo0, samples.albedo1, samples.albedo2, samples.albedo3, heightWeights); normSAO = BlendWeights(samples.normSAO0, samples.normSAO1, samples.normSAO2, samples.normSAO3, heightWeights); #if _SURFACENORMALS surfGrad = BlendWeights(samples.surf0, samples.surf1, samples.surf2, samples.surf3, heightWeights); #endif #if (_USEEMISSIVEMETAL || _PERTEXRIMLIGHT) && !_DISABLESPLATMAPS emisMetal = BlendWeights(samples.emisMetal0, samples.emisMetal1, samples.emisMetal2, samples.emisMetal3, heightWeights); #endif #if _USESPECULARWORKFLOW && !_DISABLESPLATMAPS specular = BlendWeights(samples.specular0, samples.specular1, samples.specular2, samples.specular3, heightWeights); #endif #if _PERTEXOUTLINECOLOR SAMPLE_PER_TEX(ptOutlineColor, 28.5, config, half4(0.5, 0.5, 0.5, 1)); half4 outlineColor = BlendWeights(ptOutlineColor0, ptOutlineColor1, ptOutlineColor2, ptOutlineColor3, heightWeights); half4 tstr = saturate(abs(heightWeights - 0.5) * 2); half transitionBlend = min(min(min(tstr.x, tstr.y), tstr.z), tstr.w); albedo.rgb = lerp(albedo.rgb * outlineColor.rgb * 2, albedo.rgb, outlineColor.a * transitionBlend); #endif #endif #if _MESHOVERLAYSPLATS || _MESHCOMBINED o.Alpha = 1.0; if (config.uv0.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.x; else if (config.uv1.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.y; else if (config.uv2.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.z; else if (config.uv3.z == _MeshAlphaIndex) o.Alpha = 1 - heightWeights.w; #endif // effects which don't require per texture adjustments and are part of the splats sample go here. // Often, as an optimization, you can compute the non-per tex version of above effects here.. #if ((_DETAILNOISE && !_PERTEXDETAILNOISESTRENGTH) || (_DISTANCENOISE && !_PERTEXDISTANCENOISESTRENGTH) || (_NORMALNOISE && !_PERTEXNORMALNOISESTRENGTH)) ApplyDetailDistanceNoise(albedo.rgb, normSAO, surfGrad, config, camDist, i.worldPos, worldNormalVertex); #endif #if _SPLATFADE } #endif #if _SPLATFADE float2 sfDX = ddx(config.uv * _UVScale); float2 sfDY = ddy(config.uv * _UVScale); MSBRANCHOTHER(camDist - _SplatFade.x) { float falloff = saturate(InverseLerp(_SplatFade.x, _SplatFade.y, camDist)); half4 sfalb = SAMPLE_TEXTURE2D_ARRAY_GRAD(_Diffuse, sampler_Diffuse, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY); COUNTSAMPLE albedo.rgb = lerp(albedo.rgb, sfalb.rgb, falloff); #if !_NONORMALMAP && !_AUTONORMAL half4 sfnormSAO = SAMPLE_TEXTURE2D_ARRAY_GRAD(_NormalSAO, sampler_NormalSAO, config.uv * _UVScale, _SplatFade.z, sfDX, sfDY).agrb; COUNTSAMPLE sfnormSAO.xy = sfnormSAO.xy * 2 - 1; normSAO = lerp(normSAO, sfnormSAO, falloff); #if _SURFACENORMALS surfGrad = lerp(surfGrad, ConvertNormal2ToGradient(sfnormSAO.xy), falloff); #endif #endif } #endif #if _AUTONORMAL float3 autoNormal = HeightToNormal(albedo.a * _AutoNormalHeightScale, i.worldPos); normSAO.xy = autoNormal; normSAO.z = 0; normSAO.w = (autoNormal.z * autoNormal.z); #endif #if _MESHCOMBINED SampleMeshCombined(albedo, normSAO, surfGrad, emisMetal, specular, o.Alpha, SSSThickness, SSSTint, config, heightWeights); #endif #if _ISOBJECTSHADER SampleObjectShader(i, albedo, normSAO, surfGrad, emisMetal, specular, config); #endif #if _GEOMAP GeoTexture(albedo.rgb, normSAO, surfGrad, i.worldPos, worldHeight, config, worldNormalVertex, upVector); #endif #if _SCATTER ApplyScatter( #if _MEGASPLAT config, #endif i, albedo, normSAO, surfGrad, config.uv, camDist); #endif #if _DECAL DoDecalBlend(decalOutput, albedo, normSAO, surfGrad, emisMetal, i.uv_Control0); #endif #if _GLOBALTINT && !_PERTEXGLOBALTINTSTRENGTH GlobalTintTexture(albedo.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif #if _VSGRASSMAP VSGrassTexture(albedo.rgb, config, camDist); #endif #if _GLOBALNORMALS && !_PERTEXGLOBALNORMALSTRENGTH GlobalNormalTexture(normSAO, surfGrad, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSMOOTHAOMETAL && !_PERTEXGLOBALSAOMSTRENGTH GlobalSAOMTexture(normSAO, emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALEMIS && !_PERTEXGLOBALEMISSTRENGTH GlobalEmisTexture(emisMetal, config, camDist, globalSlopeFilter, noiseUV); #endif #if _GLOBALSPECULAR && !_PERTEXGLOBALSPECULARSTRENGTH && _USESPECULARWORKFLOW GlobalSpecularTexture(specular.rgb, config, camDist, globalSlopeFilter, noiseUV); #endif o.Albedo = albedo.rgb; o.Height = albedo.a; #if _NONORMALMAP o.Normal = half3(0,0,1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #elif _SURFACENORMALS o.Normal = ResolveNormalFromSurfaceGradient(surfGrad); o.Normal = mul(GetTBN(i), o.Normal); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #else o.Normal = half3(normSAO.xy, 1); o.Smoothness = normSAO.b; o.Occlusion = normSAO.a; #endif #if _USEEMISSIVEMETAL || _GLOBALSMOOTHAOMETAL || _GLOBALEMIS || _PERTEXRIMLIGHT #if _USEEMISSIVEMETAL emisMetal.rgb *= _EmissiveMult; #endif o.Emission += emisMetal.rgb; o.Metallic = emisMetal.a; #endif #if _USESPECULARWORKFLOW o.Specular = specular; #endif #if _WETNESS || _PUDDLES || _STREAMS || _LAVA pud = DoStreams(i, o, fxLevels, config.uv, porosity, waterNormalFoam, worldNormalVertex, streamFoam, wetLevel, burnLevel, i.worldPos); #endif #if _SNOW snowCover = DoSnow(i, o, config.uv, WorldNormalVector(i, o.Normal), worldNormalVertex, i.worldPos, pud, porosity, camDist, config, weights, SSSTint, SSSThickness, traxBuffer, traxNormal); #endif #if _PERTEXSSS || _MESHCOMBINEDUSESSS || (_SNOW && _SNOWSSS) { half3 worldView = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz); o.Emission += ComputeSSS(i, worldView, WorldNormalVector(i, o.Normal), SSSTint, SSSThickness, _SSSDistance, _SSSScale, _SSSPower); } #endif #if _SNOWGLITTER DoSnowGlitter(i, config, o, camDist, worldNormalVertex, snowCover); #endif #if _WINDPARTICULATE || _SNOWPARTICULATE DoWindParticulate(i, o, config, weights, camDist, worldNormalVertex, snowCover); #endif o.Normal.z = sqrt(1 - saturate(dot(o.Normal.xy, o.Normal.xy))); #if _SPECULARFADE { float specFade = saturate((i.worldPos.y - _SpecularFades.x) / max(_SpecularFades.y - _SpecularFades.x, 0.0001)); o.Metallic *= specFade; o.Smoothness *= specFade; } #endif #if _VSSHADOWMAP VSShadowTexture(o, i, config, camDist); #endif #if _TOONWIREFRAME ToonWireframe(config.uv, o.Albedo, camDist); #endif #if _SEETHROUGHSHADER SeethroughShader(o.Albedo, o.Emission, o.Alpha, i.worldPos, o.Normal, i.worldNormal); #endif #if _DEBUG_TRAXBUFFER ClearAllButAlbedo(o, half3(traxBuffer, 0, 0) * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMALVERTEX ClearAllButAlbedo(o, worldNormalVertex * saturate(o.Albedo.z+1)); #elif _DEBUG_WORLDNORMAL ClearAllButAlbedo(o, WorldNormalVector(i, o.Normal) * saturate(o.Albedo.z+1)); #endif #if _DEBUG_MEGABARY && _MEGASPLAT o.Albedo = i.baryWeights.xyz; #endif return o; } void SampleSplats(float2 controlUV, inout half4 w0, inout half4 w1, inout half4 w2, inout half4 w3, inout half4 w4, inout half4 w5, inout half4 w6, inout half4 w7) { #if _CUSTOMSPLATTEXTURES #if !_MICROMESH controlUV = (controlUV * (_CustomControl0_TexelSize.zw - 1.0f) + 0.5f) * _CustomControl0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _CustomControl0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _CustomControl0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_CustomControl0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_CustomControl1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_CustomControl2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_CustomControl3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_CustomControl4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_CustomControl5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_CustomControl6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_CustomControl7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #else #if !_MICROMESH controlUV = (controlUV * (_Control0_TexelSize.zw - 1.0f) + 0.5f) * _Control0_TexelSize.xy; #endif #if _CONTROLNOISEUV controlUV += (SAMPLE_TEXTURE2D(_NoiseUV, sampler_Diffuse, controlUV * _Control0_TexelSize.zw * 0.2 * _NoiseUVParams.x).ga - 0.5) * _Control0_TexelSize.xy * _NoiseUVParams.y; #endif w0 = SAMPLE_TEXTURE2D(_Control0, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #if !_MAX4TEXTURES w1 = SAMPLE_TEXTURE2D(_Control1, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES w2 = SAMPLE_TEXTURE2D(_Control2, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if !_MAX4TEXTURES && !_MAX8TEXTURES && !_MAX12TEXTURES w3 = SAMPLE_TEXTURE2D(_Control3, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX20TEXTURES || _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w4 = SAMPLE_TEXTURE2D(_Control4, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX24TEXTURES || _MAX28TEXTURES || _MAX32TEXTURES w5 = SAMPLE_TEXTURE2D(_Control5, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX28TEXTURES || _MAX32TEXTURES w6 = SAMPLE_TEXTURE2D(_Control6, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #if _MAX32TEXTURES w7 = SAMPLE_TEXTURE2D(_Control7, shared_linear_clamp_sampler, controlUV); COUNTSAMPLE #endif #endif } MicroSplatLayer SurfImpl(Input i, float3 worldNormalVertex) { #if _MEGANOUV i.uv_Control0 = i.worldPos.xz; #endif float camDist = distance(_WorldSpaceCameraPos, i.worldPos); #if _FORCELOCALSPACE worldNormalVertex = mul((float3x3)GetWorldToObjectMatrix(), worldNormalVertex).xyz; i.worldPos = i.worldPos - mul(GetObjectToWorldMatrix(), float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _ORIGINSHIFT i.worldPos = i.worldPos + mul(_GlobalOriginMTX, float4(0,0,0,1)).xyz; i.worldHeight = i.worldPos.y; #endif #if _DEBUG_USE_TOPOLOGY i.worldPos = SAMPLE_TEXTURE2D(_DebugWorldPos, sampler_Diffuse, i.uv_Control0); worldNormalVertex = SAMPLE_TEXTURE2D(_DebugWorldNormal, sampler_Diffuse, i.uv_Control0); i.worldHeight = i.worldPos.y; #endif #if _ALPHABELOWHEIGHT && !_TBDISABLEALPHAHOLES ClipWaterLevel(i.worldPos); #endif #if !_TBDISABLEALPHAHOLES && defined(_ALPHATEST_ON) // UNITY 2019.3 holes ClipHoles(i.uv_Control0); #endif float2 origUV = i.uv_Control0; #if _MICROMESH && _MESHUV2 float2 controlUV = i.uv2_Diffuse; #else float2 controlUV = i.uv_Control0; #endif #if _MICROMESH controlUV = InverseLerp(_UVMeshRange.xy, _UVMeshRange.zw, controlUV); #endif half4 weights = half4(1,0,0,0); Config config = (Config)0; UNITY_INITIALIZE_OUTPUT(Config,config); config.uv = origUV; DecalOutput decalOutput = (DecalOutput)0; #if _DECAL decalOutput = DoDecals(i.uv_Control0, i.worldPos, camDist, worldNormalVertex); #endif #if _SURFACENORMALS // Initialize the surface gradient basis vectors ConstructSurfaceGradientTBN(i); #endif #if _SPLATFADE MSBRANCHOTHER(_SplatFade.y - camDist) #endif // _SPLATFADE { #if !_DISABLESPLATMAPS // Sample the splat data, from textures or vertices, and setup the config.. #if _MICRODIGGERMESH DiggerSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLAT MegaSplatVertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif _MEGASPLATTEXTURE MegaSplatTextureSetup(controlUV, weights, origUV, config, i.worldPos, decalOutput); #elif _MICROVERTEXMESH VertexSetup(i, weights, origUV, config, i.worldPos, decalOutput); #elif !_PROCEDURALTEXTURE || _PROCEDURALBLENDSPLATS half4 w0 = 0; half4 w1 = 0; half4 w2 = 0; half4 w3 = 0; half4 w4 = 0; half4 w5 = 0; half4 w6 = 0; half4 w7 = 0; SampleSplats(controlUV, w0, w1, w2, w3, w4, w5, w6, w7); Setup(weights, origUV, config, w0, w1, w2, w3, w4, w5, w6, w7, i.worldPos, decalOutput); #endif #if _PROCEDURALTEXTURE float3 procNormal = worldNormalVertex; float3 worldPos = i.worldPos; ProceduralSetup(i, worldPos, i.worldHeight, procNormal, i.worldUpVector, weights, origUV, config, ddx(origUV), ddy(origUV), ddx(worldPos), ddy(worldPos), decalOutput); #endif #else // _DISABLESPLATMAPS Setup(weights, origUV, config, half4(1,0,0,0), 0, 0, 0, 0, 0, 0, 0, i.worldPos, decalOutput); #endif #if _SLOPETEXTURE SlopeTexture(config, weights, worldNormalVertex); #endif } // _SPLATFADE else case #if _TOONFLATTEXTURE float2 quv = floor(origUV * _ToonTerrainSize); float2 fuv = frac(origUV * _ToonTerrainSize); #if !_TOONFLATTEXTUREQUAD quv = Hash2D((fuv.x > fuv.y) ? quv : quv * 0.333); #endif float2 uvq = quv / _ToonTerrainSize; config.uv0.xy = uvq; config.uv1.xy = uvq; config.uv2.xy = uvq; config.uv3.xy = uvq; #endif #if (_TEXTURECLUSTER2 || _TEXTURECLUSTER3) && !_DISABLESPLATMAPS PrepClusters(origUV, config, i.worldPos, worldNormalVertex); #endif #if (_ALPHAHOLE || _ALPHAHOLETEXTURE) && !_DISABLESPLATMAPS && !_TBDISABLEALPHAHOLES ClipAlphaHole(config, weights); #endif MicroSplatLayer l = Sample(i, weights, config, camDist, worldNormalVertex, decalOutput); // On windows, sometimes the shared samplers gets stripped, so we have to do this crap. // We sample from the lowest mip, so it shouldn't cost much, but still, I hate this, wtf.. float stripVal = saturate(SAMPLE_TEXTURE2D_LOD(_Diffuse, sampler_Diffuse, config.uv0, 11).r + 2); stripVal *= saturate(SAMPLE_TEXTURE2D_LOD(_NormalSAO, sampler_NormalSAO, config.uv0, 11).r + 2); l.Albedo *= stripVal; l.Normal *= stripVal; #if _PROCEDURALTEXTURE ProceduralTextureDebugOutput(l, weights, config); #endif return l; } float4 ConstructTerrainTangent(float3 normal, float3 positiveZ) { // Consider a flat terrain. It should have tangent be (1, 0, 0) and bitangent be (0, 0, 1) as the UV of the terrain grid mesh is a scale of the world XZ position. // In CreateTangentToWorld function (in SpaceTransform.hlsl), it is cross(normal, tangent) * sgn for the bitangent vector. // It is not true in a left-handed coordinate system for the terrain bitangent, if we provide 1 as the tangent.w. It would produce (0, 0, -1) instead of (0, 0, 1). // Also terrain's tangent calculation was wrong in a left handed system because cross((0,0,1), terrainNormalOS) points to the wrong direction as negative X. // Therefore all the 4 xyzw components of the tangent needs to be flipped to correct the tangent frame. // (See TerrainLitData.hlsl - GetSurfaceAndBuiltinData) float3 tangent = normalize(cross(normal, positiveZ)); return float4(tangent, -1); } void TerrainInstancing(inout float4 vertex, inout float3 normal, inout float2 uv) { #if _MICROTERRAIN && defined(UNITY_INSTANCING_ENABLED) && !_TERRAINBLENDABLESHADER float2 patchVertex = vertex.xy; float4 instanceData = UNITY_ACCESS_INSTANCED_PROP(Terrain, _TerrainPatchInstanceData); float2 sampleCoords = (patchVertex.xy + instanceData.xy) * instanceData.z; // (xy + float2(xBase,yBase)) * skipScale uv = sampleCoords * _TerrainHeightmapRecipSize.zw; float2 sampleUV = (uv / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleUV, 0)); vertex.xz = sampleCoords * _TerrainHeightmapScale.xz; vertex.y = height * _TerrainHeightmapScale.y; normal = float3(0, 1, 0); #endif } void ApplyMeshModification(inout VertexData input) { #if _MICROTERRAIN && !_TERRAINBLENDABLESHADER float2 uv = input.texcoord0.xy; TerrainInstancing(input.vertex, input.normal, uv); input.texcoord0.xy = uv; #endif #if _PERPIXNORMAL && !_TERRAINBLENDABLESHADER input.normal = float3(0,1,0); #endif #if _MICROVERSEPREVIEW float4 recipSize = _TerrainHeightmapTexture_TexelSize; recipSize.zw = (1.0f / (_TerrainHeightmapTexture_TexelSize.zw-1)); float2 sampleCoords = (input.texcoord0.xy / recipSize.zw + 0.5f) * recipSize.xy; float height = UnpackHeightmap(SAMPLE_TEXTURE2D_LOD(_TerrainHeightmapTexture, shared_linear_clamp_sampler, sampleCoords, 0)); input.vertex.xyz += float3(0,1,0) * height * _TerrainHeight * 2; #endif } // called by the template, so we can remove tangent from VertexData void ApplyTerrainTangent(inout VertexToPixel input) { #if (_MICROTERRAIN || _PERPIXNORMAL) && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif // digger meshes ain't got no tangent either.. #if _MICRODIGGERMESH && !_TERRAINBLENDABLESHADER input.worldTangent = ConstructTerrainTangent(input.worldNormal, float3(0, 0, 1)); #endif } void ModifyVertex(inout VertexData v, inout ExtraV2F d) { ApplyMeshModification(v); #if _MICROVERTEXMESH || _MICRODIGGERMESH EncodeVertexWorkflow(v, d); #elif _MEGASPLAT EncodeMegaSplatVertex(v, d); #endif } void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d) { #if _MICROVERSEPREVIEW v.vertex.y = OffsetVertex(v, d).y; #elif _TESSDISTANCE || _TESSEDGE v.vertex.xyz += OffsetVertex(v, d); #endif } float3 GetTessFactors () { #if _TESSEDGE return float3(_TessData1.x, _TessData1.w, 0); #endif #if _TESSDISTANCE return float3(_TessData2.x, _TessData2.y, _TessData1.x); #endif return 0; } void SurfaceFunction(inout Surface o, inout ShaderData d) { float3 worldNormalVertex = d.worldSpaceNormal; #if _MICROVERSEPREVIEW float2 sampleCoords = d.texcoord0.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif (defined(UNITY_INSTANCING_ENABLED) && _MICROTERRAIN && !_TERRAINBLENDABLESHADER) float2 sampleCoords = (d.texcoord0.xy / _TerrainHeightmapRecipSize.zw + 0.5f) * _TerrainHeightmapRecipSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_TerrainNormalmapTexture, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomNormal, geomTangent)) * -1; worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #elif _PERPIXNORMAL && (_MICROTERRAIN || _MICROMESHTERRAIN) && !_TERRAINBLENDABLESHADER float2 sampleCoords = (d.texcoord0.xy * _PerPixelNormal_TexelSize.zw + 0.5f) * _PerPixelNormal_TexelSize.xy; #if _TOONHARDEDGENORMAL sampleCoords = ToonEdgeUV(d.texcoord0.xy); #endif float3 geomNormal = normalize(SAMPLE_TEXTURE2D(_PerPixelNormal, shared_linear_clamp_sampler, sampleCoords).xyz * 2 - 1); float3 geomTangent = normalize(cross(geomNormal, float3(0, 0, 1))); float3 geomBitangent = normalize(cross(geomTangent, geomNormal)) * -1; #if _MICROMESHTERRAIN geomBitangent *= -1; #endif worldNormalVertex = geomNormal; d.worldSpaceNormal = geomNormal; d.worldSpaceTangent = geomTangent; d.TBNMatrix = float3x3(geomTangent, geomBitangent, geomNormal); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); #endif #if _TOONPOLYEDGE FlatShade(d); #endif Input i = DescToInput(d); #if _SRPTERRAINBLEND MicroSplatLayer l = BlendWithTerrain(d); #if _DEBUG_WORLDNORMAL ClearAllButAlbedo(l, normalize(TangentToWorldSpace(d, l.Normal)) * saturate(l.Albedo.z+1)); #endif #else MicroSplatLayer l = SurfImpl(i, worldNormalVertex); #endif DoDebugOutput(l); o.Albedo = l.Albedo; o.Normal = l.Normal; o.Smoothness = l.Smoothness; o.Occlusion = l.Occlusion; o.Metallic = l.Metallic; o.Emission = l.Emission; #if _USESPECULARWORKFLOW o.Specular = l.Specular; #endif o.Height = l.Height; o.Alpha = l.Alpha; } // SHADERDESC ShaderData CreateShaderData(VertexToPixel i) { ShaderData d = (ShaderData)0; d.worldSpacePosition = i.worldPos; d.worldSpaceNormal = i.worldNormal; d.worldSpaceTangent = i.worldTangent.xyz; float3 bitangent = cross(i.worldTangent.xyz, i.worldNormal) * i.worldTangent.w * -1; d.TBNMatrix = float3x3(d.worldSpaceTangent, bitangent, d.worldSpaceNormal); d.worldSpaceViewDir = normalize(_WorldSpaceCameraPos - i.worldPos); d.tangentSpaceViewDir = mul(d.worldSpaceViewDir, d.TBNMatrix); d.texcoord0 = i.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER d.texcoord1 = i.texcoord1; // d.texcoord2 = i.texcoord2; #endif // d.texcoord3 = i.texcoord3; // d.vertexColor = i.vertexColor; // these rarely get used, so we back transform them. Usually will be stripped. #if _HDRP // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(GetCameraRelativePositionWS(i.worldPos), 1)); #else // d.localSpacePosition = mul(GetWorldToObjectMatrix(), float4(i.worldPos, 1)); #endif // d.localSpaceNormal = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldNormal)); // d.localSpaceTangent = normalize(mul((float3x3)GetWorldToObjectMatrix(), i.worldTangent.xyz)); // d.screenPos = i.screenPos; // d.screenUV = i.screenPos.xy / i.screenPos.w; // d.extraV2F0 = i.extraV2F0; // d.extraV2F1 = i.extraV2F1; // d.extraV2F2 = i.extraV2F2; // d.extraV2F3 = i.extraV2F3; // d.extraV2F4 = i.extraV2F4; // d.extraV2F5 = i.extraV2F5; // d.extraV2F6 = i.extraV2F6; // d.extraV2F7 = i.extraV2F7; return d; } // CHAINS void ChainModifyVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; ModifyVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainModifyTessellatedVertex(inout VertexData v, inout VertexToPixel v2p) { ExtraV2F d = (ExtraV2F)0; // d.extraV2F0 = v2p.extraV2F0; // d.extraV2F1 = v2p.extraV2F1; // d.extraV2F2 = v2p.extraV2F2; // d.extraV2F3 = v2p.extraV2F3; // d.extraV2F4 = v2p.extraV2F4; // d.extraV2F5 = v2p.extraV2F5; // d.extraV2F6 = v2p.extraV2F6; // d.extraV2F7 = v2p.extraV2F7; ModifyTessellatedVertex(v, d); // v2p.extraV2F0 = d.extraV2F0; // v2p.extraV2F1 = d.extraV2F1; // v2p.extraV2F2 = d.extraV2F2; // v2p.extraV2F3 = d.extraV2F3; // v2p.extraV2F4 = d.extraV2F4; // v2p.extraV2F5 = d.extraV2F5; // v2p.extraV2F6 = d.extraV2F6; // v2p.extraV2F7 = d.extraV2F7; } void ChainFinalColorForward(inout Surface l, inout ShaderData d, inout half4 color) { } void ChainFinalGBufferStandard(inout Surface s, inout ShaderData d, inout half4 GBuffer0, inout half4 GBuffer1, inout half4 GBuffer2, inout half4 outEmission, inout half4 outShadowMask) { } #if _PASSSHADOW float3 _LightDirection; float3 _LightPosition; #endif // vertex shader VertexToPixel Vert (VertexData v) { VertexToPixel o = (VertexToPixel)0; UNITY_SETUP_INSTANCE_ID(v); UNITY_TRANSFER_INSTANCE_ID(v, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #if !_TESSELLATION_ON ChainModifyVertex(v, o); #endif o.texcoord0 = v.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.texcoord1 = v.texcoord1; // o.texcoord2 = v.texcoord2; #endif // o.texcoord3 = v.texcoord3; // o.vertexColor = v.vertexColor; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz); o.worldPos = TransformObjectToWorld(v.vertex.xyz); o.worldNormal = TransformObjectToWorldNormal(v.normal); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float2 uv1 = v.texcoord1.xy; float2 uv2 = v.texcoord2.xy; o.worldTangent = float4(TransformObjectToWorldDir(v.tangent.xyz), v.tangent.w); #else float2 uv1 = v.texcoord0.xy; float2 uv2 = uv1; #endif // MS Only ApplyTerrainTangent(o); #if _PASSSHADOW #if _CASTING_PUNCTUAL_LIGHT_SHADOW float3 lightDirectionWS = normalize(_LightPosition - o.worldPos); #else float3 lightDirectionWS = _LightDirection; #endif // Define shadow pass specific clip position for Universal o.pos = TransformWorldToHClip(ApplyShadowBias(o.worldPos, o.worldNormal, lightDirectionWS)); #if UNITY_REVERSED_Z o.pos.z = min(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #else o.pos.z = max(o.pos.z, o.pos.w * UNITY_NEAR_CLIP_VALUE); #endif #elif _PASSMETA #if _MICROTERRAIN o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), v.texcoord0.xy, v.texcoord0.xy, unity_LightmapST, unity_DynamicLightmapST); #else o.pos = MetaVertexPosition(float4(v.vertex.xyz, 0), uv1, uv2, unity_LightmapST, unity_DynamicLightmapST); #endif #else o.pos = TransformWorldToHClip(o.worldPos); #endif // o.screenPos = ComputeScreenPos(o.pos, _ProjectionParams.x); #if _PASSFORWARD || _PASSGBUFFER #if _MICROTERRAIN OUTPUT_LIGHTMAP_UV(v.texcoord0.xy, unity_LightmapST, o.lightmapUV); #else OUTPUT_LIGHTMAP_UV(uv1, unity_LightmapST, o.lightmapUV); #endif OUTPUT_SH(o.worldNormal, o.sh); #if defined(DYNAMICLIGHTMAP_ON) o.dynamicLightmapUV.xy = uv2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; #endif #endif #ifdef VARYINGS_NEED_FOG_AND_VERTEX_LIGHT half fogFactor = 0; #if defined(_FOG_FRAGMENT) fogFactor = ComputeFogFactor(o.pos.z); #endif #if _BAKEDLIT half3 vertexLight = VertexLighting(o.worldPos, o.worldNormal); o.fogFactorAndVertexLight = half4(fogFactor, vertexLight); #else o.fogFactorAndVertexLight = half4(fogFactor, 0,0,0); #endif #endif #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) o.shadowCoord = GetShadowCoord(vertexInput); #endif return o; } // fragment shader void Frag (VertexToPixel IN , out half4 outNormalWS : SV_Target0 #ifdef _WRITE_RENDERING_LAYERS , out float4 outRenderingLayers : SV_Target1 #endif #ifdef _DEPTHOFFSET_ON , out float outputDepth : SV_Depth #endif #if NEED_FACING , bool facing : SV_IsFrontFace #endif ) { UNITY_SETUP_INSTANCE_ID(IN); UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN); ShaderData d = CreateShaderData(IN); Surface l = (Surface)0; #ifdef _DEPTHOFFSET_ON l.outputDepth = outputDepth; #endif l.Albedo = half3(0.5, 0.5, 0.5); l.Normal = float3(0,0,1); l.Occlusion = 1; l.Alpha = 1; SurfaceFunction(l, d); #ifdef _DEPTHOFFSET_ON outputDepth = l.outputDepth; #endif #if defined(_GBUFFER_NORMALS_OCT) float3 normalWS = d.worldSpaceNormal; float2 octNormalWS = PackNormalOctQuadEncode(normalWS); // values between [-1, +1], must use fp32 on some platforms float2 remappedOctNormalWS = saturate(octNormalWS * 0.5 + 0.5); // values between [ 0, 1] half3 packedNormalWS = PackFloat2To888(remappedOctNormalWS); // values between [ 0, 1] outNormalWS = half4(packedNormalWS, 0.0); #else float3 wsn = l.Normal; #if !_WORLDSPACENORMAL wsn = TangentToWorldSpace(d, l.Normal); #endif outNormalWS = half4(NormalizeNormalPerPixel(wsn), 0.0); #endif #ifdef _WRITE_RENDERING_LAYERS uint renderingLayers = GetMeshRenderingLayer(); outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0); #endif } ENDHLSL } } Dependency "BaseMapShader" = "Hidden/ABCPolaris__Base687910241" Fallback "Hidden/ABCPolaris__Base687910241" CustomEditor "MicroSplatShaderGUI" }