VR Procedural Voxel Generation

Jarmo van Dongen

GPE Voxel terrain

Research

When I started to find out what kind of research I wanted to do, I first thought about “what do I want to learn”. I concluded that generating meshes was interesting. We had one workshop on it, and I wanted to get more knowledge. Then I did my performance assignment on Minecraft. So, what better way to combine those two? I concluded that I wanted to make a procedural voxel terrain.

But this was not enough in my opinion. After these four years of college, my goal is to make Virtual Reality. The teacher and I had a chat about what to do with this, so I combined making a voxel terrain with VR. This might bring up some performance issues because VR is hard to run, and you need to be careful of how many vertices you put into your scene.

This would lead to my final choice to make a procedural voxel terrain in VR and try to get an ‘acceptable’ performance out of it. That means you want a framerate of 60 at the bare minimum.

At the start of this project, I had no idea if this is possible and how to achieve this, but I will make it possible.

Then I did some research on how to make this voxel terrain and found some YouTube tutorials they are linked in the sources at the end of the document.

When I started looking up what a voxel terrain is and how to make it I stumbled upon this tutorial {1}. This was going to be my first try at creating a voxel. It didn’t matter to me if it was optimized or not. The tutorial taught me how to create a voxel cube by creating the vertices and triangles for all the faces.




Second try:

This time I will do it a bit differently. I found a tutorial that in my opinion has a better way of creating the meshes in a more optimized way. In this way, you draw fewer vertices and therefore fewer triangles. This taught me how to create a class where you create the values of your meshes so you can reuse the same voxel and can modify it whenever it’s needed. Because this is my first time creating something like this I needed to find YouTube tutorials to create this voxel terrain. A YouTuber called ‘b3agz’ made a series on creating a game like Minecraft throughout this project I will use his videos to create what is needed and learn a lot more about voxel terrain and creating meshes in Unity.

Locations and data of vertices and triangles and storing UV values.
In this class, I will store the values for the voxels and use these to create the vertices, triangles, and UV maps.

Create the mesh and draw it on the screen with the data that has been generated from the VoxelData script.
Now I have a class that stores the values of the voxels so now I need a way to generate the meshes. This is done by adding the vertices, triangles, and UVs to lists. When they are added to the list I can call them and create them as seen in the CreateMesh() method.

This picture under here is the first voxel that can be created with the code above. There is one voxel but with this now done I can create a whole world using multiple voxels.


Before I create a world we need to optimize the voxel so it does not render duplicate vertices. This is done by deleting the index of the triangles that is a duplicate. So {0, 3, 1, 1, 3, 2}, becomes {0, 3, 1, 2}, Index 1 and 3 were used 2 times so those can be deleted to save on performance.

Deleting duplicate triangles

To create my first chunk I create a variable for the X and Y axis, so I can populate the for loops to create a chunk in the X, Y, and Z axis. I can reuse the ChunkWidth variable, because a chunk is the same width in the Z and X axis.


Hardcoding way of generating multiple chunks:

To generate a bigger world than just a single chunk I add multiple chunks together in the same way as creating a chunk. Adding the variable ‘WorldSizeInChunks’ allows me to set a specific world size by hardcoding it. This is not the way Minecraft does it.

Using back face culling.

I am using back face culling to help my performance boost a lot by not rendering the vertices that you cannot see. By checking if the voxel is inside of a chunk and not on the outside of the world you can return them as false so you don’t have to render them. This will reduce the load on the system because it does not have to load all the vertices at once and keep the vertices count low.

Because Minecraft is built out of different blocks and layers we need to establish those here too. This is done by layering them by the height of the chunk. As seen when the value returns 0 it returns an air block. When the block’s position is a 1 it returns bedrock and so forth.

Use view distance to load new chunks in

What I did to get a better performance is added a view distance to the game so that the camera only renders a small bit of the world. This makes it so you can create a much bigger world without worrying that it has to load all the vertices in. You only load new chunks when the camera moves to the area where those chunks are and deactivates the ones that are not nearby.

Create a Vector2 and Vector3 Perlin noise.
Minecraft is not a game with just a flat surface all along the map. It has depths into it with mountains and caves. To generate a terrain that looks more like Minecraft I used 2D and 3D Perlin noise. I don’t understand much about the Perlin noise itself, except that when you have a Perlin noise (see below) the value of purely black is 0 and purely white is 1 and everything in between is a number in between that. And that you can use that as a height map. What I understand as well is that Unity provides this function already in the system but how to use it was more complex. I completely relied on the YouTube tutorial sequence from b3agz and Carlpilots video.

Implementation into the world using the Perlin noise



Now the world looks something like this. It can create a height map and different blocks

Adding more layers to the world

I wanted more layers of blocks in the world. I used the video of b3agz to get this to work, he used the 2D Perlin noise to create patterns of blocks randomly spread out over the inside of the terrain. Because we defined where the stone can be generated we can say that in between the stone the can be bits of dirt and sand.

Adding collision for the terrain so the player can walk on it.
This is done by using a mesh collider. This could be done in a more effective way and make it less resources heavy but because of time limitations and a VR controller that still had to be made. I decided to check how much FPS I had while using the VR headset using the mesh colliders, and there was no problem using that. I still had the 70FPS this is actually higher but the headset (Oculus Quest 2) caps the limit at 70 FPS. We can see the FPS shoot up to 400FPS when the VR headset is not used.

Queueing system

Right now the world loads in all the new chunks at once, this creates a big stutter when the player moves to a new area. However, this happens only once the chunks are created for the first time. But even happening once is not an optimal gaming experience, especially not in VR as you can get motion sick. A better solution for this is queueing and dequeuing the chunks when loading in. This creates a smoother experience. I did this by using the Queue function to enqueue and dequeue a chunk. The chunk is put into the queue when it is added to the list to create it and deleted from the queue when it has been created.

To create the VR controller I used the (OpenXR), XR interaction toolkit. for setting up the headset and the continuous movement I used two videos of a guy called Fist Full of Shrimp on YouTube. This toolkit provides most of the support for the headsets through built-in action maps and scripts. By adding the locomotion system you can add a script called Continuous Move Provider. This handles the input of the controller and adds force to the player, so the player can walk. But before this is possible the player needs a character controller and a rigidbody. The rigidbody has to be set to kinematic I found out else the collider of the character controller does not work. I have no idea why this is the case but I struggled with this for a little bit because I couldn’t find why my player had no collision with the world.

New Movement and Collision System
This new movement and collision system checks for nearby voxels instead of giving all the voxels a mesh collider. This will improve the load and therefore it will have a better FPS. First I have to gather the information of the voxel that I am standing on. I do this by getting the x,y and z values (floats) of the coordinates and transferring these to an integer.

Then by checking each of the corners of the player’s width we can check if the player is hitting a solid voxel. If one of the checks is true that means there is a solid voxel under the player, and what will then stop the player from falling.

The same goes for the head of the player. You don’t want your player to go through blocks with his head. So you check each of the corners plus 2 blocks on the Y axis. because a Minecraft character is 1,8 blocks tall. You have to do plus 2 because Minecraft blocks are exactly 1 block high so there is a little bit of headroom when the player is walking. Else it would get stuck in every block.



For now, the player is only blocked from walking when there is a block on top of them or below them. But the player will still walk through the blocks on the side. So we have to check for these blocks as well. We can do this by checking the front, back, left and right sides of the player’s width, and then check if there is a solid block. But we have to do two checks. One for the block at foot height and the one at head height. This is because the player’s position is based on where his feet are. If there is a block blocking the player’s head but not the player’s feet he will be walking through the block if we just check 1 block. That’s why need 2 checks, for the block at his feet and head.

Now we need to have the movement. This is done by calculating the velocity based on a couple of things. It checks if the player is falling down, jumping or walking/sprinting.
Then you can check when there is movement, if there is collision from the front, back, left or right and set the velocity to 0 so the player won’t move.


Creating and destroying blocks
Now it’s time to start destroying and placing blocks. First of all, you need to see what block you are pointing at. So I created a highlighted block so you can see what block you are about to destroy or place


To point at the block we need to use some sort of raycasting. I will use a loop to check if there is a voxel in the direction that the player is aiming. Of course, there has to be a maximum reach that the player can reach. In between the player’s hand and the maximum reach, the game will check if there is a solid voxel. This is done by checking points (steps) in space in a line. If there is a block it will report the position back of the voxel and place the highlighted blocks on the correct side.

To switch between building blocks can select the correct block type by incrementing or decreasing the index of the block type.

Before we update the chunk we need to clear the mesh data.

Now we want to edit the voxels and populate the new voxel map.

But we need to update the surrounding voxels of the border of the chunk. Because if we don’t update them. They will leave gaps in between chunks. So we check if the voxel is not in the chunk then we update the new chunk.

The last thing to do is bind the buttons to the actions.




What’s for the future?
As further improvements, I want to be able to place and remove blocks in the world. Also, I want to make it endless I already found the material for it to make it endless as seen in the sources. There is a video from Sebastiaan Lague, that I think I can use to accomplish this. I will continue working on this project for a while because I find it really interesting and I learned a lot from this project. So I will also focus on making the map more diverse and adding biomes.

Sources
ErenMakesGames. (2021, August 1). Procedural voxel terrain generation in Unity #1 – Creating a cube! [Video]. YouTube. https://www.youtube.com/watch?v=lyDJPVp7Oc8

b3agz. (2018, April 2). Make Minecraft in Unity 3D Tutorial – 01 – The First Voxel [Video]. YouTube. https://www.youtube.com/watch?v=h66IN1Pndd0&list=PLVsTSlfj0qsWEJ-5eMtXsYp03Y9yF1dEn&ab_channel=b3agz

Carlpilot. (2017, January 3). [Unity] Easy 3D Perlin Noise [Video]. YouTube. https://www.youtube.com/watch?v=Aga0TBJkchM

voxel terrain optimization – Google Zoeken. (n.d.). https://www.google.com/search?q=voxel+terrain+optimization

[Optimization] How can i optimize this voxel terrain more? – Unity Answers. (n.d.). https://answers.unity.com/questions/1644923/optimization-how-can-i-optimize-this-voxel-terrain.html

What are the ways to optimize a voxel game? (2014, August 1). Reddit. https://www.reddit.com/r/VoxelGameDev/comments/2cd0w6/what_are_the_ways_to_optimize_a_voxel_game/

Hopson. (2020, April 12). Voxel Game Mesh Optimizations. YouTube. https://www.youtube.com/watch?v=VQuN1RMEr1c

Sebastian Lague. (2016b, March 14). Procedural Landmass Generation (E07: Endless terrain) [Video]. YouTube. https://www.youtube.com/watch?v=xlSkYjiE-Ck

Input in Unity OpenXR | OpenXR Plugin | 0.1.2-preview.2. (n.d.). https://docs.unity3d.com/Packages/com.unity.xr.openxr@0.1/manual/input.html

Fist Full of Shrimp. (2022, March 4). Unity VR Game Basics – PART 1 – Setup in 10 Minutes [Video]. YouTube. https://www.youtube.com/watch?v=ojZkl8q3YBI

Fist Full of Shrimp. (2022b, April 5). Unity VR Game Basics – PART 7 –  Continuous Movement [Video]. YouTube. https://www.youtube.com/watch?v=SUmA2qUK0hc

Wikipedia contributors. (2022, July 20). Back-face culling. Wikipedia. https://en.wikipedia.org/wiki/Back-face_culling

Justin P Barnett. (2021, April 14). How to Jump in VR using Unity’s New Input System [Video]. YouTube. https://www.youtube.com/watch?v=Mfim9MlgYWY


Posted

in

, ,

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *