How to get multiple textures on one object (texture coordinates?)

I’m struggling with comprehending how chunks work in voxels.

From what I understand they are an area of blocks say 16x16x16. Instead of rendering each cube as an object (many objects aren’t good for fps I’ve read) a chunk is all the cubes in 1 mesh and 1 object. When a cube is broken or placed the mesh needs to be somehow regenerated.

What I don’t get is how you can use more than 1 texture on the same object. I know there is something called texture mapping which to me means there is an image with say 100 tiles on it, and if I put in use texture 0,0 to 128,128 then it would use the first texture on the sheet. I don’t know if I’m understanding it wrong but does that mean i’m limited to using 1 image per object? And then how would I change the texture coordinates per block? And wouldn’t it be more efficient to have large areas with the same texture instead of areas where every other tile the texture changes? Just trying to get my head around some concepts. Thanks.

You want to use only one image per object as else you get separate geometry which in turn drives performance down. If you create e.g. ten house models with the same texture atlas you can put all the models in one mesh with one geometry, even batch them on the fly in the engine, very efficient to render.

Read up on “texture atlas”.

I’m sure there must be libraries to help you out with this - but here’s a low-level description of how this functions in the block world I’m working on:

Your chunks are likely composed of sets of voxel faces. Each face will be composed of 4 vertices, forming a quad (unless you’re combining many voxel faces to form larger rectangular quads - but in any case the logic below still applies, it would just need to be expanded a bit then).

A vertex shader and a fragment shader will be used to apply the texture to your quads.

Values set in your vertex shader are interpolated across the quad for the fragment shader, from one vertex to the next (providing they’re declared using the keyword “varying”).

This means that if you set a float in your vertex shader with a value of 0 for the bottom left vertex of a quad, and a value of 1 at the top right vertex, then when the fragment shader executes for each pixel, this float will have a value somewhere between 0 and 1, depending on where between the two vertices that particular pixel lies.

Texture coordinates are vec2 values between (0, 0) and (1, 1). Given that a texture is basically a 2D array of pixels, these are indices in that array.

So if you want to cover a quad with a texture, you can set that texture as a uniform in your material, by adding something like this to the MaterialParameters section of your .j3md material definition:

[java] Texture2D VoxelTexture[/java]

This will let you declare it as a uniform in your fragment shader, like this:

[java] uniform sampler2D m_VoxelTexture;[/java]

Then, create a vec2 with the value of (0,0) at one corner vertex, and (1,1) at the opposite corner, in your vertex shader. In your fragment shader you can use this value (which will be interpolated automagically between the vertices) to select a pixel from the texture to display on the quad, like this:

[java] gl_FragColor = texture2D(m_VoxelTexture, textureCoordinate);[/java]

(That’s assuming the name of your vec2 is “textureCoordinate”).

This will paint that full image onto your quad.

If you then wanted to put two textures into the one image and display each one depending on some variable (for example, “VoxelType”), you just need to modify your textureCoordinate variable by some value.

For example, if you have an image that contains 2 textures, one directly above the other, then the texture coordinates for the image at the top will be from (0, 0.5) at the bottom left, to (1,1) at the top right (because the x-coordinates are 0 to 1, from left to right, but the y-coordinates are from 0 to 1 from bottom to top). The coordinates for the second image will go from (0,0) at the bottom left to (0,0.5) at the top right.

So if you want to paint the top image onto your quad, you’ll need to set the textureCoordinate vec2 as (0, 0.5) in the bottom left vertex, and (1,1) in the top right.

If you want to paint the bottom image onto your quad, you’ll need to set the textureCoordinate as (0, 0) in the bottom left vertex, and (0,0.5) in the top right.

In this way, if you create a list of the correct offsets for each voxel type as uniform values in your shader, the only data you’ll need to send from the the CPU to the GPU for each vertex will be a VoxelType - which is pretty efficient. You’ll also be able to store all your textures in one image.

Then at runtime you just look up the correct texture coordinates for the type, and paint the appropriate part of your texture onto each quad.

1 Like