343 lines
7.7 KiB
HLSL
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 |