Wonderbox-inspired lighting overhaul across all 3 pixel shaders: - Hemisphere ambient (sky blue above, warm brown below) replaces flat ambient - RT shadows lerp toward blue-violet tint instead of plain darkening (factor 0.55) - Rim light (fresnel) with warm golden color on silhouettes (30% on vegetation) - Soft exponential tone mapping + saturation boost in final post-process pass - CB parameters for all lighting values (skyAmbient, groundAmbient, shadowTint, etc.) - Fog color/density centralized from CB instead of hardcoded per-shader - Screenshot mode (CLI "screenshot"): fixed camera, AO convergence, auto-capture - AO noise stability: world-space hash using voxel center + tangent-axis frac position - AO distance-weighted falloff: continuous occlusion values instead of binary hit/miss
90 lines
3.7 KiB
HLSL
90 lines
3.7 KiB
HLSL
// BVLE Voxels - Toping Pixel Shader (Phase 4.2)
|
|
// Simplified version of voxelPS: triplanar texture sampling + basic lighting.
|
|
// No height-based blending (topings are small decorative meshes).
|
|
|
|
#include "voxelCommon.hlsli"
|
|
|
|
Texture2DArray<float4> materialTextures : register(t1);
|
|
SamplerState texSampler : register(s0);
|
|
|
|
struct PSInput {
|
|
float4 position : SV_POSITION;
|
|
float3 worldPos : WORLDPOS;
|
|
float3 normal : NORMAL;
|
|
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);
|
|
float rawNdotL = dot(N, L);
|
|
|
|
// ── Material-dependent lighting ─────────────────────────────
|
|
// Stone (materialID=3): standard diffuse like voxel faces
|
|
// Vegetation (grass etc.): stylized wrap lighting + translucency
|
|
// inspired by Airborn Trees (simonschreibt.de/gat/airborn-trees/)
|
|
|
|
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(rawNdotL, 0.0);
|
|
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, hemiLerp);
|
|
lit = texColor * (sunColor.rgb * NdotL + ambient);
|
|
} else {
|
|
// ── Vegetation: soft wrap lighting ──────────────────────
|
|
// Half-Lambert: wraps light around the surface, no hard terminator
|
|
float halfLambert = rawNdotL * 0.5 + 0.5;
|
|
float wrap = halfLambert * halfLambert; // squared for falloff shape
|
|
|
|
// Diffuse floor: even fully back-facing blades get some sun contribution
|
|
// Simulates light scattering through dense grass (cheap GI approximation)
|
|
wrap = max(wrap, 0.35);
|
|
|
|
// Translucency: thin blades let light through from behind
|
|
// Stronger effect to reduce contrast when orbiting around grass
|
|
float backLight = saturate(dot(V, L));
|
|
float transAmount = (1.0 - saturate(rawNdotL)) * 0.6;
|
|
float translucency = backLight * transAmount;
|
|
|
|
// Hemisphere ambient for vegetation, scaled up 1.5x for inter-reflection
|
|
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, hemiLerp) * 1.5;
|
|
|
|
// Wrap + translucency for soft, low-contrast vegetation shading
|
|
float3 diffuse = sunColor.rgb * (wrap * 0.88 + translucency * 0.55);
|
|
lit = texColor * (diffuse + ambient);
|
|
}
|
|
|
|
// ── Rim light (reduced on vegetation — thin geometry causes halos) ──
|
|
float NdotV = saturate(dot(N, V));
|
|
float rimScale = (input.materialID == 3u) ? 1.0 : 0.3; // stone full, grass 30%
|
|
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;
|
|
}
|