bvle-voxels/shaders/voxelCommon.hlsli
Samuel Bouchet 55c67686f2 Phase 7.1: stylized lighting — hemisphere ambient, colored shadows, rim light, tone mapping
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
2026-03-29 15:00:12 +02:00

129 lines
6.6 KiB
HLSL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// BVLE Voxels - Shared shader definitions
// Root signature, common structures, and constant buffers for voxel shaders.
#ifndef VOXEL_COMMON_HLSLI
#define VOXEL_COMMON_HLSLI
// Wicked Engine DX12 root signature (HLSL 6.6+ bindless model)
// b999: push constants (12 x uint32 = 48 bytes)
// b0-b2: root CBV descriptors
// t0-t15, u0-u15: SRV/UAV descriptor table
// s0-s7: dynamic samplers
// s100+: static samplers
#define VOXEL_ROOTSIG \
"RootFlags(CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED | SAMPLER_HEAP_DIRECTLY_INDEXED), " \
"RootConstants(num32BitConstants=12, b999), " \
"CBV(b0), " \
"CBV(b1), " \
"CBV(b2), " \
"DescriptorTable( " \
"CBV(b3, numDescriptors = 11, flags = DATA_STATIC_WHILE_SET_AT_EXECUTE)," \
"SRV(t0, numDescriptors = 16, flags = DESCRIPTORS_VOLATILE | DATA_STATIC_WHILE_SET_AT_EXECUTE)," \
"UAV(u0, numDescriptors = 16, flags = DESCRIPTORS_VOLATILE | DATA_STATIC_WHILE_SET_AT_EXECUTE)" \
")," \
"DescriptorTable( " \
"Sampler(s0, offset = 0, numDescriptors = 8, flags = DESCRIPTORS_VOLATILE)" \
")," \
"StaticSampler(s100, addressU = TEXTURE_ADDRESS_CLAMP, addressV = TEXTURE_ADDRESS_CLAMP, addressW = TEXTURE_ADDRESS_CLAMP, filter = FILTER_MIN_MAG_MIP_LINEAR)," \
"StaticSampler(s101, addressU = TEXTURE_ADDRESS_WRAP, addressV = TEXTURE_ADDRESS_WRAP, addressW = TEXTURE_ADDRESS_WRAP, filter = FILTER_MIN_MAG_MIP_LINEAR)," \
"StaticSampler(s102, addressU = TEXTURE_ADDRESS_MIRROR, addressV = TEXTURE_ADDRESS_MIRROR, addressW = TEXTURE_ADDRESS_MIRROR, filter = FILTER_MIN_MAG_MIP_LINEAR)," \
"StaticSampler(s103, addressU = TEXTURE_ADDRESS_CLAMP, addressV = TEXTURE_ADDRESS_CLAMP, addressW = TEXTURE_ADDRESS_CLAMP, filter = FILTER_MIN_MAG_MIP_POINT)," \
"StaticSampler(s104, addressU = TEXTURE_ADDRESS_WRAP, addressV = TEXTURE_ADDRESS_WRAP, addressW = TEXTURE_ADDRESS_WRAP, filter = FILTER_MIN_MAG_MIP_POINT)," \
"StaticSampler(s105, addressU = TEXTURE_ADDRESS_MIRROR, addressV = TEXTURE_ADDRESS_MIRROR, addressW = TEXTURE_ADDRESS_MIRROR, filter = FILTER_MIN_MAG_MIP_POINT)," \
"StaticSampler(s106, addressU = TEXTURE_ADDRESS_CLAMP, addressV = TEXTURE_ADDRESS_CLAMP, addressW = TEXTURE_ADDRESS_CLAMP, filter = FILTER_ANISOTROPIC, maxAnisotropy = 16)," \
"StaticSampler(s107, addressU = TEXTURE_ADDRESS_WRAP, addressV = TEXTURE_ADDRESS_WRAP, addressW = TEXTURE_ADDRESS_WRAP, filter = FILTER_ANISOTROPIC, maxAnisotropy = 16)," \
"StaticSampler(s108, addressU = TEXTURE_ADDRESS_MIRROR, addressV = TEXTURE_ADDRESS_MIRROR, addressW = TEXTURE_ADDRESS_MIRROR, filter = FILTER_ANISOTROPIC, maxAnisotropy = 16)," \
"StaticSampler(s109, addressU = TEXTURE_ADDRESS_CLAMP, addressV = TEXTURE_ADDRESS_CLAMP, addressW = TEXTURE_ADDRESS_CLAMP, filter = FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT, comparisonFunc = COMPARISON_GREATER_EQUAL),"
// ── Per-frame constant buffer (b0) ──────────────────────────────
cbuffer VoxelCB : register(b0) {
float4x4 viewProjection;
float4x4 inverseViewProjection; // for depth-to-world reconstruction (RT shadows)
float4x4 prevViewProjection; // previous frame's VP for temporal reprojection (RT AO)
float4 cameraPosition;
float4 sunDirection;
float4 sunColor;
float chunkSize;
float textureTiling;
float blendEnabled;
float debugBlend; // >0.5 = show blend zones as debug colors
// Frustum culling data (used by cull compute shader)
float4 frustumPlanes[6]; // ax+by+cz+d=0, xyz=normal, w=distance
uint chunkCount;
uint bleedMask; // bit N set = material N can bleed onto neighbors
uint resistBleedMask; // bit N set = material N resists bleed from neighbors
float windTime; // elapsed time for wind animation (seconds)
// ── Stylized lighting (Phase 7) ──
float4 skyAmbient; // hemisphere ambient: sky (top) color
float4 groundAmbient; // hemisphere ambient: ground (bottom) color
float4 shadowTint; // colored shadow tint (blue-violet)
float4 fogColor; // atmospheric fog color
float4 fogParams; // x=density, y=unused, z=unused, w=unused
float4 rimColor; // rim/fresnel light color
float4 rimParams; // x=exponent, y=intensity, z=unused, w=unused
float4 toneMapParams; // x=saturation boost, y=exposure, z=unused, w=unused
};
// ── Indirect draw args (must match C++ IndirectDrawArgs, 20 bytes) ──
// Wicked Engine's DrawInstancedIndirectCount command signature includes a push
// constant (1 × uint32 at b999[0]) BEFORE each D3D12_DRAW_ARGUMENTS.
// Total stride = 4 + 16 = 20 bytes per draw entry.
struct IndirectDrawArgsInstanced {
uint pushConstant; // written to b999[0] by ExecuteIndirect
uint vertexCountPerInstance;
uint instanceCount;
uint startVertexLocation;
uint startInstanceLocation;
};
// ── GPU chunk info (must match C++ GPUChunkInfo, 112 bytes) ─────
// NOTE: No arrays — scalar-only to guarantee C-style packing in StructuredBuffer.
struct GPUChunkInfo {
float4 worldPos; // xyz = chunk origin in world space, w = debug flag
uint quadOffset; // offset into mega quad buffer (in quads)
uint quadCount; // number of quads for this chunk
uint _pad0;
uint _pad1;
// Per-face data (6 faces: +X -X +Y -Y +Z -Z)
uint faceOff0, faceOff1, faceOff2, faceOff3, faceOff4, faceOff5;
uint faceCnt0, faceCnt1, faceCnt2, faceCnt3, faceCnt4, faceCnt5;
// Face neighbor chunk indices (0xFFFFFFFF = no neighbor)
uint neighbor0, neighbor1, neighbor2, neighbor3, neighbor4, neighbor5; // +X,-X,+Y,-Y,+Z,-Z
uint _pad2, _pad3;
};
// Helper functions to access scalar face fields by index
uint getFaceOffset(GPUChunkInfo info, uint f) {
switch (f) {
case 0: return info.faceOff0;
case 1: return info.faceOff1;
case 2: return info.faceOff2;
case 3: return info.faceOff3;
case 4: return info.faceOff4;
default: return info.faceOff5;
}
}
uint getFaceCount(GPUChunkInfo info, uint f) {
switch (f) {
case 0: return info.faceCnt0;
case 1: return info.faceCnt1;
case 2: return info.faceCnt2;
case 3: return info.faceCnt3;
case 4: return info.faceCnt4;
default: return info.faceCnt5;
}
}
uint getNeighborIdx(GPUChunkInfo info, uint f) {
switch (f) {
case 0: return info.neighbor0; // +X
case 1: return info.neighbor1; // -X
case 2: return info.neighbor2; // +Y
case 3: return info.neighbor3; // -Y
case 4: return info.neighbor4; // +Z
default: return info.neighbor5; // -Z
}
}
#endif // VOXEL_COMMON_HLSLI