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
This commit is contained in:
parent
40560c25ef
commit
55c67686f2
10 changed files with 162 additions and 36 deletions
14
CLAUDE.md
14
CLAUDE.md
|
|
@ -35,7 +35,7 @@ bvle-voxels/
|
|||
│ ├── voxelBLASExtractCS.hlsl # Compute shader BLAS position extraction (Phase 6.1)
|
||||
│ ├── voxelShadowCS.hlsl # Compute shader RT shadows + raw AO (inline ray queries, Phase 6.2+6.3)
|
||||
│ ├── voxelAOBlurCS.hlsl # Compute shader bilateral AO blur (separable H/V, Phase 6.3)
|
||||
│ └── voxelAOApplyCS.hlsl # Compute shader AO apply to color buffer (Phase 6.3)
|
||||
│ └── voxelAOApplyCS.hlsl # Compute shader AO apply + tone mapping + saturation (Phase 6.3 + 7)
|
||||
└── CLAUDE.md
|
||||
```
|
||||
|
||||
|
|
@ -615,6 +615,18 @@ Système de biseaux décoratifs (« topings ») sur les faces +Y exposées pour
|
|||
- Shadow maps + SSAO when RT not available
|
||||
- `CheckCapability(RAYTRACING)` gating
|
||||
|
||||
### Phase 7 - Stylized Lighting (Wonderbox-inspired) [EN COURS]
|
||||
|
||||
#### Phase 7.1 - Hemisphere Ambient + Colored Shadows + Rim Light + Tone Mapping [FAIT]
|
||||
|
||||
- **Hemisphere ambient** : `lerp(groundAmbient, skyAmbient, N.y * 0.5 + 0.5)` — warm brown below, cool blue above. Applied in all 3 pixel shaders (voxelPS, voxelSmoothPS, voxelTopingPS). Vegetation gets 1.5× ambient for inter-reflection
|
||||
- **Colored shadows** : RT shadows lerp toward `shadowTint` (blue-violet) instead of just darkening. `shadowFactor=0.55` (softer than 0.3)
|
||||
- **Rim light** : `pow(1 - NdotV, exponent) * intensity * rimColor`. Warm golden rim on silhouettes. Reduced to 30% on vegetation (thin geometry causes halos)
|
||||
- **Tone mapping + saturation** : soft exponential tone mapping (`1 - exp(-c)`) + saturation boost in `voxelAOApplyCS.hlsl` (final post-process pass)
|
||||
- **CB paramètres** : `skyAmbient`, `groundAmbient`, `shadowTint`, `fogColor`, `fogParams`, `rimColor`, `rimParams`, `toneMapParams` added to VoxelCB
|
||||
- **Fog centralisé** : fog density + color from CB instead of hardcoded per-shader
|
||||
- **Screenshot mode** : CLI argument `screenshot` → fixed camera, 60 frames AO convergence, save `bvle_screenshot.png`, quit. Small non-intrusive window (`SW_SHOWNOACTIVATE`). No HUD.
|
||||
|
||||
## Métriques cibles et résultats
|
||||
|
||||
| Métrique | Cible | Résultat (Ryzen 7 9800X3D + RX 9070 XT) |
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// BVLE Voxels - AO Apply Compute Shader (Phase 6.3)
|
||||
// Multiplies the blurred AO factor onto the color buffer.
|
||||
// BVLE Voxels - AO Apply + Tone Mapping Compute Shader (Phase 6.3 + 7)
|
||||
// Final post-process pass: applies AO, saturation boost, and tone mapping.
|
||||
|
||||
#include "voxelCommon.hlsli"
|
||||
|
||||
|
|
@ -14,6 +14,19 @@ struct ApplyPush {
|
|||
};
|
||||
[[vk::push_constant]] ConstantBuffer<ApplyPush> push : register(b999);
|
||||
|
||||
// Soft clamp tone mapping: linear up to shoulder, gentle roll-off to prevent harsh clipping
|
||||
float3 softClampToneMap(float3 color, float exposure) {
|
||||
color *= exposure;
|
||||
// Soft exponential curve: gentler than Reinhard, preserves bright midtones
|
||||
return 1.0 - exp(-color); // natural 1-e^-x curve
|
||||
}
|
||||
|
||||
// Saturation adjustment in linear space
|
||||
float3 adjustSaturation(float3 color, float saturation) {
|
||||
float luma = dot(color, float3(0.2126, 0.7152, 0.0722));
|
||||
return lerp(float3(luma, luma, luma), color, saturation);
|
||||
}
|
||||
|
||||
[RootSignature(VOXEL_ROOTSIG)]
|
||||
[numthreads(8, 8, 1)]
|
||||
void main(uint3 DTid : SV_DispatchThreadID) {
|
||||
|
|
@ -26,7 +39,16 @@ void main(uint3 DTid : SV_DispatchThreadID) {
|
|||
colorOutput[DTid.xy] = float4(ao, ao, ao, 1);
|
||||
} else {
|
||||
float4 color = colorOutput[DTid.xy];
|
||||
|
||||
// Apply AO
|
||||
color.rgb *= ao;
|
||||
|
||||
// Saturation boost (toneMapParams.x)
|
||||
color.rgb = adjustSaturation(color.rgb, toneMapParams.x);
|
||||
|
||||
// Tone mapping (toneMapParams.y = exposure)
|
||||
color.rgb = softClampToneMap(color.rgb, toneMapParams.y);
|
||||
|
||||
colorOutput[DTid.xy] = color;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,15 @@ cbuffer VoxelCB : register(b0) {
|
|||
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) ──
|
||||
|
|
|
|||
|
|
@ -293,15 +293,22 @@ PSOutput main(PSInput input)
|
|||
}
|
||||
|
||||
// ── Lighting ──
|
||||
float3 ambient = float3(0.15, 0.18, 0.25);
|
||||
float hemiLerp = N.y * 0.5 + 0.5; // 0=down, 1=up
|
||||
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, hemiLerp);
|
||||
float3 diffuse = sunColor.rgb * NdotL;
|
||||
float3 color = albedo * (ambient + diffuse);
|
||||
|
||||
// ── Rim light ──
|
||||
float3 V = normalize(cameraPosition.xyz - input.worldPos);
|
||||
float NdotV = saturate(dot(N, V));
|
||||
float rim = pow(1.0 - NdotV, rimParams.x) * rimParams.y;
|
||||
color += rimColor.rgb * rim;
|
||||
|
||||
// ── Distance fog ──
|
||||
float dist = length(input.worldPos - cameraPosition.xyz);
|
||||
float fog = 1.0 - exp(-dist * 0.003);
|
||||
float3 fogColor = float3(0.55, 0.70, 0.90);
|
||||
color = lerp(color, fogColor, saturate(fog));
|
||||
float fogDensity = fogParams.x;
|
||||
float fog = 1.0 - exp(-dist * fogDensity);
|
||||
color = lerp(color, fogColor.rgb, saturate(fog));
|
||||
|
||||
output.color = float4(color, 1.0);
|
||||
output.normal = float4(N, 0.0);
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ void main(uint3 DTid : SV_DispatchThreadID) {
|
|||
|
||||
float shadowFactor = 1.0;
|
||||
if (NdotL <= 0.0) {
|
||||
shadowFactor = 0.3; // back-facing = fully in shadow
|
||||
shadowFactor = 0.55; // back-facing = fully in shadow
|
||||
} else {
|
||||
RayDesc ray;
|
||||
ray.Origin = origin;
|
||||
|
|
@ -120,7 +120,7 @@ void main(uint3 DTid : SV_DispatchThreadID) {
|
|||
[loop] while (q.Proceed()) {}
|
||||
|
||||
if (q.CommittedStatus() == COMMITTED_TRIANGLE_HIT) {
|
||||
shadowFactor = 0.3;
|
||||
shadowFactor = 0.55;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +205,11 @@ void main(uint3 DTid : SV_DispatchThreadID) {
|
|||
colorOutput[DTid.xy] = float4(1, 1, 1, 1);
|
||||
} else {
|
||||
float4 color = colorOutput[DTid.xy];
|
||||
color.rgb *= shadowFactor;
|
||||
// Colored shadows: lerp toward shadow tint instead of just darkening
|
||||
// shadowFactor=1 → no change, shadowFactor=0.3 → blend toward tinted shadow
|
||||
float shadowAmount = 1.0 - shadowFactor; // 0=lit, 0.7=full shadow
|
||||
float3 tintedColor = color.rgb * shadowTint.rgb; // shadow = original × tint color
|
||||
color.rgb = lerp(color.rgb, tintedColor, shadowAmount);
|
||||
colorOutput[DTid.xy] = color;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,14 +217,21 @@ PSOutput main(PSInput input) {
|
|||
// Lighting
|
||||
float3 L = normalize(-sunDirection.xyz);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float3 ambient = float3(0.15, 0.18, 0.25);
|
||||
float hemiLerp = N.y * 0.5 + 0.5;
|
||||
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, hemiLerp);
|
||||
float3 color = albedo * (sunColor.rgb * NdotL + ambient);
|
||||
|
||||
// ── Rim light ──
|
||||
float3 V = normalize(cameraPosition.xyz - input.worldPos);
|
||||
float NdotV = saturate(dot(N, V));
|
||||
float rim = pow(1.0 - NdotV, rimParams.x) * rimParams.y;
|
||||
color += rimColor.rgb * rim;
|
||||
|
||||
// Distance fog
|
||||
float dist = length(input.worldPos - cameraPosition.xyz);
|
||||
float fog = 1.0 - exp(-dist * 0.003);
|
||||
float3 fogColor = float3(0.55, 0.70, 0.90);
|
||||
color = lerp(color, fogColor, saturate(fog));
|
||||
float fogDensity = fogParams.x;
|
||||
float fog = 1.0 - exp(-dist * fogDensity);
|
||||
color = lerp(color, fogColor.rgb, saturate(fog));
|
||||
|
||||
output.color = float4(color, 1.0);
|
||||
output.normal = float4(N, 0.0);
|
||||
|
|
|
|||
|
|
@ -47,10 +47,12 @@ PSOutput main(PSInput input) {
|
|||
// 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 + cool ambient (matches voxel PS)
|
||||
// Stone: classic Lambert + hemisphere ambient
|
||||
float NdotL = max(rawNdotL, 0.0);
|
||||
float3 ambient = float3(0.15, 0.18, 0.25);
|
||||
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, hemiLerp);
|
||||
lit = texColor * (sunColor.rgb * NdotL + ambient);
|
||||
} else {
|
||||
// ── Vegetation: soft wrap lighting ──────────────────────
|
||||
|
|
@ -64,20 +66,24 @@ PSOutput main(PSInput input) {
|
|||
|
||||
// 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.6;
|
||||
float translucency = backLight * transAmount;
|
||||
|
||||
// Higher ambient for vegetation: grass blades bounce light between each other
|
||||
// (simplified inter-reflection / GI), brighter than stone ambient
|
||||
float3 ambient = float3(0.30, 0.33, 0.35);
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "WickedEngine.h"
|
||||
#include "voxel/VoxelRenderer.h"
|
||||
#include "wiHelper.h"
|
||||
#include <fstream>
|
||||
#include <DbgHelp.h>
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
|
|
@ -124,6 +125,10 @@ int APIENTRY wWinMain(
|
|||
|
||||
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
|
||||
// Parse arguments early so we can adjust window creation
|
||||
wi::arguments::Parse(lpCmdLine);
|
||||
bool isScreenshot = wi::arguments::HasArgument("screenshot");
|
||||
|
||||
WNDCLASSEXW wcex = {};
|
||||
wcex.cbSize = sizeof(WNDCLASSEX);
|
||||
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
||||
|
|
@ -134,32 +139,33 @@ int APIENTRY wWinMain(
|
|||
wcex.lpszClassName = L"BVLEVoxels";
|
||||
RegisterClassExW(&wcex);
|
||||
|
||||
// Screenshot mode: small minimized window to avoid interrupting user
|
||||
HWND hWnd = CreateWindowW(
|
||||
wcex.lpszClassName,
|
||||
L"BVLE Voxels - Prototype",
|
||||
isScreenshot ? L"BVLE Screenshot" : L"BVLE Voxels - Prototype",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, 0,
|
||||
1920, 1080,
|
||||
isScreenshot ? 0 : CW_USEDEFAULT,
|
||||
isScreenshot ? 0 : 0,
|
||||
isScreenshot ? 640 : 1920,
|
||||
isScreenshot ? 480 : 1080,
|
||||
nullptr, nullptr, hInstance, nullptr
|
||||
);
|
||||
ShowWindow(hWnd, SW_SHOWMAXIMIZED);
|
||||
// SW_SHOWNOACTIVATE: visible but doesn't steal focus (minimized windows don't render)
|
||||
ShowWindow(hWnd, isScreenshot ? SW_SHOWNOACTIVATE : SW_SHOWMAXIMIZED);
|
||||
|
||||
// Initialize Wicked Engine (selects DX12 by default on Windows, Vulkan on Linux)
|
||||
// Pass "vulkan" as command line argument to force Vulkan backend
|
||||
// Pass "debugdevice" for D3D debug layer, "gpuvalidation" for GPU-based validation
|
||||
// Initialize Wicked Engine
|
||||
application.SetWindow(hWnd);
|
||||
wi::arguments::Parse(lpCmdLine);
|
||||
|
||||
|
||||
// Redirect Wicked Engine log to file
|
||||
wi::backlog::SetLogFile("bvle_backlog.txt");
|
||||
|
||||
// Info display
|
||||
application.infoDisplay.active = true;
|
||||
// Info display (disabled in screenshot mode)
|
||||
application.infoDisplay.active = !isScreenshot;
|
||||
application.infoDisplay.watermark = false;
|
||||
application.infoDisplay.resolution = true;
|
||||
application.infoDisplay.fpsinfo = true;
|
||||
application.infoDisplay.heap_allocation_counter = true;
|
||||
application.infoDisplay.resolution = !isScreenshot;
|
||||
application.infoDisplay.fpsinfo = !isScreenshot;
|
||||
application.infoDisplay.heap_allocation_counter = !isScreenshot;
|
||||
|
||||
// Check for "debug" argument to enable face-color debug mode
|
||||
if (wi::arguments::HasArgument("debug")) {
|
||||
|
|
@ -169,12 +175,17 @@ int APIENTRY wWinMain(
|
|||
if (wi::arguments::HasArgument("debugsmooth")) {
|
||||
renderPath.debugSmooth = true;
|
||||
}
|
||||
// Screenshot mode: auto-position camera, wait for AO convergence, capture, quit
|
||||
if (wi::arguments::HasArgument("screenshot")) {
|
||||
renderPath.screenshotMode = true;
|
||||
}
|
||||
|
||||
// Activate our custom voxel render path
|
||||
application.ActivatePath(&renderPath);
|
||||
|
||||
// Main loop
|
||||
MSG msg = { 0 };
|
||||
static int screenshotFrameCounter = 0;
|
||||
while (msg.message != WM_QUIT)
|
||||
{
|
||||
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
|
|
@ -183,6 +194,23 @@ int APIENTRY wWinMain(
|
|||
}
|
||||
else {
|
||||
application.Run();
|
||||
|
||||
// Screenshot mode: wait for rendering + AO convergence, then capture and quit
|
||||
if (renderPath.screenshotMode) {
|
||||
screenshotFrameCounter++;
|
||||
// Only start counting convergence frames once we have actual rendered quads
|
||||
bool hasRendered = renderPath.renderer.getGpuMeshQuadCount() > 0;
|
||||
static int convergenceFrames = 0;
|
||||
if (hasRendered) convergenceFrames++;
|
||||
// Wait 60 frames after first render for AO temporal convergence
|
||||
if (convergenceFrames == 60) {
|
||||
bool ok = wi::helper::saveTextureToFile(
|
||||
renderPath.getVoxelRT(), "bvle_screenshot.png");
|
||||
wi::backlog::post(ok ? "Screenshot saved: bvle_screenshot.png"
|
||||
: "Screenshot FAILED");
|
||||
PostQuitMessage(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1441,7 +1441,7 @@ void VoxelRenderer::render(
|
|||
cb.prevViewProjection = prevViewProjection_; // from last frame
|
||||
cb.cameraPosition = XMFLOAT4(camera.Eye.x, camera.Eye.y, camera.Eye.z, 1.0f);
|
||||
cb.sunDirection = XMFLOAT4(-0.7f, -0.4f, -0.3f, 0.0f); // lower sun = longer cast shadows
|
||||
cb.sunColor = XMFLOAT4(1.2f, 1.1f, 0.9f, 1.0f);
|
||||
cb.sunColor = XMFLOAT4(1.35f, 1.15f, 0.75f, 1.0f); // warm golden sun
|
||||
cb.chunkSize = (float)CHUNK_SIZE;
|
||||
cb.textureTiling = 0.25f;
|
||||
cb.blendEnabled = 1.0f; // Phase 3: PS-based blending enabled in GPU mesh path
|
||||
|
|
@ -1450,6 +1450,15 @@ void VoxelRenderer::render(
|
|||
cb.bleedMask = (1u << 1) | (1u << 2) | (1u << 4) | (1u << 5);
|
||||
cb.resistBleedMask = (1u << 1);
|
||||
cb.windTime = windTime_;
|
||||
// Stylized lighting (Phase 7) — Wonderbox-inspired, iteration 4
|
||||
cb.skyAmbient = XMFLOAT4(0.65f, 0.68f, 0.75f, 0.0f); // very high ambient fill
|
||||
cb.groundAmbient = XMFLOAT4(0.40f, 0.33f, 0.22f, 0.0f); // warm brown, high fill
|
||||
cb.shadowTint = XMFLOAT4(0.55f, 0.45f, 0.72f, 0.0f); // purple-blue shadows
|
||||
cb.fogColor = XMFLOAT4(0.80f, 0.75f, 0.60f, 1.0f); // warm sandy-golden fog
|
||||
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.40f, 2.2f, 0.0f, 0.0f); // vivid saturation, high exposure
|
||||
dev->UpdateBuffer(&constantBuffer_, &cb, cmd, sizeof(cb));
|
||||
// Save current VP for next frame's temporal reprojection
|
||||
XMStoreFloat4x4(&prevViewProjection_, vpMatrix);
|
||||
|
|
@ -2268,6 +2277,13 @@ void VoxelRenderPath::Start() {
|
|||
} else {
|
||||
world.generateAround(cameraPos.x, cameraPos.y, cameraPos.z, 4);
|
||||
}
|
||||
|
||||
// Screenshot mode: fixed camera with good framing of terrain
|
||||
if (screenshotMode) {
|
||||
cameraPos = { 270.0f, 50.0f, 240.0f }; // above terrain, below sky
|
||||
cameraPitch = -0.25f; // slight downward look
|
||||
cameraYaw = 0.6f; // angled view for depth
|
||||
}
|
||||
if (renderer.isInitialized()) {
|
||||
renderer.updateMeshes(world);
|
||||
}
|
||||
|
|
@ -2693,6 +2709,9 @@ void VoxelRenderPath::Compose(CommandList cmd) const {
|
|||
wi::image::Draw(&voxelRT_, fx, cmd);
|
||||
}
|
||||
|
||||
// No HUD in screenshot mode
|
||||
if (screenshotMode) return;
|
||||
|
||||
// HUD overlay
|
||||
wi::font::Params fp;
|
||||
fp.posX = 10; fp.posY = 10; fp.size = 20;
|
||||
|
|
|
|||
|
|
@ -158,9 +158,18 @@ private:
|
|||
float debugBlend;
|
||||
XMFLOAT4 frustumPlanes[6]; // ax+by+cz+d=0
|
||||
uint32_t chunkCount;
|
||||
uint32_t bleedMask; // bit N set = material N can bleed onto neighbors
|
||||
uint32_t resistBleedMask; // bit N set = material N resists bleed from neighbors
|
||||
uint32_t bleedMask;
|
||||
uint32_t resistBleedMask;
|
||||
float windTime;
|
||||
// Stylized lighting (Phase 7)
|
||||
XMFLOAT4 skyAmbient;
|
||||
XMFLOAT4 groundAmbient;
|
||||
XMFLOAT4 shadowTint;
|
||||
XMFLOAT4 fogColor;
|
||||
XMFLOAT4 fogParams;
|
||||
XMFLOAT4 rimColor;
|
||||
XMFLOAT4 rimParams;
|
||||
XMFLOAT4 toneMapParams;
|
||||
};
|
||||
wi::graphics::GPUBuffer constantBuffer_;
|
||||
|
||||
|
|
@ -307,6 +316,7 @@ public:
|
|||
|
||||
bool debugMode = false;
|
||||
bool debugSmooth = false;
|
||||
bool screenshotMode = false; // CLI "screenshot": auto-position camera, capture, quit
|
||||
|
||||
float cameraSpeed = 50.0f;
|
||||
float cameraSensitivity = 0.003f;
|
||||
|
|
@ -315,6 +325,8 @@ public:
|
|||
float cameraYaw = 0.0f;
|
||||
bool mouseCaptured = false;
|
||||
|
||||
const wi::graphics::Texture& getVoxelRT() const { return voxelRT_; }
|
||||
|
||||
void Start() override;
|
||||
void Update(float dt) override;
|
||||
void Render() const override;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue