Add debug tools
This commit is contained in:
parent
4c50727cb6
commit
d5bf499375
4 changed files with 123 additions and 19 deletions
|
|
@ -106,7 +106,7 @@ float3 triplanarWeights(float3 normal, float sharpness) {
|
|||
// Triplanar sampling — RGB only (non-blended path)
|
||||
float3 sampleTriplanar(float3 worldPos, float3 normal, uint texIndex, float tiling) {
|
||||
float3 w = triplanarWeights(normal, 4.0);
|
||||
float3 colX = materialTextures.Sample(materialSampler, float3(worldPos.yz * tiling, (float)texIndex)).rgb;
|
||||
float3 colX = materialTextures.Sample(materialSampler, float3(worldPos.zy * tiling, (float)texIndex)).rgb;
|
||||
float3 colY = materialTextures.Sample(materialSampler, float3(worldPos.xz * tiling, (float)texIndex)).rgb;
|
||||
float3 colZ = materialTextures.Sample(materialSampler, float3(worldPos.xy * tiling, (float)texIndex)).rgb;
|
||||
return colX * w.x + colY * w.y + colZ * w.z;
|
||||
|
|
@ -115,7 +115,7 @@ float3 sampleTriplanar(float3 worldPos, float3 normal, uint texIndex, float tili
|
|||
// Triplanar sampling — RGBA (includes heightmap in alpha)
|
||||
float4 sampleTriplanarRGBA(float3 worldPos, float3 normal, uint texIndex, float tiling) {
|
||||
float3 w = triplanarWeights(normal, 4.0);
|
||||
float4 colX = materialTextures.Sample(materialSampler, float3(worldPos.yz * tiling, (float)texIndex));
|
||||
float4 colX = materialTextures.Sample(materialSampler, float3(worldPos.zy * tiling, (float)texIndex));
|
||||
float4 colY = materialTextures.Sample(materialSampler, float3(worldPos.xz * tiling, (float)texIndex));
|
||||
float4 colZ = materialTextures.Sample(materialSampler, float3(worldPos.xy * tiling, (float)texIndex));
|
||||
return colX * w.x + colY * w.y + colZ * w.z;
|
||||
|
|
@ -340,22 +340,27 @@ PSOutput main(PSInput input)
|
|||
}
|
||||
|
||||
// ── Normal map perturbation ──
|
||||
float3 perturbedN = sampleTriplanarNormal(input.worldPos, N, texIndex, tiling);
|
||||
// Blend between flat and perturbed normal (strength control)
|
||||
N = normalize(lerp(N, perturbedN, 0.7));
|
||||
float3 flatN = N; // preserve flat face normal for ambient + side-darkening
|
||||
float nmStrength = toneMapParams.z; // 0 = off (F9 toggle)
|
||||
if (nmStrength > 0.0) {
|
||||
float3 perturbedN = sampleTriplanarNormal(input.worldPos, N, texIndex, tiling);
|
||||
N = normalize(lerp(N, perturbedN, nmStrength));
|
||||
}
|
||||
|
||||
// ── Lighting ──
|
||||
// Use FLAT normal for hemisphere ambient + side-darkening (consistent per face)
|
||||
// Use PERTURBED normal for NdotL only (organic detail variation)
|
||||
float3 L = normalize(-sunDirection.xyz);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float hemiLerp = N.y * 0.5 + 0.5; // 0=down, 1=up
|
||||
float hemiLerp = flatN.y * 0.5 + 0.5; // flat: consistent per face orientation
|
||||
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, hemiLerp);
|
||||
float3 diffuse = sunColor.rgb * NdotL;
|
||||
|
||||
// Grass-specific shading (Wonderbox style)
|
||||
bool isGrass = (texIndex == 0); // material 1 = grass = texture layer 0
|
||||
if (isGrass) {
|
||||
// Vertical face darkening: grass sides are darker green (not black)
|
||||
float verticalDarken = saturate(abs(N.y)); // 1=top, 0=side
|
||||
// Vertical face darkening: use FLAT normal for consistency
|
||||
float verticalDarken = saturate(abs(flatN.y)); // 1=top, 0=side
|
||||
float sideFactor = lerp(0.60, 1.0, verticalDarken); // sides at 60% brightness
|
||||
albedo *= sideFactor;
|
||||
|
||||
|
|
@ -369,6 +374,39 @@ PSOutput main(PSInput input)
|
|||
ambient *= 1.15;
|
||||
}
|
||||
|
||||
// ── Debug lighting modes (F9 cycle) ──
|
||||
uint dbgLight = (uint)toneMapParams.w;
|
||||
if (dbgLight == 2) {
|
||||
// FLAT: uniform color per face, no texture, no blend, no normal map
|
||||
// Pure lighting with flat face normal. If two +X faces differ here, it's a VS/mesher bug.
|
||||
float flatNdotL = max(dot(flatN, normalize(-sunDirection.xyz)), 0.0);
|
||||
float flatHemi = flatN.y * 0.5 + 0.5;
|
||||
float3 flatAmb = lerp(groundAmbient.rgb, skyAmbient.rgb, flatHemi);
|
||||
float3 flatColor = float3(0.5, 0.5, 0.5) * (flatAmb + sunColor.rgb * flatNdotL);
|
||||
output.color = float4(flatColor, 1.0);
|
||||
output.normal = float4(flatN, 0.0);
|
||||
return output;
|
||||
}
|
||||
if (dbgLight == 3) {
|
||||
// ALBEDO only: texture + blend, no lighting
|
||||
output.color = float4(albedo, 1.0);
|
||||
output.normal = float4(flatN, 0.0);
|
||||
return output;
|
||||
}
|
||||
if (dbgLight == 4) {
|
||||
// NdotL only: grayscale NdotL with flat normal (no normal map)
|
||||
float flatNdotL = max(dot(flatN, normalize(-sunDirection.xyz)), 0.0);
|
||||
output.color = float4(flatNdotL, flatNdotL, flatNdotL, 1.0);
|
||||
output.normal = float4(flatN, 0.0);
|
||||
return output;
|
||||
}
|
||||
if (dbgLight == 5) {
|
||||
// NORMAL viz: geometric normal mapped to RGB (XYZ → [0,1])
|
||||
output.color = float4(flatN * 0.5 + 0.5, 1.0);
|
||||
output.normal = float4(flatN, 0.0);
|
||||
return output;
|
||||
}
|
||||
|
||||
float3 color = albedo * (ambient + diffuse);
|
||||
|
||||
// ── Rim light ──
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ float3 triplanarWeights(float3 n, float sharpness) {
|
|||
|
||||
float3 sampleTriplanar(float3 wp, float3 n, uint texIdx, float tiling) {
|
||||
float3 w = triplanarWeights(n, 4.0);
|
||||
float3 cx = materialTextures.Sample(texSampler, float3(wp.yz * tiling, (float)texIdx)).rgb;
|
||||
float3 cx = materialTextures.Sample(texSampler, float3(wp.zy * tiling, (float)texIdx)).rgb;
|
||||
float3 cy = materialTextures.Sample(texSampler, float3(wp.xz * tiling, (float)texIdx)).rgb;
|
||||
float3 cz = materialTextures.Sample(texSampler, float3(wp.xy * tiling, (float)texIdx)).rgb;
|
||||
return cx * w.x + cy * w.y + cz * w.z;
|
||||
|
|
@ -85,7 +85,7 @@ float3 sampleTriplanar(float3 wp, float3 n, uint texIdx, float tiling) {
|
|||
|
||||
float4 sampleTriplanarRGBA(float3 wp, float3 n, uint texIdx, float tiling) {
|
||||
float3 w = triplanarWeights(n, 4.0);
|
||||
float4 cx = materialTextures.Sample(texSampler, float3(wp.yz * tiling, (float)texIdx));
|
||||
float4 cx = materialTextures.Sample(texSampler, float3(wp.zy * tiling, (float)texIdx));
|
||||
float4 cy = materialTextures.Sample(texSampler, float3(wp.xz * tiling, (float)texIdx));
|
||||
float4 cz = materialTextures.Sample(texSampler, float3(wp.xy * tiling, (float)texIdx));
|
||||
return cx * w.x + cy * w.y + cz * w.z;
|
||||
|
|
@ -249,13 +249,49 @@ PSOutput main(PSInput input) {
|
|||
}
|
||||
|
||||
// ── Normal map perturbation ──
|
||||
float3 perturbedN = sampleTriplanarNormal(input.worldPos, geoN, selfTexIdx, tiling);
|
||||
N = normalize(lerp(N, perturbedN, 0.5)); // lighter strength on smooth surfaces
|
||||
float3 flatN = N; // preserve for ambient
|
||||
float nmStrength = toneMapParams.z;
|
||||
if (nmStrength > 0.0) {
|
||||
float3 perturbedN = sampleTriplanarNormal(input.worldPos, geoN, selfTexIdx, tiling);
|
||||
N = normalize(lerp(N, perturbedN, nmStrength * 0.7)); // lighter on smooth
|
||||
}
|
||||
|
||||
// Lighting
|
||||
// ── Debug lighting modes (F9 cycle) ──
|
||||
uint dbgLight = (uint)toneMapParams.w;
|
||||
if (dbgLight == 2) {
|
||||
// FLAT: uniform gray, no texture, no normal map — pure lighting with geometric normal
|
||||
float flatNdotL = max(dot(flatN, normalize(-sunDirection.xyz)), 0.0);
|
||||
float flatHemi = flatN.y * 0.5 + 0.5;
|
||||
float3 flatAmb = lerp(groundAmbient.rgb, skyAmbient.rgb, flatHemi);
|
||||
float3 flatColor = float3(0.5, 0.5, 0.5) * (flatAmb + sunColor.rgb * flatNdotL);
|
||||
output.color = float4(flatColor, 1.0);
|
||||
output.normal = float4(flatN, 0.0);
|
||||
return output;
|
||||
}
|
||||
if (dbgLight == 3) {
|
||||
// ALBEDO only: texture + blend, no lighting
|
||||
output.color = float4(albedo, 1.0);
|
||||
output.normal = float4(flatN, 0.0);
|
||||
return output;
|
||||
}
|
||||
if (dbgLight == 4) {
|
||||
// NdotL only: grayscale NdotL with geometric normal (no normal map)
|
||||
float flatNdotL = max(dot(flatN, normalize(-sunDirection.xyz)), 0.0);
|
||||
output.color = float4(flatNdotL, flatNdotL, flatNdotL, 1.0);
|
||||
output.normal = float4(flatN, 0.0);
|
||||
return output;
|
||||
}
|
||||
if (dbgLight == 5) {
|
||||
// NORMAL viz: geometric normal mapped to RGB (XYZ → [0,1])
|
||||
output.color = float4(flatN * 0.5 + 0.5, 1.0);
|
||||
output.normal = float4(flatN, 0.0);
|
||||
return output;
|
||||
}
|
||||
|
||||
// Lighting: flat normal for ambient (consistent), perturbed for NdotL (detail)
|
||||
float3 L = normalize(-sunDirection.xyz);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float hemiLerp = N.y * 0.5 + 0.5;
|
||||
float hemiLerp = flatN.y * 0.5 + 0.5;
|
||||
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, hemiLerp);
|
||||
float3 color = albedo * (sunColor.rgb * NdotL + ambient);
|
||||
|
||||
|
|
|
|||
|
|
@ -785,7 +785,7 @@ void VoxelRenderer::render(
|
|||
cb.fogParams = XMFLOAT4(0.004f, 0.0f, 0.0f, 0.0f); // fog density
|
||||
cb.rimColor = XMFLOAT4(0.90f, 0.80f, 0.55f, 0.0f); // warm golden rim
|
||||
cb.rimParams = XMFLOAT4(2.5f, 0.45f, 0.0f, 0.0f); // exponent, intensity
|
||||
cb.toneMapParams = XMFLOAT4(1.10f, 1.8f, 0.0f, 0.0f); // natural saturation, balanced exposure
|
||||
cb.toneMapParams = XMFLOAT4(1.10f, 1.8f, normalStrength_, (float)debugLighting_); // saturation, exposure, normalStrength, debugLighting
|
||||
dev->UpdateBuffer(&constantBuffer_, &cb, cmd, sizeof(cb));
|
||||
// Save current VP for next frame's temporal reprojection
|
||||
XMStoreFloat4x4(&rt_.prevViewProjection, vpMatrix);
|
||||
|
|
@ -1519,6 +1519,18 @@ void VoxelRenderPath::Update(float dt) {
|
|||
anim_.showCrosshair = !anim_.showCrosshair;
|
||||
wi::backlog::post(anim_.showCrosshair ? "Crosshair + debug: ON" : "Crosshair + debug: OFF");
|
||||
}
|
||||
if (wi::input::Press(wi::input::KEYBOARD_BUTTON_F9)) {
|
||||
anim_.debugLighting = (anim_.debugLighting + 1) % AnimationState::DEBUG_LIGHTING_MODES;
|
||||
static const char* modeNames[] = {
|
||||
"F9: ALL ON (normals+lighting+blend)",
|
||||
"F9: Normal maps OFF",
|
||||
"F9: FLAT lighting (no tex, no blend, no nmap)",
|
||||
"F9: ALBEDO only (texture, no lighting)",
|
||||
"F9: NdotL only (grayscale, flat normal)",
|
||||
"F9: NORMAL viz (RGB = XYZ geometric normal)"
|
||||
};
|
||||
wi::backlog::post(modeNames[anim_.debugLighting]);
|
||||
}
|
||||
if (wi::input::Press(wi::input::KEYBOARD_BUTTON_F5)) {
|
||||
if (!renderer.rt_.isShadowsEnabled()) {
|
||||
renderer.rt_.setShadowsEnabled(true); renderer.rt_.setShadowDebug(0);
|
||||
|
|
@ -1538,6 +1550,8 @@ void VoxelRenderPath::Update(float dt) {
|
|||
if (camera) camera_.handleInput(dt, camera);
|
||||
anim_.windTime += dt;
|
||||
renderer.windTime_ = anim_.windTime;
|
||||
renderer.normalStrength_ = (anim_.debugLighting == 0) ? 0.7f : 0.0f;
|
||||
renderer.debugLighting_ = anim_.debugLighting;
|
||||
|
||||
// Sun direction: fixed or orbiting (F7)
|
||||
if (anim_.sunOrbit) {
|
||||
|
|
@ -1757,8 +1771,8 @@ void VoxelRenderPath::Render() const {
|
|||
renderer.renderSmooth(cmd, voxelDepth_, voxelRT_, voxelNormalRT_);
|
||||
device->QueryEnd(&renderer.timestampHeap_, VoxelRenderer::TS_DRAW_END, cmd);
|
||||
|
||||
// Phase 6.2: RT Shadows + AO
|
||||
if (renderer.isRTShadowsEnabled() && renderer.isRTReady()) {
|
||||
// Phase 6.2: RT Shadows + AO (skip in F9 debug modes 2-4 for raw diagnostic output)
|
||||
if (renderer.isRTShadowsEnabled() && renderer.isRTReady() && renderer.debugLighting_ < 2) {
|
||||
device->QueryEnd(&renderer.timestampHeap_, VoxelRenderer::TS_RT_SHADOWS_BEGIN, cmd);
|
||||
renderer.rt_.dispatchShadows(cmd, voxelDepth_, voxelRT_, voxelNormalRT_,
|
||||
renderer.constantBuffer_);
|
||||
|
|
@ -1934,7 +1948,13 @@ void VoxelRenderPath::Compose(CommandList cmd) const {
|
|||
+ "] | F4: dbg [" + std::string(renderer.debugBlend_ ? "ON" : "OFF")
|
||||
+ "] | F5: shd+ao [" + std::string(renderer.rt_.getShadowDebug() == 1 ? "SHD" : (renderer.rt_.getShadowDebug() == 2 ? "AO" : (renderer.isRTShadowsEnabled() ? "ON" : "OFF")))
|
||||
+ "] | F7: sun [" + std::string(anim_.sunOrbit ? "ORBIT" : "FIXED")
|
||||
+ "] | F8: xhair [" + std::string(anim_.showCrosshair ? "ON" : "OFF") + "]";
|
||||
+ "] | F8: xhair [" + std::string(anim_.showCrosshair ? "ON" : "OFF")
|
||||
+ "] | F9: " + std::string(
|
||||
anim_.debugLighting == 0 ? "ALL" :
|
||||
anim_.debugLighting == 1 ? "noNMAP" :
|
||||
anim_.debugLighting == 2 ? "FLAT" :
|
||||
anim_.debugLighting == 3 ? "ALBEDO" :
|
||||
anim_.debugLighting == 4 ? "NdotL" : "NORMAL");
|
||||
|
||||
wi::font::Draw(stats, fp, cmd);
|
||||
|
||||
|
|
@ -2088,7 +2108,7 @@ std::string VoxelRenderPath::buildDebugLog() const {
|
|||
"FPS: %.1f (%.2f ms)\n"
|
||||
"Chunks: %u/%u Quads: %u GPU Mesh: %u\n"
|
||||
"Smooth verts: %u Toping instances: %zu\n"
|
||||
"Animation: %s | Blend debug: %s | Sun orbit: %s | Crosshair: %s\n"
|
||||
"Animation: %s | Blend debug: %s | Sun orbit: %s | Crosshair: %s | F9: %s\n"
|
||||
"RT available: %s | RT shadows+AO: %s | RT debug: %s\n"
|
||||
"Debug face mode: %s | Debug smooth: %s\n",
|
||||
smoothFps_, lastDt_ * 1000.0f,
|
||||
|
|
@ -2099,6 +2119,11 @@ std::string VoxelRenderPath::buildDebugLog() const {
|
|||
renderer.debugBlend_ ? "ON" : "OFF",
|
||||
anim_.sunOrbit ? "ORBIT" : "FIXED",
|
||||
anim_.showCrosshair ? "ON" : "OFF",
|
||||
anim_.debugLighting == 0 ? "ALL" :
|
||||
anim_.debugLighting == 1 ? "noNMAP" :
|
||||
anim_.debugLighting == 2 ? "FLAT" :
|
||||
anim_.debugLighting == 3 ? "ALBEDO" :
|
||||
anim_.debugLighting == 4 ? "NdotL" : "NORMAL",
|
||||
renderer.isRTAvailable() ? "yes" : "no",
|
||||
renderer.isRTShadowsEnabled() ? "ON" : "OFF",
|
||||
renderer.rt_.getShadowDebug() == 1 ? "SHADOWS" :
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ public:
|
|||
bool debugFaceColors_ = false;
|
||||
bool debugBlend_ = false;
|
||||
float windTime_ = 0.0f; // set by VoxelRenderPath::Update each frame
|
||||
float normalStrength_ = 0.7f; // normal map strength (0=off)
|
||||
int debugLighting_ = 0; // 0=all, 1=no nmap, 2=flat, 3=albedo, 4=NdotL
|
||||
XMFLOAT4 sunDirection_ = { -0.7f, -0.4f, -0.3f, 0.0f }; // set by VoxelRenderPath::Update
|
||||
|
||||
private:
|
||||
|
|
@ -303,6 +305,9 @@ struct AnimationState {
|
|||
bool terrainAnimated = false; // toggled with F3
|
||||
bool sunOrbit = false; // toggled with F7: sun orbits in ~10s cycle
|
||||
bool showCrosshair = true; // toggled with F8: crosshair + face debug info
|
||||
// F9 debug cycle: 0=all ON, 1=normals OFF, 2=flat lighting, 3=albedo only, 4=NdotL only, 5=normal viz
|
||||
int debugLighting = 0;
|
||||
static constexpr int DEBUG_LIGHTING_MODES = 6;
|
||||
float time = 0.0f; // current animation time offset
|
||||
float accum = 0.0f; // accumulator for 30 Hz timer
|
||||
static constexpr float INTERVAL = 1.0f / 30.0f; // ~33.3ms = 30 Hz
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue