diff --git a/Assets/Scripts/Components/PictureFrameBuilder.cs b/Assets/Scripts/Components/PictureFrameBuilder.cs index de11e85..570a7c8 100644 --- a/Assets/Scripts/Components/PictureFrameBuilder.cs +++ b/Assets/Scripts/Components/PictureFrameBuilder.cs @@ -6,10 +6,10 @@ namespace Chameleon.Components // Procedural picture frame, generated live in the editor (and at runtime) from the inspector // parameters. Sizing is inside-out: the picture keeps the texture's aspect ratio EXACTLY and the // outer frame grows around it by padding + thickness (Size = longer outer side) - insetting a - // uniform border from an aspect-scaled outer rect would distort the picture. All shape parameters - // are relative fractions (thickness/padding of the picture's longer side, radius of the maximum - // possible rounding, depths of the frame depth), so a frame keeps its proportions when resized via - // Size - resize via Size, NOT via transform scale. Four submeshes with their own + // uniform border from an aspect-scaled outer rect would distort the picture. Thickness and padding + // are absolute meters (independent of Size - the picture shrinks as they grow); corner radius is a + // fraction of the maximum possible rounding and the depths are fractions of the frame depth. + // Resize via Size, NOT via transform scale. Four submeshes with their own // material slots: 0 picture, 1 inner cavity (recess walls + padding ring), 2 frame (front ring, outer // sides, edge rounding), 3 back plate. Front faces -Z like Unity's built-in Quad. An assigned // PictureTexture becomes the picture material's base map via a per-frame DontSave material @@ -46,8 +46,8 @@ namespace Chameleon.Components [SerializeField] Vector2 _aspect = Vector2.one; [Header("Frame")] - [Tooltip("Border width of the frame as a fraction of the picture's longer side.")] - [SerializeField, Range(0f, 1f)] float _frameThickness = 0.05f; + [Tooltip("Border width of the frame in meters (independent of Size).")] + [SerializeField, Range(0.001f, 0.5f)] float _frameThickness = 0.03f; [Tooltip("Depth of the frame, front face to back face.")] [SerializeField, Range(0.002f, 0.500f)] float _frameDepth = 0.05f; [Tooltip("Rounding of the outer frame edges relative to the maximum possible radius (thickness / half depth); 0 = sharp box, 1 = fully rounded.")] @@ -58,27 +58,27 @@ namespace Chameleon.Components [SerializeField, Range(-1f, 1f)] float _frameInnerDepth = -0.1f; [Header("Picture")] - [Tooltip("Gap between the frame's inner edge and the picture as a fraction of the picture's longer side.")] - [SerializeField, Range(0f, 1f)] float _padding = 0.01f; + [Tooltip("Gap between the frame's inner edge and the picture in meters (independent of Size).")] + [SerializeField, Range(0f, 0.5f)] float _padding = 0.005f; [Tooltip("Picture plane: -1 = fully inside (on the back plate), 0 = flush with the inner floor, 1 = a full frame depth in front of the frame.")] [SerializeField, Range(-1f, 1f)] float _pictureDepth = 0.1f; [Tooltip("Photo for this frame: applied as the picture material's base map via a per-frame material instance (the material asset itself is never modified) and drives the aspect ratio. Empty = the material's own base map is shown.")] [SerializeField] Texture _pictureTexture; [Tooltip("Optional normal map for the picture: applied as the picture material's bump map via the same per-frame material instance.")] [SerializeField] Texture _pictureNormalMap; - [Tooltip("Intensity of the picture normal map (URP _BumpScale). 1 = as authored.")] - [SerializeField, Range(0f, 3f)] float _normalMapStrength = 1f; + [Tooltip("Intensity of the picture normal map (URP _BumpScale). 0 = flat, negative inverts the relief.")] + [SerializeField, Range(-3f, 3f)] float _normalMapStrength = 0; [Tooltip("Optional height map for the picture: set as _HeightMap on the per-frame material instance (tessellation shader); on plain URP Lit it falls back to the parallax map.")] [SerializeField] Texture _pictureHeightMap; - [Tooltip("Scales the material's occlusion (_OcclusionStrength). 1 = as authored.")] - [SerializeField, Range(0f, 3f)] float _occlusionStrength = 1f; + [Tooltip("Scales the material's occlusion (_OcclusionStrength). 0 = no occlusion darkening.")] + [SerializeField, Range(0f, 3f)] float _occlusionStrength = 0; [Tooltip("Contrast of the occlusion map around mid-gray (_OcclusionContrast). 0 = unchanged, -1 = flat, 3 = strongly exaggerated.")] [SerializeField, Range(-1f, 3f)] float _occlusionContrast; [Tooltip("Brightness multiplier on the picture base map (_Brightness) to compensate the darkening from occlusion. 1 = unchanged.")] - [SerializeField, Range(0f, 3f)] float _brightness = 1f; - [Tooltip("Scales the material's tessellation displacement (_TessellationStrength). 1 = as authored.")] - [SerializeField, Range(0f, 10f)] float _tessellationStrength = 1f; - [Tooltip("Triangle grid resolution of the picture plane (cells along its longer side); raise it so displacement/tessellation has vertices to work with. 1 = plain quad.")] + [SerializeField, Range(0f, 3f)] float _brightness = 1; + [Tooltip("Scales the material's tessellation displacement (_TessellationStrength). 0 = flat (GPU tessellation is skipped entirely), negative inverts the relief.")] + [SerializeField, Range(-10f, 10f)] float _tessellationStrength = 0; + [Tooltip("Triangle grid resolution of the picture plane mesh (cells along its longer side). 1 = plain quad; extra triangles visible in wireframe beyond this come from the shader's GPU Tessellation Factor, not this mesh.")] [SerializeField, Range(1, 10)] int _pictureSubdivisions = 1; [Header("Picture Crop")] @@ -162,13 +162,13 @@ namespace Chameleon.Components ResolveAspect(); #endif // Inside-out sizing: the picture keeps the texture aspect exactly; thickness and padding - // are fractions of the picture's longer side, and Size stays the longer OUTER side: - // size = pLong + 2*(t + pad) = pLong * (1 + thickness + padding). No clamp dead zones - - // every 0..1 combination yields valid geometry. + // are absolute meters (they do NOT scale with Size), and Size stays the longer OUTER + // side: pLong = size - 2*(t + pad), clamped so the picture never degenerates (the outer + // side then exceeds Size instead). float d = Mathf.Max(_frameDepth, 0.002f); - float pLong = Mathf.Max(_size, 0.01f) / (1f + _frameThickness + _padding); - float t = Mathf.Max(_frameThickness * pLong * 0.5f, 0.001f); - float pad = _padding * pLong * 0.5f; + float t = Mathf.Max(_frameThickness, 0.001f); + float pad = Mathf.Max(_padding, 0f); + float pLong = Mathf.Max(Mathf.Max(_size, 0.01f) - 2f * (t + pad), 0.01f); float pw = Mathf.Max(pLong * _aspect.x, 0.001f); float ph = Mathf.Max(pLong * _aspect.y, 0.001f); float ow = pw + 2f * pad; diff --git a/Assets/Shaders/PictureTessellationLit.shader b/Assets/Shaders/PictureTessellationLit.shader index 2d17106..258b671 100644 --- a/Assets/Shaders/PictureTessellationLit.shader +++ b/Assets/Shaders/PictureTessellationLit.shader @@ -19,10 +19,10 @@ Shader "Chameleon/PictureTessellationLit" _Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5 _Metallic("Metallic", Range(0.0, 1.0)) = 0.0 [Normal] _BumpMap("Normal Map", 2D) = "bump" {} - _BumpScale("Normal Strength", Range(0.0, 3.0)) = 1.0 + _BumpScale("Normal Strength", Range(-3.0, 3.0)) = 1.0 _HeightMap("Height Map", 2D) = "gray" {} _Displacement("Displacement (m)", Range(0.0, 0.2)) = 0.02 - _TessellationStrength("Tessellation Strength", Range(0.0, 3.0)) = 1.0 + _TessellationStrength("Tessellation Strength", Range(-10.0, 10.0)) = 1.0 _TessellationFactor("Tessellation Factor", Range(1.0, 32.0)) = 8.0 _OcclusionMap("Occlusion Map", 2D) = "white" {} _OcclusionStrength("Occlusion Strength", Range(0.0, 3.0)) = 1.0 @@ -66,6 +66,8 @@ Shader "Chameleon/PictureTessellationLit" float3 normalOS : NORMAL; float4 tangentOS : TANGENT; float2 uv : TEXCOORD0; + float2 staticLightmapUV : TEXCOORD1; + float2 dynamicLightmapUV : TEXCOORD2; }; // Hull/domain control point: everything stays in object space until the domain stage. @@ -75,6 +77,8 @@ Shader "Chameleon/PictureTessellationLit" float3 normalOS : NORMAL; float4 tangentOS : TANGENT; float2 uv : TEXCOORD0; + float2 staticLightmapUV : TEXCOORD1; + float2 dynamicLightmapUV : TEXCOORD2; }; struct TessFactors @@ -90,16 +94,20 @@ Shader "Chameleon/PictureTessellationLit" p.normalOS = input.normalOS; p.tangentOS = input.tangentOS; p.uv = input.uv; + p.staticLightmapUV = input.staticLightmapUV; + p.dynamicLightmapUV = input.dynamicLightmapUV; return p; } TessFactors PatchConstants(InputPatch patch) { TessFactors f; - f.edge[0] = _TessellationFactor; - f.edge[1] = _TessellationFactor; - f.edge[2] = _TessellationFactor; - f.inside = _TessellationFactor; + // No displacement -> no reason to subdivide (keeps the wireframe a plain grid). + half factor = (_TessellationStrength == 0.0h || _Displacement == 0.0h) ? 1.0h : _TessellationFactor; + f.edge[0] = factor; + f.edge[1] = factor; + f.edge[2] = factor; + f.inside = factor; return f; } @@ -124,6 +132,8 @@ Shader "Chameleon/PictureTessellationLit" v.normalOS = normalize(TESS_INTERPOLATE(normalOS)); v.tangentOS = TESS_INTERPOLATE(tangentOS); v.uv = TESS_INTERPOLATE(uv); + v.staticLightmapUV = TESS_INTERPOLATE(staticLightmapUV); + v.dynamicLightmapUV = TESS_INTERPOLATE(dynamicLightmapUV); float height = SAMPLE_TEXTURE2D_LOD(_HeightMap, sampler_HeightMap, TRANSFORM_TEX(v.uv, _BaseMap), 0).r; v.positionOS.xyz += v.normalOS * ((height - 0.5) * _Displacement * _TessellationStrength); return v; @@ -145,22 +155,39 @@ Shader "Chameleon/PictureTessellationLit" #pragma domain ForwardDomain #pragma fragment ForwardFragment - // Keyword set mirrored from PlayerCamoLit's working Deferred+ forward-only pass, - // minus lightmap/detail/emission features this shader does not have. + // Keyword set mirrored from URP Lit.shader's ForwardLit pass (Universal Pipeline + + // Unity defined keywords) so every lighting feature the standard Lit supports works + // here too: lightmaps, shadowmask/mixed lighting, APV probe volumes, decals (DBuffer), + // vertex additional lights, rendering-layer output, reflection probe rotation. #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS + #pragma multi_compile _ EVALUATE_SH_MIXED EVALUATE_SH_VERTEX #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING #pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION #pragma multi_compile_fragment _ _REFLECTION_PROBE_ATLAS #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH #pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION + #pragma multi_compile_fragment _ _SCREEN_SPACE_IRRADIANCE + #pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3 #pragma multi_compile_fragment _ _LIGHT_COOKIES #pragma multi_compile _ _LIGHT_LAYERS #pragma multi_compile _ _CLUSTER_LIGHT_LOOP - #pragma multi_compile_fog + #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl" + + #pragma multi_compile _ LIGHTMAP_SHADOW_MIXING + #pragma multi_compile _ SHADOWS_SHADOWMASK + #pragma multi_compile _ DIRLIGHTMAP_COMBINED + #pragma multi_compile _ LIGHTMAP_ON + #pragma multi_compile_fragment _ LIGHTMAP_BICUBIC_SAMPLING + #pragma multi_compile_fragment _ REFLECTION_PROBE_ROTATION + #pragma multi_compile _ DYNAMICLIGHTMAP_ON + #pragma multi_compile _ USE_LEGACY_LIGHTMAPS + #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Fog.hlsl" + #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ProbeVolumeVariants.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" + #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl" TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); TEXTURE2D(_BumpMap); SAMPLER(sampler_BumpMap); @@ -173,7 +200,18 @@ Shader "Chameleon/PictureTessellationLit" float3 positionWS : TEXCOORD1; float3 normalWS : TEXCOORD2; float4 tangentWS : TEXCOORD3; // w = bitangent sign - float fogCoord : TEXCOORD4; + #ifdef _ADDITIONAL_LIGHTS_VERTEX + half4 fogFactorAndVertexLight : TEXCOORD4; // x: fog, yzw: vertex light + #else + half fogFactor : TEXCOORD4; + #endif + DECLARE_LIGHTMAP_OR_SH(staticLightmapUV, vertexSH, 5); + #ifdef DYNAMICLIGHTMAP_ON + float2 dynamicLightmapUV : TEXCOORD6; + #endif + #ifdef USE_APV_PROBE_OCCLUSION + float4 probeOcclusion : TEXCOORD7; + #endif }; [domain("tri")] @@ -183,17 +221,38 @@ Shader "Chameleon/PictureTessellationLit" VertexPositionInputs pos = GetVertexPositionInputs(v.positionOS.xyz); VertexNormalInputs nrm = GetVertexNormalInputs(v.normalOS, v.tangentOS); - Varyings o; + Varyings o = (Varyings)0; o.positionCS = pos.positionCS; o.positionWS = pos.positionWS; o.normalWS = nrm.normalWS; o.tangentWS = float4(nrm.tangentWS, v.tangentOS.w * GetOddNegativeScale()); o.uv = TRANSFORM_TEX(v.uv, _BaseMap); - o.fogCoord = ComputeFogFactor(pos.positionCS.z); + + half fogFactor = 0; + #if !defined(_FOG_FRAGMENT) + fogFactor = ComputeFogFactor(pos.positionCS.z); + #endif + #ifdef _ADDITIONAL_LIGHTS_VERTEX + o.fogFactorAndVertexLight = half4(fogFactor, VertexLighting(pos.positionWS, nrm.normalWS)); + #else + o.fogFactor = fogFactor; + #endif + + OUTPUT_LIGHTMAP_UV(v.staticLightmapUV, unity_LightmapST, o.staticLightmapUV); + #ifdef DYNAMICLIGHTMAP_ON + o.dynamicLightmapUV = v.dynamicLightmapUV * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; + #endif + OUTPUT_SH4(pos.positionWS, o.normalWS, GetWorldSpaceNormalizeViewDir(pos.positionWS), o.vertexSH, o.probeOcclusion); return o; } - half4 ForwardFragment(Varyings input) : SV_Target + void ForwardFragment( + Varyings input + , out half4 outColor : SV_Target0 + #ifdef _WRITE_RENDERING_LAYERS + , out uint outRenderingLayers : SV_Target1 + #endif + ) { half4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor; // Height-weighted brightness: raised areas get the full multiplier, deep areas stay @@ -220,9 +279,32 @@ Shader "Chameleon/PictureTessellationLit" #else inputData.shadowCoord = TransformWorldToShadowCoord(input.positionWS); #endif - inputData.fogCoord = input.fogCoord; - inputData.bakedGI = SampleSH(inputData.normalWS); - inputData.shadowMask = half4(1, 1, 1, 1); + #ifdef _ADDITIONAL_LIGHTS_VERTEX + inputData.fogCoord = InitializeInputDataFog(float4(input.positionWS, 1.0), input.fogFactorAndVertexLight.x); + inputData.vertexLighting = input.fogFactorAndVertexLight.yzw; + #else + inputData.fogCoord = InitializeInputDataFog(float4(input.positionWS, 1.0), input.fogFactor); + #endif + + // Baked GI exactly like LitForwardPass.InitializeBakedGIData: lightmaps (static/ + // dynamic), APV probe volumes, screen-space irradiance, shadowmask - else SH. + #if defined(_SCREEN_SPACE_IRRADIANCE) + inputData.bakedGI = SAMPLE_GI(_ScreenSpaceIrradiance, input.positionCS.xy); + #elif defined(DYNAMICLIGHTMAP_ON) + inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.dynamicLightmapUV, input.vertexSH, inputData.normalWS); + inputData.shadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV); + #elif !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2)) + inputData.bakedGI = SAMPLE_GI(input.vertexSH, + GetAbsolutePositionWS(inputData.positionWS), + inputData.normalWS, + inputData.viewDirectionWS, + input.positionCS.xy, + input.probeOcclusion, + inputData.shadowMask); + #else + inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.vertexSH, inputData.normalWS); + inputData.shadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV); + #endif SurfaceData surfaceData = (SurfaceData)0; surfaceData.albedo = albedo.rgb; @@ -233,9 +315,16 @@ Shader "Chameleon/PictureTessellationLit" // Strength > 1 keeps darkening past the authored map (saturate stops negatives). surfaceData.occlusion = saturate(LerpWhiteTo(occ, _OcclusionStrength)); + #if defined(_DBUFFER) + ApplyDecalToSurfaceData(input.positionCS, surfaceData, inputData); + #endif + half4 color = UniversalFragmentPBR(inputData, surfaceData); - color.rgb = MixFog(color.rgb, input.fogCoord); - return color; + color.rgb = MixFog(color.rgb, inputData.fogCoord); + outColor = color; + #ifdef _WRITE_RENDERING_LAYERS + outRenderingLayers = EncodeMeshRenderingLayer(); + #endif } ENDHLSL } @@ -353,6 +442,9 @@ Shader "Chameleon/PictureTessellationLit" #pragma domain DepthNormalsDomain #pragma fragment DepthNormalsFragment + // Same as URP Lit's DepthNormals pass: decals need the rendering-layers target. + #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl" + struct DepthNormalsVaryings { float4 positionCS : SV_POSITION; @@ -369,9 +461,183 @@ Shader "Chameleon/PictureTessellationLit" return o; } - half4 DepthNormalsFragment(DepthNormalsVaryings input) : SV_Target + void DepthNormalsFragment( + DepthNormalsVaryings input + , out half4 outNormalWS : SV_Target0 + #ifdef _WRITE_RENDERING_LAYERS + , out uint outRenderingLayers : SV_Target1 + #endif + ) { - return half4(NormalizeNormalPerPixel(input.normalWS), 0); + outNormalWS = half4(NormalizeNormalPerPixel(input.normalWS), 0); + #ifdef _WRITE_RENDERING_LAYERS + outRenderingLayers = EncodeMeshRenderingLayer(); + #endif + } + ENDHLSL + } + + Pass + { + // The project renders forward(+); the ONLY consumer of this pass is HTrace WSGI, which + // renders its thin GBuffer through the "UniversalGBuffer" tag and composites its traced + // irradiance from GBuffer0.rgb (albedo) / GBuffer1.a (occlusion) AFTER post-processing. + // Without it these pixels keep stale GBuffer data and get wrong injected GI (the cyan + // sky tint) - same failure class as the HeroCutout/PlayerCamo GBuffer fixes. Albedo is + // graded exactly like the forward pass (brightness/occlusion/contrast) so the injected + // indirect light matches the visible picture. + Name "GBuffer" + Tags { "LightMode" = "UniversalGBuffer" } + + ZWrite On + ZTest LEqual + Cull Back + + HLSLPROGRAM + #pragma target 4.6 + #pragma exclude_renderers gles3 glcore + + #pragma vertex TessVertex + #pragma hull TessHull + #pragma domain GBufferDomain + #pragma fragment GBufferFragment + + #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN + #pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING + #pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION + #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH + #pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3 + #pragma multi_compile_fragment _ _RENDER_PASS_ENABLED + #pragma multi_compile _ _CLUSTER_LIGHT_LOOP + #pragma multi_compile _ EVALUATE_SH_MIXED EVALUATE_SH_VERTEX + #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl" + + #pragma multi_compile _ LIGHTMAP_SHADOW_MIXING + #pragma multi_compile _ SHADOWS_SHADOWMASK + #pragma multi_compile _ DIRLIGHTMAP_COMBINED + #pragma multi_compile _ LIGHTMAP_ON + #pragma multi_compile_fragment _ LIGHTMAP_BICUBIC_SAMPLING + #pragma multi_compile_fragment _ REFLECTION_PROBE_ROTATION + #pragma multi_compile _ DYNAMICLIGHTMAP_ON + #pragma multi_compile _ USE_LEGACY_LIGHTMAPS + #pragma multi_compile_fragment _ _GBUFFER_NORMALS_OCT + #pragma multi_compile_fragment _ _SCREEN_SPACE_IRRADIANCE + #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ProbeVolumeVariants.hlsl" + + #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" + #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/GBufferOutput.hlsl" + #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl" + + TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); + TEXTURE2D(_BumpMap); SAMPLER(sampler_BumpMap); + TEXTURE2D(_OcclusionMap); SAMPLER(sampler_OcclusionMap); + + struct GBufferVaryings + { + float4 positionCS : SV_POSITION; + float2 uv : TEXCOORD0; + float3 positionWS : TEXCOORD1; + float3 normalWS : TEXCOORD2; + float4 tangentWS : TEXCOORD3; // w = bitangent sign + DECLARE_LIGHTMAP_OR_SH(staticLightmapUV, vertexSH, 4); + #ifdef DYNAMICLIGHTMAP_ON + float2 dynamicLightmapUV : TEXCOORD5; + #endif + #ifdef USE_APV_PROBE_OCCLUSION + float4 probeOcclusion : TEXCOORD6; + #endif + }; + + [domain("tri")] + GBufferVaryings GBufferDomain(TessFactors factors, OutputPatch patch, float3 bary : SV_DomainLocation) + { + Attributes v = TessInterpolate(patch, bary); + VertexPositionInputs pos = GetVertexPositionInputs(v.positionOS.xyz); + VertexNormalInputs nrm = GetVertexNormalInputs(v.normalOS, v.tangentOS); + + GBufferVaryings o = (GBufferVaryings)0; + o.positionCS = pos.positionCS; + o.positionWS = pos.positionWS; + o.normalWS = nrm.normalWS; + o.tangentWS = float4(nrm.tangentWS, v.tangentOS.w * GetOddNegativeScale()); + o.uv = TRANSFORM_TEX(v.uv, _BaseMap); + + OUTPUT_LIGHTMAP_UV(v.staticLightmapUV, unity_LightmapST, o.staticLightmapUV); + #ifdef DYNAMICLIGHTMAP_ON + o.dynamicLightmapUV = v.dynamicLightmapUV * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw; + #endif + OUTPUT_SH4(pos.positionWS, o.normalWS, GetWorldSpaceNormalizeViewDir(pos.positionWS), o.vertexSH, o.probeOcclusion); + return o; + } + + GBufferFragOutput GBufferFragment(GBufferVaryings input) + { + // Surface build mirrors ForwardFragment 1:1 (graded albedo, contrast'd occlusion). + half4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor; + half heightBr = SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, input.uv).r; + albedo.rgb *= lerp(1.0h, _Brightness, heightBr); + half3 normalTS = UnpackNormalScale(SAMPLE_TEXTURE2D(_BumpMap, sampler_BumpMap, input.uv), _BumpScale); + half occ = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, input.uv).g; + occ = saturate((occ - 0.5h) * (1.0h + _OcclusionContrast) + 0.5h); + half occlusion = saturate(LerpWhiteTo(occ, _OcclusionStrength)); + + float sgn = input.tangentWS.w; + float3 bitangentWS = sgn * cross(input.normalWS, input.tangentWS.xyz); + half3x3 tbn = half3x3(input.tangentWS.xyz, bitangentWS, input.normalWS); + + InputData inputData = (InputData)0; + inputData.positionWS = input.positionWS; + inputData.positionCS = input.positionCS; + inputData.normalWS = NormalizeNormalPerPixel(TransformTangentToWorld(normalTS, tbn)); + inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(input.positionWS); + inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS); + #if defined(_MAIN_LIGHT_SHADOWS_SCREEN) + inputData.shadowCoord = float4(inputData.normalizedScreenSpaceUV, 0, 1); + #else + inputData.shadowCoord = TransformWorldToShadowCoord(input.positionWS); + #endif + + #if defined(_SCREEN_SPACE_IRRADIANCE) + inputData.bakedGI = SAMPLE_GI(_ScreenSpaceIrradiance, input.positionCS.xy); + #elif defined(DYNAMICLIGHTMAP_ON) + inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.dynamicLightmapUV, input.vertexSH, inputData.normalWS); + inputData.shadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV); + #elif !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2)) + inputData.bakedGI = SAMPLE_GI(input.vertexSH, + GetAbsolutePositionWS(inputData.positionWS), + inputData.normalWS, + inputData.viewDirectionWS, + input.positionCS.xy, + input.probeOcclusion, + inputData.shadowMask); + #else + inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.vertexSH, inputData.normalWS); + inputData.shadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV); + #endif + + SurfaceData surfaceData = (SurfaceData)0; + surfaceData.albedo = saturate(albedo.rgb); // GBuffer0 is UNORM; HTrace multiplies GI by it + surfaceData.alpha = 1; + surfaceData.metallic = _Metallic; + surfaceData.smoothness = _Smoothness; + surfaceData.normalTS = normalTS; + surfaceData.occlusion = occlusion; + + #if defined(_DBUFFER) + ApplyDecalToSurfaceData(input.positionCS, surfaceData, inputData); + #endif + + BRDFData brdfData; + InitializeBRDFData(surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.alpha, brdfData); + + Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, inputData.shadowMask); + MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, inputData.shadowMask); + + half3 color = GlobalIllumination(brdfData, (BRDFData)0, 0, + inputData.bakedGI, surfaceData.occlusion, inputData.positionWS, + inputData.normalWS, inputData.viewDirectionWS, inputData.normalizedScreenSpaceUV); + + return PackGBuffersBRDFData(brdfData, inputData, surfaceData.smoothness, color, surfaceData.occlusion); } ENDHLSL }