bvle-voxels/shaders/voxelSmoothPS.hlsl
Samuel Bouchet aab38bb9b9 Phase 5.1: Naive Surface Nets smooth rendering
Implement CPU-side Naive Surface Nets for smooth voxel surfaces (SmoothStone,
Snow) coexisting with blocky voxels (Grass, Dirt, Stone, Sand).

Key features:
- SmoothMesher with binary SDF, centroid vertex placement, per-axis boundary
  clamping to align with blocky grid at smooth↔blocky transitions
- Cross-chunk connectivity: PAD=2 SDF grid, vertex range [-1, CHUNK_SIZE),
  canonical edge ownership (no duplicate triangles, no z-fighting)
- Face normals oriented by edge axis+sign (robust with binary SDF, unlike
  SDF gradient dot or centroid sampling approaches)
- Y-axis winding fix: sharing cells have different spatial arrangement,
  requiring opposite winding from X and Z axes
- GPU mesher treats smooth neighbors as solid (no blocky faces toward smooth)
- Material blending: primary (smooth-only) + secondary (all counts) per vertex
- Dedicated shaders: voxelSmoothVS (vertex pulling t6) + voxelSmoothPS
  (triplanar + lerp blending between two materials)
- Separate render pass with LoadOp::LOAD after voxels+topings
- New materials: SmoothStone (mat 6), blocky Stone (mat 3) and Dirt patches
  added to world generation for boundary testing
2026-03-27 13:03:55 +01:00

57 lines
2.1 KiB
HLSL

// BVLE Voxels - Smooth Surface Nets Pixel Shader (Phase 5.1)
// Triplanar texture sampling + material blending + same lighting as voxel PS.
#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 matPacked : MATERIALID;
};
// Sample triplanar texture for a given material index
float3 sampleTriplanar(float3 worldPos, float3 blend, float tiling, uint matIdx) {
uint texIdx = clamp(matIdx - 1u, 0u, 5u);
float4 xS = materialTextures.Sample(texSampler, float3(worldPos.yz * tiling, (float)texIdx));
float4 yS = materialTextures.Sample(texSampler, float3(worldPos.xz * tiling, (float)texIdx));
float4 zS = materialTextures.Sample(texSampler, float3(worldPos.xy * tiling, (float)texIdx));
return xS.rgb * blend.x + yS.rgb * blend.y + zS.rgb * blend.z;
}
[RootSignature(VOXEL_ROOTSIG)]
float4 main(PSInput input) : SV_TARGET0 {
float3 N = normalize(input.normal);
float tiling = textureTiling;
// Unpack materials: materialID(8) | secondaryMat(8) | blendWeight(8) | pad(8)
uint primaryMat = input.matPacked & 0xFF;
uint secondaryMat = (input.matPacked >> 8) & 0xFF;
float blendWeight = ((input.matPacked >> 16) & 0xFF) / 255.0;
// Triplanar blend weights
float3 blend = abs(N);
blend = blend / (blend.x + blend.y + blend.z + 0.001);
// Sample primary and secondary materials
float3 primaryColor = sampleTriplanar(input.worldPos, blend, tiling, primaryMat);
float3 texColor;
if (blendWeight > 0.01 && secondaryMat != primaryMat) {
float3 secondaryColor = sampleTriplanar(input.worldPos, blend, tiling, secondaryMat);
texColor = lerp(primaryColor, secondaryColor, blendWeight);
} else {
texColor = primaryColor;
}
// Lighting (same model as voxel PS)
float3 L = normalize(-sunDirection.xyz);
float NdotL = max(dot(N, L), 0.0);
float3 ambient = float3(0.15, 0.18, 0.25);
float3 lit = texColor * (sunColor.rgb * NdotL + ambient);
return float4(lit, 1.0);
}