IDEA:
The idea I had for this shader was to make a real-time voxel shader, to convert triangle meshes into voxel meshes. Most of the references I found used a 2-step approach where you firstly generate a 3dtexture from your model and then display that 3dtexture with voxels. The only thing is that that uses 2 effect files, I wanted to do it in 1 single file so I have thought out another method that calculates the voxels in real-time and displays them directly.
MAKING THE SHADER:
O.K., we want to make a shader using the geometry shader to create voxels from triangles. We must first calculate how many voxels fit inside of every triangle, this can be done with some clever math. Using the three given points, we will call them positionA, positionB and positionC, we want to loop over all the possible points between them. First, we transpose the points to world coordinates so we have sensible values to work with, then we will find a point A which has all the lowest values of the positions and B with all the highest.
//Corners of the triangle to world coordinates float3 positionA = mul(float4(vertices[0].Position, 1), m_MatrixWorld).xyz; float3 positionB = mul(float4(vertices[1].Position, 1), m_MatrixWorld).xyz; float3 positionC = mul(float4(vertices[2].Position, 1), m_MatrixWorld).xyz; float3 A = (float3) 0; float3 B = (float3) 0; //Find A.x and B.x A.x = min(min(positionA.x, positionB.x), positionC.x); B.x = max(max(positionA.x, positionB.x), positionC.x); //Find A.y and B.y A.y = min(min(positionA.y, positionB.y), positionC.y); B.y = max(max(positionA.y, positionB.y), positionC.y); //Find A.z and B.z A.z = min(min(positionA.z, positionB.z), positionC.z); B.z = max(max(positionA.z, positionB.z), positionC.z);
We now loop over every position between those values and put them into an array.
float3 cubes[100]; int numcubes = 0; //Loop over every point and add it to the array for(float x = A.x; x < B.x + 1; x++) { for(float x = A.x; x < B.x + 1; x++) { for(float x = A.x; x < B.x + 1; x++) { float3 pos = (float3) 0; pos = float3(x, y, z); cubes[numcubes] = pos; numcubes++; } } }
Nice, now we have an array of all the points in a bounding box around the triangle, we will now loop over all the points and draw a box there. This can be easily done with 2 functions, one to generate the 6 faces of a box from a point and one to draw a vertex for convenience sake.
To be a little fancier I have also added texcoords and normal for the boxes, all the properties of a box; indices, normals and texcoords I have put into arrays. This way we can just loop over them to make it more efficient.
Let’s see where we are at:
Well… it’s a start. The thing we are missing is that the boxes are drawn on every point without them being aligned at all, also, we are using magic numbers in the loop where we look for all the possible points in the bounding box, let’s fix that.
We make a function that will align the boxes to each other, this can be done by using a variable for our voxel cube size, then we loop over all the values of our current position and subtract the modulo of the current value with the size from the current value.
Okay, now we tackle the point loop, we want to add the size instead of the magic 1 number, since we only are going to draw the squares that are on our grid we don’t have to loop over points that are not on the grid, this would be wasted GPU power!
Now that that part is solved let’s look at what we have:
That looks a lot better! But we still have 1 problem. This happens if we scale our model big enough:
We have overlapping blocks! Let’s fix it! First by making the align to grid function better, it worked before, but floating points were a bit of a problem using a different method: taking the floor of our voxel size divided by the current value times our voxel size, not only is more accurate but also fixes a lot of floating point errors, it doesn’t fix our overlapping completely though. To fix that we should create another array to store the currently rendered blocks and look through those each time we render a new block, if we find an exact match, then we discard that block.
There we go, all clean and neat in real-time too:
I hope this article helped if you want to make a shader like this yourself. Have fun and enjoy.
SOURCES:
https://github.com/shuhuai/GPU_Based_Voxelization_D3D11