Files
Ipseity-Demo/Assets/_Content/Shaders/Include/Details.hlsl
T
2026-07-07 19:49:54 +02:00

343 lines
7.7 KiB
HLSL

#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