Various buffer optimisations

This commit is contained in:
Lucas Dower 2021-07-24 14:54:35 +01:00
parent 8c364ae06b
commit 2038e036fe
6 changed files with 182 additions and 50 deletions

View File

@ -1,5 +1,6 @@
uniform mat4 u_worldViewProjection;
uniform sampler2D u_texture;
uniform float u_voxelSize;
attribute vec4 position;
attribute vec3 normal;
@ -20,5 +21,5 @@ void main() {
//float lighting = 0.2 * occlusion;
//v_colour = vec4(abs(normal), 1.0);
v_lighting = dot(light, abs(normal));
gl_Position = u_worldViewProjection * vec4(position.xyz, 1.0);
gl_Position = u_worldViewProjection * vec4(position.xyz * u_voxelSize, 1.0);
}

View File

@ -25,6 +25,8 @@ class SegmentedBuffer {
this._indicesInsertIndex = 0;
this._maxIndex = 0;
this._sanityCheck = false;
this._getNewBuffer();
}
@ -63,7 +65,7 @@ class SegmentedBuffer {
}
for (const attr in this._attributes) {
if (data[attr].length > this._bufferSize) {
if (this._sanityCheck && data[attr].length > this._bufferSize) {
// TODO: Automatically partition into smaller segments
throw Error(`Data for ${attr} does not fit within the buffer.`);
}
@ -107,10 +109,13 @@ class SegmentedBuffer {
if (this._compiled) {
throw Error("Buffer already compiled, cannot add more data");
}
this._checkDataMatchesAttributes(data);
if (this._sanityCheck) {
this._checkDataMatchesAttributes(data);
}
if (this._willOverflow(data)) {
console.log("Cycling buffer...");
this._cycle();
}
@ -150,4 +155,120 @@ class SegmentedBuffer {
}
class BottomlessBuffer {
constructor(attributes) {
this._completeBuffers = [];
this._compiled = false;
this.WebGLBuffers = [];
this._attributes = {};
for (const attr of attributes) {
this._attributes[attr.name] = {
numComponents: attr.numComponents
};
}
this._maxIndex = 0;
this._sanityCheck = false;
this._getNewBuffer();
}
_getNewBuffer() {
this._buffer = {
indices: {numComponents: 1, data: []}
};
for (const attr in this._attributes) {
this._buffer[attr] = {
numComponents: this._attributes[attr].numComponents,
data: []
};
}
}
_cycle() {
this._completeBuffers.push({
buffer: this._buffer,
numElements: this._buffer.indices.data.length,
});
this._getNewBuffer();
this._maxIndex = 0;
}
_willOverflow(data) {
// Check for indices Uint16 overflow
const dataMaxIndex = Math.max(...data.indices);
return ((this._maxIndex + dataMaxIndex) > 65535);
}
_checkDataMatchesAttributes(data) {
if (!('indices'in data)) {
throw `Given data does not have indices data`;
}
const setsRequired = Math.max(...data.indices) + 1;
for (const attr in this._attributes) {
if (!(attr in data)) {
throw Error(`Given data does not have ${attr} data`);
}
if (data[attr].length % this._attributes[attr].numComponents != 0) {
throw Error(`Not enough/too much ${attr} data given`);
}
const numSets = data[attr].length / this._attributes[attr].numComponents;
if (numSets != setsRequired) {
//throw `Number of indices does not match number of ${attr} components given`;
throw Error(`Expected ${setsRequired * this._attributes[attr].numComponents} values for ${attr}, got ${data[attr].length}`);
}
}
}
add(data) {
if (this._compiled) {
throw Error("Buffer already compiled, cannot add more data");
}
if (this._sanityCheck) {
this._checkDataMatchesAttributes(data);
}
if (this._willOverflow(data)) {
console.log("Cycling buffer...");
this._cycle();
}
// Add the new indices data
this._buffer.indices.data.push(...data.indices.map(x => x + this._maxIndex));
this._maxIndex += 1 + Math.max(...data.indices);
for (const attr in this._attributes) {
this._buffer[attr].data.push(...data[attr]);
}
}
compile(gl) {
if (this._compiled) {
return;
}
this._cycle();
this.WebGLBuffers = new Array(this._completeBuffers.length);
this._completeBuffers.forEach((buffer, i) => {
this.WebGLBuffers[i] = {
buffer: twgl.createBufferInfoFromArrays(gl, buffer.buffer),
numElements: buffer.numElements
};
});
this._compiled = true;
}
}
module.exports.SegmentedBuffer = SegmentedBuffer;
module.exports.BottomlessBuffer = BottomlessBuffer;

View File

@ -51,6 +51,7 @@ $("#loadBtn").on("click", () => {
renderer.registerMesh(loadedMesh);
renderer.compile();
console.log(renderer);
$('#voxelInput').prop('disabled', false);
$('#voxelBtn').prop('disabled', false);
@ -73,9 +74,9 @@ $("#voxelBtn").on("click", () => {
voxelManager.clear();
voxelManager.setVoxelSize(voxelSize);
voxelManager.voxeliseMesh(loadedMesh);
//console.log(voxelManager.voxels);
renderer.clear();
//renderer.setDebug(true);
renderer.registerVoxelMesh(voxelManager);
/*
@ -163,7 +164,7 @@ $(document).resize(function() {
});
function render(time) {
renderer.draw();
renderer.draw(voxelManager._voxelSize);
requestAnimationFrame(render);
}

View File

@ -43,9 +43,11 @@ class GeometryTemplates {
}
}
static getBoxBufferData(centre, size, debug) {
const a = Vector3.sub(centre, Vector3.mulScalar(size, 0.5));
const b = Vector3.add(centre, Vector3.mulScalar(size, 0.5));
static getBoxBufferData(centre, debug) {
//const a = Vector3.sub(centre, Vector3.mulScalar(size, 0.5));
//const b = Vector3.add(centre, Vector3.mulScalar(size, 0.5));
const a = Vector3.subScalar(centre, 0.5);
const b = Vector3.addScalar(centre, 0.5);
if (debug) {
return {
@ -80,9 +82,9 @@ class GeometryTemplates {
cube.texcoord.set(default_cube.texcoord);
for (let i = 0; i < 72; i += 3) {
cube.position[i + 0] = (cube.position[i + 0] * size.x) + centre.x;
cube.position[i + 1] = (cube.position[i + 1] * size.y) + centre.y;
cube.position[i + 2] = (cube.position[i + 2] * size.z) + centre.z;
cube.position[i + 0] += centre.x;
cube.position[i + 1] += centre.y;
cube.position[i + 2] += centre.z;
}
return cube;

View File

@ -4,9 +4,10 @@ const { Vector3 } = require('./vector.js');
const { ArcballCamera } = require('./camera.js');
const mouseManager = require('./mouse.js');
const shaderManager = require('./shaders.js');
const { SegmentedBuffer } = require('./buffer.js');
const { SegmentedBuffer, BottomlessBuffer } = require('./buffer.js');
const { GeometryTemplates } = require('./geometry.js');
class Renderer {
constructor(fov, backgroundColour) {
@ -41,36 +42,36 @@ class Renderer {
this._registerData(data);
}
_registerVoxel(centre, voxelSize, voxelManager) {
const sizeVector = new Vector3(voxelSize, voxelSize, voxelSize);
_registerVoxel(centre, voxelManager) {
let occlusions = new Array(6);
// For each face
for (let f = 0; f < 6; ++f) {
// For each vertex
occlusions[f] = [0, 0, 0, 0];
for (let v = 0; v < 4; ++v) {
// For each occlusion vertex
for (let o = 0; o < 3; ++o) {
occlusions[f][v] += voxelManager.isVoxelAt(Vector3.add(centre, this.occlusions[f][v][o]));
}
}
// Convert from occlusion denoting the occlusion factor to the
// attenuation in light value: 0 -> 1.0, 1 -> 0.8, 2 -> 0.6, 3 -> 0.4
occlusions[f] = occlusions[f].map(x => 1.0 - 0.2 * x);
}
let data = GeometryTemplates.getBoxBufferData(centre, false);
// Each vertex of a face needs the occlusion data for the other 3 vertices
// in it's face, not just itself, pack this into a vec4.
for (let i = 0; i < 6; ++i) {
occlusions[i] = [].concat(...new Array(4).fill(occlusions[i]));
}
let data = GeometryTemplates.getBoxBufferData(centre, sizeVector, false);
data.occlusion = [].concat(...occlusions);
// in it's face, not just itself. Also flatten occlusion data.
data.occlusion = new Array(96);
for (let j = 0; j < 6; ++j) {
for (let k = 0; k < 16; ++k) {
data.occlusion[j * 16 + k] = occlusions[j][k % 4];
}
}
// Convert from occlusion denoting the occlusion factor to the
// attenuation in light value: 0 -> 1.0, 1 -> 0.8, 2 -> 0.6, 3 -> 0.4
data.occlusion = data.occlusion.map(x => 1.0 - 0.2 * x);
//this._registerData(data);
this._registerVoxels.add(data);
}
@ -87,7 +88,9 @@ class Renderer {
registerVoxelMesh(voxelManager) {
const voxelSize = voxelManager._voxelSize;
const sizeVector = new Vector3(voxelSize, voxelSize, voxelSize);
//const sizeVector = new Vector3(voxelSize, voxelSize, voxelSize);
const sizeVector = new Vector3(1.0, 1.0, 1.0);
if (this._debug) {
voxelManager.voxels.forEach((voxel) => {
this.registerBox(voxel, sizeVector);
@ -96,7 +99,7 @@ class Renderer {
this._setupOcclusions(voxelSize); // Setup arrays for calculating voxel ambient occlusion
voxelManager.voxels.forEach((voxel) => {
this._registerVoxel(voxel, voxelSize, voxelManager);
this._registerVoxel(voxel, voxelManager);
});
}
@ -119,7 +122,7 @@ class Renderer {
this._compiled = true;
}
draw() {
draw(voxelSize) {
if (!this._compiled) {
this.compile();
return;
@ -135,7 +138,8 @@ class Renderer {
// Draw voxel register
this._drawRegister(this._registerVoxels, this._gl.TRIANGLES, shaderManager.aoProgram, {
u_worldViewProjection: this._camera.getWorldViewProjection(),
u_texture: this._blockTexture
u_texture: this._blockTexture,
u_voxelSize: voxelSize
});
// Draw default register
@ -152,7 +156,7 @@ class Renderer {
}
}
_setupOcclusions(voxelSize) {
_setupOcclusions() {
this.occlusions = new Array(6).fill(null).map(function() { return new Array(4).fill(0); });
this.occlusions[0][0] = [new Vector3( 1, 1, 0), new Vector3( 1, 1, -1), new Vector3( 1, 0, -1)];
@ -186,6 +190,7 @@ class Renderer {
this.occlusions[5][3] = [new Vector3( 0, -1, -1), new Vector3( 1, -1, -1), new Vector3( 1, 0, -1)];
// Scale each Vector3 by voxelSize
/*
for (let i = 0; i < 6; ++i) {
for (let j = 0; j < 4; ++j) {
for (let k = 0; k < 3; ++k) {
@ -193,6 +198,7 @@ class Renderer {
}
}
}
*/
}
_registerData(data) {
@ -248,17 +254,18 @@ class Renderer {
}
_getNewBuffers() {
this._registerDebug = new SegmentedBuffer(16384, [
const bufferSize = 16384 * 16;
this._registerDebug = new SegmentedBuffer(bufferSize, [
{name: 'position', numComponents: 3},
{name: 'colour', numComponents: 3}
]);
this._registerVoxels = new SegmentedBuffer(16384, [
this._registerVoxels = new SegmentedBuffer(bufferSize, [
{name: 'position', numComponents: 3},
{name: 'normal', numComponents: 3},
{name: 'occlusion', numComponents: 4},
{name: 'texcoord', numComponents: 2}
]);
this._registerDefault = new SegmentedBuffer(16384, [
this._registerDefault = new SegmentedBuffer(bufferSize, [
{name: 'position', numComponents: 3},
{name: 'normal', numComponents: 3}
]);

View File

@ -60,7 +60,7 @@ class VoxelManager {
return cubeAABB;
}
_voxelCentreToPosition(vec) {
_toGridPosition(vec) {
//Vector3.round(Vector3.subScalar(Vector3.divScalar(vec, this._voxelSize), 0.5));
return new Vector3(
Math.round(vec.x / this._voxelSize),
@ -69,7 +69,7 @@ class VoxelManager {
);
}
_voxelPositionToCentre(vec) {
_toModelPosition(vec) {
return new Vector3(
vec.x * this._voxelSize,
vec.y * this._voxelSize,
@ -77,8 +77,7 @@ class VoxelManager {
);
}
isVoxelAt(vec) {
const pos = this._voxelCentreToPosition(vec);
isVoxelAt(pos) {
return this.voxelsHash.contains(pos);
}
@ -87,22 +86,20 @@ class VoxelManager {
// (0.5, 0.5, 0.5) -> (0, 0, 0);
vec = Vector3.subScalar(vec, this._voxelSize / 2);
const test = Vector3.divScalar(vec, this._voxelSize);
// [HACK] FIXME: Fix misaligned voxels
// Some vec data is not not grid-snapped to voxelSize-spacing
const test = Vector3.divScalar(vec, this._voxelSize);
if ((test.x % 1 < 0.9 && test.x % 1 > 0.1) || (test.y % 1 < 0.9 && test.y % 1 > 0.1) || (test.z % 1 < 0.9 && test.z % 1 > 0.1)) {
console.warn("Misaligned voxel, skipping...");
return;
}
const pos = this._voxelCentreToPosition(vec);
// Convert to
const pos = this._toGridPosition(vec);
if (this.voxelsHash.contains(pos)) {
return;
}
//console.log(pos);
this.voxels.push(vec);
//console.log("pos:", pos);
this.voxels.push(pos);
this.voxelsHash.add(pos);
this.minX = Math.min(this.minX, vec.x);
@ -113,6 +110,8 @@ class VoxelManager {
this.maxZ = Math.max(this.maxZ, vec.z);
}
// TODO: Fix voxel meshing
/*
_findXExtent(pos) {
let xEnd = pos.x + 1;
@ -188,8 +187,8 @@ class VoxelManager {
this.seen = new HashSet(2048);
//console.log(this.voxelsHash);
const minPos = this._voxelCentreToPosition(new Vector3(this.minX, this.minY, this.minZ));
const maxPos = this._voxelCentreToPosition(new Vector3(this.maxX, this.maxY, this.maxZ));
const minPos = this._toGridPosition(new Vector3(this.minX, this.minY, this.minZ));
const maxPos = this._toGridPosition(new Vector3(this.maxX, this.maxY, this.maxZ));
for (let y = minPos.y; y <= maxPos.y; ++y) {
for (let z = minPos.z; z <= maxPos.z; ++z) {
@ -209,8 +208,8 @@ class VoxelManager {
let size = new Vector3(xEnd - x + 1, yEnd - y + 1, zEnd - z + 1);
this.mesh.push({
centre: this._voxelPositionToCentre(centre),
size: this._voxelPositionToCentre(size)
centre: this._toModelPosition(centre),
size: this._toModelPosition(size)
});
}
}
@ -220,6 +219,7 @@ class VoxelManager {
return this.mesh;
}
*/
splitVoxels() {
this._voxelSize /= 2;