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)
94 lines
3.7 KiB
HLSL
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;
|
|
}
|