bvle-voxels/shaders/voxelTopingPS.hlsl
Samuel Bouchet 3d0c4f2f80 Phase 4.2+7: grass blade rework + soft RT shadows + toping BLAS optimization
Grass blades:
- Leaf-shaped profile (4 sections: base→belly→taper→tip) instead of spiky triangles
- Wider blades (base 0.055-0.095), more spacing between blades (±0.07 scatter)
- Natural green texture (50,140,35 → 80,180,55) instead of neon lime
- Reduced warm shift and removed artificial saturation boost
- Side faces at 60% brightness (dark green) instead of 38% (near-black)

Soft RT shadows:
- 2 jittered shadow rays per pixel with IGN+Cranley-Patterson temporal variation
- 2.3° cone around sun direction for soft penumbra
- Gradual shadow factor (0-100%) instead of binary on/off

Performance:
- Toping BLAS removed from TLAS (23M+ tris caused massive ray traversal slowdown)
- Toping BLAS position/index buffer construction skipped entirely
- Shadow rays reduced from 4 to 2 (temporal accumulation compensates)
2026-03-29 19:46:25 +02:00

94 lines
3.7 KiB
HLSL

// BVLE Voxels - Toping Pixel Shader (Phase 4.2)
// Vegetation: vertical gradient + up-biased lighting for stable grass look.
// Stone: classic Lambert + hemisphere ambient.
#include "voxelCommon.hlsli"
Texture2DArray<float4> materialTextures : register(t1);
SamplerState texSampler : register(s0);
struct PSInput {
float4 position : SV_POSITION;
float3 worldPos : WORLDPOS;
float3 normal : NORMAL;
float localHeight : LOCALHEIGHT;
nointerpolation uint materialID : MATERIALID;
};
struct PSOutput {
float4 color : SV_TARGET0;
float4 normal : SV_TARGET1;
};
[RootSignature(VOXEL_ROOTSIG)]
PSOutput main(PSInput input) {
PSOutput output;
float3 N = normalize(input.normal);
float tiling = textureTiling;
// Material texture index (materialID 1-5 → array layer 0-4)
uint texIdx = clamp(input.materialID - 1u, 0u, 4u);
// Triplanar sampling (same as voxel PS)
float3 blend = abs(N);
blend = blend / (blend.x + blend.y + blend.z + 0.001);
float4 xSample = materialTextures.Sample(texSampler, float3(input.worldPos.yz * tiling, (float)texIdx));
float4 ySample = materialTextures.Sample(texSampler, float3(input.worldPos.xz * tiling, (float)texIdx));
float4 zSample = materialTextures.Sample(texSampler, float3(input.worldPos.xy * tiling, (float)texIdx));
float3 texColor = (xSample.rgb * blend.x + ySample.rgb * blend.y + zSample.rgb * blend.z);
float3 L = normalize(-sunDirection.xyz);
// ── Material-dependent lighting ─────────────────────────────
float3 lit;
float hemiLerp = N.y * 0.5 + 0.5;
float3 V = normalize(cameraPosition.xyz - input.worldPos);
if (input.materialID == 3u) {
// Stone: classic Lambert + hemisphere ambient
float NdotL = max(dot(N, L), 0.0);
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, hemiLerp);
lit = texColor * (sunColor.rgb * NdotL + ambient);
} else {
// ── Vegetation shading ──────────────────────────────────
// Vertical gradient: dark green at base → lighter at tip
float h = input.localHeight; // 0=base, ~1=tip
float3 baseColor = texColor * 0.55; // darker at roots
float3 tipColor = texColor * 1.15; // brighter at tips
float3 grassColor = lerp(baseColor, tipColor, h);
// Up-biased normal for stable lighting on thin geometry
// Blend between actual normal and UP vector based on how "grassy" (non-stone)
float3 lightN = normalize(lerp(N, float3(0, 1, 0), 0.65));
float NdotL = dot(lightN, L);
// Soft wrap lighting (half-Lambert)
float halfLambert = NdotL * 0.5 + 0.5;
float wrap = halfLambert * halfLambert;
// Translucency: light passing through thin blades from behind
float backLight = saturate(dot(V, L));
float transAmount = (1.0 - saturate(NdotL)) * 0.5;
float translucency = backLight * transAmount;
// Hemisphere ambient, boosted for vegetation inter-reflection
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, lightN.y * 0.5 + 0.5) * 1.3;
// Combine
float3 diffuse = sunColor.rgb * (wrap * 0.85 + translucency * 0.35);
lit = grassColor * (diffuse + ambient);
}
// ── Rim light (reduced on vegetation) ──
float NdotV = saturate(dot(N, V));
float rimScale = (input.materialID == 3u) ? 1.0 : 0.2;
float rim = pow(1.0 - NdotV, rimParams.x) * rimParams.y * rimScale;
lit += rimColor.rgb * rim;
output.color = float4(lit, 1.0);
output.normal = float4(N, 0.0);
return output;
}