2
\$\begingroup\$

At the moment, I mesh every voxel in my terrain data to a block with 4 vertices for each face. This makes assigning texture coordinates of my texture atlas very easy, since I can just pick the desired part for each face independently.

Unfortunately, rendering a bigger amount of such terrain chunks becomes very slow because of the unoptimized meshes. Therefore I want to redue the number of faces and vertices produced by the meshing algorithm. This is an image I found on the web showing what an optimized mesh could look like.

example of a simplified terrain mesh

Grouping faces of different voxels make me think of how to still handle distinctive textures for each block. Is my worry right and how can this be handled?

\$\endgroup\$
  • \$\begingroup\$ did you try mesh instancing? but am not sure if it's applicable because I don't know what does your engine do ? \$\endgroup\$ – concept3d Nov 23 '13 at 16:55
  • \$\begingroup\$ @concept3d The basic concept is to pick a chunk of given size out of the voxel data and generate the representing mesh hull of it. The result will not contain enclosed blocks but only the outer shape. So I think drawing instanced blocks isn't the way to go. \$\endgroup\$ – danijar Nov 23 '13 at 17:58
  • \$\begingroup\$ Your question contradicts your statement that you're using texture atlases. The answer is texture atlases. What am I missing from the question? \$\endgroup\$ – Sean Middleditch Nov 23 '13 at 18:59
  • 3
    \$\begingroup\$ The larger faces shown in the example are multiple materials optimized into a single face? So you need to find a way to have multiple textures per face so that larger faces can still represent multiple materials? \$\endgroup\$ – MichaelHouse Nov 23 '13 at 19:49
  • \$\begingroup\$ @Byte56 Exactly! \$\endgroup\$ – danijar Nov 24 '13 at 16:15
4
\$\begingroup\$

I put together a greedy meshing implementation for voxel data which includes various attributes (https://www.youtube.com/watch?v=0OZxZZCea8I). I understand you're looking to merge voxel faces even when they aren't the same voxel type - but it would seem that the only way to do that is to send the set of voxel types represented by the quad into the shader and then work out the texture coordinates for each point on the fragments by referring to this data and offsetting read from the texture atlas.

I didn't do this - instead I only merge voxel faces that are exactly equal, including lighting data - but it might be possible. The problem, I would think, would be that you'd be increasing the size of your vertex buffers, thus losing some of the gains of greedy meshing in the first place - and, of course, adding calculation to your shaders. Also, if you do need to light the mesh using any kind of baked ambient occlusion, you might also find that splitting up the quads by lighting values is necessary in any case.

Best of luck!

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I decided against an advanced meshing algorithm because the block types are distributed so diverse that it wouldn't increase performance much. You project seems very interesting though, I follow you on Youtube so I've already seen the video ;) \$\endgroup\$ – danijar Mar 27 '14 at 17:34
  • \$\begingroup\$ Thanks! In case anyone else stumbles on this - if the block types are the same, textures can be tiled over larger quads providing you send the quad dimensions into the shader. The texture coordinates in the fragment shader will tell you where on the quad you are. Multiply that by the quad dimensions to find which voxel you're in, and then get the fract() of that value to find where on the single voxel you are - and use that as your texture coordinate. If you're using a whizzy texture atlas you may have to do more - but if you have texture coordinates you should be most of the way there! \$\endgroup\$ – Rob Mar 28 '14 at 11:14
  • \$\begingroup\$ In the long run using a texture array over a texture atlas might be the best option. Not only does it prevent bleeding artifacts, it'll also allow shaders to GL_REPEAT textures properly. I will address this in some of the next releases of my game. \$\endgroup\$ – danijar Mar 28 '14 at 11:45
2
\$\begingroup\$

In theory (untested), the fallowing could work. Might vary depending on the Graphics Pipeline.

Assuming you are using custom shaders. The basic concept (in my probably failing attempts to explain it) is to take the world position of each fragment, and use that to generate your texture coordinates. Those coordinates ranging from (0,0) to (1,1), representing an entire image. Say you wanted 1/1 unit/image ratio (the image repeated once for every unit of space), In GLSL:

vec3 worldTexCoord = fract(vertexPosition.xyz); // 1 unit in the game world will
                                                // vary from (0,0) to (1,1) in TexCoords

You could then transform that into a smaller portion of the atlas. So then, instead of repeating an entire image, only the small portion of the image could be repeated.

vec2 trueTexCoord = mix(texCoordMin.xy, texCoordMax.xy, worldTexCoord.xy);

So then, if you want the image to repeat from the texture-coordinates (0.1,0.1) to (0.5,0.5) (instead of (0,0) to (1,1)), you would set texCoordMin.xy = vec2(0.1, 0.1) and texCoordMax.xy = vec2(0.5, 0.5)

You could also say texCoordMax.xy = texCoordMin.xy + vec2(0.4, 0.4) and get the same result as above.

So, after probably failing at that explanation, here is an overview.

  1. Pass Vertex Position (in world coordinates) to the fragment/pixel shader
  2. Use the Vertex Position to generate the texture-coordinates.
  3. Manipulate the Vertex Position coordinates into coordinates relative to a smaller portion of the image, as indicated by the Texture Coordinates.

Sorry, scratch that.

Personally, I would suggest Sparse-Voxel-Octrees (SVO) if your looking to optimize your meshes. It works by clumping up large chunks formed of the same type of terrain. (Air, dirt, etc) so you wind up with, not only an optimized mesh, but a memory efficient structure, which could double as a Space Partitioning Tree for whatever else you may do.

Another thing usually done, is RLE (Run Length Encoding), it is used in BMP images to unify strips of similar colors from X color, X color, X color, ... down to saying X color is repeated Y times, reducing the required space when stored on your hard-drive. That get's interpreted when rendering Voxels as X textured polygon is Y times it's usual length

What does the guy in that image use? Looking at it, I would guess an SVO. It seems to have some resemblance. Large polygons for flat surfaces, little squares around corners that vary from solid type to air type. Though his flat surfaces do tend to be more rectangular, with less of an identifiable structure. could be some further optimizations on his part. But he doesn't actually texture the terrain, so can't actually know if there are multiple voxel types per polygon.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I think the guy in that image uses an algorithm called greedy meshing in this blogpost. Moreover, I am not quite sure if the meshing depends on the data structure. For now, plain arrays worked fine for me in terms of algorithms and performance. \$\endgroup\$ – danijar Nov 24 '13 at 16:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.