structures, also they're a little broken, they need to check for neighbouring YZones as well
This commit is contained in:
parent
a0b00b0f1c
commit
d1abde919d
12 changed files with 604 additions and 134 deletions
|
|
@ -53,7 +53,9 @@ layout(push_constant) uniform ChunkInfo {
|
|||
uint faceOffset;
|
||||
uint voxelTypeCount;
|
||||
uint indirectCommandIndex;
|
||||
uint padding0;
|
||||
uint biomeCount;
|
||||
uint structureCount;
|
||||
uint worldSeed;
|
||||
};
|
||||
|
||||
uint flatten(ivec3 pos) {
|
||||
|
|
|
|||
|
|
@ -21,9 +21,11 @@ layout(push_constant) uniform ChunkInfo {
|
|||
ivec3 chunkPos;
|
||||
int slot;
|
||||
uint faceOffset;
|
||||
uint unused0;
|
||||
uint voxelTypeCount;
|
||||
uint indirectCommandIndex;
|
||||
uint padding0;
|
||||
uint biomeCount;
|
||||
uint structureCount;
|
||||
uint worldSeed;
|
||||
};
|
||||
|
||||
void main() {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,23 @@ layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
|
|||
|
||||
const int CHUNK_SIZE = 16;
|
||||
const int CHUNK_AREA = CHUNK_SIZE * CHUNK_SIZE;
|
||||
const int STRUCTURE_CELL_SIZE = 16;
|
||||
const int REPLACE_AIR_ONLY = 1;
|
||||
const int REPLACE_FOLIAGE = 2;
|
||||
const int REQUIRE_FLAT_GROUND = 4;
|
||||
const int HAS_FOUNDATION = 8;
|
||||
|
||||
// A 1D storage buffer representing a flat 3D array of voxel IDs
|
||||
layout(std430, binding = 0) buffer VoxelData {
|
||||
uint voxels[];
|
||||
layout(push_constant) uniform ChunkInfo {
|
||||
ivec3 chunkPos;
|
||||
int slot;
|
||||
uint faceOffset;
|
||||
uint voxelTypeCount;
|
||||
uint indirectCommandIndex;
|
||||
uint biomeCount;
|
||||
uint structureCount;
|
||||
uint worldSeed;
|
||||
};
|
||||
|
||||
struct Biome {
|
||||
uint surfaceBlock;
|
||||
uint dirtBlock;
|
||||
|
|
@ -18,19 +30,45 @@ struct Biome {
|
|||
uint[4] otherFoliage;
|
||||
};
|
||||
|
||||
struct Structure {
|
||||
uint sizeX;
|
||||
uint sizeY;
|
||||
uint sizeZ;
|
||||
uint blockOffset;
|
||||
uint spawnSpacing;
|
||||
uint spawnChance;
|
||||
uint allowedBiome;
|
||||
uint groundBlock;
|
||||
uint flags;
|
||||
};
|
||||
|
||||
struct StructureVoxelResult {
|
||||
uint block;
|
||||
uint flags;
|
||||
};
|
||||
|
||||
layout(std430, binding = 0) buffer VoxelData {
|
||||
uint voxels[];
|
||||
};
|
||||
|
||||
layout(std430, set = 1, binding = 0) readonly buffer BiomeUniform {
|
||||
Biome biomes[];
|
||||
} BiomeReg;
|
||||
|
||||
layout(push_constant) uniform ChunkInfo {
|
||||
ivec3 chunkPos;
|
||||
int slot;
|
||||
uint faceOffset;
|
||||
uint unused0;
|
||||
uint indirectCommandIndex;
|
||||
uint biomeCount;
|
||||
};
|
||||
layout(std430, set = 2, binding = 0) readonly buffer StructureRegistry {
|
||||
Structure structures[];
|
||||
} structureReg;
|
||||
|
||||
layout(std430, set = 2, binding = 1) readonly buffer StructureBlocks {
|
||||
uint blocks[];
|
||||
} structureBlocks;
|
||||
|
||||
uint hash21(uvec2 p) {
|
||||
p = p * 1664525u + 1013904223u;
|
||||
p.x += p.y * 1664525u;
|
||||
p.y += p.x * 1664525u;
|
||||
return p.x ^ (p.x >> 16);
|
||||
}
|
||||
float random3to1(vec3 st) {
|
||||
return fract(sin(dot(st, vec3(127.1, 311.7, 74.7))) * 43758.5453123);
|
||||
}
|
||||
|
|
@ -127,6 +165,331 @@ bool isCave(vec3 worldPos) {
|
|||
|
||||
return n > 0.5;
|
||||
}
|
||||
|
||||
bool shouldSpawn(uint seed, Structure s) {
|
||||
return pcg_hash(seed) % 1000u < s.spawnChance;
|
||||
}
|
||||
|
||||
bool hasFlag(uint flags, uint flag) {
|
||||
return (flags & flag) != 0u;
|
||||
}
|
||||
|
||||
int floorDiv(int value, int divisor) {
|
||||
int q = value / divisor;
|
||||
int r = value - q * divisor;
|
||||
|
||||
if (r != 0 && ((r < 0) != (divisor < 0))) {
|
||||
q -= 1;
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
ivec2 getStructureCell(ivec2 worldXZ, int spacing) {
|
||||
return ivec2(
|
||||
floorDiv(worldXZ.x, spacing),
|
||||
floorDiv(worldXZ.y, spacing)
|
||||
);
|
||||
}
|
||||
float getPrimarySurfaceHeight(int worldX, int worldY, int worldZ) {
|
||||
float YZone = floor(float(worldY) / 150.0) * 150.0;
|
||||
|
||||
return valueNoise(vec2(float(worldX) + YZone * 2.0, float(worldZ) + YZone * 2.0) * 0.005) * 100.0 +
|
||||
valueNoise(vec2(float(worldX) + YZone * 2.0, float(worldZ) + YZone * 2.0) * 0.01) * 10.0 +
|
||||
valueNoise(vec2(float(worldX) + YZone * 2.0, float(worldZ) + YZone * 2.0) * 0.1) * 5.0 +
|
||||
valueNoise(vec2(float(worldX) + YZone * 2.0, float(worldZ) + YZone * 2.0)) * 0.5 +
|
||||
YZone;
|
||||
}
|
||||
|
||||
float getSecondarySurfaceHeight(int worldX, int worldY, int worldZ) {
|
||||
float YZone2 = floor(float(worldY) / 75.0) * 75.0;
|
||||
|
||||
return valueNoise(vec2(float(worldX) + YZone2 * 2.0, float(worldZ) + YZone2 * 2.0) * 0.005) * 50.0 +
|
||||
valueNoise(vec2(float(worldX) + YZone2 * 2.0, float(worldZ) + YZone2 * 2.0) * 0.01) * 10.0 +
|
||||
valueNoise(vec2(float(worldX) + YZone2 * 2.0, float(worldZ) + YZone2 * 2.0) * 0.1) * 5.0 +
|
||||
valueNoise(vec2(float(worldX) + YZone2 * 2.0, float(worldZ) + YZone2 * 2.0)) * 0.5 +
|
||||
YZone2;
|
||||
}
|
||||
|
||||
float getSurfaceHeight(int worldX, int worldZ, int referenceY) {
|
||||
float primary = getPrimarySurfaceHeight(worldX, referenceY, worldZ);
|
||||
float secondary = getSecondarySurfaceHeight(worldX, referenceY, worldZ);
|
||||
|
||||
float primaryDist = abs(primary - float(referenceY));
|
||||
float secondaryDist = abs(secondary - float(referenceY));
|
||||
|
||||
return primaryDist <= secondaryDist ? primary : secondary;
|
||||
}
|
||||
|
||||
bool cellSpawnsStructure(uint seed, Structure s) {
|
||||
if (s.spawnChance == 0u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pcg_hash(seed) % 1000u < s.spawnChance;
|
||||
}
|
||||
uint chooseStructure(uint seed, uint biome) {
|
||||
if (structureCount == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint start = pcg_hash(seed ^ 0xA53A9D31u) % structureCount;
|
||||
|
||||
for (uint i = 0u; i < structureCount; i++) {
|
||||
uint id = (start + i) % structureCount;
|
||||
Structure s = structureReg.structures[id];
|
||||
|
||||
if (s.allowedBiome == 0u || s.allowedBiome == biome + 1u) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
ivec2 getStructureAnchorXZ(ivec2 cell, uint seed, uint spacing) {
|
||||
int structureSpacing = int(max(spacing, 1u));
|
||||
|
||||
uint jitterX = randomRangeUint(seed ^ 0xB5297A4Du, 0u, uint(structureSpacing - 1));
|
||||
uint jitterZ = randomRangeUint(seed ^ 0x68E31DA4u, 0u, uint(structureSpacing - 1));
|
||||
|
||||
return cell * structureSpacing + ivec2(int(jitterX), int(jitterZ));
|
||||
}
|
||||
|
||||
uint getStructureBlock(uint structureId, ivec3 localPos) {
|
||||
Structure s = structureReg.structures[structureId];
|
||||
|
||||
if (localPos.x < 0 || localPos.x >= int(s.sizeX)) return 0u;
|
||||
if (localPos.y < 0 || localPos.y >= int(s.sizeY)) return 0u;
|
||||
if (localPos.z < 0 || localPos.z >= int(s.sizeZ)) return 0u;
|
||||
|
||||
uint index =
|
||||
uint(localPos.x) * s.sizeY * s.sizeZ +
|
||||
uint(localPos.y) * s.sizeZ +
|
||||
uint(localPos.z);
|
||||
|
||||
return structureBlocks.blocks[s.blockOffset + index];
|
||||
}
|
||||
|
||||
bool isGroundFlatEnough(ivec2 anchorXZ, uint structureId, int referenceY) {
|
||||
Structure s = structureReg.structures[structureId];
|
||||
|
||||
float h0 = getSurfaceHeight(anchorXZ.x, anchorXZ.y, referenceY);
|
||||
float h1 = getSurfaceHeight(anchorXZ.x + int(s.sizeX) - 1, anchorXZ.y, referenceY);
|
||||
float h2 = getSurfaceHeight(anchorXZ.x, anchorXZ.y + int(s.sizeZ) - 1, referenceY);
|
||||
float h3 = getSurfaceHeight(anchorXZ.x + int(s.sizeX) - 1, anchorXZ.y + int(s.sizeZ) - 1, referenceY);
|
||||
|
||||
float minH = min(min(h0, h1), min(h2, h3));
|
||||
float maxH = max(max(h0, h1), max(h2, h3));
|
||||
|
||||
return maxH - minH <= 2.0;
|
||||
}
|
||||
|
||||
ivec3 getStructureCell3D(ivec3 worldPos, int spacing) {
|
||||
return ivec3(
|
||||
floorDiv(worldPos.x, spacing),
|
||||
floorDiv(worldPos.y, 75),
|
||||
floorDiv(worldPos.z, spacing)
|
||||
);
|
||||
}
|
||||
|
||||
uint hash31(ivec3 p) {
|
||||
uvec3 v = uvec3(p) * uvec3(1664525u, 22695477u, 1103515245u) + uvec3(1013904223u);
|
||||
v.x += v.y * v.z;
|
||||
v.y += v.z * v.x;
|
||||
v.z += v.x * v.y;
|
||||
return v.x ^ v.y ^ v.z;
|
||||
}
|
||||
|
||||
uint getBaseTerrainVoxelAt(ivec3 worldPos, uint biome) {
|
||||
Biome currentBiome = BiomeReg.biomes[biome];
|
||||
|
||||
float YZone = floor(float(worldPos.y) / 150.0) * 150.0;
|
||||
float SurfaceHeight =
|
||||
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.005) * 100.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.01) * 10.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.1) * 5.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0)) * 0.5 +
|
||||
YZone;
|
||||
|
||||
float IslandUnderneathHeight =
|
||||
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.005) * 100.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.1) * 20.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0) * 0.1) * 5.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone * 2.0, float(worldPos.z) + YZone * 2.0)) * 0.5 -
|
||||
15.0 +
|
||||
YZone;
|
||||
|
||||
float grassNoise = valueNoise(vec2(float(worldPos.z) * 0.5 - YZone * 2.0, float(worldPos.x) * 0.5 - YZone * 2.0));
|
||||
uint randomBlockVal = randomRangeUint(uint(worldPos.x) * 73856093u ^ uint(worldPos.z) * 19349663u, 0u, 3u);
|
||||
|
||||
uint voxelType = 0u;
|
||||
|
||||
if (float(worldPos.y) <= SurfaceHeight && float(worldPos.y) >= IslandUnderneathHeight) {
|
||||
float depthBelowSurface = SurfaceHeight - float(worldPos.y);
|
||||
|
||||
if (depthBelowSurface < 0.5) {
|
||||
if (grassNoise < 0.65) voxelType = currentBiome.grassFoliage;
|
||||
else if (grassNoise < 0.75) voxelType = currentBiome.flowers[randomBlockVal];
|
||||
else if (grassNoise < 0.85) voxelType = currentBiome.otherFoliage[randomBlockVal];
|
||||
else voxelType = 0u;
|
||||
} else if (depthBelowSurface < 2.5) {
|
||||
voxelType = currentBiome.surfaceBlock;
|
||||
} else if (depthBelowSurface < 6.0) {
|
||||
voxelType = currentBiome.dirtBlock;
|
||||
} else {
|
||||
voxelType = currentBiome.stoneBlock;
|
||||
}
|
||||
} else {
|
||||
float YZone2 = floor(float(worldPos.y) / 75.0) * 75.0;
|
||||
|
||||
float IslandUnderneathHeight2 =
|
||||
valueNoise(vec2(float(worldPos.x) - YZone2 * 2.0, float(worldPos.z) - YZone2 * 2.0) * 0.005) * 60.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0) * 0.1) * 20.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0) * 0.1) * 10.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0)) * 0.5 -
|
||||
5.0 +
|
||||
YZone2;
|
||||
|
||||
float height2 =
|
||||
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0) * 0.005) * 50.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0) * 0.01) * 10.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0) * 0.1) * 5.0 +
|
||||
valueNoise(vec2(float(worldPos.x) + YZone2 * 2.0, float(worldPos.z) + YZone2 * 2.0)) * 0.5 +
|
||||
YZone2;
|
||||
|
||||
if (float(worldPos.y) <= height2 && float(worldPos.y) >= IslandUnderneathHeight2) {
|
||||
float depthBelowSurface = height2 - float(worldPos.y);
|
||||
|
||||
if (depthBelowSurface < 0.5) {
|
||||
if (grassNoise < 0.65) voxelType = currentBiome.grassFoliage;
|
||||
else if (grassNoise < 0.75) voxelType = currentBiome.flowers[randomBlockVal];
|
||||
else if (grassNoise < 0.85) voxelType = currentBiome.otherFoliage[randomBlockVal];
|
||||
else voxelType = 0u;
|
||||
} else if (depthBelowSurface < 1.5) {
|
||||
voxelType = currentBiome.surfaceBlock;
|
||||
} else if (depthBelowSurface < 6.0) {
|
||||
voxelType = currentBiome.dirtBlock;
|
||||
} else {
|
||||
voxelType = currentBiome.stoneBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (voxelType != 0u && isCave(vec3(worldPos) * 0.05)) {
|
||||
voxelType = 0u;
|
||||
}
|
||||
|
||||
return voxelType;
|
||||
}
|
||||
|
||||
bool isValidStructureAnchor(ivec3 origin, uint structureId, uint biome) {
|
||||
Structure s = structureReg.structures[structureId];
|
||||
|
||||
ivec3 groundPos = origin + ivec3(0, -1, 0);
|
||||
ivec3 basePos = origin;
|
||||
|
||||
uint groundVoxel = getBaseTerrainVoxelAt(groundPos, biome);
|
||||
uint baseVoxel = getBaseTerrainVoxelAt(basePos, biome);
|
||||
|
||||
if (groundVoxel == 0u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s.groundBlock != 0u && groundVoxel != s.groundBlock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (baseVoxel != 0u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isCave(vec3(groundPos) * 0.05)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int getStructureReferenceY(int worldY) {
|
||||
return floorDiv(worldY, 75) * 75;
|
||||
}
|
||||
|
||||
bool isStructureFootprintSupported(ivec3 origin, uint structureId, uint biome) {
|
||||
Structure s = structureReg.structures[structureId];
|
||||
|
||||
int supportedCorners = 0;
|
||||
|
||||
ivec3 p0 = origin + ivec3(0, -1, 0);
|
||||
ivec3 p1 = origin + ivec3(int(s.sizeX) - 1, -1, 0);
|
||||
ivec3 p2 = origin + ivec3(0, -1, int(s.sizeZ) - 1);
|
||||
ivec3 p3 = origin + ivec3(int(s.sizeX) - 1, -1, int(s.sizeZ) - 1);
|
||||
|
||||
if (getBaseTerrainVoxelAt(p0, biome) != 0u) supportedCorners++;
|
||||
if (getBaseTerrainVoxelAt(p1, biome) != 0u) supportedCorners++;
|
||||
if (getBaseTerrainVoxelAt(p2, biome) != 0u) supportedCorners++;
|
||||
if (getBaseTerrainVoxelAt(p3, biome) != 0u) supportedCorners++;
|
||||
|
||||
return supportedCorners == 4;
|
||||
}
|
||||
|
||||
StructureVoxelResult getStructureVoxelAt(ivec3 worldPos, uint biome) {
|
||||
StructureVoxelResult result;
|
||||
result.block = 0u;
|
||||
result.flags = 0u;
|
||||
|
||||
if (structureCount == 0u) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (uint structureId = 0u; structureId < structureCount; structureId++) {
|
||||
Structure s = structureReg.structures[structureId];
|
||||
|
||||
if (s.allowedBiome != 0u && s.allowedBiome != biome + 1u) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int spacing = int(max(s.spawnSpacing, 1u));
|
||||
ivec3 baseCell = getStructureCell3D(worldPos, spacing);
|
||||
|
||||
for (int ox = -1; ox <= 1; ox++) {
|
||||
for (int oz = -1; oz <= 1; oz++) {
|
||||
ivec3 cell = baseCell + ivec3(ox, 0, oz);
|
||||
|
||||
uint seed = hash31(cell + ivec3(0, int(structureId) * 97, 0)) ^ worldSeed;
|
||||
|
||||
if (!cellSpawnsStructure(seed, s)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ivec2 anchorXZ = getStructureAnchorXZ(cell.xz, seed, s.spawnSpacing);
|
||||
|
||||
int referenceY = cell.y * 75;
|
||||
float anchorHeight = getSurfaceHeight(anchorXZ.x, anchorXZ.y, referenceY);
|
||||
int anchorY = int(floor(anchorHeight)) + 1;
|
||||
|
||||
ivec3 origin = ivec3(anchorXZ.x, anchorY, anchorXZ.y);
|
||||
|
||||
if (!isValidStructureAnchor(origin, structureId, biome)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ivec3 local = worldPos - origin;
|
||||
|
||||
uint block = getStructureBlock(structureId, local);
|
||||
|
||||
if (block != 0u) {
|
||||
result.block = block;
|
||||
result.flags = s.flags;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec3 localPos = ivec3(gl_GlobalInvocationID.xyz);
|
||||
ivec3 worldPos = chunkPos * CHUNK_SIZE + localPos;
|
||||
|
|
@ -202,6 +565,18 @@ void main() {
|
|||
}
|
||||
}
|
||||
|
||||
StructureVoxelResult structureVoxel = getStructureVoxelAt(worldPos, biome);
|
||||
|
||||
if (structureVoxel.block != 0u) {
|
||||
if (hasFlag(structureVoxel.flags, uint(REPLACE_AIR_ONLY))) {
|
||||
if (voxelType == 0u) {
|
||||
voxelType = structureVoxel.block;
|
||||
}
|
||||
} else {
|
||||
voxelType = structureVoxel.block;
|
||||
}
|
||||
}
|
||||
|
||||
uint index = uint((localPos.x * CHUNK_AREA) + (localPos.y * CHUNK_SIZE) + localPos.z);
|
||||
voxels[index] = voxelType;
|
||||
}
|
||||
|
|
@ -110,7 +110,8 @@ void main() {
|
|||
faceId = min(faceId, 7u);
|
||||
|
||||
vec3 localPos = vec3(voxelX, voxelY, voxelZ) + FACE_CORNERS[faceId][cornerIndex];
|
||||
vec3 worldPos = localPos + push_constants.ModelOffset.xyz * 16.0;
|
||||
vec3 Offset = vec3(0,0,0);
|
||||
vec3 worldPos = localPos + Offset + push_constants.ModelOffset.xyz * 16.0;
|
||||
|
||||
vec4 worldPosVec4 = vec4(worldPos, 1.0);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
#version 460 core
|
||||
|
||||
in vec3 FragPos;
|
||||
in vec2 TexCoords;
|
||||
in vec3 Normal;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform vec3 cameraPos;
|
||||
uniform vec3 lightPos;
|
||||
|
||||
// Material constants
|
||||
const vec3 waterShallowColor = vec3(0.0, 0.6, 0.7);
|
||||
const vec3 waterDeepColor = vec3(0.05, 0.15, 0.3);
|
||||
const vec3 sunColor = vec3(1.0, 0.95, 0.8);
|
||||
|
||||
void main() {
|
||||
// Normalize vectors
|
||||
vec3 n = normalize(Normal);
|
||||
vec3 viewDir = normalize(cameraPos - FragPos);
|
||||
vec3 lightDir = normalize(lightPos - FragPos);
|
||||
|
||||
// 1. Ambient Lighting
|
||||
vec3 ambient = 0.3 * waterShallowColor;
|
||||
|
||||
// 2. Diffuse Shading
|
||||
float diff = max(dot(n, lightDir), 0.0);
|
||||
vec3 diffuse = diff * sunColor * 0.4;
|
||||
|
||||
// 3. Specular Highlights (Blinn-Phong)
|
||||
vec3 halfwayDir = normalize(lightDir + viewDir);
|
||||
float spec = pow(max(dot(n, halfwayDir), 0.0), 64.0); // High shininess for water glaze
|
||||
vec3 specular = spec * sunColor * 0.8;
|
||||
|
||||
// 4. Fresnel Approximation (Schlick's approximation)
|
||||
// Water has a base reflectivity of roughly 0.02 at a perpendicular view angle
|
||||
float F0 = 0.02;
|
||||
float fresnel = F0 + (1.0 - F0) * pow(1.0 - max(dot(n, viewDir), 0.0), 5.0);
|
||||
|
||||
// Mix deep and shallow water colors based on the normal angle
|
||||
vec3 baseWaterColor = mix(waterShallowColor, waterDeepColor, max(dot(n, vec3(0.0, 1.0, 0.0)), 0.0));
|
||||
|
||||
// Combine lighting results
|
||||
vec3 lightingResult = ambient + diffuse + specular;
|
||||
|
||||
// Blend final look with Fresnel reflection dominance
|
||||
vec3 finalColor = mix(baseWaterColor + specular, lightingResult + vec3(fresnel * 0.5), fresnel);
|
||||
|
||||
// Output final color with subtle translucency opacity
|
||||
FragColor = vec4(finalColor, 0.85);
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#version 450
|
||||
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec2 aTexCoords;
|
||||
|
||||
out vec3 FragPos;
|
||||
out vec2 TexCoords;
|
||||
out vec3 Normal;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
uniform float uTime;
|
||||
|
||||
// Wave configuration constants
|
||||
const float AMPLITUDE = 0.15;
|
||||
const float FREQUENCY = 1.5;
|
||||
const float SPEED = 2.0;
|
||||
|
||||
// Simple wave function that modifies elevation based on position and time
|
||||
float calculateWave(vec3 pos, float time, vec2 direction) {
|
||||
return AMPLITUDE * sin(dot(pos.xz, direction) * FREQUENCY + time * SPEED);
|
||||
}
|
||||
|
||||
// Derivative of the wave function to calculate accurate dynamic surface normals
|
||||
vec3 calculateWaveNormal(vec3 pos, float time) {
|
||||
float dx = AMPLITUDE * FREQUENCY * cos(pos.x * FREQUENCY + time * SPEED);
|
||||
float dz = AMPLITUDE * FREQUENCY * cos(pos.z * FREQUENCY + time * SPEED);
|
||||
return normalize(vec3(-dx, 1.0, -dz));
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 displacedPos = aPos;
|
||||
|
||||
// Combine two wave directions for a less predictable, more organic look
|
||||
displacedPos.y += calculateWave(aPos, uTime, vec2(1.0, 0.0));
|
||||
displacedPos.y += calculateWave(aPos, uTime * 1.2, vec2(0.5, 0.8));
|
||||
|
||||
FragPos = vec3(model * vec4(displacedPos, 1.0));
|
||||
TexCoords = aTexCoords;
|
||||
|
||||
// Pass transformed normals and position to the fragment shader
|
||||
Normal = mat3(transpose(inverse(model))) * calculateWaveNormal(displacedPos, uTime);
|
||||
|
||||
gl_Position = projection * view * model * vec4(displacedPos, 1.0);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue