render block drops.

This commit is contained in:
IntegratedQuantum 2023-01-30 17:03:31 +01:00
parent 1a3c1e56c9
commit c1fac3ff3e
5 changed files with 270 additions and 33 deletions

View File

@ -222,4 +222,5 @@ void main() {
}
fragColor.rgb /= 4;
position = vec4(mvVertexPos, 1);
// TODO: Update the depth.
}

View File

@ -4,8 +4,10 @@ in vec3 startPosition;
in vec3 direction;
in vec3 cameraSpacePos;
flat in int faceNormal;
flat in uint voxelModel;
flat in uvec3 size;
flat in int voxelModel;
flat in int blockType;
flat in uvec3 lower;
flat in uvec3 upper;
layout(location = 0) out vec4 fragColor;
layout(location = 1) out vec4 position;
@ -19,14 +21,10 @@ struct Fog {
uniform vec3 ambientLight;
uniform mat4 projectionMatrix;
uniform float sizeScale;
uniform int time;
uniform Fog fog;
layout(std430, binding = 2) buffer _voxelModels
{
uint voxelModels[];
};
const float[6] normalVariations = float[6](
1.0, //vec3(0, 1, 0),
0.80, //vec3(0, -1, 0),
@ -36,11 +34,6 @@ const float[6] normalVariations = float[6](
0.85 //vec3(0, 0, -1)
);
uint getVoxel(uvec3 pos) {
uint index = (pos.x | pos.y*size.x)*size.z | pos.z;
return voxelModels[voxelModel + index];
}
vec4 calcFog(vec3 pos, vec4 color, Fog fog) {
float distance = length(pos);
float fogFactor = 1.0/exp((distance*fog.density)*(distance*fog.density));
@ -49,11 +42,207 @@ vec4 calcFog(vec3 pos, vec4 color, Fog fog) {
return vec4(resultColor.xyz, color.w + 1 - fogFactor);
}
// blockDrops ------------------------------------------------------------------------------------------------------------------------
const vec3[6] normals = vec3[6](
vec3(0, 1, 0),
vec3(0, -1, 0),
vec3(1, 0, 0),
vec3(-1, 0, 0),
vec3(0, 0, 1),
vec3(0, 0, -1)
);
#define modelSize 16
struct VoxelModel {
ivec4 minimum;
ivec4 maximum;
uint bitPackedData[modelSize*modelSize*modelSize/8];
};
struct AnimationData {
int frames;
int time;
};
layout(std430, binding = 0) buffer _animation
{
AnimationData animation[];
};
layout(std430, binding = 1) buffer _textureIndices
{
int textureIndices[][6];
};
layout(std430, binding = 4) buffer _blockVoxelModels
{
VoxelModel blockVoxelModels[];
};
uniform sampler2DArray texture_sampler;
uniform sampler2DArray emissionSampler;
int getVoxel(int voxelIndex) {
voxelIndex = (voxelIndex & 0xf) | (voxelIndex>>1 & 0xf0) | (voxelIndex>>2 & 0xf00);
int shift = 4*(voxelIndex & 7);
int arrayIndex = voxelIndex >> 3;
return (int(blockVoxelModels[voxelModel].bitPackedData[arrayIndex])>>shift & 15) - 6;
}
struct RayMarchResult {
bool hitAThing;
int normal;
int textureDir;
ivec3 voxelPosition;
};
RayMarchResult rayMarching(vec3 startPosition, vec3 direction) { // TODO: Mipmapped voxel models. (or maybe just remove them when they are far enough away?)
// Branchless implementation of "A Fast Voxel Traversal Algorithm for Ray Tracing" http://www.cse.yorku.ca/~amana/research/grid.pdf
vec3 step = sign(direction);
vec3 stepInIndex = step*vec3(1 << 10, 1 << 5, 1);
int overflowMask = 1<<14 | 1<<9 | 1<<4;
vec3 t1 = (floor(startPosition) - startPosition)/direction;
vec3 tDelta = 1/(direction);
vec3 t2 = t1 + tDelta;
tDelta = abs(tDelta);
vec3 tMax = max(t1, t2) - tDelta;
if(direction.x == 0) tMax.x = 1.0/0.0;
if(direction.y == 0) tMax.y = 1.0/0.0;
if(direction.z == 0) tMax.z = 1.0/0.0;
ivec3 voxelPos = ivec3(floor(startPosition));
int voxelIndex = voxelPos.x<<10 | voxelPos.y<<5 | voxelPos.z; // Stores the position as 0b0xxxx0yyyy0zzzz
int lastNormal = faceNormal;
int block = getVoxel(voxelIndex);
float total_tMax = 0;
int size = 16;
ivec3 sizeMask = ivec3(size - 1);
int it = 0;
while(block > 0 && it < 48) {
it++;
vec3 tNext = tMax + block*tDelta;
total_tMax = min(tNext.x, min(tNext.y, tNext.z));
vec3 missingSteps = floor((total_tMax - tMax)/tDelta + 0.00001);
voxelIndex += int(dot(missingSteps, stepInIndex));
tMax += missingSteps*tDelta;
if((voxelIndex & overflowMask) != 0)
return RayMarchResult(false, 0, 0, ivec3(0, 0, 0));
block = getVoxel(voxelIndex);
}
if(total_tMax != 0) {
if(tMax.x > tMax.y) {
if(tMax.x > tMax.z) {
lastNormal = 2 + (1 + int(step.x))/2;
} else {
lastNormal = 4 + (1 + int(step.z))/2;
}
} else {
if(tMax.y > tMax.z) {
lastNormal = 0 + (1 + int(step.y))/2;
} else {
lastNormal = 4 + (1 + int(step.z))/2;
}
}
}
voxelPos.x = voxelIndex>>10 & 15;
voxelPos.y = voxelIndex>>5 & 15;
voxelPos.z = voxelIndex & 15;
int textureDir = -block;
if(textureDir == 6) textureDir = lastNormal;
vec3 modifiedCameraSpacePos = cameraSpacePos*(1 + total_tMax*sizeScale*length(direction)/length(cameraSpacePos));
vec4 projection = projectionMatrix*vec4(modifiedCameraSpacePos, 1);
float depth = projection.z/projection.w;
gl_FragDepth = ((gl_DepthRange.diff * depth) + gl_DepthRange.near + gl_DepthRange.far)/2.0;
return RayMarchResult(true, lastNormal, textureDir, voxelPos);
}
ivec2 getTextureCoords(ivec3 voxelPosition, int textureDir) {
switch(textureDir) {
case 0:
return ivec2(15 - voxelPosition.x, voxelPosition.z);
case 1:
return ivec2(voxelPosition.x, voxelPosition.z);
case 2:
return ivec2(15 - voxelPosition.z, voxelPosition.y);
case 3:
return ivec2(voxelPosition.z, voxelPosition.y);
case 4:
return ivec2(voxelPosition.x, voxelPosition.y);
case 5:
return ivec2(15 - voxelPosition.x, voxelPosition.y);
}
}
float getLod(ivec3 voxelPosition, int normal, vec3 direction, float variance) {
return max(0, min(4, log2(variance*length(direction)/abs(dot(vec3(normals[normal]), direction)))));
}
float perpendicularFwidth(vec3 direction) { // Estimates how big fwidth would be if the fragment normal was perpendicular to the light direction.
vec3 varianceX = dFdx(direction);
vec3 varianceY = dFdx(direction);
varianceX += direction;
varianceX = varianceX*length(direction)/length(varianceX);
varianceX -= direction;
varianceY += direction;
varianceY = varianceY*length(direction)/length(varianceY);
varianceY -= direction;
vec3 variance = abs(varianceX) + abs(varianceY);
return 8*length(variance);
}
vec4 mipMapSample(sampler2DArray texture, ivec2 textureCoords, int textureIndex, float lod) { // TODO: anisotropic filtering?
int lowerLod = int(floor(lod));
int higherLod = lowerLod+1;
float interpolation = lod - lowerLod;
vec4 lower = texelFetch(texture, ivec3(textureCoords >> lowerLod, textureIndex), lowerLod);
vec4 higher = texelFetch(texture, ivec3(textureCoords >> higherLod, textureIndex), higherLod);
return higher*interpolation + (1 - interpolation)*lower;
}
void mainBlockDrop() {
RayMarchResult result;
float variance = perpendicularFwidth(direction);
if(variance <= 4.0) {
result = rayMarching(startPosition, direction);
} else {
result = RayMarchResult(true, faceNormal, faceNormal, ivec3(startPosition)); // At some point it doesn't make sense to even draw the model.
}
if(!result.hitAThing) discard;
int textureIndex = textureIndices[blockType][result.textureDir];
textureIndex = textureIndex + time / animation[textureIndex].time % animation[textureIndex].frames;
float normalVariation = normalVariations[result.normal];
float lod = getLod(result.voxelPosition, result.normal, direction, variance);
ivec2 textureCoords = getTextureCoords(result.voxelPosition, result.textureDir);
fragColor = mipMapSample(texture_sampler, textureCoords, textureIndex, lod)*vec4(ambientLight*normalVariation, 1);
if (fragColor.a <= 0.1f) fragColor.a = 1; // TODO: Proper alpha handling.
fragColor.rgb += mipMapSample(emissionSampler, textureCoords, textureIndex, lod).rgb;
if (fog.activ) {
fragColor = calcFog(startPosition, fragColor, fog);
}
fragColor.rgb /= 4;
position = vec4(startPosition, 1);
}
// itemDrops -------------------------------------------------------------------------------------------------------------------------
layout(std430, binding = 2) buffer _itemVoxelModels
{
uint itemVoxelModels[];
};
uint getVoxel(uvec3 pos) {
uint index = (pos.x | pos.y*upper.x)*upper.z | pos.z;
return itemVoxelModels[voxelModel + index];
}
vec4 decodeColor(uint block) {
return vec4(block >> 16 & uint(255), block >> 8 & uint(255), block & uint(255), block >> 24 & uint(255))/255.0;
}
void main() {
void mainItemDrop() {
// Implementation of "A Fast Voxel Traversal Algorithm for Ray Tracing" http://www.cse.yorku.ca/~amana/research/grid.pdf
ivec3 step = ivec3(sign(direction));
vec3 t1 = (floor(startPosition) - startPosition)/direction;
@ -70,7 +259,7 @@ void main() {
uint block = getVoxel(voxelPosition);
float total_tMax = 0;
uvec3 sizeMask = size - 1;
uvec3 sizeMask = upper - 1;
while(block == 0) {
if(tMax.x < tMax.y) {
@ -127,3 +316,11 @@ void main() {
fragColor.rgb /= 4;
position = vec4(modifiedCameraSpacePos, 1);
}
void main() {
if(blockType != 0) {
mainBlockDrop();
} else {
mainItemDrop();
}
}

View File

@ -6,36 +6,59 @@ out vec3 startPosition;
out vec3 direction;
out vec3 cameraSpacePos;
flat out int faceNormal;
flat out uint voxelModel;
flat out uvec3 size;
flat out int voxelModel;
flat out int blockType;
flat out uvec3 lower;
flat out uvec3 upper;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform int modelIndex;
uniform int block;
uniform float sizeScale;
layout(std430, binding = 2) buffer _voxelModels
layout(std430, binding = 2) buffer _itemVoxelModels
{
uint voxelModels[];
uint itemVoxelModels[];
};
#define modelSize 16
struct VoxelModel {
ivec4 minimum;
ivec4 maximum;
uint bitPackedData[modelSize*modelSize*modelSize/8];
};
layout(std430, binding = 4) buffer _blockVoxelModels
{
VoxelModel blockVoxelModels[];
};
void main() {
int x = positionAndNormals >> 2 & 1;
int y = positionAndNormals >> 1 & 1;
int z = positionAndNormals >> 0 & 1;
ivec3 pos = ivec3 (
positionAndNormals >> 2 & 1,
positionAndNormals >> 1 & 1,
positionAndNormals >> 0 & 1
);
faceNormal = positionAndNormals >> 3;
uint voxelModelIndex = modelIndex;
size.x = voxelModels[voxelModelIndex++];
size.y = voxelModels[voxelModelIndex++];
size.z = voxelModels[voxelModelIndex++];
int voxelModelIndex = modelIndex;
bool isBlock = block != 0;
if(isBlock) {
lower = uvec3(blockVoxelModels[voxelModelIndex].minimum.xyz);
upper = uvec3(blockVoxelModels[voxelModelIndex].maximum.xyz);
} else {
upper.x = itemVoxelModels[voxelModelIndex++];
upper.y = itemVoxelModels[voxelModelIndex++];
upper.z = itemVoxelModels[voxelModelIndex++];
lower = uvec3(0);
}
voxelModel = voxelModelIndex;
blockType = block;
startPosition.x = float(size.x)*0.999*x;
startPosition.y = float(size.y)*0.999*y;
startPosition.z = float(size.z)*0.999*z;
startPosition = lower + vec3(upper)*0.999*pos;
vec4 worldSpace = modelMatrix*vec4(vec3(x*size.x, y*size.y, z*size.z)*sizeScale + sizeScale/2, 1);
vec4 worldSpace = modelMatrix*vec4(pos*(upper - lower)*sizeScale + sizeScale/2, 1);
direction = (transpose(mat3(modelMatrix))*worldSpace.xyz).xyz;
vec4 cameraSpace = viewMatrix*worldSpace;

View File

@ -1,6 +1,7 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const blocks = @import("blocks.zig");
const chunk_zig = @import("chunk.zig");
const Chunk = chunk_zig.Chunk;
const game = @import("game.zig");
@ -560,7 +561,11 @@ pub const ItemDropRenderer = struct {
@"fog.color": c_int,
@"fog.density": c_int,
modelIndex: c_int,
block: c_int,
sizeScale: c_int,
time: c_int,
texture_sampler: c_int,
emissionSampler: c_int,
} = undefined;
var itemModelSSBO: graphics.SSBO = undefined;
@ -738,8 +743,11 @@ pub const ItemDropRenderer = struct {
return (try voxelModels.findOrCreate(compareObject, ItemVoxelModel.init)).index;
}
pub fn renderItemDrops(projMatrix: Mat4f, ambientLight: Vec3f, playerPos: Vec3d) !void {
pub fn renderItemDrops(projMatrix: Mat4f, ambientLight: Vec3f, playerPos: Vec3d, time: u32) !void {
itemShader.bind();
c.glUniform1i(itemUniforms.texture_sampler, 0);
c.glUniform1i(itemUniforms.emissionSampler, 1);
c.glUniform1i(itemUniforms.time, @truncate(u31, time));
c.glUniform1i(itemUniforms.@"fog.activ", if(game.fog.active) 1 else 0);
c.glUniform3fv(itemUniforms.@"fog.color", 1, @ptrCast([*c]const f32, &game.fog.color));
c.glUniform1f(itemUniforms.@"fog.density", game.fog.density);
@ -769,9 +777,17 @@ pub const ItemDropRenderer = struct {
modelMatrix = modelMatrix.mul(Mat4f.rotationY(-rot[1]));
modelMatrix = modelMatrix.mul(Mat4f.rotationZ(-rot[2]));
c.glUniformMatrix4fv(itemUniforms.modelMatrix, 1, c.GL_FALSE, @ptrCast([*c]const f32, &modelMatrix));
const index = try getModelIndex(item);
c.glUniform1i(itemUniforms.modelIndex, index);
if(item == .baseItem and item.baseItem.block != null) {
const blockType = item.baseItem.block.?;
const block = blocks.Block{.typ = blockType, .data = 0};
c.glUniform1i(itemUniforms.modelIndex, block.mode().model(block).modelIndex);
c.glUniform1i(itemUniforms.block, blockType);
} else {
const index = try getModelIndex(item);
c.glUniform1i(itemUniforms.modelIndex, index);
c.glUniform1i(itemUniforms.block, 0);
}
c.glBindVertexArray(itemVAO);
c.glDrawElements(c.GL_TRIANGLES, 36, c.GL_UNSIGNED_INT, null);
}

View File

@ -267,7 +267,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
entity.ClientEntityManager.render(game.projectionMatrix, ambientLight, .{1, 0.5, 0.25}, playerPos);
try itemdrop.ItemDropRenderer.renderItemDrops(game.projectionMatrix, ambientLight, playerPos);
try itemdrop.ItemDropRenderer.renderItemDrops(game.projectionMatrix, ambientLight, playerPos, time);
// // Render transparent chunk meshes:
// NormalChunkMesh.bindTransparentShader(ambientLight, directionalLight.getDirection(), time);