//////////////////////////////////////// // MicroSplat // Copyright (c) Jason Booth // // Auto-generated shader code, don't hand edit! // // Unity Version: 2021.3.4f1 // MicroSplat Version: 3.9 // Render Pipeline: Standard // Platform: OSXEditor //////////////////////////////////////// Shader "MicroSplat/Example" { Properties { [HideInInspector] _Control0 ("Control0", 2D) = "red" {} // 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" {} _HybridHeightBlendDistance("Hybrid Blend Distance", Float) = 300 _TriplanarUVScale("Triplanar UV Scale", Vector) = (1, 1, 0, 0) } SubShader { Tags {"RenderType" = "Opaque" "Queue" = "Geometry+100" "IgnoreProjector" = "False" "TerrainCompatible" = "true" "SplatCount" = "4"} Pass { Name "FORWARD" Tags { "LightMode" = "ForwardBase" } CGPROGRAM // compile directives #pragma vertex Vert #pragma fragment Frag #pragma target 3.0 #pragma multi_compile_instancing #pragma multi_compile_local __ _ALPHATEST_ON #pragma multi_compile_fog #pragma multi_compile_fwdbase #include "HLSLSupport.cginc" #define _PASSFORWARD 1 #include "UnityShaderVariables.cginc" #include "UnityShaderUtilities.cginc" // -------- variant for: #define _MICROSPLAT 1 #define _MICROTERRAIN 1 #define _HYBRIDHEIGHTBLEND 1 #define _USEGRADMIP 1 #define _MAX4TEXTURES 1 #define _MSRENDERLOOP_SURFACESHADER 1 #pragma instancing_options assumeuniformscaling nomatrices nolightprobe nolightmap forwardadd #define _STANDARD 1 // If your looking in here and thinking WTF, yeah, I know. These are taken from the SRPs, to allow us to use the same // texturing library they use. However, since they are not included in the standard pipeline by default, there is no // way to include them in and they have to be inlined, since someone could copy this shader onto another machine without // MicroSplat installed. Unfortunate, but I'd rather do this and have a nice library for texture sampling instead // of the patchy one Unity provides being inlined/emulated in HDRP/URP. Strangely, PSSL and XBoxOne libraries are not // included in the standard SRP code, but they are in tons of Unity own projects on the web, so I grabbed them from there. #if defined(SHADER_API_GAMECORE) #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURECUBE(textureName) TextureCube textureName #define TEXTURECUBE_ARRAY(textureName) TextureCubeArray textureName #define TEXTURE3D(textureName) Texture3D textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_FLOAT(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_FLOAT(textureName) TEXTURECUBE_ARRAY(textureName) #define TEXTURE3D_FLOAT(textureName) TEXTURE3D(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_HALF(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_HALF(textureName) TEXTURECUBE_ARRAY(textureName) #define TEXTURE3D_HALF(textureName) TEXTURE3D(textureName) #define TEXTURE2D_SHADOW(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_SHADOW(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_SHADOW(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_SHADOW(textureName) TEXTURECUBE_ARRAY(textureName) #define RW_TEXTURE2D(type, textureName) RWTexture2D textureName #define RW_TEXTURE2D_ARRAY(type, textureName) RWTexture2DArray textureName #define RW_TEXTURE3D(type, textureName) RWTexture3D textureName #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define ASSIGN_SAMPLER(samplerName, samplerValue) samplerName = samplerValue #define TEXTURE2D_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER(samplerName) #define TEXTURE2D_ARRAY_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER(samplerName) #define TEXTURECUBE_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER(samplerName) #define TEXTURECUBE_ARRAY_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER(samplerName) #define TEXTURE3D_PARAM(textureName, samplerName) TEXTURE3D(textureName), SAMPLER(samplerName) #define TEXTURE2D_SHADOW_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER_CMP(samplerName) #define TEXTURE2D_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER_CMP(samplerName) #define TEXTURECUBE_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER_CMP(samplerName) #define TEXTURECUBE_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER_CMP(samplerName) #define TEXTURE2D_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_ARRAY_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARRAY_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE3D_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #define PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3) #define PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod) #define PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) textureName.SampleBias(samplerName, coord3, bias) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Sample(samplerName, float4(coord3, index)) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) textureName.SampleLevel(samplerName, float4(coord3, index), lod) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) textureName.SampleBias(samplerName, float4(coord3, index), bias) #define PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3) #define PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod) #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) #define SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) #define SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) #define SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) #define SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) #define SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) #define SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) #define SAMPLE_TEXTURE3D(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3) #define SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) #define SAMPLE_TEXTURE2D_SHADOW(textureName, samplerName, coord3) textureName.SampleCmpLevelZero(samplerName, (coord3).xy, (coord3).z) #define SAMPLE_TEXTURE2D_ARRAY_SHADOW(textureName, samplerName, coord3, index) textureName.SampleCmpLevelZero(samplerName, float3((coord3).xy, index), (coord3).z) #define SAMPLE_TEXTURECUBE_SHADOW(textureName, samplerName, coord4) textureName.SampleCmpLevelZero(samplerName, (coord4).xyz, (coord4).w) #define SAMPLE_TEXTURECUBE_ARRAY_SHADOW(textureName, samplerName, coord4, index) textureName.SampleCmpLevelZero(samplerName, float4((coord4).xyz, index), (coord4).w) #define SAMPLE_DEPTH_TEXTURE(textureName, samplerName, coord2) SAMPLE_TEXTURE2D(textureName, samplerName, coord2).r #define SAMPLE_DEPTH_TEXTURE_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod).r #define LOAD_TEXTURE2D(textureName, unCoord2) textureName.Load(int3(unCoord2, 0)) #define LOAD_TEXTURE2D_LOD(textureName, unCoord2, lod) textureName.Load(int3(unCoord2, lod)) #define LOAD_TEXTURE2D_MSAA(textureName, unCoord2, sampleIndex) textureName.Load(unCoord2, sampleIndex) #define LOAD_TEXTURE2D_ARRAY(textureName, unCoord2, index) textureName.Load(int4(unCoord2, index, 0)) #define LOAD_TEXTURE2D_ARRAY_MSAA(textureName, unCoord2, index, sampleIndex) textureName.Load(int3(unCoord2, index), sampleIndex) #define LOAD_TEXTURE2D_ARRAY_LOD(textureName, unCoord2, index, lod) textureName.Load(int4(unCoord2, index, lod)) #define LOAD_TEXTURE3D(textureName, unCoord3) textureName.Load(int4(unCoord3, 0)) #define LOAD_TEXTURE3D_LOD(textureName, unCoord3, lod) textureName.Load(int4(unCoord3, lod)) #define PLATFORM_SUPPORT_GATHER #define GATHER_TEXTURE2D(textureName, samplerName, coord2) textureName.Gather(samplerName, coord2) #define GATHER_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Gather(samplerName, float3(coord2, index)) #define GATHER_TEXTURECUBE(textureName, samplerName, coord3) textureName.Gather(samplerName, coord3) #define GATHER_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Gather(samplerName, float4(coord3, index)) #define GATHER_RED_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherRed(samplerName, coord2) #define GATHER_GREEN_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherGreen(samplerName, coord2) #define GATHER_BLUE_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherBlue(samplerName, coord2) #define GATHER_ALPHA_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherAlpha(samplerName, coord2) #elif defined(SHADER_API_XBOXONE) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_PSSL) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.GetLOD(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_D3D11) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_METAL) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_VULKAN) // This file assume SHADER_API_VULKAN is defined // TODO: This is a straight copy from D3D11.hlsl. Go through all this stuff and adjust where needed. // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_SWITCH) // This file assume SHADER_API_SWITCH is defined // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLCORE) // OpenGL 4.1 SM 5.0 https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html #if (SHADER_TARGET >= 46) #define OPENGL4_1_SM5 1 #else #define OPENGL4_1_SM5 0 #endif // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLES3) // GLES 3.1 + AEP shader feature https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html #if (SHADER_TARGET >= 40) #define GLES3_1_AEP 1 #else #define GLES3_1_AEP 0 #endif // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLES) #define uint int #define rcp(x) 1.0 / (x) #define ddx_fine ddx #define ddy_fine ddy #define asfloat #define asuint(x) asint(x) #define f32tof16 #define f16tof32 #define ERROR_ON_UNSUPPORTED_FUNCTION(funcName) #error #funcName is not supported on GLES 2.0 // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) #error calculate Level of Detail not supported in GLES2 // Texture abstraction #define TEXTURE2D(textureName) sampler2D textureName #define TEXTURE2D_ARRAY(textureName) samplerCUBE textureName // No support to texture2DArray #define TEXTURECUBE(textureName) samplerCUBE textureName #define TEXTURECUBE_ARRAY(textureName) samplerCUBE textureName // No supoport to textureCubeArray and can't emulate with texture2DArray #define TEXTURE3D(textureName) sampler3D textureName #define TEXTURE2D_FLOAT(textureName) sampler2D_float textureName #define TEXTURECUBE_FLOAT(textureName) samplerCUBE_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURECUBE_FLOAT(textureName) // No support to texture2DArray #define TEXTURE2D_HALF(textureName) sampler2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURECUBE_HALF(textureName) // No support to texture2DArray #define SAMPLER(samplerName) #define SAMPLER_CMP(samplerName) #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) tex2D(textureName, coord2) #if (SHADER_TARGET >= 30) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) tex2Dlod(textureName, float4(coord2, 0, lod)) #else // No lod support. Very poor approximation with bias. #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, lod) #endif #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) tex2Dbias(textureName, float4(coord2, 0, bias)) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) SAMPLE_TEXTURE2D(textureName, samplerName, coord2) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_LOD) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_BIAS) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_GRAD) #else #error unsupported shader api #endif // default flow control attributes #ifndef UNITY_BRANCH # define UNITY_BRANCH #endif #ifndef UNITY_FLATTEN # define UNITY_FLATTEN #endif #ifndef UNITY_UNROLL # define UNITY_UNROLL #endif #ifndef UNITY_UNROLLX # define UNITY_UNROLLX(_x) #endif #ifndef UNITY_LOOP # define UNITY_LOOP #endif #include "UnityCG.cginc" #if _NOMINDIELETRIC // for Standard #ifdef unity_ColorSpaceDielectricSpec #undef unity_ColorSpaceDielectricSpec #endif #define unity_ColorSpaceDielectricSpec half4(0,0,0,1) #endif #include "Lighting.cginc" #include "UnityPBSLighting.cginc" #include "AutoLight.cginc" #if _MICROTERRAIN && !_TERRAINBLENDABLESHADER #define UNITY_ASSUME_UNIFORM_SCALING #define UNITY_DONT_INSTANCE_OBJECT_MATRICES #define UNITY_INSTANCED_LOD_FADE #else #define UNITY_INSTANCED_LOD_FADE #define UNITY_INSTANCED_SH #define UNITY_INSTANCED_LIGHTMAPSTS #endif // data across stages, stripped like the above. struct VertexToPixel { UNITY_POSITION(pos); 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 lmap : TEXCOORD8; #if UNITY_SHOULD_SAMPLE_SH half3 sh : TEXCOORD9; // SH #endif #ifdef LIGHTMAP_ON UNITY_LIGHTING_COORDS(10,11) UNITY_FOG_COORDS(12) #else UNITY_FOG_COORDS(10) UNITY_SHADOW_COORDS(11) #endif // float4 extraV2F0 : TEXCOORD13; // float4 extraV2F1 : TEXCOORD14; // float4 extraV2F2 : TEXCOORD15; // float4 extraV2F3 : TEXCOORD16; // float4 extraV2F4 : TEXCOORD17; // float4 extraV2F5 : TEXCOORD18; // float4 extraV2F6 : TEXCOORD19; // float4 extraV2F7 : TEXCOORD20; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; // 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 // unity_WorldToObject in HDRP, since it already does macro-fu there #if _STANDARD float3 TransformWorldToObject(float3 p) { return mul(unity_WorldToObject, float4(p, 1)); }; float3 TransformObjectToWorld(float3 p) { return mul(unity_ObjectToWorld, float4(p, 1)); }; float4 TransformWorldToObject(float4 p) { return mul(unity_WorldToObject, p); }; float4 TransformObjectToWorld(float4 p) { return mul(unity_ObjectToWorld, p); }; float4x4 GetWorldToObjectMatrix() { return unity_WorldToObject; } float4x4 GetObjectToWorldMatrix() { return unity_ObjectToWorld; } #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 } #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 #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); 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 fixed4 w0; fixed4 w1; fixed4 w2; fixed4 w3; fixed4 w4; fixed4 w5; fixed4 w6; // megasplat data half4 layer0; half4 layer1; half3 baryWeights; half4 scatter0; half4 scatter1; // wetness, puddles, streams, lava from vertex or megasplat fixed4 fx; // snow min, snow max fixed4 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(unity_WorldToObject, float4(n, 1)).xyz; surfTangent = t;//mul(unity_WorldToObject, 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, fixed4 w0, fixed4 w1, fixed4 w2, fixed4 w3, fixed4 w4, fixed4 w5, fixed4 w6, fixed4 w7, float3 worldPos, DecalOutput decalOutput) { config = (Config)0; half4 indexes = 0; config.uv = uv; #if _WORLDUV uv = worldPos.xz * float2(-1,1); #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 fixed 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) { fixed 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; } } #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 fixed2 UnpackNormal2(fixed4 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; } // Stochastic shared code // Compute local triangle barycentric coordinates and vertex IDs void TriangleGrid(float2 uv, float scale, out float w1, out float w2, out float w3, out int2 vertex1, out int2 vertex2, out int2 vertex3) { // Scaling of the input uv *= 3.464 * scale; // 2 * sqrt(3) // Skew input space into simplex triangle grid const float2x2 gridToSkewedGrid = float2x2(1.0, 0.0, -0.57735027, 1.15470054); float2 skewedCoord = mul(gridToSkewedGrid, uv); // Compute local triangle vertex IDs and local barycentric coordinates int2 baseId = int2(floor(skewedCoord)); float3 temp = float3(frac(skewedCoord), 0); temp.z = 1.0 - temp.x - temp.y; if (temp.z > 0.0) { w1 = temp.z; w2 = temp.y; w3 = temp.x; vertex1 = baseId; vertex2 = baseId + int2(0, 1); vertex3 = baseId + int2(1, 0); } else { w1 = -temp.z; w2 = 1.0 - temp.y; w3 = 1.0 - temp.x; vertex1 = baseId + int2(1, 1); vertex2 = baseId + int2(1, 0); vertex3 = baseId + int2(0, 1); } } // Fast random hash function float2 SimpleHash2(float2 p) { return frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), p)) * 43758.5453); } half3 BaryWeightBlend(half3 iWeights, half tex0, half tex1, half tex2, half contrast) { // compute weight with height map const half epsilon = 1.0f / 1024.0f; half3 weights = half3(iWeights.x * (tex0 + epsilon), iWeights.y * (tex1 + epsilon), iWeights.z * (tex2 + epsilon)); // Contrast weights half maxWeight = max(weights.x, max(weights.y, weights.z)); half transition = contrast * maxWeight; half threshold = maxWeight - transition; half scale = 1.0f / transition; weights = saturate((weights - threshold) * scale); // Normalize weights. half weightScale = 1.0f / (weights.x + weights.y + weights.z); weights *= weightScale; return weights; } void PrepareStochasticUVs(float scale, float3 uv, out float3 uv1, out float3 uv2, out float3 uv3, out half3 weights) { // Get triangle info float w1, w2, w3; int2 vertex1, vertex2, vertex3; TriangleGrid(uv.xy, scale, w1, w2, w3, vertex1, vertex2, vertex3); // Assign random offset to each triangle vertex uv1 = uv; uv2 = uv; uv3 = uv; uv1.xy += SimpleHash2(vertex1); uv2.xy += SimpleHash2(vertex2); uv3.xy += SimpleHash2(vertex3); weights = half3(w1, w2, w3); } void PrepareStochasticUVs(float scale, float2 uv, out float2 uv1, out float2 uv2, out float2 uv3, out half3 weights) { // Get triangle info float w1, w2, w3; int2 vertex1, vertex2, vertex3; TriangleGrid(uv, scale, w1, w2, w3, vertex1, vertex2, vertex3); // Assign random offset to each triangle vertex uv1 = uv; uv2 = uv; uv3 = uv; uv1.xy += SimpleHash2(vertex1); uv2.xy += SimpleHash2(vertex2); uv3.xy += SimpleHash2(vertex3); weights = half3(w1, w2, w3); } 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 _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 fixed4 w0, inout fixed4 w1, inout fixed4 w2, inout fixed4 w3, inout fixed4 w4, inout fixed4 w5, inout fixed4 w6, inout fixed4 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)unity_WorldToObject, worldNormalVertex).xyz; i.worldPos = i.worldPos - mul(unity_ObjectToWorld, 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 fixed4 w0 = 0; fixed4 w1 = 0; fixed4 w2 = 0; fixed4 w3 = 0; fixed4 w4 = 0; fixed4 w5 = 0; fixed4 w6 = 0; fixed4 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.. l.Albedo *= saturate(SAMPLE_TEXTURE2D_LOD(_Diffuse, sampler_Diffuse, config.uv0, 11).r + 2); l.Albedo *= saturate(SAMPLE_TEXTURE2D_LOD(_NormalSAO, sampler_NormalSAO, config.uv0, 11).r + 2); #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 = 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 } // 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); #elif _PLANETVECTORS DoPlanetVectorVertex(v, d); #endif } void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d) { #if _TESSDISTANCE v.vertex.xyz += OffsetVertex(v, d); #endif } float3 GetTessFactors () { #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 (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; 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(unity_WorldToObject, float4(GetCameraRelativePositionWS(i.worldPos), 1)); #else // d.localSpacePosition = mul(unity_WorldToObject, float4(i.worldPos, 1)); #endif // d.localSpaceNormal = normalize(mul((float3x3)unity_WorldToObject, i.worldNormal)); // d.localSpaceTangent = normalize(mul((float3x3)unity_WorldToObject, 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) { } // vertex shader VertexToPixel Vert (VertexData v) { UNITY_SETUP_INSTANCE_ID(v); VertexToPixel o; UNITY_INITIALIZE_OUTPUT(VertexToPixel,o); UNITY_TRANSFER_INSTANCE_ID(v,o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #if !_TESSELLATION_ON ChainModifyVertex(v, o); #endif o.pos = UnityObjectToClipPos(v.vertex); o.texcoord0 = v.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.texcoord1 = v.texcoord1; // o.texcoord2 = v.texcoord2; #endif // o.texcoord3 = v.texcoord3; // o.vertexColor = v.vertexColor; // o.screenPos = ComputeScreenPos(o.pos); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.worldNormal = UnityObjectToWorldNormal(v.normal); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.worldTangent.xyz = UnityObjectToWorldDir(v.tangent.xyz); fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w; o.worldTangent.w = tangentSign; #endif // MS Only ApplyTerrainTangent(o); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float2 uv1 = v.texcoord1.xy; float2 uv2 = v.texcoord2.xy; #else float2 uv1 = v.texcoord0.xy; float2 uv2 = uv1; #endif #ifdef DYNAMICLIGHTMAP_ON o.lmap.zw = uv2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; #endif #ifdef LIGHTMAP_ON o.lmap.xy = uv1 * unity_LightmapST.xy + unity_LightmapST.zw; #endif // SH/ambient and vertex lights #ifndef LIGHTMAP_ON #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL o.sh = 0; // Approximated illumination from non-important point lights #ifdef VERTEXLIGHT_ON o.sh += Shade4PointLights ( unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0, unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb, unity_4LightAtten0, o.worldPos, o.worldNormal); #endif o.sh = ShadeSHPerVertex (o.worldNormal, o.sh); #endif #endif // !LIGHTMAP_ON UNITY_TRANSFER_LIGHTING(o, uv1.xy); // pass shadow and, possibly, light cookie coordinates to pixel shader #ifdef FOG_COMBINED_WITH_TSPACE UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,o.pos); // pass fog coordinates to pixel shader #elif defined (FOG_COMBINED_WITH_WORLD_POS) UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,o.pos); // pass fog coordinates to pixel shader #else UNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader #endif return o; } // fragment shader fixed4 Frag (VertexToPixel IN) : SV_Target { UNITY_SETUP_INSTANCE_ID(IN); // prepare and unpack data #ifdef FOG_COMBINED_WITH_TSPACE UNITY_EXTRACT_FOG_FROM_TSPACE(IN); #elif defined (FOG_COMBINED_WITH_WORLD_POS) UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN); #else UNITY_EXTRACT_FOG(IN); #endif 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); #ifndef USING_DIRECTIONAL_LIGHT fixed3 lightDir = normalize(UnityWorldSpaceLightDir(d.worldSpacePosition)); #else fixed3 lightDir = _WorldSpaceLightPos0.xyz; #endif // compute lighting & shadowing factor UNITY_LIGHT_ATTENUATION(atten, IN, d.worldSpacePosition) #if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC #ifdef UNITY_COMPILER_HLSL SurfaceOutputStandardSpecular o = (SurfaceOutputStandardSpecular)0; #else SurfaceOutputStandardSpecular o; #endif o.Specular = l.Specular; #elif _BDRFLAMBERT || _BDRF3 #ifdef UNITY_COMPILER_HLSL SurfaceOutput o = (SurfaceOutput)0; #else SurfaceOutput o; #endif #else #ifdef UNITY_COMPILER_HLSL SurfaceOutputStandard o = (SurfaceOutputStandard)0; #else SurfaceOutputStandard o; #endif o.Metallic = l.Metallic; #endif o.Albedo = l.Albedo; o.Emission = l.Emission; o.Alpha = l.Alpha; o.Normal = normalize(TangentToWorldSpace(d, l.Normal)); #if _BDRFLAMBERT || _BDRF3 o.Specular = l.Specular; o.Gloss = l.Smoothness; #elif _SPECULARFROMMETALLIC o.Occlusion = l.Occlusion; o.Smoothness = l.Smoothness; o.Albedo = MicroSplatDiffuseAndSpecularFromMetallic(l.Albedo, l.Metallic, o.Specular, o.Smoothness); o.Smoothness = 1-o.Smoothness; #elif _USESPECULARWORKFLOW o.Occlusion = l.Occlusion; o.Smoothness = l.Smoothness; o.Specular = l.Specular; #else o.Smoothness = l.Smoothness; o.Metallic = l.Metallic; o.Occlusion = l.Occlusion; #endif #if !_UNLIT fixed4 c = 0; // Setup lighting environment UnityGI gi; UNITY_INITIALIZE_OUTPUT(UnityGI, gi); gi.indirect.diffuse = 0; gi.indirect.specular = 0; gi.light.color = _LightColor0.rgb; gi.light.dir = lightDir; // Call GI (lightmaps/SH/reflections) lighting function UnityGIInput giInput; UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput); giInput.light = gi.light; giInput.worldPos = d.worldSpacePosition; giInput.worldViewDir = d.worldSpaceViewDir; giInput.atten = atten; #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON) giInput.lightmapUV = IN.lmap; #else giInput.lightmapUV = 0; #endif #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL giInput.ambient = IN.sh; #else giInput.ambient.rgb = 0.0; #endif giInput.probeHDR[0] = unity_SpecCube0_HDR; giInput.probeHDR[1] = unity_SpecCube1_HDR; #if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION) giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending #endif #ifdef UNITY_SPECCUBE_BOX_PROJECTION giInput.boxMax[0] = unity_SpecCube0_BoxMax; giInput.probePosition[0] = unity_SpecCube0_ProbePosition; giInput.boxMax[1] = unity_SpecCube1_BoxMax; giInput.boxMin[1] = unity_SpecCube1_BoxMin; giInput.probePosition[1] = unity_SpecCube1_ProbePosition; #endif #if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC LightingStandardSpecular_GI(o, giInput, gi); c += LightingStandardSpecular (o, d.worldSpaceViewDir, gi); #elif _BDRFLAMBERT LightingLambert_GI(o, giInput, gi); c += LightingLambert (o, gi); #elif _BDRF3 LightingBlinnPhong_GI(o, giInput, gi); c += LightingBlinnPhong (o, d.worldSpaceViewDir, gi); #else LightingStandard_GI(o, giInput, gi); c += LightingStandard (o, d.worldSpaceViewDir, gi); #endif c.rgb += o.Emission; UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fog #else fixed4 c = fixed4(o.Albedo.rgb, o.Alpha); UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fog #endif #if !_ALPHABLEND_ON UNITY_OPAQUE_ALPHA(c.a); #endif ChainFinalColorForward(l, d, c); return c; } ENDCG } // ---- forward rendering additive lights pass: Pass { Name "FORWARD" Tags { "LightMode" = "ForwardAdd" } ZWrite Off Blend One One CGPROGRAM #pragma vertex Vert #pragma fragment Frag // compile directives #pragma target 3.0 #pragma multi_compile_instancing #pragma multi_compile_fog #pragma multi_compile_local __ _ALPHATEST_ON #pragma multi_compile_fwdadd_fullshadows #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #include "UnityShaderUtilities.cginc" #define _PASSFORWARD 1 #define _MICROSPLAT 1 #define _MICROTERRAIN 1 #define _HYBRIDHEIGHTBLEND 1 #define _USEGRADMIP 1 #define _MAX4TEXTURES 1 #define _MSRENDERLOOP_SURFACESHADER 1 #pragma instancing_options assumeuniformscaling nomatrices nolightprobe nolightmap forwardadd #define _STANDARD 1 // If your looking in here and thinking WTF, yeah, I know. These are taken from the SRPs, to allow us to use the same // texturing library they use. However, since they are not included in the standard pipeline by default, there is no // way to include them in and they have to be inlined, since someone could copy this shader onto another machine without // MicroSplat installed. Unfortunate, but I'd rather do this and have a nice library for texture sampling instead // of the patchy one Unity provides being inlined/emulated in HDRP/URP. Strangely, PSSL and XBoxOne libraries are not // included in the standard SRP code, but they are in tons of Unity own projects on the web, so I grabbed them from there. #if defined(SHADER_API_GAMECORE) #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURECUBE(textureName) TextureCube textureName #define TEXTURECUBE_ARRAY(textureName) TextureCubeArray textureName #define TEXTURE3D(textureName) Texture3D textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_FLOAT(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_FLOAT(textureName) TEXTURECUBE_ARRAY(textureName) #define TEXTURE3D_FLOAT(textureName) TEXTURE3D(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_HALF(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_HALF(textureName) TEXTURECUBE_ARRAY(textureName) #define TEXTURE3D_HALF(textureName) TEXTURE3D(textureName) #define TEXTURE2D_SHADOW(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_SHADOW(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_SHADOW(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_SHADOW(textureName) TEXTURECUBE_ARRAY(textureName) #define RW_TEXTURE2D(type, textureName) RWTexture2D textureName #define RW_TEXTURE2D_ARRAY(type, textureName) RWTexture2DArray textureName #define RW_TEXTURE3D(type, textureName) RWTexture3D textureName #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define ASSIGN_SAMPLER(samplerName, samplerValue) samplerName = samplerValue #define TEXTURE2D_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER(samplerName) #define TEXTURE2D_ARRAY_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER(samplerName) #define TEXTURECUBE_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER(samplerName) #define TEXTURECUBE_ARRAY_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER(samplerName) #define TEXTURE3D_PARAM(textureName, samplerName) TEXTURE3D(textureName), SAMPLER(samplerName) #define TEXTURE2D_SHADOW_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER_CMP(samplerName) #define TEXTURE2D_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER_CMP(samplerName) #define TEXTURECUBE_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER_CMP(samplerName) #define TEXTURECUBE_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER_CMP(samplerName) #define TEXTURE2D_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_ARRAY_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARRAY_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE3D_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #define PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3) #define PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod) #define PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) textureName.SampleBias(samplerName, coord3, bias) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Sample(samplerName, float4(coord3, index)) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) textureName.SampleLevel(samplerName, float4(coord3, index), lod) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) textureName.SampleBias(samplerName, float4(coord3, index), bias) #define PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3) #define PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod) #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) #define SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) #define SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) #define SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) #define SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) #define SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) #define SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) #define SAMPLE_TEXTURE3D(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3) #define SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) #define SAMPLE_TEXTURE2D_SHADOW(textureName, samplerName, coord3) textureName.SampleCmpLevelZero(samplerName, (coord3).xy, (coord3).z) #define SAMPLE_TEXTURE2D_ARRAY_SHADOW(textureName, samplerName, coord3, index) textureName.SampleCmpLevelZero(samplerName, float3((coord3).xy, index), (coord3).z) #define SAMPLE_TEXTURECUBE_SHADOW(textureName, samplerName, coord4) textureName.SampleCmpLevelZero(samplerName, (coord4).xyz, (coord4).w) #define SAMPLE_TEXTURECUBE_ARRAY_SHADOW(textureName, samplerName, coord4, index) textureName.SampleCmpLevelZero(samplerName, float4((coord4).xyz, index), (coord4).w) #define SAMPLE_DEPTH_TEXTURE(textureName, samplerName, coord2) SAMPLE_TEXTURE2D(textureName, samplerName, coord2).r #define SAMPLE_DEPTH_TEXTURE_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod).r #define LOAD_TEXTURE2D(textureName, unCoord2) textureName.Load(int3(unCoord2, 0)) #define LOAD_TEXTURE2D_LOD(textureName, unCoord2, lod) textureName.Load(int3(unCoord2, lod)) #define LOAD_TEXTURE2D_MSAA(textureName, unCoord2, sampleIndex) textureName.Load(unCoord2, sampleIndex) #define LOAD_TEXTURE2D_ARRAY(textureName, unCoord2, index) textureName.Load(int4(unCoord2, index, 0)) #define LOAD_TEXTURE2D_ARRAY_MSAA(textureName, unCoord2, index, sampleIndex) textureName.Load(int3(unCoord2, index), sampleIndex) #define LOAD_TEXTURE2D_ARRAY_LOD(textureName, unCoord2, index, lod) textureName.Load(int4(unCoord2, index, lod)) #define LOAD_TEXTURE3D(textureName, unCoord3) textureName.Load(int4(unCoord3, 0)) #define LOAD_TEXTURE3D_LOD(textureName, unCoord3, lod) textureName.Load(int4(unCoord3, lod)) #define PLATFORM_SUPPORT_GATHER #define GATHER_TEXTURE2D(textureName, samplerName, coord2) textureName.Gather(samplerName, coord2) #define GATHER_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Gather(samplerName, float3(coord2, index)) #define GATHER_TEXTURECUBE(textureName, samplerName, coord3) textureName.Gather(samplerName, coord3) #define GATHER_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Gather(samplerName, float4(coord3, index)) #define GATHER_RED_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherRed(samplerName, coord2) #define GATHER_GREEN_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherGreen(samplerName, coord2) #define GATHER_BLUE_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherBlue(samplerName, coord2) #define GATHER_ALPHA_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherAlpha(samplerName, coord2) #elif defined(SHADER_API_XBOXONE) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_PSSL) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.GetLOD(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_D3D11) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_METAL) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_VULKAN) // This file assume SHADER_API_VULKAN is defined // TODO: This is a straight copy from D3D11.hlsl. Go through all this stuff and adjust where needed. // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_SWITCH) // This file assume SHADER_API_SWITCH is defined // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLCORE) // OpenGL 4.1 SM 5.0 https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html #if (SHADER_TARGET >= 46) #define OPENGL4_1_SM5 1 #else #define OPENGL4_1_SM5 0 #endif // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLES3) // GLES 3.1 + AEP shader feature https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html #if (SHADER_TARGET >= 40) #define GLES3_1_AEP 1 #else #define GLES3_1_AEP 0 #endif // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLES) #define uint int #define rcp(x) 1.0 / (x) #define ddx_fine ddx #define ddy_fine ddy #define asfloat #define asuint(x) asint(x) #define f32tof16 #define f16tof32 #define ERROR_ON_UNSUPPORTED_FUNCTION(funcName) #error #funcName is not supported on GLES 2.0 // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) #error calculate Level of Detail not supported in GLES2 // Texture abstraction #define TEXTURE2D(textureName) sampler2D textureName #define TEXTURE2D_ARRAY(textureName) samplerCUBE textureName // No support to texture2DArray #define TEXTURECUBE(textureName) samplerCUBE textureName #define TEXTURECUBE_ARRAY(textureName) samplerCUBE textureName // No supoport to textureCubeArray and can't emulate with texture2DArray #define TEXTURE3D(textureName) sampler3D textureName #define TEXTURE2D_FLOAT(textureName) sampler2D_float textureName #define TEXTURECUBE_FLOAT(textureName) samplerCUBE_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURECUBE_FLOAT(textureName) // No support to texture2DArray #define TEXTURE2D_HALF(textureName) sampler2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURECUBE_HALF(textureName) // No support to texture2DArray #define SAMPLER(samplerName) #define SAMPLER_CMP(samplerName) #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) tex2D(textureName, coord2) #if (SHADER_TARGET >= 30) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) tex2Dlod(textureName, float4(coord2, 0, lod)) #else // No lod support. Very poor approximation with bias. #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, lod) #endif #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) tex2Dbias(textureName, float4(coord2, 0, bias)) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) SAMPLE_TEXTURE2D(textureName, samplerName, coord2) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_LOD) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_BIAS) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_GRAD) #else #error unsupported shader api #endif // default flow control attributes #ifndef UNITY_BRANCH # define UNITY_BRANCH #endif #ifndef UNITY_FLATTEN # define UNITY_FLATTEN #endif #ifndef UNITY_UNROLL # define UNITY_UNROLL #endif #ifndef UNITY_UNROLLX # define UNITY_UNROLLX(_x) #endif #ifndef UNITY_LOOP # define UNITY_LOOP #endif #include "UnityCG.cginc" #if _NOMINDIELETRIC // for Standard #ifdef unity_ColorSpaceDielectricSpec #undef unity_ColorSpaceDielectricSpec #endif #define unity_ColorSpaceDielectricSpec half4(0,0,0,1) #endif #include "Lighting.cginc" #include "UnityPBSLighting.cginc" #include "AutoLight.cginc" #if _MICROTERRAIN && !_TERRAINBLENDABLESHADER #define UNITY_ASSUME_UNIFORM_SCALING #define UNITY_DONT_INSTANCE_OBJECT_MATRICES #define UNITY_INSTANCED_LOD_FADE #else #define UNITY_INSTANCED_LOD_FADE #define UNITY_INSTANCED_SH #define UNITY_INSTANCED_LIGHTMAPSTS #endif // data across stages, stripped like the above. struct VertexToPixel { UNITY_POSITION(pos); // must be named pos because Unity does stupid macro stuff 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; UNITY_LIGHTING_COORDS(8,9) UNITY_FOG_COORDS(10) // float4 extraV2F0 : TEXCOORD11; // float4 extraV2F1 : TEXCOORD12; // float4 extraV2F2 : TEXCOORD13; // float4 extraV2F3 : TEXCOORD14; // float4 extraV2F4 : TEXCOORD15; // float4 extraV2F5 : TEXCOORD16; // float4 extraV2F6 : TEXCOORD17; // float4 extraV2F7 : TEXCOORD18; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; // 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 // unity_WorldToObject in HDRP, since it already does macro-fu there #if _STANDARD float3 TransformWorldToObject(float3 p) { return mul(unity_WorldToObject, float4(p, 1)); }; float3 TransformObjectToWorld(float3 p) { return mul(unity_ObjectToWorld, float4(p, 1)); }; float4 TransformWorldToObject(float4 p) { return mul(unity_WorldToObject, p); }; float4 TransformObjectToWorld(float4 p) { return mul(unity_ObjectToWorld, p); }; float4x4 GetWorldToObjectMatrix() { return unity_WorldToObject; } float4x4 GetObjectToWorldMatrix() { return unity_ObjectToWorld; } #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 } #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 #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); 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 fixed4 w0; fixed4 w1; fixed4 w2; fixed4 w3; fixed4 w4; fixed4 w5; fixed4 w6; // megasplat data half4 layer0; half4 layer1; half3 baryWeights; half4 scatter0; half4 scatter1; // wetness, puddles, streams, lava from vertex or megasplat fixed4 fx; // snow min, snow max fixed4 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(unity_WorldToObject, float4(n, 1)).xyz; surfTangent = t;//mul(unity_WorldToObject, 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, fixed4 w0, fixed4 w1, fixed4 w2, fixed4 w3, fixed4 w4, fixed4 w5, fixed4 w6, fixed4 w7, float3 worldPos, DecalOutput decalOutput) { config = (Config)0; half4 indexes = 0; config.uv = uv; #if _WORLDUV uv = worldPos.xz * float2(-1,1); #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 fixed 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) { fixed 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; } } #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 fixed2 UnpackNormal2(fixed4 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; } // Stochastic shared code // Compute local triangle barycentric coordinates and vertex IDs void TriangleGrid(float2 uv, float scale, out float w1, out float w2, out float w3, out int2 vertex1, out int2 vertex2, out int2 vertex3) { // Scaling of the input uv *= 3.464 * scale; // 2 * sqrt(3) // Skew input space into simplex triangle grid const float2x2 gridToSkewedGrid = float2x2(1.0, 0.0, -0.57735027, 1.15470054); float2 skewedCoord = mul(gridToSkewedGrid, uv); // Compute local triangle vertex IDs and local barycentric coordinates int2 baseId = int2(floor(skewedCoord)); float3 temp = float3(frac(skewedCoord), 0); temp.z = 1.0 - temp.x - temp.y; if (temp.z > 0.0) { w1 = temp.z; w2 = temp.y; w3 = temp.x; vertex1 = baseId; vertex2 = baseId + int2(0, 1); vertex3 = baseId + int2(1, 0); } else { w1 = -temp.z; w2 = 1.0 - temp.y; w3 = 1.0 - temp.x; vertex1 = baseId + int2(1, 1); vertex2 = baseId + int2(1, 0); vertex3 = baseId + int2(0, 1); } } // Fast random hash function float2 SimpleHash2(float2 p) { return frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), p)) * 43758.5453); } half3 BaryWeightBlend(half3 iWeights, half tex0, half tex1, half tex2, half contrast) { // compute weight with height map const half epsilon = 1.0f / 1024.0f; half3 weights = half3(iWeights.x * (tex0 + epsilon), iWeights.y * (tex1 + epsilon), iWeights.z * (tex2 + epsilon)); // Contrast weights half maxWeight = max(weights.x, max(weights.y, weights.z)); half transition = contrast * maxWeight; half threshold = maxWeight - transition; half scale = 1.0f / transition; weights = saturate((weights - threshold) * scale); // Normalize weights. half weightScale = 1.0f / (weights.x + weights.y + weights.z); weights *= weightScale; return weights; } void PrepareStochasticUVs(float scale, float3 uv, out float3 uv1, out float3 uv2, out float3 uv3, out half3 weights) { // Get triangle info float w1, w2, w3; int2 vertex1, vertex2, vertex3; TriangleGrid(uv.xy, scale, w1, w2, w3, vertex1, vertex2, vertex3); // Assign random offset to each triangle vertex uv1 = uv; uv2 = uv; uv3 = uv; uv1.xy += SimpleHash2(vertex1); uv2.xy += SimpleHash2(vertex2); uv3.xy += SimpleHash2(vertex3); weights = half3(w1, w2, w3); } void PrepareStochasticUVs(float scale, float2 uv, out float2 uv1, out float2 uv2, out float2 uv3, out half3 weights) { // Get triangle info float w1, w2, w3; int2 vertex1, vertex2, vertex3; TriangleGrid(uv, scale, w1, w2, w3, vertex1, vertex2, vertex3); // Assign random offset to each triangle vertex uv1 = uv; uv2 = uv; uv3 = uv; uv1.xy += SimpleHash2(vertex1); uv2.xy += SimpleHash2(vertex2); uv3.xy += SimpleHash2(vertex3); weights = half3(w1, w2, w3); } 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 _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 fixed4 w0, inout fixed4 w1, inout fixed4 w2, inout fixed4 w3, inout fixed4 w4, inout fixed4 w5, inout fixed4 w6, inout fixed4 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)unity_WorldToObject, worldNormalVertex).xyz; i.worldPos = i.worldPos - mul(unity_ObjectToWorld, 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 fixed4 w0 = 0; fixed4 w1 = 0; fixed4 w2 = 0; fixed4 w3 = 0; fixed4 w4 = 0; fixed4 w5 = 0; fixed4 w6 = 0; fixed4 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.. l.Albedo *= saturate(SAMPLE_TEXTURE2D_LOD(_Diffuse, sampler_Diffuse, config.uv0, 11).r + 2); l.Albedo *= saturate(SAMPLE_TEXTURE2D_LOD(_NormalSAO, sampler_NormalSAO, config.uv0, 11).r + 2); #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 = 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 } // 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); #elif _PLANETVECTORS DoPlanetVectorVertex(v, d); #endif } void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d) { #if _TESSDISTANCE v.vertex.xyz += OffsetVertex(v, d); #endif } float3 GetTessFactors () { #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 (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; 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(unity_WorldToObject, float4(GetCameraRelativePositionWS(i.worldPos), 1)); #else // d.localSpacePosition = mul(unity_WorldToObject, float4(i.worldPos, 1)); #endif // d.localSpaceNormal = normalize(mul((float3x3)unity_WorldToObject, i.worldNormal)); // d.localSpaceTangent = normalize(mul((float3x3)unity_WorldToObject, 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) { } // vertex shader VertexToPixel Vert (VertexData v) { UNITY_SETUP_INSTANCE_ID(v); VertexToPixel o; UNITY_INITIALIZE_OUTPUT(VertexToPixel,o); UNITY_TRANSFER_INSTANCE_ID(v,o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #if !_TESSELLATION_ON ChainModifyVertex(v, o); #endif o.pos = UnityObjectToClipPos(v.vertex); o.texcoord0 = v.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.texcoord1 = v.texcoord1; // o.texcoord2 = v.texcoord2; #endif // o.texcoord3 = v.texcoord3; // o.vertexColor = v.vertexColor; // o.screenPos = ComputeScreenPos(o.pos); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.worldNormal = UnityObjectToWorldNormal(v.normal); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.worldTangent.xyz = UnityObjectToWorldDir(v.tangent.xyz); fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w; o.worldTangent.w = tangentSign; #endif // MS Only ApplyTerrainTangent(o); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float2 uv1 = v.texcoord1.xy; float2 uv2 = v.texcoord2.xy; #else float2 uv1 = v.texcoord0.xy; float2 uv2 = uv1; #endif UNITY_TRANSFER_LIGHTING(o,uv1); // pass shadow and, possibly, light cookie coordinates to pixel shader UNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader return o; } // fragment shader fixed4 Frag (VertexToPixel IN) : SV_Target { UNITY_SETUP_INSTANCE_ID(IN); // prepare and unpack data #ifdef FOG_COMBINED_WITH_TSPACE UNITY_EXTRACT_FOG_FROM_TSPACE(IN); #elif defined (FOG_COMBINED_WITH_WORLD_POS) UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN); #else UNITY_EXTRACT_FOG(IN); #endif 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); #ifndef USING_DIRECTIONAL_LIGHT fixed3 lightDir = normalize(UnityWorldSpaceLightDir(d.worldSpacePosition)); #else fixed3 lightDir = _WorldSpaceLightPos0.xyz; #endif float3 worldViewDir = normalize(UnityWorldSpaceViewDir(d.worldSpacePosition)); #if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC #ifdef UNITY_COMPILER_HLSL SurfaceOutputStandardSpecular o = (SurfaceOutputStandardSpecular)0; #else SurfaceOutputStandardSpecular o; #endif o.Specular = l.Specular; #elif _BDRFLAMBERT || _BDRF3 #ifdef UNITY_COMPILER_HLSL SurfaceOutput o = (SurfaceOutput)0; #else SurfaceOutput o; #endif #else #ifdef UNITY_COMPILER_HLSL SurfaceOutputStandard o = (SurfaceOutputStandard)0; #else SurfaceOutputStandard o; #endif o.Metallic = l.Metallic; #endif o.Albedo = l.Albedo; o.Emission = l.Emission; o.Alpha = l.Alpha; o.Normal = normalize(TangentToWorldSpace(d, l.Normal)); #if _BDRFLAMBERT || _BDRF3 o.Specular = l.Specular; o.Gloss = l.Smoothness; #elif _SPECULARFROMMETALLIC o.Occlusion = l.Occlusion; o.Smoothness = l.Smoothness; o.Albedo = MicroSplatDiffuseAndSpecularFromMetallic(l.Albedo, l.Metallic, o.Specular, o.Smoothness); o.Smoothness = 1-o.Smoothness; #elif _USESPECULARWORKFLOW o.Occlusion = l.Occlusion; o.Smoothness = l.Smoothness; o.Specular = l.Specular; #else o.Smoothness = l.Smoothness; o.Metallic = l.Metallic; o.Occlusion = l.Occlusion; #endif UNITY_LIGHT_ATTENUATION(atten, IN, d.worldSpacePosition) half4 c = 0; // Setup lighting environment UnityGI gi; UNITY_INITIALIZE_OUTPUT(UnityGI, gi); gi.indirect.diffuse = 0; gi.indirect.specular = 0; gi.light.color = _LightColor0.rgb; gi.light.dir = lightDir; gi.light.color *= atten; #if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC c += LightingStandardSpecular (o, worldViewDir, gi); #elif _BDRFLAMBERT c += LightingLambert(o, gi); c.a = 0; #elif _BDRF3 c += LightingBlinnPhong (o, worldViewDir, gi); #else c += LightingStandard (o, worldViewDir, gi); #endif ChainFinalColorForward(l, d, c); UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fog #if !_ALPHABLEND_ON UNITY_OPAQUE_ALPHA(c.a); #endif return c; } ENDCG } // ---- deferred shading pass: Pass { Name "DEFERRED" Tags { "LightMode" = "Deferred" } CGPROGRAM #pragma vertex Vert #pragma fragment Frag // compile directives #pragma target 3.0 #pragma multi_compile_instancing #pragma exclude_renderers nomrt #pragma multi_compile_local __ _ALPHATEST_ON #pragma skip_variants FOG_LINEAR FOG_EXP FOG_EXP2 #pragma multi_compile_prepassfinal #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #include "UnityShaderUtilities.cginc" #include "UnityCG.cginc" #define _PASSGBUFFER 1 #define _MICROSPLAT 1 #define _MICROTERRAIN 1 #define _HYBRIDHEIGHTBLEND 1 #define _USEGRADMIP 1 #define _MAX4TEXTURES 1 #define _MSRENDERLOOP_SURFACESHADER 1 #pragma instancing_options assumeuniformscaling nomatrices nolightprobe nolightmap forwardadd #define _STANDARD 1 // If your looking in here and thinking WTF, yeah, I know. These are taken from the SRPs, to allow us to use the same // texturing library they use. However, since they are not included in the standard pipeline by default, there is no // way to include them in and they have to be inlined, since someone could copy this shader onto another machine without // MicroSplat installed. Unfortunate, but I'd rather do this and have a nice library for texture sampling instead // of the patchy one Unity provides being inlined/emulated in HDRP/URP. Strangely, PSSL and XBoxOne libraries are not // included in the standard SRP code, but they are in tons of Unity own projects on the web, so I grabbed them from there. #if defined(SHADER_API_GAMECORE) #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURECUBE(textureName) TextureCube textureName #define TEXTURECUBE_ARRAY(textureName) TextureCubeArray textureName #define TEXTURE3D(textureName) Texture3D textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_FLOAT(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_FLOAT(textureName) TEXTURECUBE_ARRAY(textureName) #define TEXTURE3D_FLOAT(textureName) TEXTURE3D(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_HALF(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_HALF(textureName) TEXTURECUBE_ARRAY(textureName) #define TEXTURE3D_HALF(textureName) TEXTURE3D(textureName) #define TEXTURE2D_SHADOW(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_SHADOW(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_SHADOW(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_SHADOW(textureName) TEXTURECUBE_ARRAY(textureName) #define RW_TEXTURE2D(type, textureName) RWTexture2D textureName #define RW_TEXTURE2D_ARRAY(type, textureName) RWTexture2DArray textureName #define RW_TEXTURE3D(type, textureName) RWTexture3D textureName #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define ASSIGN_SAMPLER(samplerName, samplerValue) samplerName = samplerValue #define TEXTURE2D_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER(samplerName) #define TEXTURE2D_ARRAY_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER(samplerName) #define TEXTURECUBE_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER(samplerName) #define TEXTURECUBE_ARRAY_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER(samplerName) #define TEXTURE3D_PARAM(textureName, samplerName) TEXTURE3D(textureName), SAMPLER(samplerName) #define TEXTURE2D_SHADOW_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER_CMP(samplerName) #define TEXTURE2D_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER_CMP(samplerName) #define TEXTURECUBE_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER_CMP(samplerName) #define TEXTURECUBE_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER_CMP(samplerName) #define TEXTURE2D_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_ARRAY_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARRAY_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE3D_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #define PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3) #define PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod) #define PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) textureName.SampleBias(samplerName, coord3, bias) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Sample(samplerName, float4(coord3, index)) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) textureName.SampleLevel(samplerName, float4(coord3, index), lod) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) textureName.SampleBias(samplerName, float4(coord3, index), bias) #define PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3) #define PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod) #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) #define SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) #define SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) #define SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) #define SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) #define SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) #define SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) #define SAMPLE_TEXTURE3D(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3) #define SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) #define SAMPLE_TEXTURE2D_SHADOW(textureName, samplerName, coord3) textureName.SampleCmpLevelZero(samplerName, (coord3).xy, (coord3).z) #define SAMPLE_TEXTURE2D_ARRAY_SHADOW(textureName, samplerName, coord3, index) textureName.SampleCmpLevelZero(samplerName, float3((coord3).xy, index), (coord3).z) #define SAMPLE_TEXTURECUBE_SHADOW(textureName, samplerName, coord4) textureName.SampleCmpLevelZero(samplerName, (coord4).xyz, (coord4).w) #define SAMPLE_TEXTURECUBE_ARRAY_SHADOW(textureName, samplerName, coord4, index) textureName.SampleCmpLevelZero(samplerName, float4((coord4).xyz, index), (coord4).w) #define SAMPLE_DEPTH_TEXTURE(textureName, samplerName, coord2) SAMPLE_TEXTURE2D(textureName, samplerName, coord2).r #define SAMPLE_DEPTH_TEXTURE_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod).r #define LOAD_TEXTURE2D(textureName, unCoord2) textureName.Load(int3(unCoord2, 0)) #define LOAD_TEXTURE2D_LOD(textureName, unCoord2, lod) textureName.Load(int3(unCoord2, lod)) #define LOAD_TEXTURE2D_MSAA(textureName, unCoord2, sampleIndex) textureName.Load(unCoord2, sampleIndex) #define LOAD_TEXTURE2D_ARRAY(textureName, unCoord2, index) textureName.Load(int4(unCoord2, index, 0)) #define LOAD_TEXTURE2D_ARRAY_MSAA(textureName, unCoord2, index, sampleIndex) textureName.Load(int3(unCoord2, index), sampleIndex) #define LOAD_TEXTURE2D_ARRAY_LOD(textureName, unCoord2, index, lod) textureName.Load(int4(unCoord2, index, lod)) #define LOAD_TEXTURE3D(textureName, unCoord3) textureName.Load(int4(unCoord3, 0)) #define LOAD_TEXTURE3D_LOD(textureName, unCoord3, lod) textureName.Load(int4(unCoord3, lod)) #define PLATFORM_SUPPORT_GATHER #define GATHER_TEXTURE2D(textureName, samplerName, coord2) textureName.Gather(samplerName, coord2) #define GATHER_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Gather(samplerName, float3(coord2, index)) #define GATHER_TEXTURECUBE(textureName, samplerName, coord3) textureName.Gather(samplerName, coord3) #define GATHER_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Gather(samplerName, float4(coord3, index)) #define GATHER_RED_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherRed(samplerName, coord2) #define GATHER_GREEN_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherGreen(samplerName, coord2) #define GATHER_BLUE_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherBlue(samplerName, coord2) #define GATHER_ALPHA_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherAlpha(samplerName, coord2) #elif defined(SHADER_API_XBOXONE) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_PSSL) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.GetLOD(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_D3D11) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_METAL) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_VULKAN) // This file assume SHADER_API_VULKAN is defined // TODO: This is a straight copy from D3D11.hlsl. Go through all this stuff and adjust where needed. // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_SWITCH) // This file assume SHADER_API_SWITCH is defined // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLCORE) // OpenGL 4.1 SM 5.0 https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html #if (SHADER_TARGET >= 46) #define OPENGL4_1_SM5 1 #else #define OPENGL4_1_SM5 0 #endif // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLES3) // GLES 3.1 + AEP shader feature https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html #if (SHADER_TARGET >= 40) #define GLES3_1_AEP 1 #else #define GLES3_1_AEP 0 #endif // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLES) #define uint int #define rcp(x) 1.0 / (x) #define ddx_fine ddx #define ddy_fine ddy #define asfloat #define asuint(x) asint(x) #define f32tof16 #define f16tof32 #define ERROR_ON_UNSUPPORTED_FUNCTION(funcName) #error #funcName is not supported on GLES 2.0 // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) #error calculate Level of Detail not supported in GLES2 // Texture abstraction #define TEXTURE2D(textureName) sampler2D textureName #define TEXTURE2D_ARRAY(textureName) samplerCUBE textureName // No support to texture2DArray #define TEXTURECUBE(textureName) samplerCUBE textureName #define TEXTURECUBE_ARRAY(textureName) samplerCUBE textureName // No supoport to textureCubeArray and can't emulate with texture2DArray #define TEXTURE3D(textureName) sampler3D textureName #define TEXTURE2D_FLOAT(textureName) sampler2D_float textureName #define TEXTURECUBE_FLOAT(textureName) samplerCUBE_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURECUBE_FLOAT(textureName) // No support to texture2DArray #define TEXTURE2D_HALF(textureName) sampler2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURECUBE_HALF(textureName) // No support to texture2DArray #define SAMPLER(samplerName) #define SAMPLER_CMP(samplerName) #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) tex2D(textureName, coord2) #if (SHADER_TARGET >= 30) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) tex2Dlod(textureName, float4(coord2, 0, lod)) #else // No lod support. Very poor approximation with bias. #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, lod) #endif #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) tex2Dbias(textureName, float4(coord2, 0, bias)) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) SAMPLE_TEXTURE2D(textureName, samplerName, coord2) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_LOD) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_BIAS) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_GRAD) #else #error unsupported shader api #endif // default flow control attributes #ifndef UNITY_BRANCH # define UNITY_BRANCH #endif #ifndef UNITY_FLATTEN # define UNITY_FLATTEN #endif #ifndef UNITY_UNROLL # define UNITY_UNROLL #endif #ifndef UNITY_UNROLLX # define UNITY_UNROLLX(_x) #endif #ifndef UNITY_LOOP # define UNITY_LOOP #endif #if _NOMINDIELETRIC // for Standard #ifdef unity_ColorSpaceDielectricSpec #undef unity_ColorSpaceDielectricSpec #endif #define unity_ColorSpaceDielectricSpec half4(0,0,0,1) #endif #include "Lighting.cginc" #include "UnityPBSLighting.cginc" #if _MICROTERRAIN && !_TERRAINBLENDABLESHADER #define UNITY_ASSUME_UNIFORM_SCALING #define UNITY_DONT_INSTANCE_OBJECT_MATRICES #define UNITY_INSTANCED_LOD_FADE #else #define UNITY_INSTANCED_LOD_FADE #define UNITY_INSTANCED_SH #define UNITY_INSTANCED_LIGHTMAPSTS #endif // data across stages, stripped like the above. struct VertexToPixel { UNITY_POSITION(pos); // must be named pos because Unity does stupid macro stuff 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; #ifndef DIRLIGHTMAP_OFF float3 viewDir : TEXCOORD8; #endif float4 lmap : TEXCOORD9; #ifndef LIGHTMAP_ON #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL half3 sh : TEXCOORD10; // SH #endif #else #ifdef DIRLIGHTMAP_OFF float4 lmapFadePos : TEXCOORD11; #endif #endif // float4 extraV2F0 : TEXCOORD12; // float4 extraV2F1 : TEXCOORD13; // float4 extraV2F2 : TEXCOORD14; // float4 extraV2F3 : TEXCOORD15; // float4 extraV2F4 : TEXCOORD16; // float4 extraV2F5 : TEXCOORD17; // float4 extraV2F6 : TEXCOORD18; // float4 extraV2F7 : TEXCOORD19; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; // 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 // unity_WorldToObject in HDRP, since it already does macro-fu there #if _STANDARD float3 TransformWorldToObject(float3 p) { return mul(unity_WorldToObject, float4(p, 1)); }; float3 TransformObjectToWorld(float3 p) { return mul(unity_ObjectToWorld, float4(p, 1)); }; float4 TransformWorldToObject(float4 p) { return mul(unity_WorldToObject, p); }; float4 TransformObjectToWorld(float4 p) { return mul(unity_ObjectToWorld, p); }; float4x4 GetWorldToObjectMatrix() { return unity_WorldToObject; } float4x4 GetObjectToWorldMatrix() { return unity_ObjectToWorld; } #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 } #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 #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); 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 fixed4 w0; fixed4 w1; fixed4 w2; fixed4 w3; fixed4 w4; fixed4 w5; fixed4 w6; // megasplat data half4 layer0; half4 layer1; half3 baryWeights; half4 scatter0; half4 scatter1; // wetness, puddles, streams, lava from vertex or megasplat fixed4 fx; // snow min, snow max fixed4 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(unity_WorldToObject, float4(n, 1)).xyz; surfTangent = t;//mul(unity_WorldToObject, 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, fixed4 w0, fixed4 w1, fixed4 w2, fixed4 w3, fixed4 w4, fixed4 w5, fixed4 w6, fixed4 w7, float3 worldPos, DecalOutput decalOutput) { config = (Config)0; half4 indexes = 0; config.uv = uv; #if _WORLDUV uv = worldPos.xz * float2(-1,1); #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 fixed 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) { fixed 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; } } #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 fixed2 UnpackNormal2(fixed4 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; } // Stochastic shared code // Compute local triangle barycentric coordinates and vertex IDs void TriangleGrid(float2 uv, float scale, out float w1, out float w2, out float w3, out int2 vertex1, out int2 vertex2, out int2 vertex3) { // Scaling of the input uv *= 3.464 * scale; // 2 * sqrt(3) // Skew input space into simplex triangle grid const float2x2 gridToSkewedGrid = float2x2(1.0, 0.0, -0.57735027, 1.15470054); float2 skewedCoord = mul(gridToSkewedGrid, uv); // Compute local triangle vertex IDs and local barycentric coordinates int2 baseId = int2(floor(skewedCoord)); float3 temp = float3(frac(skewedCoord), 0); temp.z = 1.0 - temp.x - temp.y; if (temp.z > 0.0) { w1 = temp.z; w2 = temp.y; w3 = temp.x; vertex1 = baseId; vertex2 = baseId + int2(0, 1); vertex3 = baseId + int2(1, 0); } else { w1 = -temp.z; w2 = 1.0 - temp.y; w3 = 1.0 - temp.x; vertex1 = baseId + int2(1, 1); vertex2 = baseId + int2(1, 0); vertex3 = baseId + int2(0, 1); } } // Fast random hash function float2 SimpleHash2(float2 p) { return frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), p)) * 43758.5453); } half3 BaryWeightBlend(half3 iWeights, half tex0, half tex1, half tex2, half contrast) { // compute weight with height map const half epsilon = 1.0f / 1024.0f; half3 weights = half3(iWeights.x * (tex0 + epsilon), iWeights.y * (tex1 + epsilon), iWeights.z * (tex2 + epsilon)); // Contrast weights half maxWeight = max(weights.x, max(weights.y, weights.z)); half transition = contrast * maxWeight; half threshold = maxWeight - transition; half scale = 1.0f / transition; weights = saturate((weights - threshold) * scale); // Normalize weights. half weightScale = 1.0f / (weights.x + weights.y + weights.z); weights *= weightScale; return weights; } void PrepareStochasticUVs(float scale, float3 uv, out float3 uv1, out float3 uv2, out float3 uv3, out half3 weights) { // Get triangle info float w1, w2, w3; int2 vertex1, vertex2, vertex3; TriangleGrid(uv.xy, scale, w1, w2, w3, vertex1, vertex2, vertex3); // Assign random offset to each triangle vertex uv1 = uv; uv2 = uv; uv3 = uv; uv1.xy += SimpleHash2(vertex1); uv2.xy += SimpleHash2(vertex2); uv3.xy += SimpleHash2(vertex3); weights = half3(w1, w2, w3); } void PrepareStochasticUVs(float scale, float2 uv, out float2 uv1, out float2 uv2, out float2 uv3, out half3 weights) { // Get triangle info float w1, w2, w3; int2 vertex1, vertex2, vertex3; TriangleGrid(uv, scale, w1, w2, w3, vertex1, vertex2, vertex3); // Assign random offset to each triangle vertex uv1 = uv; uv2 = uv; uv3 = uv; uv1.xy += SimpleHash2(vertex1); uv2.xy += SimpleHash2(vertex2); uv3.xy += SimpleHash2(vertex3); weights = half3(w1, w2, w3); } 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 _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 fixed4 w0, inout fixed4 w1, inout fixed4 w2, inout fixed4 w3, inout fixed4 w4, inout fixed4 w5, inout fixed4 w6, inout fixed4 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)unity_WorldToObject, worldNormalVertex).xyz; i.worldPos = i.worldPos - mul(unity_ObjectToWorld, 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 fixed4 w0 = 0; fixed4 w1 = 0; fixed4 w2 = 0; fixed4 w3 = 0; fixed4 w4 = 0; fixed4 w5 = 0; fixed4 w6 = 0; fixed4 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.. l.Albedo *= saturate(SAMPLE_TEXTURE2D_LOD(_Diffuse, sampler_Diffuse, config.uv0, 11).r + 2); l.Albedo *= saturate(SAMPLE_TEXTURE2D_LOD(_NormalSAO, sampler_NormalSAO, config.uv0, 11).r + 2); #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 = 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 } // 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); #elif _PLANETVECTORS DoPlanetVectorVertex(v, d); #endif } void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d) { #if _TESSDISTANCE v.vertex.xyz += OffsetVertex(v, d); #endif } float3 GetTessFactors () { #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 (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; 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(unity_WorldToObject, float4(GetCameraRelativePositionWS(i.worldPos), 1)); #else // d.localSpacePosition = mul(unity_WorldToObject, float4(i.worldPos, 1)); #endif // d.localSpaceNormal = normalize(mul((float3x3)unity_WorldToObject, i.worldNormal)); // d.localSpaceTangent = normalize(mul((float3x3)unity_WorldToObject, 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) { } // vertex shader VertexToPixel Vert (VertexData v) { UNITY_SETUP_INSTANCE_ID(v); VertexToPixel o; UNITY_INITIALIZE_OUTPUT(VertexToPixel,o); UNITY_TRANSFER_INSTANCE_ID(v,o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #if !_TESSELLATION_ON ChainModifyVertex(v, o); #endif o.pos = UnityObjectToClipPos(v.vertex); o.texcoord0 = v.texcoord0; #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.texcoord1 = v.texcoord1; // o.texcoord2 = v.texcoord2; #endif // o.texcoord3 = v.texcoord3; // o.vertexColor = v.vertexColor; // o.screenPos = ComputeScreenPos(o.pos); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.worldNormal = UnityObjectToWorldNormal(v.normal); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.worldTangent.xyz = UnityObjectToWorldDir(v.tangent.xyz); fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w; float3 worldBinormal = cross(o.worldNormal, o.worldTangent.xyz) * tangentSign; o.worldTangent.w = tangentSign; #else // MS Only ApplyTerrainTangent(o); float3 worldBinormal = cross(o.worldNormal, o.worldTangent.xyz) * unity_WorldTransformParams.w; #endif float3 viewDirForLight = UnityWorldSpaceViewDir(o.worldPos); #ifndef DIRLIGHTMAP_OFF o.viewDir.x = dot(viewDirForLight, o.worldTangent.xyz); o.viewDir.y = dot(viewDirForLight, worldBinormal); o.viewDir.z = dot(viewDirForLight, o.worldNormal); #endif #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float2 uv1 = v.texcoord1.xy; float2 uv2 = v.texcoord2.xy; #else float2 uv1 = v.texcoord0.xy; float2 uv2 = uv1; #endif #ifdef DYNAMICLIGHTMAP_ON o.lmap.zw = uv2 * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; #else o.lmap.zw = 0; #endif #ifdef LIGHTMAP_ON o.lmap.xy = uv1 * unity_LightmapST.xy + unity_LightmapST.zw; #ifdef DIRLIGHTMAP_OFF o.lmapFadePos.xyz = (mul(unity_ObjectToWorld, v.vertex).xyz - unity_ShadowFadeCenterAndType.xyz) * unity_ShadowFadeCenterAndType.w; o.lmapFadePos.w = (-UnityObjectToViewPos(v.vertex).z) * (1.0 - unity_ShadowFadeCenterAndType.w); #endif #else o.lmap.xy = 0; #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL o.sh = 0; o.sh = ShadeSHPerVertex (o.worldNormal, o.sh); #endif #endif return o; } #ifdef LIGHTMAP_ON float4 unity_LightmapFade; #endif fixed4 unity_Ambient; // fragment shader void Frag (VertexToPixel IN, out half4 outGBuffer0 : SV_Target0, out half4 outGBuffer1 : SV_Target1, out half4 outGBuffer2 : SV_Target2, out half4 outEmission : SV_Target3 #if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4) , out half4 outShadowMask : SV_Target4 #endif ) { UNITY_SETUP_INSTANCE_ID(IN); // prepare and unpack data #ifdef FOG_COMBINED_WITH_TSPACE UNITY_EXTRACT_FOG_FROM_TSPACE(IN); #elif defined (FOG_COMBINED_WITH_WORLD_POS) UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN); #else UNITY_EXTRACT_FOG(IN); #endif 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); #ifndef USING_DIRECTIONAL_LIGHT fixed3 lightDir = normalize(UnityWorldSpaceLightDir(d.worldSpacePosition)); #else fixed3 lightDir = _WorldSpaceLightPos0.xyz; #endif float3 worldViewDir = normalize(UnityWorldSpaceViewDir(d.worldSpacePosition)); #if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC #ifdef UNITY_COMPILER_HLSL SurfaceOutputStandardSpecular o = (SurfaceOutputStandardSpecular)0; #else SurfaceOutputStandardSpecular o; #endif o.Specular = l.Specular; #elif _BDRFLAMBERT || _BDRF3 #ifdef UNITY_COMPILER_HLSL SurfaceOutput o = (SurfaceOutput)0; #else SurfaceOutput o; #endif #else #ifdef UNITY_COMPILER_HLSL SurfaceOutputStandard o = (SurfaceOutputStandard)0; #else SurfaceOutputStandard o; #endif o.Metallic = l.Metallic; #endif o.Albedo = l.Albedo; o.Emission = l.Emission; o.Alpha = l.Alpha; o.Normal = normalize(TangentToWorldSpace(d, l.Normal)); #if _BDRFLAMBERT || _BDRF3 o.Specular = l.Occlusion; o.Gloss = l.Smoothness; #elif _SPECULARFROMMETALLIC o.Occlusion = l.Occlusion; o.Smoothness = l.Smoothness; o.Albedo = MicroSplatDiffuseAndSpecularFromMetallic(l.Albedo, l.Metallic, o.Specular, o.Smoothness); o.Smoothness = 1-o.Smoothness; #elif _USESPECULARWORKFLOW o.Occlusion = l.Occlusion; o.Smoothness = l.Smoothness; o.Specular = l.Specular; #else o.Smoothness = l.Smoothness; o.Metallic = l.Metallic; o.Occlusion = l.Occlusion; #endif half atten = 1; // Setup lighting environment UnityGI gi; UNITY_INITIALIZE_OUTPUT(UnityGI, gi); gi.indirect.diffuse = 0; gi.indirect.specular = 0; gi.light.color = 0; gi.light.dir = half3(0,1,0); // Call GI (lightmaps/SH/reflections) lighting function UnityGIInput giInput; UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput); giInput.light = gi.light; giInput.worldPos = d.worldSpacePosition; giInput.worldViewDir = worldViewDir; giInput.atten = atten; #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON) giInput.lightmapUV = IN.lmap; #else giInput.lightmapUV = 0.0; #endif #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL giInput.ambient = IN.sh; #else giInput.ambient.rgb = 0.0; #endif giInput.probeHDR[0] = unity_SpecCube0_HDR; giInput.probeHDR[1] = unity_SpecCube1_HDR; #if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION) giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending #endif #ifdef UNITY_SPECCUBE_BOX_PROJECTION giInput.boxMax[0] = unity_SpecCube0_BoxMax; giInput.probePosition[0] = unity_SpecCube0_ProbePosition; giInput.boxMax[1] = unity_SpecCube1_BoxMax; giInput.boxMin[1] = unity_SpecCube1_BoxMin; giInput.probePosition[1] = unity_SpecCube1_ProbePosition; #endif #if _USESPECULAR || _USESPECULARWORKFLOW || _SPECULARFROMMETALLIC LightingStandardSpecular_GI(o, giInput, gi); // call lighting function to output g-buffer outEmission = LightingStandardSpecular_Deferred (o, worldViewDir, gi, outGBuffer0, outGBuffer1, outGBuffer2); #if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4) outShadowMask = UnityGetRawBakedOcclusions (IN.lmap.xy, d.worldSpacePosition); #endif #ifndef UNITY_HDR_ON outEmission.rgb = exp2(-outEmission.rgb); #endif #else LightingStandard_GI(o, giInput, gi); // call lighting function to output g-buffer outEmission = LightingStandard_Deferred (o, worldViewDir, gi, outGBuffer0, outGBuffer1, outGBuffer2); #if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4) outShadowMask = UnityGetRawBakedOcclusions (IN.lmap.xy, d.worldSpacePosition); #endif #ifndef UNITY_HDR_ON outEmission.rgb = exp2(-outEmission.rgb); #endif #endif #if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4) ChainFinalGBufferStandard(l, d, outGBuffer0, outGBuffer1, outGBuffer2, outEmission, outShadowMask); #else half4 outShadowMask = 0; ChainFinalGBufferStandard(l, d, outGBuffer0, outGBuffer1, outGBuffer2, outEmission, outShadowMask); #endif } ENDCG } Pass { Name "ShadowCaster" Tags { "LightMode" = "ShadowCaster" } ZWrite On ZTest LEqual CGPROGRAM #pragma vertex Vert #pragma fragment Frag // compile directives #pragma target 3.0 #pragma multi_compile_instancing #pragma skip_variants FOG_LINEAR FOG_EXP FOG_EXP2 #pragma multi_compile_shadowcaster #pragma multi_compile_local __ _ALPHATEST_ON #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #include "UnityShaderUtilities.cginc" #include "UnityCG.cginc" #include "Lighting.cginc" #include "UnityPBSLighting.cginc" #define _PASSSHADOW 1 #define _MICROSPLAT 1 #define _MICROTERRAIN 1 #define _HYBRIDHEIGHTBLEND 1 #define _USEGRADMIP 1 #define _MAX4TEXTURES 1 #define _MSRENDERLOOP_SURFACESHADER 1 #pragma instancing_options assumeuniformscaling nomatrices nolightprobe nolightmap forwardadd #define _STANDARD 1 // If your looking in here and thinking WTF, yeah, I know. These are taken from the SRPs, to allow us to use the same // texturing library they use. However, since they are not included in the standard pipeline by default, there is no // way to include them in and they have to be inlined, since someone could copy this shader onto another machine without // MicroSplat installed. Unfortunate, but I'd rather do this and have a nice library for texture sampling instead // of the patchy one Unity provides being inlined/emulated in HDRP/URP. Strangely, PSSL and XBoxOne libraries are not // included in the standard SRP code, but they are in tons of Unity own projects on the web, so I grabbed them from there. #if defined(SHADER_API_GAMECORE) #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURECUBE(textureName) TextureCube textureName #define TEXTURECUBE_ARRAY(textureName) TextureCubeArray textureName #define TEXTURE3D(textureName) Texture3D textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_FLOAT(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_FLOAT(textureName) TEXTURECUBE_ARRAY(textureName) #define TEXTURE3D_FLOAT(textureName) TEXTURE3D(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_HALF(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_HALF(textureName) TEXTURECUBE_ARRAY(textureName) #define TEXTURE3D_HALF(textureName) TEXTURE3D(textureName) #define TEXTURE2D_SHADOW(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_SHADOW(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_SHADOW(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_SHADOW(textureName) TEXTURECUBE_ARRAY(textureName) #define RW_TEXTURE2D(type, textureName) RWTexture2D textureName #define RW_TEXTURE2D_ARRAY(type, textureName) RWTexture2DArray textureName #define RW_TEXTURE3D(type, textureName) RWTexture3D textureName #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define ASSIGN_SAMPLER(samplerName, samplerValue) samplerName = samplerValue #define TEXTURE2D_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER(samplerName) #define TEXTURE2D_ARRAY_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER(samplerName) #define TEXTURECUBE_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER(samplerName) #define TEXTURECUBE_ARRAY_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER(samplerName) #define TEXTURE3D_PARAM(textureName, samplerName) TEXTURE3D(textureName), SAMPLER(samplerName) #define TEXTURE2D_SHADOW_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER_CMP(samplerName) #define TEXTURE2D_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER_CMP(samplerName) #define TEXTURECUBE_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER_CMP(samplerName) #define TEXTURECUBE_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER_CMP(samplerName) #define TEXTURE2D_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_ARRAY_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARRAY_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE3D_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #define PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3) #define PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod) #define PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) textureName.SampleBias(samplerName, coord3, bias) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Sample(samplerName, float4(coord3, index)) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) textureName.SampleLevel(samplerName, float4(coord3, index), lod) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) textureName.SampleBias(samplerName, float4(coord3, index), bias) #define PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3) #define PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod) #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) #define SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) #define SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) #define SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) #define SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) #define SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) #define SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) #define SAMPLE_TEXTURE3D(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3) #define SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) #define SAMPLE_TEXTURE2D_SHADOW(textureName, samplerName, coord3) textureName.SampleCmpLevelZero(samplerName, (coord3).xy, (coord3).z) #define SAMPLE_TEXTURE2D_ARRAY_SHADOW(textureName, samplerName, coord3, index) textureName.SampleCmpLevelZero(samplerName, float3((coord3).xy, index), (coord3).z) #define SAMPLE_TEXTURECUBE_SHADOW(textureName, samplerName, coord4) textureName.SampleCmpLevelZero(samplerName, (coord4).xyz, (coord4).w) #define SAMPLE_TEXTURECUBE_ARRAY_SHADOW(textureName, samplerName, coord4, index) textureName.SampleCmpLevelZero(samplerName, float4((coord4).xyz, index), (coord4).w) #define SAMPLE_DEPTH_TEXTURE(textureName, samplerName, coord2) SAMPLE_TEXTURE2D(textureName, samplerName, coord2).r #define SAMPLE_DEPTH_TEXTURE_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod).r #define LOAD_TEXTURE2D(textureName, unCoord2) textureName.Load(int3(unCoord2, 0)) #define LOAD_TEXTURE2D_LOD(textureName, unCoord2, lod) textureName.Load(int3(unCoord2, lod)) #define LOAD_TEXTURE2D_MSAA(textureName, unCoord2, sampleIndex) textureName.Load(unCoord2, sampleIndex) #define LOAD_TEXTURE2D_ARRAY(textureName, unCoord2, index) textureName.Load(int4(unCoord2, index, 0)) #define LOAD_TEXTURE2D_ARRAY_MSAA(textureName, unCoord2, index, sampleIndex) textureName.Load(int3(unCoord2, index), sampleIndex) #define LOAD_TEXTURE2D_ARRAY_LOD(textureName, unCoord2, index, lod) textureName.Load(int4(unCoord2, index, lod)) #define LOAD_TEXTURE3D(textureName, unCoord3) textureName.Load(int4(unCoord3, 0)) #define LOAD_TEXTURE3D_LOD(textureName, unCoord3, lod) textureName.Load(int4(unCoord3, lod)) #define PLATFORM_SUPPORT_GATHER #define GATHER_TEXTURE2D(textureName, samplerName, coord2) textureName.Gather(samplerName, coord2) #define GATHER_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Gather(samplerName, float3(coord2, index)) #define GATHER_TEXTURECUBE(textureName, samplerName, coord3) textureName.Gather(samplerName, coord3) #define GATHER_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Gather(samplerName, float4(coord3, index)) #define GATHER_RED_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherRed(samplerName, coord2) #define GATHER_GREEN_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherGreen(samplerName, coord2) #define GATHER_BLUE_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherBlue(samplerName, coord2) #define GATHER_ALPHA_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherAlpha(samplerName, coord2) #elif defined(SHADER_API_XBOXONE) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_PSSL) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.GetLOD(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_D3D11) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_METAL) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_VULKAN) // This file assume SHADER_API_VULKAN is defined // TODO: This is a straight copy from D3D11.hlsl. Go through all this stuff and adjust where needed. // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_SWITCH) // This file assume SHADER_API_SWITCH is defined // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLCORE) // OpenGL 4.1 SM 5.0 https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html #if (SHADER_TARGET >= 46) #define OPENGL4_1_SM5 1 #else #define OPENGL4_1_SM5 0 #endif // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLES3) // GLES 3.1 + AEP shader feature https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html #if (SHADER_TARGET >= 40) #define GLES3_1_AEP 1 #else #define GLES3_1_AEP 0 #endif // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLES) #define uint int #define rcp(x) 1.0 / (x) #define ddx_fine ddx #define ddy_fine ddy #define asfloat #define asuint(x) asint(x) #define f32tof16 #define f16tof32 #define ERROR_ON_UNSUPPORTED_FUNCTION(funcName) #error #funcName is not supported on GLES 2.0 // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) #error calculate Level of Detail not supported in GLES2 // Texture abstraction #define TEXTURE2D(textureName) sampler2D textureName #define TEXTURE2D_ARRAY(textureName) samplerCUBE textureName // No support to texture2DArray #define TEXTURECUBE(textureName) samplerCUBE textureName #define TEXTURECUBE_ARRAY(textureName) samplerCUBE textureName // No supoport to textureCubeArray and can't emulate with texture2DArray #define TEXTURE3D(textureName) sampler3D textureName #define TEXTURE2D_FLOAT(textureName) sampler2D_float textureName #define TEXTURECUBE_FLOAT(textureName) samplerCUBE_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURECUBE_FLOAT(textureName) // No support to texture2DArray #define TEXTURE2D_HALF(textureName) sampler2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURECUBE_HALF(textureName) // No support to texture2DArray #define SAMPLER(samplerName) #define SAMPLER_CMP(samplerName) #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) tex2D(textureName, coord2) #if (SHADER_TARGET >= 30) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) tex2Dlod(textureName, float4(coord2, 0, lod)) #else // No lod support. Very poor approximation with bias. #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, lod) #endif #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) tex2Dbias(textureName, float4(coord2, 0, bias)) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) SAMPLE_TEXTURE2D(textureName, samplerName, coord2) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_LOD) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_BIAS) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_GRAD) #else #error unsupported shader api #endif // default flow control attributes #ifndef UNITY_BRANCH # define UNITY_BRANCH #endif #ifndef UNITY_FLATTEN # define UNITY_FLATTEN #endif #ifndef UNITY_UNROLL # define UNITY_UNROLL #endif #ifndef UNITY_UNROLLX # define UNITY_UNROLLX(_x) #endif #ifndef UNITY_LOOP # define UNITY_LOOP #endif #if _MICROTERRAIN && !_TERRAINBLENDABLESHADER #define UNITY_ASSUME_UNIFORM_SCALING #define UNITY_DONT_INSTANCE_OBJECT_MATRICES #define UNITY_INSTANCED_LOD_FADE #else #define UNITY_INSTANCED_LOD_FADE #define UNITY_INSTANCED_SH #define UNITY_INSTANCED_LIGHTMAPSTS #endif // data across stages, stripped like the above. struct VertexToPixel { V2F_SHADOW_CASTER; 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 : TEXCOORD8; // float4 extraV2F1 : TEXCOORD9; // float4 extraV2F2 : TEXCOORD10; // float4 extraV2F3 : TEXCOORD11; // float4 extraV2F4 : TEXCOORD12; // float4 extraV2F5 : TEXCOORD13; // float4 extraV2F6 : TEXCOORD14; // float4 extraV2F7 : TEXCOORD15; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; // 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 // unity_WorldToObject in HDRP, since it already does macro-fu there #if _STANDARD float3 TransformWorldToObject(float3 p) { return mul(unity_WorldToObject, float4(p, 1)); }; float3 TransformObjectToWorld(float3 p) { return mul(unity_ObjectToWorld, float4(p, 1)); }; float4 TransformWorldToObject(float4 p) { return mul(unity_WorldToObject, p); }; float4 TransformObjectToWorld(float4 p) { return mul(unity_ObjectToWorld, p); }; float4x4 GetWorldToObjectMatrix() { return unity_WorldToObject; } float4x4 GetObjectToWorldMatrix() { return unity_ObjectToWorld; } #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 } #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 #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); 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 fixed4 w0; fixed4 w1; fixed4 w2; fixed4 w3; fixed4 w4; fixed4 w5; fixed4 w6; // megasplat data half4 layer0; half4 layer1; half3 baryWeights; half4 scatter0; half4 scatter1; // wetness, puddles, streams, lava from vertex or megasplat fixed4 fx; // snow min, snow max fixed4 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(unity_WorldToObject, float4(n, 1)).xyz; surfTangent = t;//mul(unity_WorldToObject, 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, fixed4 w0, fixed4 w1, fixed4 w2, fixed4 w3, fixed4 w4, fixed4 w5, fixed4 w6, fixed4 w7, float3 worldPos, DecalOutput decalOutput) { config = (Config)0; half4 indexes = 0; config.uv = uv; #if _WORLDUV uv = worldPos.xz * float2(-1,1); #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 fixed 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) { fixed 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; } } #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 fixed2 UnpackNormal2(fixed4 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; } // Stochastic shared code // Compute local triangle barycentric coordinates and vertex IDs void TriangleGrid(float2 uv, float scale, out float w1, out float w2, out float w3, out int2 vertex1, out int2 vertex2, out int2 vertex3) { // Scaling of the input uv *= 3.464 * scale; // 2 * sqrt(3) // Skew input space into simplex triangle grid const float2x2 gridToSkewedGrid = float2x2(1.0, 0.0, -0.57735027, 1.15470054); float2 skewedCoord = mul(gridToSkewedGrid, uv); // Compute local triangle vertex IDs and local barycentric coordinates int2 baseId = int2(floor(skewedCoord)); float3 temp = float3(frac(skewedCoord), 0); temp.z = 1.0 - temp.x - temp.y; if (temp.z > 0.0) { w1 = temp.z; w2 = temp.y; w3 = temp.x; vertex1 = baseId; vertex2 = baseId + int2(0, 1); vertex3 = baseId + int2(1, 0); } else { w1 = -temp.z; w2 = 1.0 - temp.y; w3 = 1.0 - temp.x; vertex1 = baseId + int2(1, 1); vertex2 = baseId + int2(1, 0); vertex3 = baseId + int2(0, 1); } } // Fast random hash function float2 SimpleHash2(float2 p) { return frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), p)) * 43758.5453); } half3 BaryWeightBlend(half3 iWeights, half tex0, half tex1, half tex2, half contrast) { // compute weight with height map const half epsilon = 1.0f / 1024.0f; half3 weights = half3(iWeights.x * (tex0 + epsilon), iWeights.y * (tex1 + epsilon), iWeights.z * (tex2 + epsilon)); // Contrast weights half maxWeight = max(weights.x, max(weights.y, weights.z)); half transition = contrast * maxWeight; half threshold = maxWeight - transition; half scale = 1.0f / transition; weights = saturate((weights - threshold) * scale); // Normalize weights. half weightScale = 1.0f / (weights.x + weights.y + weights.z); weights *= weightScale; return weights; } void PrepareStochasticUVs(float scale, float3 uv, out float3 uv1, out float3 uv2, out float3 uv3, out half3 weights) { // Get triangle info float w1, w2, w3; int2 vertex1, vertex2, vertex3; TriangleGrid(uv.xy, scale, w1, w2, w3, vertex1, vertex2, vertex3); // Assign random offset to each triangle vertex uv1 = uv; uv2 = uv; uv3 = uv; uv1.xy += SimpleHash2(vertex1); uv2.xy += SimpleHash2(vertex2); uv3.xy += SimpleHash2(vertex3); weights = half3(w1, w2, w3); } void PrepareStochasticUVs(float scale, float2 uv, out float2 uv1, out float2 uv2, out float2 uv3, out half3 weights) { // Get triangle info float w1, w2, w3; int2 vertex1, vertex2, vertex3; TriangleGrid(uv, scale, w1, w2, w3, vertex1, vertex2, vertex3); // Assign random offset to each triangle vertex uv1 = uv; uv2 = uv; uv3 = uv; uv1.xy += SimpleHash2(vertex1); uv2.xy += SimpleHash2(vertex2); uv3.xy += SimpleHash2(vertex3); weights = half3(w1, w2, w3); } 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 _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 fixed4 w0, inout fixed4 w1, inout fixed4 w2, inout fixed4 w3, inout fixed4 w4, inout fixed4 w5, inout fixed4 w6, inout fixed4 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)unity_WorldToObject, worldNormalVertex).xyz; i.worldPos = i.worldPos - mul(unity_ObjectToWorld, 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 fixed4 w0 = 0; fixed4 w1 = 0; fixed4 w2 = 0; fixed4 w3 = 0; fixed4 w4 = 0; fixed4 w5 = 0; fixed4 w6 = 0; fixed4 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.. l.Albedo *= saturate(SAMPLE_TEXTURE2D_LOD(_Diffuse, sampler_Diffuse, config.uv0, 11).r + 2); l.Albedo *= saturate(SAMPLE_TEXTURE2D_LOD(_NormalSAO, sampler_NormalSAO, config.uv0, 11).r + 2); #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 = 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 } // 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); #elif _PLANETVECTORS DoPlanetVectorVertex(v, d); #endif } void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d) { #if _TESSDISTANCE v.vertex.xyz += OffsetVertex(v, d); #endif } float3 GetTessFactors () { #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 (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; 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(unity_WorldToObject, float4(GetCameraRelativePositionWS(i.worldPos), 1)); #else // d.localSpacePosition = mul(unity_WorldToObject, float4(i.worldPos, 1)); #endif // d.localSpaceNormal = normalize(mul((float3x3)unity_WorldToObject, i.worldNormal)); // d.localSpaceTangent = normalize(mul((float3x3)unity_WorldToObject, 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) { } // vertex shader VertexToPixel Vert (VertexData v) { UNITY_SETUP_INSTANCE_ID(v); VertexToPixel o; UNITY_INITIALIZE_OUTPUT(VertexToPixel,o); 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; o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.worldNormal = UnityObjectToWorldNormal(v.normal); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.worldTangent.xyz = UnityObjectToWorldDir(v.tangent.xyz); fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w; o.worldTangent.w = tangentSign; #endif // MS Only ApplyTerrainTangent(o); TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) // o.screenPos = ComputeScreenPos(o.pos); return o; } // fragment shader fixed4 Frag (VertexToPixel IN) : SV_Target { UNITY_SETUP_INSTANCE_ID(IN); // prepare and unpack data #ifdef FOG_COMBINED_WITH_TSPACE UNITY_EXTRACT_FOG_FROM_TSPACE(IN); #elif defined (FOG_COMBINED_WITH_WORLD_POS) UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN); #else UNITY_EXTRACT_FOG(IN); #endif #ifndef USING_DIRECTIONAL_LIGHT fixed3 lightDir = normalize(UnityWorldSpaceLightDir(IN.worldPos)); #else fixed3 lightDir = _WorldSpaceLightPos0.xyz; #endif 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); SHADOW_CASTER_FRAGMENT(IN) } ENDCG } // ---- meta information extraction pass: Pass { Name "Meta" Tags { "LightMode" = "Meta" } Cull Off CGPROGRAM #pragma vertex Vert #pragma fragment Frag // compile directives #pragma target 3.0 #pragma multi_compile_instancing #pragma multi_compile_local __ _ALPHATEST_ON #pragma skip_variants FOG_LINEAR FOG_EXP FOG_EXP2 #pragma shader_feature EDITOR_VISUALIZATION #include "HLSLSupport.cginc" #include "UnityShaderVariables.cginc" #include "UnityShaderUtilities.cginc" #include "UnityCG.cginc" #define _PASSMETA 1 #define _MICROSPLAT 1 #define _MICROTERRAIN 1 #define _HYBRIDHEIGHTBLEND 1 #define _USEGRADMIP 1 #define _MAX4TEXTURES 1 #define _MSRENDERLOOP_SURFACESHADER 1 #pragma instancing_options assumeuniformscaling nomatrices nolightprobe nolightmap forwardadd #define _STANDARD 1 // If your looking in here and thinking WTF, yeah, I know. These are taken from the SRPs, to allow us to use the same // texturing library they use. However, since they are not included in the standard pipeline by default, there is no // way to include them in and they have to be inlined, since someone could copy this shader onto another machine without // MicroSplat installed. Unfortunate, but I'd rather do this and have a nice library for texture sampling instead // of the patchy one Unity provides being inlined/emulated in HDRP/URP. Strangely, PSSL and XBoxOne libraries are not // included in the standard SRP code, but they are in tons of Unity own projects on the web, so I grabbed them from there. #if defined(SHADER_API_GAMECORE) #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURECUBE(textureName) TextureCube textureName #define TEXTURECUBE_ARRAY(textureName) TextureCubeArray textureName #define TEXTURE3D(textureName) Texture3D textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_FLOAT(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_FLOAT(textureName) TEXTURECUBE_ARRAY(textureName) #define TEXTURE3D_FLOAT(textureName) TEXTURE3D(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_HALF(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_HALF(textureName) TEXTURECUBE_ARRAY(textureName) #define TEXTURE3D_HALF(textureName) TEXTURE3D(textureName) #define TEXTURE2D_SHADOW(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_SHADOW(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURECUBE_SHADOW(textureName) TEXTURECUBE(textureName) #define TEXTURECUBE_ARRAY_SHADOW(textureName) TEXTURECUBE_ARRAY(textureName) #define RW_TEXTURE2D(type, textureName) RWTexture2D textureName #define RW_TEXTURE2D_ARRAY(type, textureName) RWTexture2DArray textureName #define RW_TEXTURE3D(type, textureName) RWTexture3D textureName #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define ASSIGN_SAMPLER(samplerName, samplerValue) samplerName = samplerValue #define TEXTURE2D_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER(samplerName) #define TEXTURE2D_ARRAY_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER(samplerName) #define TEXTURECUBE_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER(samplerName) #define TEXTURECUBE_ARRAY_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER(samplerName) #define TEXTURE3D_PARAM(textureName, samplerName) TEXTURE3D(textureName), SAMPLER(samplerName) #define TEXTURE2D_SHADOW_PARAM(textureName, samplerName) TEXTURE2D(textureName), SAMPLER_CMP(samplerName) #define TEXTURE2D_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURE2D_ARRAY(textureName), SAMPLER_CMP(samplerName) #define TEXTURECUBE_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER_CMP(samplerName) #define TEXTURECUBE_ARRAY_SHADOW_PARAM(textureName, samplerName) TEXTURECUBE_ARRAY(textureName), SAMPLER_CMP(samplerName) #define TEXTURE2D_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_ARRAY_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARRAY_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE3D_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURE2D_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define TEXTURECUBE_ARRAY_SHADOW_ARGS(textureName, samplerName) textureName, samplerName #define PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #define PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3) #define PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod) #define PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) textureName.SampleBias(samplerName, coord3, bias) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Sample(samplerName, float4(coord3, index)) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) textureName.SampleLevel(samplerName, float4(coord3, index), lod) #define PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) textureName.SampleBias(samplerName, float4(coord3, index), bias) #define PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3) #define PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod) #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) PLATFORM_SAMPLE_TEXTURE2D(textureName, samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) PLATFORM_SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) PLATFORM_SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) PLATFORM_SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) PLATFORM_SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) #define SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) #define SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) #define SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) PLATFORM_SAMPLE_TEXTURECUBE_BIAS(textureName, samplerName, coord3, bias) #define SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) #define SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_LOD(textureName, samplerName, coord3, index, lod) #define SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) PLATFORM_SAMPLE_TEXTURECUBE_ARRAY_BIAS(textureName, samplerName, coord3, index, bias) #define SAMPLE_TEXTURE3D(textureName, samplerName, coord3) PLATFORM_SAMPLE_TEXTURE3D(textureName, samplerName, coord3) #define SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) PLATFORM_SAMPLE_TEXTURE3D_LOD(textureName, samplerName, coord3, lod) #define SAMPLE_TEXTURE2D_SHADOW(textureName, samplerName, coord3) textureName.SampleCmpLevelZero(samplerName, (coord3).xy, (coord3).z) #define SAMPLE_TEXTURE2D_ARRAY_SHADOW(textureName, samplerName, coord3, index) textureName.SampleCmpLevelZero(samplerName, float3((coord3).xy, index), (coord3).z) #define SAMPLE_TEXTURECUBE_SHADOW(textureName, samplerName, coord4) textureName.SampleCmpLevelZero(samplerName, (coord4).xyz, (coord4).w) #define SAMPLE_TEXTURECUBE_ARRAY_SHADOW(textureName, samplerName, coord4, index) textureName.SampleCmpLevelZero(samplerName, float4((coord4).xyz, index), (coord4).w) #define SAMPLE_DEPTH_TEXTURE(textureName, samplerName, coord2) SAMPLE_TEXTURE2D(textureName, samplerName, coord2).r #define SAMPLE_DEPTH_TEXTURE_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod).r #define LOAD_TEXTURE2D(textureName, unCoord2) textureName.Load(int3(unCoord2, 0)) #define LOAD_TEXTURE2D_LOD(textureName, unCoord2, lod) textureName.Load(int3(unCoord2, lod)) #define LOAD_TEXTURE2D_MSAA(textureName, unCoord2, sampleIndex) textureName.Load(unCoord2, sampleIndex) #define LOAD_TEXTURE2D_ARRAY(textureName, unCoord2, index) textureName.Load(int4(unCoord2, index, 0)) #define LOAD_TEXTURE2D_ARRAY_MSAA(textureName, unCoord2, index, sampleIndex) textureName.Load(int3(unCoord2, index), sampleIndex) #define LOAD_TEXTURE2D_ARRAY_LOD(textureName, unCoord2, index, lod) textureName.Load(int4(unCoord2, index, lod)) #define LOAD_TEXTURE3D(textureName, unCoord3) textureName.Load(int4(unCoord3, 0)) #define LOAD_TEXTURE3D_LOD(textureName, unCoord3, lod) textureName.Load(int4(unCoord3, lod)) #define PLATFORM_SUPPORT_GATHER #define GATHER_TEXTURE2D(textureName, samplerName, coord2) textureName.Gather(samplerName, coord2) #define GATHER_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Gather(samplerName, float3(coord2, index)) #define GATHER_TEXTURECUBE(textureName, samplerName, coord3) textureName.Gather(samplerName, coord3) #define GATHER_TEXTURECUBE_ARRAY(textureName, samplerName, coord3, index) textureName.Gather(samplerName, float4(coord3, index)) #define GATHER_RED_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherRed(samplerName, coord2) #define GATHER_GREEN_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherGreen(samplerName, coord2) #define GATHER_BLUE_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherBlue(samplerName, coord2) #define GATHER_ALPHA_TEXTURE2D(textureName, samplerName, coord2) textureName.GatherAlpha(samplerName, coord2) #elif defined(SHADER_API_XBOXONE) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_PSSL) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.GetLOD(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_D3D11) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_METAL) // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_VULKAN) // This file assume SHADER_API_VULKAN is defined // TODO: This is a straight copy from D3D11.hlsl. Go through all this stuff and adjust where needed. // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_SWITCH) // This file assume SHADER_API_SWITCH is defined // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, dpdx, dpdy) textureName.SampleGrad(samplerName, coord2, dpdx, dpdy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLCORE) // OpenGL 4.1 SM 5.0 https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html #if (SHADER_TARGET >= 46) #define OPENGL4_1_SM5 1 #else #define OPENGL4_1_SM5 0 #endif // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURE2D_ARRAY(textureName) #define TEXTURE2D_HALF(textureName) TEXTURE2D(textureName) #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURE2D_ARRAY(textureName) #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLES3) // GLES 3.1 + AEP shader feature https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html #if (SHADER_TARGET >= 40) #define GLES3_1_AEP 1 #else #define GLES3_1_AEP 0 #endif // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) textureName.CalculateLevelOfDetail(samplerName, coord2) // Texture abstraction #define TEXTURE2D(textureName) Texture2D textureName #define TEXTURE2D_ARRAY(textureName) Texture2DArray textureName #define TEXTURE2D_FLOAT(textureName) Texture2D_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define TEXTURE2D_HALF(textureName) Texture2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) Texture2DArray textureName // no support to _float on Array, it's being added #define SAMPLER(samplerName) SamplerState samplerName #define SAMPLER_CMP(samplerName) SamplerComparisonState samplerName #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod) #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) textureName.SampleBias(samplerName, coord2, bias) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) textureName.SampleGrad(samplerName, coord2, ddx, ddy) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) textureName.Sample(samplerName, float3(coord2, index)) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) textureName.SampleLevel(samplerName, float3(coord2, index), lod) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) textureName.SampleBias(samplerName, float3(coord2, index), bias) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) textureName.SampleGrad(samplerName, float3(coord2, index), dpdx, dpdy) #elif defined(SHADER_API_GLES) #define uint int #define rcp(x) 1.0 / (x) #define ddx_fine ddx #define ddy_fine ddy #define asfloat #define asuint(x) asint(x) #define f32tof16 #define f16tof32 #define ERROR_ON_UNSUPPORTED_FUNCTION(funcName) #error #funcName is not supported on GLES 2.0 // Initialize arbitrary structure with zero values. // Do not exist on some platform, in this case we need to have a standard name that call a function that will initialize all parameters to 0 #define ZERO_INITIALIZE(type, name) name = (type)0; #define ZERO_INITIALIZE_ARRAY(type, name, arraySize) { for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { name[arrayIndex] = (type)0; } } // Texture util abstraction #define CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2) #error calculate Level of Detail not supported in GLES2 // Texture abstraction #define TEXTURE2D(textureName) sampler2D textureName #define TEXTURE2D_ARRAY(textureName) samplerCUBE textureName // No support to texture2DArray #define TEXTURECUBE(textureName) samplerCUBE textureName #define TEXTURECUBE_ARRAY(textureName) samplerCUBE textureName // No supoport to textureCubeArray and can't emulate with texture2DArray #define TEXTURE3D(textureName) sampler3D textureName #define TEXTURE2D_FLOAT(textureName) sampler2D_float textureName #define TEXTURECUBE_FLOAT(textureName) samplerCUBE_float textureName #define TEXTURE2D_ARRAY_FLOAT(textureName) TEXTURECUBE_FLOAT(textureName) // No support to texture2DArray #define TEXTURE2D_HALF(textureName) sampler2D_half textureName #define TEXTURE2D_ARRAY_HALF(textureName) TEXTURECUBE_HALF(textureName) // No support to texture2DArray #define SAMPLER(samplerName) #define SAMPLER_CMP(samplerName) #define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) tex2D(textureName, coord2) #if (SHADER_TARGET >= 30) #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) tex2Dlod(textureName, float4(coord2, 0, lod)) #else // No lod support. Very poor approximation with bias. #define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, lod) #endif #define SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, coord2, bias) tex2Dbias(textureName, float4(coord2, 0, bias)) #define SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, coord2, ddx, ddy) SAMPLE_TEXTURE2D(textureName, samplerName, coord2) #define SAMPLE_TEXTURE2D_ARRAY(textureName, samplerName, coord2, index) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY) #define SAMPLE_TEXTURE2D_ARRAY_LOD(textureName, samplerName, coord2, index, lod) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_LOD) #define SAMPLE_TEXTURE2D_ARRAY_BIAS(textureName, samplerName, coord2, index, bias) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_BIAS) #define SAMPLE_TEXTURE2D_ARRAY_GRAD(textureName, samplerName, coord2, index, dpdx, dpdy) ERROR_ON_UNSUPPORTED_FUNCTION(SAMPLE_TEXTURE2D_ARRAY_GRAD) #else #error unsupported shader api #endif // default flow control attributes #ifndef UNITY_BRANCH # define UNITY_BRANCH #endif #ifndef UNITY_FLATTEN # define UNITY_FLATTEN #endif #ifndef UNITY_UNROLL # define UNITY_UNROLL #endif #ifndef UNITY_UNROLLX # define UNITY_UNROLLX(_x) #endif #ifndef UNITY_LOOP # define UNITY_LOOP #endif #if _NOMINDIELETRIC // for Standard #ifdef unity_ColorSpaceDielectricSpec #undef unity_ColorSpaceDielectricSpec #endif #define unity_ColorSpaceDielectricSpec half4(0,0,0,1) #endif #include "Lighting.cginc" #include "UnityPBSLighting.cginc" #include "UnityMetaPass.cginc" #if _MICROTERRAIN && !_TERRAINBLENDABLESHADER #define UNITY_ASSUME_UNIFORM_SCALING #define UNITY_DONT_INSTANCE_OBJECT_MATRICES #define UNITY_INSTANCED_LOD_FADE #else #define UNITY_INSTANCED_LOD_FADE #define UNITY_INSTANCED_SH #define UNITY_INSTANCED_LIGHTMAPSTS #endif // data across stages, stripped like the above. struct VertexToPixel { UNITY_POSITION(pos); 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; #ifdef EDITOR_VISUALIZATION float2 vizUV : TEXCOORD8; float4 lightCoord : TEXCOORD9; #endif // float4 extraV2F0 : TEXCOORD10; // float4 extraV2F1 : TEXCOORD11; // float4 extraV2F2 : TEXCOORD12; // float4 extraV2F3 : TEXCOORD13; // float4 extraV2F4 : TEXCOORD14; // float4 extraV2F5 : TEXCOORD15; // float4 extraV2F6 : TEXCOORD16; // float4 extraV2F7 : TEXCOORD17; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; // 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 // unity_WorldToObject in HDRP, since it already does macro-fu there #if _STANDARD float3 TransformWorldToObject(float3 p) { return mul(unity_WorldToObject, float4(p, 1)); }; float3 TransformObjectToWorld(float3 p) { return mul(unity_ObjectToWorld, float4(p, 1)); }; float4 TransformWorldToObject(float4 p) { return mul(unity_WorldToObject, p); }; float4 TransformObjectToWorld(float4 p) { return mul(unity_ObjectToWorld, p); }; float4x4 GetWorldToObjectMatrix() { return unity_WorldToObject; } float4x4 GetObjectToWorldMatrix() { return unity_ObjectToWorld; } #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 } #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 #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); 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 fixed4 w0; fixed4 w1; fixed4 w2; fixed4 w3; fixed4 w4; fixed4 w5; fixed4 w6; // megasplat data half4 layer0; half4 layer1; half3 baryWeights; half4 scatter0; half4 scatter1; // wetness, puddles, streams, lava from vertex or megasplat fixed4 fx; // snow min, snow max fixed4 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(unity_WorldToObject, float4(n, 1)).xyz; surfTangent = t;//mul(unity_WorldToObject, 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, fixed4 w0, fixed4 w1, fixed4 w2, fixed4 w3, fixed4 w4, fixed4 w5, fixed4 w6, fixed4 w7, float3 worldPos, DecalOutput decalOutput) { config = (Config)0; half4 indexes = 0; config.uv = uv; #if _WORLDUV uv = worldPos.xz * float2(-1,1); #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 fixed 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) { fixed 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; } } #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 fixed2 UnpackNormal2(fixed4 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; } // Stochastic shared code // Compute local triangle barycentric coordinates and vertex IDs void TriangleGrid(float2 uv, float scale, out float w1, out float w2, out float w3, out int2 vertex1, out int2 vertex2, out int2 vertex3) { // Scaling of the input uv *= 3.464 * scale; // 2 * sqrt(3) // Skew input space into simplex triangle grid const float2x2 gridToSkewedGrid = float2x2(1.0, 0.0, -0.57735027, 1.15470054); float2 skewedCoord = mul(gridToSkewedGrid, uv); // Compute local triangle vertex IDs and local barycentric coordinates int2 baseId = int2(floor(skewedCoord)); float3 temp = float3(frac(skewedCoord), 0); temp.z = 1.0 - temp.x - temp.y; if (temp.z > 0.0) { w1 = temp.z; w2 = temp.y; w3 = temp.x; vertex1 = baseId; vertex2 = baseId + int2(0, 1); vertex3 = baseId + int2(1, 0); } else { w1 = -temp.z; w2 = 1.0 - temp.y; w3 = 1.0 - temp.x; vertex1 = baseId + int2(1, 1); vertex2 = baseId + int2(1, 0); vertex3 = baseId + int2(0, 1); } } // Fast random hash function float2 SimpleHash2(float2 p) { return frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), p)) * 43758.5453); } half3 BaryWeightBlend(half3 iWeights, half tex0, half tex1, half tex2, half contrast) { // compute weight with height map const half epsilon = 1.0f / 1024.0f; half3 weights = half3(iWeights.x * (tex0 + epsilon), iWeights.y * (tex1 + epsilon), iWeights.z * (tex2 + epsilon)); // Contrast weights half maxWeight = max(weights.x, max(weights.y, weights.z)); half transition = contrast * maxWeight; half threshold = maxWeight - transition; half scale = 1.0f / transition; weights = saturate((weights - threshold) * scale); // Normalize weights. half weightScale = 1.0f / (weights.x + weights.y + weights.z); weights *= weightScale; return weights; } void PrepareStochasticUVs(float scale, float3 uv, out float3 uv1, out float3 uv2, out float3 uv3, out half3 weights) { // Get triangle info float w1, w2, w3; int2 vertex1, vertex2, vertex3; TriangleGrid(uv.xy, scale, w1, w2, w3, vertex1, vertex2, vertex3); // Assign random offset to each triangle vertex uv1 = uv; uv2 = uv; uv3 = uv; uv1.xy += SimpleHash2(vertex1); uv2.xy += SimpleHash2(vertex2); uv3.xy += SimpleHash2(vertex3); weights = half3(w1, w2, w3); } void PrepareStochasticUVs(float scale, float2 uv, out float2 uv1, out float2 uv2, out float2 uv3, out half3 weights) { // Get triangle info float w1, w2, w3; int2 vertex1, vertex2, vertex3; TriangleGrid(uv, scale, w1, w2, w3, vertex1, vertex2, vertex3); // Assign random offset to each triangle vertex uv1 = uv; uv2 = uv; uv3 = uv; uv1.xy += SimpleHash2(vertex1); uv2.xy += SimpleHash2(vertex2); uv3.xy += SimpleHash2(vertex3); weights = half3(w1, w2, w3); } 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 _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 fixed4 w0, inout fixed4 w1, inout fixed4 w2, inout fixed4 w3, inout fixed4 w4, inout fixed4 w5, inout fixed4 w6, inout fixed4 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)unity_WorldToObject, worldNormalVertex).xyz; i.worldPos = i.worldPos - mul(unity_ObjectToWorld, 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 fixed4 w0 = 0; fixed4 w1 = 0; fixed4 w2 = 0; fixed4 w3 = 0; fixed4 w4 = 0; fixed4 w5 = 0; fixed4 w6 = 0; fixed4 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.. l.Albedo *= saturate(SAMPLE_TEXTURE2D_LOD(_Diffuse, sampler_Diffuse, config.uv0, 11).r + 2); l.Albedo *= saturate(SAMPLE_TEXTURE2D_LOD(_NormalSAO, sampler_NormalSAO, config.uv0, 11).r + 2); #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 = 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 } // 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); #elif _PLANETVECTORS DoPlanetVectorVertex(v, d); #endif } void ModifyTessellatedVertex(inout VertexData v, inout ExtraV2F d) { #if _TESSDISTANCE v.vertex.xyz += OffsetVertex(v, d); #endif } float3 GetTessFactors () { #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 (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; 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(unity_WorldToObject, float4(GetCameraRelativePositionWS(i.worldPos), 1)); #else // d.localSpacePosition = mul(unity_WorldToObject, float4(i.worldPos, 1)); #endif // d.localSpaceNormal = normalize(mul((float3x3)unity_WorldToObject, i.worldNormal)); // d.localSpaceTangent = normalize(mul((float3x3)unity_WorldToObject, 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) { } // vertex shader VertexToPixel Vert (VertexData v) { UNITY_SETUP_INSTANCE_ID(v); VertexToPixel o; UNITY_INITIALIZE_OUTPUT(VertexToPixel,o); UNITY_TRANSFER_INSTANCE_ID(v,o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); #if !_TESSELLATION_ON ChainModifyVertex(v, o); #endif #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER float2 uv1 = v.texcoord1.xy; float2 uv2 = v.texcoord2.xy; #else float2 uv1 = v.texcoord0.xy; float2 uv2 = uv1; #endif o.pos = UnityMetaVertexPosition(v.vertex, uv1, uv2, unity_LightmapST, unity_DynamicLightmapST); #ifdef EDITOR_VISUALIZATION o.vizUV = 0; o.lightCoord = 0; if (unity_VisualizationMode == EDITORVIZ_TEXTURE) o.vizUV = UnityMetaVizUV(unity_EditorViz_UVIndex, v.texcoord0.xy, uv1, uv2, unity_EditorViz_Texture_ST); else if (unity_VisualizationMode == EDITORVIZ_SHOWLIGHTMASK) { o.vizUV = uv1 * unity_LightmapST.xy + unity_LightmapST.zw; o.lightCoord = mul(unity_EditorViz_WorldToLight, mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1))); } #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; // o.screenPos = ComputeScreenPos(o.pos); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.worldNormal = UnityObjectToWorldNormal(v.normal); #if !_MICROTERRAIN || _TERRAINBLENDABLESHADER o.worldTangent.xyz = UnityObjectToWorldDir(v.tangent.xyz); fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w; o.worldTangent.w = tangentSign; #endif // MS Only ApplyTerrainTangent(o); return o; } // fragment shader fixed4 Frag (VertexToPixel IN) : SV_Target { UNITY_SETUP_INSTANCE_ID(IN); #ifdef FOG_COMBINED_WITH_TSPACE UNITY_EXTRACT_FOG_FROM_TSPACE(IN); #elif defined (FOG_COMBINED_WITH_WORLD_POS) UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN); #else UNITY_EXTRACT_FOG(IN); #endif 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); UnityMetaInput metaIN; UNITY_INITIALIZE_OUTPUT(UnityMetaInput, metaIN); metaIN.Albedo = l.Albedo; metaIN.Emission = l.Emission; #if _USESPECULAR metaIN.SpecularColor = l.Specular; #endif #ifdef EDITOR_VISUALIZATION metaIN.VizUV = IN.vizUV; metaIN.LightCoord = IN.lightCoord; #endif return UnityMetaFragment(metaIN); } ENDCG } UsePass "Hidden/Nature/Terrain/Utilities/PICKING" UsePass "Hidden/Nature/Terrain/Utilities/SELECTION" } Dependency "BaseMapShader" = "Hidden/MicroSplat/Example_Base519282531" Fallback "Hidden/MicroSplat/Example_Base519282531" CustomEditor "MicroSplatShaderGUI" }