// 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 materialTextures : register(t1); SamplerState texSampler : register(s0); struct PSInput { float4 position : SV_POSITION; float3 worldPos : WORLDPOS; float3 normal : NORMAL; nointerpolation uint materialID : MATERIALID; }; [RootSignature(VOXEL_ROOTSIG)] float4 main(PSInput input) : SV_TARGET0 { 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; if (input.materialID == 3u) { // Stone: classic Lambert + cool ambient (matches voxel PS) float NdotL = max(rawNdotL, 0.0); float3 ambient = float3(0.15, 0.18, 0.25); 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 // Translucency: thin blades let light through from behind // Stronger effect to reduce contrast when orbiting around grass float3 V = normalize(cameraPosition.xyz - input.worldPos); float backLight = saturate(dot(V, L)); float transAmount = (1.0 - saturate(rawNdotL)) * 0.8; float translucency = backLight * transAmount; // Same ambient as voxel faces for color consistency float3 ambient = float3(0.15, 0.18, 0.25); // Wrap + strong translucency to minimize light/dark contrast float3 diffuse = sunColor.rgb * (wrap * 0.85 + translucency * 0.6); lit = texColor * (diffuse + ambient); } return float4(lit, 1.0); }