Phase 3: per-material bleed flags + patch-based terrain for blend testing
- Add bleedMask/resistBleedMask bitmasks to CB for per-material blend control - Grass: canBleed + resistsBleed (bleeds onto others, nothing bleeds onto it) - Stone: no bleed (doesn't overflow, but accepts bleed from others) - Other materials: normal bidirectional blending - PS checks flags before blending: mainResists → skip, !neighCanBleed → skip - Flatten terrain (heightScale 64→20) for better surface visibility - Replace altitude-based material bands with noise-based 2D patches (3 noise channels create organic patches of all 5 materials on surface) - Make stone/sand more visually distinct (stone=blue-gray, sand=warm yellow) - Lower stone heightContrast (1.2→0.5) so neighbors bleed onto it more
This commit is contained in:
parent
d7e69f97ca
commit
f166394b60
6 changed files with 53 additions and 26 deletions
|
|
@ -107,8 +107,8 @@ GPU: frustum cull compute → indirect args → DrawInstancedIndirectCount (1 ap
|
|||
## Phases de développement
|
||||
|
||||
- [x] **Phase 1** — Setup, meshing CPU, rendu basique
|
||||
- [ ] **Phase 2** — GPU-driven pipeline, mega-buffer, culling, compute shaders
|
||||
- [ ] **Phase 3** — Texture blending (triplanar, height-based)
|
||||
- [x] **Phase 2** — GPU-driven pipeline, mega-buffer, culling, compute shaders
|
||||
- [x] **Phase 3** — Texture blending (triplanar, height-based)
|
||||
- [ ] **Phase 4** — Toping (rebords, bordures procédurales)
|
||||
- [ ] **Phase 5** — Rendu smooth (Surface Nets / Marching Cubes)
|
||||
- [ ] **Phase 6** — Ray tracing hybride (RT shadows + AO)
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ cbuffer VoxelCB : register(b0) {
|
|||
// Frustum culling data (used by cull compute shader)
|
||||
float4 frustumPlanes[6]; // ax+by+cz+d=0, xyz=normal, w=distance
|
||||
uint chunkCount;
|
||||
uint _cullPad0;
|
||||
uint _cullPad1;
|
||||
uint bleedMask; // bit N set = material N can bleed onto neighbors
|
||||
uint resistBleedMask; // bit N set = material N resists bleed from neighbors
|
||||
uint _cullPad2;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -203,9 +203,16 @@ float4 main(PSInput input) : SV_TARGET0
|
|||
float uWeight = saturate((uAdj - blendStart) / (1.0 - blendStart)) * 0.5;
|
||||
float vWeight = saturate((vAdj - blendStart) / (1.0 - blendStart)) * 0.5;
|
||||
|
||||
// Only blend if neighbor has a different material
|
||||
bool uBlend = (uNeighborMat > 0u && uNeighborMat != input.materialID && uWeight > 0.001);
|
||||
bool vBlend = (vNeighborMat > 0u && vNeighborMat != input.materialID && vWeight > 0.001);
|
||||
// Only blend if neighbor has a different material AND blend flags allow it:
|
||||
// - Current material must NOT resist bleed (resistBleedMask)
|
||||
// - Neighbor material must be allowed to bleed (bleedMask)
|
||||
bool mainResists = (resistBleedMask >> input.materialID) & 1u;
|
||||
bool uNeighCanBleed = (bleedMask >> uNeighborMat) & 1u;
|
||||
bool vNeighCanBleed = (bleedMask >> vNeighborMat) & 1u;
|
||||
bool uBlend = (uNeighborMat > 0u && uNeighborMat != input.materialID && uWeight > 0.001
|
||||
&& !mainResists && uNeighCanBleed);
|
||||
bool vBlend = (vNeighborMat > 0u && vNeighborMat != input.materialID && vWeight > 0.001
|
||||
&& !mainResists && vNeighCanBleed);
|
||||
|
||||
// ── DEBUG BLEND MODE (F4): show blend zones as colors ──
|
||||
if (debugBlend > 0.5) {
|
||||
|
|
|
|||
|
|
@ -230,8 +230,8 @@ void VoxelRenderer::generateTextures() {
|
|||
MatColor colors[NUM_MATERIALS] = {
|
||||
{ 60, 140, 40, 80, 180, 60, 101, 1.5f, 0.8f }, // Grass: medium bumps
|
||||
{ 100, 70, 40, 140, 100, 60, 202, 0.8f, 0.6f }, // Dirt: smooth mounds
|
||||
{ 110, 110, 105, 140, 140, 130, 303, 2.5f, 1.2f }, // Stone: rough, high peaks
|
||||
{ 200, 190, 140, 230, 220, 170, 404, 3.0f, 0.4f }, // Sand: fine, uniform
|
||||
{ 80, 80, 90, 120, 120, 130, 303, 2.5f, 0.5f }, // Stone: darker blue-gray, moderate height (was 1.2, lowered so neighbors bleed onto it more)
|
||||
{ 220, 200, 130, 245, 230, 160, 404, 3.0f, 0.4f }, // Sand: warmer yellow, fine
|
||||
{ 220, 225, 230, 245, 248, 252, 505, 1.0f, 0.5f }, // Snow: smooth, soft
|
||||
};
|
||||
|
||||
|
|
@ -690,8 +690,12 @@ void VoxelRenderer::render(
|
|||
cb.blendEnabled = 1.0f; // Phase 3: PS-based blending enabled in GPU mesh path
|
||||
cb.debugBlend = debugBlend_ ? 1.0f : 0.0f;
|
||||
cb.chunkCount = chunkCount_;
|
||||
cb._cullPad0 = 0;
|
||||
cb._cullPad1 = 0;
|
||||
// Per-material blend flags (bit N = material N):
|
||||
// canBleed: material can overflow visually onto adjacent voxels
|
||||
// resistBleed: adjacent materials cannot overflow onto this material
|
||||
// Material IDs: 1=Grass, 2=Dirt, 3=Stone, 4=Sand, 5=Snow
|
||||
cb.bleedMask = (1u << 1) | (1u << 2) | (1u << 4) | (1u << 5); // Grass, Dirt, Sand, Snow can bleed (NOT Stone)
|
||||
cb.resistBleedMask = (1u << 1); // Grass resists bleed (she bleeds onto others, not the reverse)
|
||||
cb._cullPad2 = 0;
|
||||
dev->UpdateBuffer(&constantBuffer_, &cb, cmd, sizeof(cb));
|
||||
|
||||
|
|
@ -777,8 +781,8 @@ void VoxelRenderer::render(
|
|||
cb.textureTiling = 0.25f;
|
||||
cb.blendEnabled = 0.0f; // Phase 3: blending disabled in CPU/MDI paths (no voxel data SRV)
|
||||
cb.debugBlend = 0.0f;
|
||||
cb._cullPad0 = 0;
|
||||
cb._cullPad1 = 0;
|
||||
cb.bleedMask = 0;
|
||||
cb.resistBleedMask = 0;
|
||||
cb._cullPad2 = 0;
|
||||
cb.chunkCount = chunkCount_;
|
||||
extractFrustumPlanes(vpMatrix, cb.frustumPlanes);
|
||||
|
|
|
|||
|
|
@ -125,8 +125,8 @@ private:
|
|||
float debugBlend;
|
||||
XMFLOAT4 frustumPlanes[6]; // ax+by+cz+d=0
|
||||
uint32_t chunkCount;
|
||||
uint32_t _cullPad0;
|
||||
uint32_t _cullPad1;
|
||||
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 _cullPad2;
|
||||
};
|
||||
wi::graphics::GPUBuffer constantBuffer_;
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ float VoxelWorld::fbm(float x, float y, float z, int octaves) const {
|
|||
|
||||
void VoxelWorld::generateChunk(Chunk& chunk, float timeOffset) {
|
||||
const float scale = 0.02f; // terrain horizontal scale
|
||||
const float heightScale = 64.0f;
|
||||
const float heightScale = 20.0f; // flatter terrain (was 64)
|
||||
const float baseHeight = 40.0f;
|
||||
const float caveScale = 0.05f;
|
||||
const float caveThreshold = 0.3f;
|
||||
|
|
@ -129,6 +129,28 @@ void VoxelWorld::generateChunk(Chunk& chunk, float timeOffset) {
|
|||
// to create a rolling wave effect across the terrain
|
||||
float height = baseHeight + heightScale * fbm(wx * scale, timeOffset, wz * scale, heightOctaves);
|
||||
|
||||
// ── Surface material via noise-based patches ──
|
||||
// Use 2D noise at different frequencies/seeds to create organic patches
|
||||
// of each material on the surface, instead of altitude bands.
|
||||
float matNoise1 = fbm(wx * 0.03f + 500.0f, 0.0f, wz * 0.03f + 500.0f, 3); // large patches
|
||||
float matNoise2 = fbm(wx * 0.08f + 1000.0f, 0.0f, wz * 0.08f + 1000.0f, 2); // medium detail
|
||||
float matNoise3 = fbm(wx * 0.05f + 2000.0f, 0.0f, wz * 0.05f + 2000.0f, 3); // third channel
|
||||
// Combined noise for material selection (range roughly -1..1)
|
||||
float matVal = matNoise1 * 0.6f + matNoise2 * 0.4f;
|
||||
|
||||
uint8_t surfaceMat;
|
||||
if (matVal < -0.25f) {
|
||||
surfaceMat = 4; // Sand
|
||||
} else if (matVal < 0.0f) {
|
||||
surfaceMat = 3; // Stone
|
||||
} else if (matVal < 0.30f) {
|
||||
surfaceMat = 1; // Grass
|
||||
} else if (matNoise3 > 0.1f) {
|
||||
surfaceMat = 5; // Snow (patches via independent noise)
|
||||
} else {
|
||||
surfaceMat = 2; // Dirt
|
||||
}
|
||||
|
||||
for (int y = 0; y < CHUNK_SIZE; y++) {
|
||||
float wy = (float)(chunk.pos.y * CHUNK_SIZE + y);
|
||||
VoxelData v;
|
||||
|
|
@ -142,22 +164,16 @@ void VoxelWorld::generateChunk(Chunk& chunk, float timeOffset) {
|
|||
if (std::abs(cave) < caveThreshold && wy > 10.0f && wy < height - 3.0f) {
|
||||
v = VoxelData(); // Cave
|
||||
} else if (wy > height - 1.0f) {
|
||||
if (wy > 90.0f) v = VoxelData(5);
|
||||
else if (wy > 70.0f) v = VoxelData(3);
|
||||
else if (wy < 25.0f) v = VoxelData(4);
|
||||
else v = VoxelData(1);
|
||||
v = VoxelData(surfaceMat);
|
||||
} else if (wy > height - 4.0f) {
|
||||
v = VoxelData(2);
|
||||
v = VoxelData(2); // Dirt sub-surface
|
||||
} else {
|
||||
v = VoxelData(3);
|
||||
v = VoxelData(3); // Stone deep underground
|
||||
}
|
||||
} else {
|
||||
// Animation path: simplified material assignment (no caves)
|
||||
if (wy > height - 1.0f) {
|
||||
if (wy > 90.0f) v = VoxelData(5);
|
||||
else if (wy > 70.0f) v = VoxelData(3);
|
||||
else if (wy < 25.0f) v = VoxelData(4);
|
||||
else v = VoxelData(1);
|
||||
v = VoxelData(surfaceMat);
|
||||
} else if (wy > height - 4.0f) {
|
||||
v = VoxelData(2);
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue