Phase 4.2+7: grass blade rework + soft RT shadows + toping BLAS optimization
Grass blades: - Leaf-shaped profile (4 sections: base→belly→taper→tip) instead of spiky triangles - Wider blades (base 0.055-0.095), more spacing between blades (±0.07 scatter) - Natural green texture (50,140,35 → 80,180,55) instead of neon lime - Reduced warm shift and removed artificial saturation boost - Side faces at 60% brightness (dark green) instead of 38% (near-black) Soft RT shadows: - 2 jittered shadow rays per pixel with IGN+Cranley-Patterson temporal variation - 2.3° cone around sun direction for soft penumbra - Gradual shadow factor (0-100%) instead of binary on/off Performance: - Toping BLAS removed from TLAS (23M+ tris caused massive ray traversal slowdown) - Toping BLAS position/index buffer construction skipped entirely - Shadow rays reduced from 4 to 2 (temporal accumulation compensates)
This commit is contained in:
parent
82307269e8
commit
3d0c4f2f80
8 changed files with 165 additions and 77 deletions
|
|
@ -296,6 +296,25 @@ PSOutput main(PSInput input)
|
|||
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;
|
||||
|
||||
// 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
|
||||
float sideFactor = lerp(0.60, 1.0, verticalDarken); // sides at 60% brightness
|
||||
albedo *= sideFactor;
|
||||
|
||||
// Subtle warm shift: sunlit grass slightly warmer
|
||||
if (NdotL > 0.0) {
|
||||
float3 warmShift = float3(0.08, 0.05, -0.03) * NdotL;
|
||||
diffuse += warmShift;
|
||||
}
|
||||
|
||||
// Boost ambient for grass: inter-reflection from dense foliage
|
||||
ambient *= 1.15;
|
||||
}
|
||||
|
||||
float3 color = albedo * (ambient + diffuse);
|
||||
|
||||
// ── Rim light ──
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ void main(uint3 DTid : SV_DispatchThreadID) {
|
|||
float3 N = normalTexture[DTid.xy].xyz;
|
||||
float3 origin = worldPos + N * push.normalBias;
|
||||
|
||||
// ── Shadow ray toward sun ──────────────────────────────────
|
||||
// ── Soft shadow: multiple jittered rays toward sun ─────────
|
||||
float3 L = normalize(-sunDirection.xyz);
|
||||
float NdotL = dot(N, L);
|
||||
|
||||
|
|
@ -109,9 +109,31 @@ void main(uint3 DTid : SV_DispatchThreadID) {
|
|||
if (NdotL <= 0.0) {
|
||||
shadowFactor = 0.45; // back-facing = fully in shadow
|
||||
} else {
|
||||
// Build basis around sun direction for jitter cone
|
||||
float3 sunT, sunB;
|
||||
buildBasis(L, sunT, sunB);
|
||||
|
||||
// 2 shadow rays with IGN-based jitter (soft penumbra, temporally accumulated)
|
||||
const uint shadowRays = 2;
|
||||
const float coneAngle = 0.04; // ~2.3° cone = soft sun
|
||||
float shadowHits = 0;
|
||||
float ignBase = interleavedGradientNoise(float2(DTid.xy));
|
||||
float frameRot = float(push.frameIndex) * GOLDEN_RATIO;
|
||||
|
||||
[loop]
|
||||
for (uint si = 0; si < shadowRays; si++) {
|
||||
// Per-ray noise with temporal variation
|
||||
float xi1 = frac(ignBase + frameRot + float(si) * GOLDEN_RATIO);
|
||||
float xi2 = frac(ignBase * 1.7 + frameRot * 0.7 + float(si) * 0.3819);
|
||||
|
||||
// Uniform disk → cone direction
|
||||
float r = sqrt(xi1) * coneAngle;
|
||||
float phi = 6.28318530718 * xi2;
|
||||
float3 jitteredL = normalize(L + r * cos(phi) * sunT + r * sin(phi) * sunB);
|
||||
|
||||
RayDesc ray;
|
||||
ray.Origin = origin;
|
||||
ray.Direction = L;
|
||||
ray.Direction = jitteredL;
|
||||
ray.TMin = 0.01;
|
||||
ray.TMax = push.shadowMaxDist;
|
||||
|
||||
|
|
@ -120,10 +142,14 @@ void main(uint3 DTid : SV_DispatchThreadID) {
|
|||
[loop] while (q.Proceed()) {}
|
||||
|
||||
if (q.CommittedStatus() == COMMITTED_TRIANGLE_HIT) {
|
||||
shadowFactor = 0.45;
|
||||
shadowHits += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
float shadowAmount = shadowHits / float(shadowRays); // 0=fully lit, 1=fully shadowed
|
||||
shadowFactor = lerp(1.0, 0.45, shadowAmount);
|
||||
}
|
||||
|
||||
// ── AO: hemisphere rays with IGN + temporal rotation ──────
|
||||
float aoFactor = 1.0;
|
||||
uint rayCount = push.aoRayCount;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// 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).
|
||||
// Vegetation: vertical gradient + up-biased lighting for stable grass look.
|
||||
// Stone: classic Lambert + hemisphere ambient.
|
||||
|
||||
#include "voxelCommon.hlsli"
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ struct PSInput {
|
|||
float4 position : SV_POSITION;
|
||||
float3 worldPos : WORLDPOS;
|
||||
float3 normal : NORMAL;
|
||||
float localHeight : LOCALHEIGHT;
|
||||
nointerpolation uint materialID : MATERIALID;
|
||||
};
|
||||
|
||||
|
|
@ -39,48 +40,51 @@ PSOutput main(PSInput input) {
|
|||
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;
|
||||
float hemiLerp = N.y * 0.5 + 0.5;
|
||||
float3 V = normalize(cameraPosition.xyz - input.worldPos);
|
||||
|
||||
if (input.materialID == 3u) {
|
||||
// Stone: classic Lambert + hemisphere ambient
|
||||
float NdotL = max(rawNdotL, 0.0);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, hemiLerp);
|
||||
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
|
||||
// ── Vegetation shading ──────────────────────────────────
|
||||
|
||||
// Diffuse floor: even fully back-facing blades get some sun contribution
|
||||
// Simulates light scattering through dense grass (cheap GI approximation)
|
||||
wrap = max(wrap, 0.35);
|
||||
// Vertical gradient: dark green at base → lighter at tip
|
||||
float h = input.localHeight; // 0=base, ~1=tip
|
||||
float3 baseColor = texColor * 0.55; // darker at roots
|
||||
float3 tipColor = texColor * 1.15; // brighter at tips
|
||||
float3 grassColor = lerp(baseColor, tipColor, h);
|
||||
|
||||
// Translucency: thin blades let light through from behind
|
||||
// Stronger effect to reduce contrast when orbiting around grass
|
||||
// Up-biased normal for stable lighting on thin geometry
|
||||
// Blend between actual normal and UP vector based on how "grassy" (non-stone)
|
||||
float3 lightN = normalize(lerp(N, float3(0, 1, 0), 0.65));
|
||||
float NdotL = dot(lightN, L);
|
||||
|
||||
// Soft wrap lighting (half-Lambert)
|
||||
float halfLambert = NdotL * 0.5 + 0.5;
|
||||
float wrap = halfLambert * halfLambert;
|
||||
|
||||
// Translucency: light passing through thin blades from behind
|
||||
float backLight = saturate(dot(V, L));
|
||||
float transAmount = (1.0 - saturate(rawNdotL)) * 0.6;
|
||||
float transAmount = (1.0 - saturate(NdotL)) * 0.5;
|
||||
float translucency = backLight * transAmount;
|
||||
|
||||
// Hemisphere ambient for vegetation, scaled up 1.5x for inter-reflection
|
||||
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, hemiLerp) * 1.5;
|
||||
// Hemisphere ambient, boosted for vegetation inter-reflection
|
||||
float3 ambient = lerp(groundAmbient.rgb, skyAmbient.rgb, lightN.y * 0.5 + 0.5) * 1.3;
|
||||
|
||||
// Wrap + translucency for soft, low-contrast vegetation shading
|
||||
float3 diffuse = sunColor.rgb * (wrap * 0.88 + translucency * 0.55);
|
||||
lit = texColor * (diffuse + ambient);
|
||||
// Combine
|
||||
float3 diffuse = sunColor.rgb * (wrap * 0.85 + translucency * 0.35);
|
||||
lit = grassColor * (diffuse + ambient);
|
||||
}
|
||||
|
||||
// ── Rim light (reduced on vegetation — thin geometry causes halos) ──
|
||||
// ── Rim light (reduced on vegetation) ──
|
||||
float NdotV = saturate(dot(N, V));
|
||||
float rimScale = (input.materialID == 3u) ? 1.0 : 0.3; // stone full, grass 30%
|
||||
float rimScale = (input.materialID == 3u) ? 1.0 : 0.2;
|
||||
float rim = pow(1.0 - NdotV, rimParams.x) * rimParams.y * rimScale;
|
||||
lit += rimColor.rgb * rim;
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ struct VSOutput {
|
|||
float4 position : SV_POSITION;
|
||||
float3 worldPos : WORLDPOS;
|
||||
float3 normal : NORMAL;
|
||||
float localHeight : LOCALHEIGHT; // height above voxel face (0=base, 1=tip)
|
||||
nointerpolation uint materialID : MATERIALID;
|
||||
};
|
||||
|
||||
|
|
@ -66,6 +67,7 @@ VSOutput main(uint vertexID : SV_VertexID, uint instanceID : SV_InstanceID) {
|
|||
output.position = mul(viewProjection, float4(worldPos, 1.0));
|
||||
output.worldPos = worldPos;
|
||||
output.normal = vtx.normal;
|
||||
output.localHeight = saturate((vtx.position.y - 1.0) * 5.0); // normalized: 0=base, 1=tip (blades ~0.06-0.24 tall)
|
||||
output.materialID = push.materialID;
|
||||
return output;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,10 +199,9 @@ int APIENTRY wWinMain(
|
|||
if (renderPath.screenshotMode) {
|
||||
struct CamView { float x, y, z, pitch, yaw; const char* name; };
|
||||
static const CamView views[] = {
|
||||
{ 270.f, 48.f, 240.f, -0.30f, 0.6f, "landscape" }, // wide terrain, slight down
|
||||
{ 272.f, 44.f, 248.f, -0.20f, 1.0f, "sideview" }, // side angle, ground level
|
||||
{ 268.f, 55.f, 242.f, -0.70f, 0.5f, "topdown" }, // steep top-down
|
||||
{ 275.f, 46.f, 235.f, -0.15f, 2.8f, "backlit" }, // looking toward sun
|
||||
{ 223.f, 36.5f, 261.f, -0.20f, 0.7f, "closeup" }, // close-up: slightly above grass, looking across
|
||||
{ 222.5f, 36.2f, 261.f, -0.10f, 0.5f,"blade" }, // eye-level with grass blades
|
||||
{ 220.f, 39.f, 258.f, -0.35f, 0.7f, "medium" }, // medium shot of grass patch
|
||||
};
|
||||
static const int numViews = sizeof(views) / sizeof(views[0]);
|
||||
static int currentView = 0;
|
||||
|
|
|
|||
|
|
@ -98,9 +98,10 @@ static float hashF(int a, int b, int c) {
|
|||
return x - floorf(x);
|
||||
}
|
||||
|
||||
// ── Emit a single grass blade (2 segments, double-sided) ────────
|
||||
// The blade is a tapered ribbon curving outward from the voxel face.
|
||||
// 2 segments give curvature; double-sided for backface-culled PSO.
|
||||
// ── Emit a single grass blade (3 segments, double-sided) ────────
|
||||
// The blade is a wide, leaf-shaped ribbon with rounded tip.
|
||||
// 4 cross-sections: wide base → widest at 1/3 height → taper → rounded tip.
|
||||
// Double-sided for backface-culled PSO.
|
||||
//
|
||||
// midLeanRatio controls the curvature profile:
|
||||
// low (0.05-0.15): mostly straight bottom, sharp curve at top
|
||||
|
|
@ -122,15 +123,18 @@ static void emitBlade(std::vector<TopingVertex>& verts,
|
|||
// Width direction (perpendicular to lean in XZ plane)
|
||||
float wx = -lz, wz = lx;
|
||||
|
||||
// 3 cross-sections with variable curvature
|
||||
// 4 cross-sections: leaf shape (wide base, widest at 1/3, taper, round tip)
|
||||
float leanMid1 = lean * midLeanRatio * 0.5f;
|
||||
float leanMid2 = lean * (midLeanRatio + (1.0f - midLeanRatio) * 0.5f);
|
||||
struct Section { float x, y, z, hw; };
|
||||
Section secs[3] = {
|
||||
{ px, 1.0f, pz, baseW * 0.50f },
|
||||
{ px + lean * midLeanRatio * lx, 1.0f + height * 0.5f, pz + lean * midLeanRatio * lz, baseW * 0.32f },
|
||||
{ px + lean * lx, 1.0f + height, pz + lean * lz, baseW * 0.08f },
|
||||
Section secs[4] = {
|
||||
{ px, 1.0f, pz, baseW * 0.50f }, // base
|
||||
{ px + leanMid1 * lx, 1.0f + height * 0.30f, pz + leanMid1 * lz, baseW * 0.55f }, // widest (leaf belly)
|
||||
{ px + leanMid2 * lx, 1.0f + height * 0.70f, pz + leanMid2 * lz, baseW * 0.30f }, // taper
|
||||
{ px + lean * lx, 1.0f + height, pz + lean * lz, baseW * 0.10f }, // rounded tip
|
||||
};
|
||||
|
||||
for (int s = 0; s < 2; s++) {
|
||||
for (int s = 0; s < 3; s++) {
|
||||
const auto& s0 = secs[s];
|
||||
const auto& s1 = secs[s + 1];
|
||||
|
||||
|
|
@ -350,10 +354,10 @@ void TopingSystem::generateGrassVariant(TopingDef& def, uint8_t bitmask) {
|
|||
// Tuft count per edge (more open edges → fewer tufts each)
|
||||
int tuftsPerEdge;
|
||||
switch (openEdges) {
|
||||
case 1: tuftsPerEdge = 7; break;
|
||||
case 2: tuftsPerEdge = 5; break;
|
||||
case 3: tuftsPerEdge = 4; break;
|
||||
default: tuftsPerEdge = 4; break;
|
||||
case 1: tuftsPerEdge = 9; break;
|
||||
case 2: tuftsPerEdge = 7; break;
|
||||
case 3: tuftsPerEdge = 5; break;
|
||||
default: tuftsPerEdge = 5; break;
|
||||
}
|
||||
|
||||
// ── 1. Tufts along open edges ───────────────────────────────
|
||||
|
|
@ -379,9 +383,9 @@ void TopingSystem::generateGrassVariant(TopingDef& def, uint8_t bitmask) {
|
|||
float tuftCenterZ = e.sz + t * dz + tuftInset * e.iz;
|
||||
|
||||
// ── Per-tuft personality ─────────────────────────────
|
||||
float tuftHeightScale = 0.20f + hashF(edge, ti, 77) * 0.80f;
|
||||
float tuftLeanScale = 0.3f + hashF(edge, ti, 55) * 1.5f;
|
||||
int bladesInTuft = 3 + (int)(hashF(edge, ti, 33) * 6.99f);
|
||||
float tuftHeightScale = 0.40f + hashF(edge, ti, 77) * 0.60f;
|
||||
float tuftLeanScale = 0.2f + hashF(edge, ti, 55) * 0.8f;
|
||||
int bladesInTuft = 5 + (int)(hashF(edge, ti, 33) * 6.99f);
|
||||
|
||||
// Emit blades tightly clustered around tuft center
|
||||
for (int bi = 0; bi < bladesInTuft; bi++) {
|
||||
|
|
@ -399,17 +403,17 @@ void TopingSystem::generateGrassVariant(TopingDef& def, uint8_t bitmask) {
|
|||
float angle = (fanT - 0.5f) * 2.0f * spreadAngle;
|
||||
angle += (h1 - 0.5f) * 20.0f;
|
||||
|
||||
// Height: per-blade × per-tuft
|
||||
float bladeHeight = (0.09f + h2 * 0.42f) * tuftHeightScale; // +50% height
|
||||
// Height: short leaf-like blades
|
||||
float bladeHeight = (0.08f + h2 * 0.20f) * tuftHeightScale;
|
||||
|
||||
float baseWidth = 0.030f + h3 * 0.030f;
|
||||
float lean = (0.03f + h4 * 0.12f) * tuftLeanScale;
|
||||
float baseWidth = 0.055f + h3 * 0.040f; // wider blades
|
||||
float lean = (0.02f + h4 * 0.08f) * tuftLeanScale;
|
||||
float midLean = 0.08f + h5 * 0.35f;
|
||||
|
||||
// Tight scatter around tuft center (±0.03 — clustered)
|
||||
// Spread blades within tuft (±0.07 — clearly separated)
|
||||
float h6 = hashF(edge + 8, ti, bi * 3 + 2);
|
||||
float offX = (h1 - 0.5f) * 0.06f;
|
||||
float offZ = (h6 - 0.5f) * 0.06f;
|
||||
float offX = (h1 - 0.5f) * 0.14f;
|
||||
float offZ = (h6 - 0.5f) * 0.14f;
|
||||
|
||||
float bx = tuftCenterX + offX;
|
||||
float bz = tuftCenterZ + offZ;
|
||||
|
|
@ -436,14 +440,14 @@ void TopingSystem::generateGrassVariant(TopingDef& def, uint8_t bitmask) {
|
|||
if (diagLen > 1e-6f) { diagX /= diagLen; diagZ /= diagLen; }
|
||||
|
||||
// Corner tuft: cluster filling the diagonal gap
|
||||
int cornerBlades = 4 + (int)(hashF(c + 10, 0, 33) * 5.99f);
|
||||
int cornerBlades = 5 + (int)(hashF(c + 10, 0, 33) * 6.99f);
|
||||
// Corner tuft inset: 0.05 to 0.35 diagonally inward
|
||||
float cDistHash = hashF(c + 10, 0, 44);
|
||||
float cornerInset = 0.05f + cDistHash * 0.30f;
|
||||
float cbx = corner.cx + cornerInset * (eA.ix + eB.ix);
|
||||
float cbz = corner.cz + cornerInset * (eA.iz + eB.iz);
|
||||
|
||||
float cornerHeightScale = 0.25f + hashF(c + 10, 0, 77) * 0.75f;
|
||||
float cornerHeightScale = 0.40f + hashF(c + 10, 0, 77) * 0.60f;
|
||||
float cornerLeanScale = 0.3f + hashF(c + 10, 0, 55) * 1.5f;
|
||||
|
||||
for (int bi = 0; bi < cornerBlades; bi++) {
|
||||
|
|
@ -458,9 +462,9 @@ void TopingSystem::generateGrassVariant(TopingDef& def, uint8_t bitmask) {
|
|||
float angle = (fanT - 0.5f) * 2.0f * 70.0f;
|
||||
angle += (h1 - 0.5f) * 15.0f;
|
||||
|
||||
float height = (0.15f + h2 * 0.33f) * cornerHeightScale; // +50% height
|
||||
float baseWidth = 0.030f + h3 * 0.025f;
|
||||
float lean = (0.04f + h4 * 0.10f) * cornerLeanScale;
|
||||
float height = (0.08f + h2 * 0.20f) * cornerHeightScale;
|
||||
float baseWidth = 0.055f + h3 * 0.040f;
|
||||
float lean = (0.02f + h4 * 0.06f) * cornerLeanScale;
|
||||
float midLean = 0.10f + h5 * 0.30f;
|
||||
|
||||
// Tight scatter around corner tuft center (clustered)
|
||||
|
|
|
|||
|
|
@ -366,7 +366,7 @@ void VoxelRenderer::generateTextures() {
|
|||
float heightContrast; // heightmap contrast (higher = more defined peaks)
|
||||
};
|
||||
MatColor colors[NUM_MATERIALS] = {
|
||||
{ 60, 140, 40, 80, 180, 60, 101, 1.5f, 0.8f }, // 1: Grass: medium bumps
|
||||
{ 50, 140, 35, 80, 180, 55, 101, 1.5f, 0.8f }, // 1: Grass: natural rich green
|
||||
{ 100, 70, 40, 140, 100, 60, 202, 0.8f, 0.6f }, // 2: Dirt: smooth mounds
|
||||
{ 80, 80, 90, 120, 120, 130, 303, 2.5f, 0.5f }, // 3: Stone (blocky): darker blue-gray
|
||||
{ 220, 200, 130, 245, 230, 160, 404, 3.0f, 0.4f }, // 4: Sand: warmer yellow, fine
|
||||
|
|
@ -1103,6 +1103,11 @@ void VoxelRenderer::buildAccelerationStructures(CommandList cmd) const {
|
|||
rtSmoothVertexCount_ = smoothVertCount;
|
||||
}
|
||||
|
||||
// ── Toping BLAS: SKIPPED ─────────────────────────────────────
|
||||
// Topings generate 23M+ tris which massively slows ray traversal
|
||||
// for negligible shadow contribution (blades too thin).
|
||||
// Toping BLAS build and TLAS inclusion both disabled for performance.
|
||||
|
||||
// ── Memory barrier: sync BLAS builds before TLAS ──────────────
|
||||
// Without this, TLAS build can execute before BLASes are complete.
|
||||
// (Same pattern as wiRenderer.cpp line 5788)
|
||||
|
|
@ -1111,7 +1116,9 @@ void VoxelRenderer::buildAccelerationStructures(CommandList cmd) const {
|
|||
dev->Barrier(barriers, 1, cmd);
|
||||
}
|
||||
|
||||
// ── TLAS (2 instances: blocky + smooth) ──────────────────────
|
||||
// ── TLAS (2 instances: blocky + smooth) ─────────────────────
|
||||
// Topings excluded from TLAS: 23M+ tris slows all ray traversal
|
||||
// for negligible shadow contribution (blades too thin).
|
||||
// Always recreate TLAS with pre-filled instance data via CreateBuffer2.
|
||||
// RAY_TRACING instance buffers have special resource state requirements,
|
||||
// so UpdateBuffer (CopyBufferRegion) would crash on state mismatch.
|
||||
|
|
@ -1172,6 +1179,8 @@ void VoxelRenderer::buildAccelerationStructures(CommandList cmd) const {
|
|||
dev->WriteTopLevelAccelerationStructureInstance(&inst, (uint8_t*)dest + idx * instSize);
|
||||
idx++;
|
||||
}
|
||||
|
||||
// Topings excluded from TLAS for performance (23M+ tris, negligible shadows)
|
||||
};
|
||||
|
||||
bool ok = dev->CreateBuffer2(&bufdesc, initInstances, &desc.top_level.instance_buffer);
|
||||
|
|
@ -1452,13 +1461,13 @@ void VoxelRenderer::render(
|
|||
cb.windTime = windTime_;
|
||||
// Stylized lighting (Phase 7) — Wonderbox-inspired
|
||||
cb.skyAmbient = XMFLOAT4(0.50f, 0.55f, 0.65f, 0.0f); // cool sky fill
|
||||
cb.groundAmbient = XMFLOAT4(0.28f, 0.22f, 0.15f, 0.0f); // warm brown ground
|
||||
cb.groundAmbient = XMFLOAT4(0.25f, 0.24f, 0.14f, 0.0f); // warm green-brown ground bounce
|
||||
cb.shadowTint = XMFLOAT4(0.50f, 0.42f, 0.70f, 0.0f); // purple-blue shadows
|
||||
cb.fogColor = XMFLOAT4(0.78f, 0.73f, 0.60f, 1.0f); // warm sandy 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.15f, 1.8f, 0.0f, 0.0f); // moderate saturation, good exposure
|
||||
cb.toneMapParams = XMFLOAT4(1.10f, 1.8f, 0.0f, 0.0f); // natural saturation, balanced exposure
|
||||
dev->UpdateBuffer(&constantBuffer_, &cb, cmd, sizeof(cb));
|
||||
// Save current VP for next frame's temporal reprojection
|
||||
XMStoreFloat4x4(&prevViewProjection_, vpMatrix);
|
||||
|
|
@ -1948,6 +1957,22 @@ void VoxelRenderer::uploadTopingData(const TopingSystem& topingSystem) {
|
|||
const auto& instances = topingSystem.getInstances();
|
||||
if (instances.empty()) return;
|
||||
|
||||
// Log a few grass toping positions (one-time, for camera placement debug)
|
||||
static bool loggedPositions = false;
|
||||
if (!loggedPositions) {
|
||||
loggedPositions = true;
|
||||
int count = 0;
|
||||
for (const auto& inst : instances) {
|
||||
if (inst.topingType == 1 && count < 5) { // type 1 = grass
|
||||
char msg[128];
|
||||
snprintf(msg, sizeof(msg), "GRASS toping: pos=(%.0f, %.0f, %.0f) variant=%d",
|
||||
inst.wx, inst.wy, inst.wz, inst.variant);
|
||||
wi::backlog::post(msg);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GPU instances are just float3 (12 bytes), sorted by (type, variant) for batched draws.
|
||||
// We sort a copy and build a draw group table.
|
||||
// Reuse persistent vectors to avoid per-frame allocations.
|
||||
|
|
@ -1977,6 +2002,8 @@ void VoxelRenderer::uploadTopingData(const TopingSystem& topingSystem) {
|
|||
ibDesc.stride = sizeof(TopingGPUInst);
|
||||
ibDesc.usage = Usage::DEFAULT;
|
||||
device_->CreateBuffer(&ibDesc, topingGpuInsts_.data(), &topingInstanceBuffer_);
|
||||
|
||||
// Toping BLAS: SKIPPED (23M+ tris slows RT for negligible shadow contribution)
|
||||
}
|
||||
|
||||
void VoxelRenderer::renderTopings(
|
||||
|
|
@ -2764,7 +2791,8 @@ void VoxelRenderPath::Compose(CommandList cmd) const {
|
|||
if (renderer.isRTReady()) {
|
||||
stats += "RT: TLAS ready | Blocky "
|
||||
+ std::to_string(renderer.getRTBlockyTriCount()) + " tris | Smooth "
|
||||
+ std::to_string(renderer.getRTSmoothTriCount()) + " tris"
|
||||
+ std::to_string(renderer.getRTSmoothTriCount()) + " tris | Topings "
|
||||
+ std::to_string(renderer.getRTTopingTriCount()) + " tris"
|
||||
+ " | Shadows+AO " + std::string(renderer.rtShadowDebug_ == 1 ? "DBG_SHD" : (renderer.rtShadowDebug_ == 2 ? "DBG_AO" : (renderer.isRTShadowsEnabled() ? "ON" : "OFF"))) + "\n";
|
||||
} else {
|
||||
stats += "RT: building...\n";
|
||||
|
|
|
|||
|
|
@ -206,12 +206,17 @@ private:
|
|||
wi::graphics::GPUBuffer blasIndexBuffer_; // sequential uint32 indices [0,1,2,...] for BLAS
|
||||
mutable wi::graphics::RaytracingAccelerationStructure blockyBLAS_;
|
||||
mutable wi::graphics::RaytracingAccelerationStructure smoothBLAS_;
|
||||
mutable wi::graphics::RaytracingAccelerationStructure topingBLAS_;
|
||||
mutable wi::graphics::RaytracingAccelerationStructure tlas_;
|
||||
mutable wi::graphics::GPUBuffer topingBLASPositionBuffer_; // float3[] world-space toping positions
|
||||
mutable wi::graphics::GPUBuffer topingBLASIndexBuffer_; // sequential indices for toping BLAS
|
||||
mutable uint32_t topingBLASIndexCount_ = 0; // size of toping index buffer
|
||||
static constexpr uint32_t MAX_BLAS_VERTICES = MEGA_BUFFER_CAPACITY * 6; // 6 verts per quad
|
||||
mutable bool rtAvailable_ = false; // GPU supports RT
|
||||
mutable bool rtDirty_ = true; // BLAS/TLAS need rebuild
|
||||
mutable uint32_t rtBlockyVertexCount_ = 0; // current blocky BLAS vertex count
|
||||
mutable uint32_t rtSmoothVertexCount_ = 0; // current smooth BLAS vertex count
|
||||
mutable uint32_t rtTopingVertexCount_ = 0; // current toping BLAS vertex count
|
||||
|
||||
void dispatchBLASExtract(wi::graphics::CommandList cmd) const;
|
||||
void buildAccelerationStructures(wi::graphics::CommandList cmd) const;
|
||||
|
|
@ -304,6 +309,7 @@ public:
|
|||
bool isRTShadowsEnabled() const { return rtShadowsEnabled_; }
|
||||
uint32_t getRTBlockyTriCount() const { return rtBlockyVertexCount_ / 3; }
|
||||
uint32_t getRTSmoothTriCount() const { return rtSmoothVertexCount_ / 3; }
|
||||
uint32_t getRTTopingTriCount() const { return rtTopingVertexCount_ / 3; }
|
||||
const wi::graphics::RaytracingAccelerationStructure& getTLAS() const { return tlas_; }
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue