Michael's Blog

Rendering Chunks! DevBlog #3 & #4

Published 18th March 20205 min readDevBlog
gif
Renderer: WireFrame

This week’s DevBlog will be part of last week’s and this week’s progress combined as I have had a busy week last week and was unable to post my weekly blog. The progress since then is that I have made some strides and I am heading towards my goal with a few issues along the way as expected. Some updates I will talk about is instancing, occlusion culling, lighting and looking into procedural generation.

Instancing

Since my second blog I have since looked into instancing where I send data to display a voxel on the screen to the GPU and use that same data to use it again and again to render a chunk by offsetting the position each time I render a voxel and decide how many times I want to instance the object. This is a very good technique when you want to render the same object multiple times which in my case I do. I had some issues at first but managed to get instancing in quite quickly and it worked very but with some draw backs such as how I would be able not render any faces the user can’t see as it wastes a lot of GPU power rendering faces that will never be seen and will give a big increase in performance. I realised with lots of research it would take a lot of work to use instancing and culling faces you don’t see. Although instancing allowed me to render a chunk with 1 draw call compared to 4096, I ended up deciding to not use instancing as it would be too much work to get it working with occlusion culling.

image
Renderer: Instance Rendering

Culling

Culling is a very important part of optimisation and I knew going forward I would need to do some culling to maximise performance. From instancing I decided to unfortunately scrap instancing for now (may try instancing and culling in the future) so I can cull all unseen faces, resulting in a big performance boost. I firstly decided to create a chunk, block class and in the chunk class make a 3D array of blocks that represents the position in world in the array e.g. blocks[0][0][0] = position 0,0,0. In the block class I set a Boolean for if that block is active meaning if you should render it or not in the world and a block type that I will use in the future for different colours such as grass, dirt, sand etc. Using all this I created the array in the chunk class and using for loops I check each element in the array and if its active check its neighbours like left side, right side etc. and if those neighbouring blocks are active don’t render that face and repeat that for each block. This turned out to be very effective and using chunk sizes of 16x16x16 is most suited as it doesn’t take too long to create the mesh of faces to render. As you can see below the benefits of occlusion culling with performance boosts. I also added in back face culling that OpenGL has built in that you enable but making sure the winding order of rendering the vertices is right so the GPU doesn’t render back faces or side faces as the winding order is different to front facing vertices, as seen in the picture below with the performance difference with culling. (1100FPS without culling, 3100FPS with culling)

image
Renderer: Instancing without culling
image
Renderer: Occlusion Culling & Back Face Culling

Lighting

After rendering a chunk, I decided I needed some lighting to be able to see the corners more and be able to see the world a bit better with some basic lighting. I the resources I used to investigate how to do lighting was LearnOpenGL. Now I have some basic lighting with shadows! I am hoping the future I can move the lighting around using my graphics interface.

image
OpenGL: Basic Lighting

Overview

Overall my progress has been good I do wish I spent less time trying to figure out how I would use instancing and culling together as that wasted a lot of time to come to realising it was best to not use instancing for now but other than that over these two weeks progress was good along with looking into procedural generation using libnoise open source library.

Goals
  • Procedurally generate terrain using libnoise
  • Generate a map of chunks with procedural terrain
  • Load and unload chunks as you move