2
\$\begingroup\$

I am creating a voxel game with OpenGL, and I would like to implement water. My voxel world is divided into chunks of 16x256x16 cubes (or voxels). Each chunk has its own VAO with two VBOs, the first one containing a cube, and the second one being used to store the model matrices and the texture coordinates of the visible cubes. I use the Instancing method from OpenGL to render every cube (or voxels) in a chunk with a single draw call.

Recently I've wanted to implement water. For the sake of simplicity, water in my game is simply a cube with a blueish translucent texture. However, the fact that the texture is not completely opaque nor completely transparent causes issues with the depth buffer. With completely transparent cubes, I can use the discard; method in the fragment shader. However here, I can't, or my water texture would be completely transparent.

I've looked for solutions to my problem on Internet, and one way I found to achieve proper semi-transparency would be to render the cubes from back to front. But rebuilding the second VBO every frame would absolutely destroy my game's performance. Another solution I found was to first render all the opaque cubes in the world, then render all the translucent cubes. While this solution sounds good on paper, it means I would have to create a second VAO containing only the translucent cubes for every chunk, which again, would be terrible for the performance.

So, what would be the best way to implement water in my voxel game ?

\$\endgroup\$
6
  • 2
    \$\begingroup\$ The second approach you describe (render solids in one pass, then transparent) is the technique I've seen used elsewhere. \$\endgroup\$
    – Basic
    Commented Sep 13, 2022 at 16:49
  • 1
    \$\begingroup\$ @Basic Does this mean I should indeed create 2 VAOs per chunk? Wouldn't this be bad for the performance? \$\endgroup\$
    – Vincent
    Commented Sep 13, 2022 at 16:55
  • \$\begingroup\$ I'm coming at this with experience in a couple of higher-level engines, rather than opengl directly so I can't offer good advice. The technique is to use the first draw call to render solids and also populate the depth buffer which you can then reuse on the second draw call to handle occlusion of your transparent objects. Unfortunately, I don't know how that translates directly to opengl primitives, so can't answer your question. \$\endgroup\$
    – Basic
    Commented Sep 13, 2022 at 17:10
  • 1
    \$\begingroup\$ I understand. Thanks for the answer though! I will try to implement it following your advice and see if that works well with my engine. \$\endgroup\$
    – Vincent
    Commented Sep 13, 2022 at 17:18
  • 1
    \$\begingroup\$ My pleasure... Glad you got it working. For the assistance of others, consider posting some details and accepting your own answer. \$\endgroup\$
    – Basic
    Commented Oct 22, 2022 at 1:04

1 Answer 1

1
\$\begingroup\$

I've been able to implement water (or semi-transparent voxels) by creating a second VAO containing water voxels for every chunk. There was no impact on the FPS, and little impact on the memory usage either, although creating a second VAO for every chunks is a little costly (a few MB). When rendering, I first render all the solid voxels from the first VAO, then I render water voxels from the second VAO. I also made sure to render chunks from nearest to furthest to avoid transparency overlap when rendering water. Thanks to @Basic for the help!

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .