The `voxel` project contains the code for each voxel rendering method described in my [Voxel YouTube video](https://www.youtube.com/watch?v=40JzyaOYJeY). Below is a summary of each method, it's best to watch the video for the full explanation + visualisation. When you run the project, press the Left and Right arrow keys to change between rendering methods. The FPS can be viewed in the bottom-right, and the rendering method name in the bottom-left. ## Method 1 - Cull Occluded The first method produces regular triangles (3 vertices per triangle), but doesn't create triangles for faces that are between two voxels, as you can't see them. This method produces 6 vertices per face. ## Method 2 - Greedy Meshing Triangles can be combined together into one longer triangle when there are multiple voxels of the same type next to each other. ## Method 3 - Packed Data Each vertex in the above method contains 28 bytes of data: - vec3 float position - vec3 float normal - int textureID However since the voxels within each chunk only have positions in the range 0-32 (inclusive), we don't need all 32 bits in each float. There are only 6 possible normals (up, down, left, right, forward, back), so we only need 3 bits to store normals. We also don't have that many textures, so we'll use 11 bits (2047 maximum value) for the texture ID. Therefore we can use 6 bits for each position, 3 bits for the normal, and 11 bits for texture ID, which adds up to 32 bits and can be stored in one 32 bit integer. This means we're only using 4 bytes per vertex rather than 28. ## Method 4 - Instancing Currently we're storing 6 vertices for each face, to make 2 triangles. Each triangle shares a common line, meaning there are 2 pairs of vertices that store the same data. This is a waste and it would be better to use a triangle strip of 4 vertices, but a triangle strip would combine all of our voxel faces together, producing large unwanted triangles. To use one triangle strip per voxel face, we need to use instancing. We'll have our base voxel strip of 4 vertices, and our instance data will tell the shader where to move it to. So rather than producing triangles in our meshing code, we'll store 1 'vertex' in the instance buffer for each voxel face. This vertex contains a position, normal and textureID just like before: - the position is used to move the base voxel strip - the normal is used to rotate the base voxel strip - the textureID is the same as before The fragment shader is the same as before, the only change is in the vertex shader. We're using 6x less memory, and only running the vertex shader 4 times per face instead of 6. ## Method 5 - Indirect Rendering The last step is to reduce CPU usage and batch our chunk rendering calls together. At the moment we're issuing these calls to render each chunk: - set the chunk position shader uniform - bind the chunk's VAO - render the chunk With hundreds or thousands of chunks, this is inefficient. The GPU is rendering each chunk individually 'singlethreaded', and its likely much of the GPUs cores aren't being used as each chunk doesn't have that many triangles in it. To batch this all into one render command, we need to have one large buffer that stores the instance data for every chunk. At the moment we have one buffer of instance data for each chunk. So we'll create a massive buffer on startup, large enough to hold the data for every chunk in the map. Then we'll map it persistently, so we can freely read/write to it without needing to unmap it to flush data. Then each chunk will reserve a section of this buffer and will be meshed to this section. Then we can render multiple sections of this one buffer using `glMultiDrawArraysIndirect`. However this will render all chunks on top of each other, as we're no longer setting the `chunkPosition` shader uniform. So we'll create an [[SSBO]] and store the position of each chunk in it. Then in the shader we can read the position for each chunk using `gl_DrawID`, which is the ID/index of the draw command within the indirect command. See [[IndirectVoxelShader]] for an example of using `gl_DrawID`