#ifndef DETAILS_HLSL #define DETAILS_HLSL #include "Assets/_Content/Shaders/Include/Utilities.hlsl" /* DETAILS - MEDIUM LOD VERSION Goal: Keep the large performance gain from the optimized triplanar version, while restoring the old distance-based "detail upscale" behavior with smoother transitions. Main changes compared to the original version: - Replaced the 7-direction projection system with standard 3-axis triplanar mapping. - Removed the expensive 2x2 texture bombing by default. - Restored manual distance-based detail scaling. - Reduced the LOD ratio from 4.0 to 2.0 to make transitions less visible. - Added stable spatial jitter to avoid the whole screen switching LOD at the same distance. - Kept the public function signature SampleProjectedDetailLOD(...) so existing Shader Graph nodes do not break. Original cost per detail layer: 7 projections * 2 LOD levels * 4 bombing samples = 56 texture samples. This version: 3 projections * 2 LOD levels = 6 texture samples. If EnkiBillal samples both DetailTexA and DetailTexB: 6 * 2 = 12 texture samples. Still much cheaper than the original 112 samples. */ #define DETAILS_USE_LIGHT_BOMBING 0 /* Distance-based detail LOD. This restores the old idea: - close camera -> smaller, sharper details; - far camera -> larger, more readable details. Difference from the original: lodRatio is reduced from 4.0 to 2.0. This makes each transition less visible. */ void GetDetailLOD ( float Depth, float DetailSizeA, float LODJitter, out float DetailSizeAA, out float DetailSizeAB, out float LODBlend ) { const float lodRatio = 2.0; const float baseScale = 1.0; float dist = max(Depth, 1e-5); float lod = log(dist) / log(lodRatio) + LODJitter; float lodFloor = floor(lod); float lodFrac = lod - lodFloor; /* Quintic smootherstep. This is smoother than classic smoothstep and helps hide the LOD blend. */ float t = saturate(lodFrac); LODBlend = t * t * t * (t * (t * 6.0 - 15.0) + 10.0); DetailSizeAA = DetailSizeA * baseScale * pow(lodRatio, lodFloor); DetailSizeAB = DetailSizeA * baseScale * pow(lodRatio, lodFloor + 1.0); } /* Default fast texture sample. Uses normal GPU mipmapping. */ float4 SampleDetailTextureFast ( UnityTexture2D Tex, float2 UV ) { return Tex.tex.Sample(Tex.samplerstate, UV); } /* Optional lightweight texture bombing. Disabled by default. Compared to the original SampleBombedTexture: - original: 4 texture samples + 4 rotations + 4 weights; - this version: 1 texture sample with stable per-cell offset and rotation. Warning: This can create visible cell transitions because neighboring cells are not blended. */ float4 SampleDetailTextureLightBombed ( UnityTexture2D Tex, float2 UV ) { float2 cell = floor(UV); float2 randomOffset = (Hash2D(cell) - 0.5) * 0.35; float randomAngle = Hash1From2D(cell) * TWO_PI; float2 sampleUV = UV + randomOffset; sampleUV = RotateUV ( sampleUV, cell + 0.5, randomAngle ); return Tex.tex.Sample(Tex.samplerstate, sampleUV); } /* Compatibility function. Existing code can still call SampleBombedTexture. In Medium mode, it routes to: - direct sampling by default; - optional one-sample light bombing if enabled. */ float4 SampleBombedTexture ( UnityTexture2D Tex, float2 UV ) { #if DETAILS_USE_LIGHT_BOMBING return SampleDetailTextureLightBombed(Tex, UV); #else return SampleDetailTextureFast(Tex, UV); #endif } /* Triplanar weights. ProjectionBlend keeps the same artistic role: - low value -> softer blend between axes; - high value -> sharper projection selection. The original shader used up to 32. This version uses 16 by default to reduce overly sharp transitions. */ float3 GetTriplanarWeights ( float3 NormalOS, float ProjectionBlend ) { float3 n = abs(normalize(NormalOS)); float sharpness = lerp(2.0, 16.0, saturate(ProjectionBlend)); float3 w = pow(saturate(n), sharpness); float sumW = max(w.x + w.y + w.z, 1e-5); return w / sumW; } /* Compatibility structure. Kept because old code or Shader Graph nodes may still expect this type. The optimized SampleProjectedDetailLOD below does not use the old 7-projection path. */ struct ProjectionData { float2 uv[7]; float mask[7]; }; /* Compatibility fallback. Fills only the first three entries using standard triplanar projections. Entries 3..6 are disabled. */ ProjectionData GetProjections ( float3 PositionOS, float3 NormalOS, float ProjectionBlend ) { ProjectionData data; float3 w = GetTriplanarWeights(NormalOS, ProjectionBlend); /* Standard triplanar UVs: - X projection samples the YZ plane. - Y projection samples the XZ plane. - Z projection samples the XY plane. */ data.uv[0] = PositionOS.zy; data.uv[1] = PositionOS.xz; data.uv[2] = PositionOS.xy; data.mask[0] = w.x; data.mask[1] = w.y; data.mask[2] = w.z; [unroll] for (int i = 3; i < 7; i++) { data.uv[i] = 0.0; data.mask[i] = 0.0; } return data; } /* Samples one triplanar axis with two distance-based detail sizes. This restores the "detail upscale with distance" effect without returning to the original 7-projection and 2x2 bombing cost. */ float4 SampleDetailAxisLOD ( UnityTexture2D DetailTex, float2 BaseUV, float DetailSizeAA, float DetailSizeAB, float LODBlend ) { float4 detailA = SampleBombedTexture ( DetailTex, BaseUV / max(DetailSizeAA, 1e-5) ); float4 detailB = SampleBombedTexture ( DetailTex, BaseUV / max(DetailSizeAB, 1e-5) ); return lerp(detailA, detailB, LODBlend); } /* Optimized projected detail sampling with smoother distance LOD. Cost: 3 triplanar axes * 2 LOD samples = 6 texture samples. The old version also added per-projection jitter. This version uses one stable object-space jitter for the whole sample. That keeps the transition less synchronized without reintroducing too much cost. */ float4 SampleProjectedDetailLOD ( UnityTexture2D DetailTex, float3 PositionOS, float3 NormalOS, float ProjectionBlend, float DetailSizeA, float Depth ) { float detailSize = max(DetailSizeA, 1e-5); float3 weights = GetTriplanarWeights(NormalOS, ProjectionBlend); /* Stable spatial jitter. This prevents the whole screen from switching detail scale at exactly the same camera distance. Keep this value low. Too much jitter makes the transition patchy. */ float lodJitter = (Hash1From2D(floor(PositionOS.xz * 0.75)) - 0.5) * 0.35; float DetailSizeAA; float DetailSizeAB; float lodBlend; GetDetailLOD ( Depth, detailSize, lodJitter, DetailSizeAA, DetailSizeAB, lodBlend ); /* Triplanar base UVs in object space. */ float2 uvX = PositionOS.zy; float2 uvY = PositionOS.xz; float2 uvZ = PositionOS.xy; float4 sampleX = SampleDetailAxisLOD ( DetailTex, uvX, DetailSizeAA, DetailSizeAB, lodBlend ); float4 sampleY = SampleDetailAxisLOD ( DetailTex, uvY, DetailSizeAA, DetailSizeAB, lodBlend ); float4 sampleZ = SampleDetailAxisLOD ( DetailTex, uvZ, DetailSizeAA, DetailSizeAB, lodBlend ); return sampleX * weights.x + sampleY * weights.y + sampleZ * weights.z; } #endif