1
\$\begingroup\$

See edit:

How would I go about rendering a model with multiple tiling textures? I have a few ideas, but all of them have drawbacks, here are a few (by the way this is for an opengl game):

-Use a huge texture, but this comes at the cost of the texture being expensive

-Render diffrent objects for each textured object (eg: one model could be bricks for a house, another could by the roof, each with their own tiled texture), but this would become very messy, very quickly.

Here is an (amazing) image to help demonstrate what I'm trying to do. enter image description here

The repeating "R" on the red area represents perhaps a repeating brick texture, while the green one represents a roof, maybe roof shingles. To add on to that, I've added a window texture.

My question is, how do most games deal with this? I can't seem to wrap my head around how I could fit this in one texture, but yet I see many other games do exactly what I want.

EDIT:

This should give a better visual demo. I want a large mesh (not very large in the demo) using one texture atlas. (Please keep in mind not all textures in the atlas will be the same size!)

enter image description here

Lets say this is my object, it uses each texture on the atlas, but not the same amount of repeats (brick repeats 3 times, when the smiley face repeats 9 or so times)

enter image description here

How do most games achieve something like this? Without using some huge file with repeating textures.

Example (from BOTW) I assume they didn't just repeat this texture on the atlas, considering how large that would be (and expensive). enter image description here

\$\endgroup\$
  • \$\begingroup\$ Generally, 3D models are rendered with a UV mapping, so you use a single texture for the entire model. Any 3D modeling software should support UV mapping, it's then up to you to render the UV data correctly. en.wikipedia.org/wiki/UV_mapping \$\endgroup\$ – Blue Wizard Apr 17 '17 at 20:40
  • \$\begingroup\$ @unixunited I understand the mapping part, but I can't wrap my head around how people tile this way. Usually if they want to tile, they just put the bounds of the uvs outside the 0-1 range, which repeats the image onto the opposite side, but how would this work in an atlas (all the required textures for the model)? It would start to bleed into others textures, which is what confuses me the most. \$\endgroup\$ – Jason Apr 17 '17 at 20:43
  • \$\begingroup\$ If by "a huge texture" you mean a texture atlas, then this doesn't need to cost significantly more memory/texture bandwidth than you'd spend in total across all the component textures anyway. In fact it can actually improve performance by saving on draw calls. You can apply the tiling by wrapping texture coordinates in the shader, so the atlas doesn't need to store more than one repeat of the texture. Or, on sufficiently recent hardware, you can use array textures to get the best of both worlds. \$\endgroup\$ – DMGregory Apr 17 '17 at 21:26
  • \$\begingroup\$ @DMGregory This seems intriguing, but I can't wrap my head around how I could wrap the textures. Lets say I have a huge model of a house, and I decide to store all the textures in one texture, so an atlas. How would I wrap the textures if I had lets say a 64 x 64 texture for bricks which I would want to repeat 16 times on the x and y axis, but then I also had a window texture which I wouldn't want to repeat? I'll try to add an image on my post to help demonstrate this. \$\endgroup\$ – Jason Apr 17 '17 at 21:34
1
\$\begingroup\$

I'm not very fluent in OpenGL, so I'll present a rough sketch here and leave it as a community wiki in case someone who knows the tech better would like to update it with precise code hints.

First, if you're targeting OpenGL 3.0 or higher, you can use Array Textures. These let you pack multiple tiling textures of the same resolution into a single sampler. Your workflow would look like this:

  1. In your modelling tool, set up and export your model with 3-channel texture coordinates (often called UVW). U & V will be the normal 2D texture coordinates that you're used to, and can range over whatever parts of the texture space you want, even overlapping other parts of the UV net. The third component, W, will be a material index - so for instance the parts that are supposed to be "brick" might have a W coordinate of 0, "cobblestone" might be W = 1, and "shingles" might be W = 2.

    Alternatively, you could set up an importer that reads a material or submesh index from your model and uses that to construct these W coordinates, if that's more convenient than authoring them.

  2. Load your textures into a Texture Array, in the same order as the W coordinates you assigned (ie. 0 = brick, 1 = cobblestone, 2 = shingles, etc...)

  3. In your shader, use a sampler2DArray to reference this texture, and pass it the 3D texture coordinate created above. It will use the third channel of the texture coordinate to select the right material "page" from the array and sample that.

If you need to target devices that do not support Array Textures, then you can do a flavour of this using a texture atlas, with a bit more fuss:

  • Instead of storing your textures as pages in an array, they'll be laid out side-by-side in a single large 2D texture, like tiles.
  • In your shader, you'll use the third texture coordinate to select an offset into this grid of tiles.
  • In the pixel/fragment shader, you'll use the frac or fmod functions to wrap your texture coordinates into the 0...1 range (effectively doing what the Repeat texture sampling mode normally does in hardware), and scale this range to the size of a single tile within the atlas.
  • Combining this tile offset and wrapped coordinates-within-tile you'll get your final sample position within the atlas.
  • You may need to make your tiles slightly smaller to allow some padding between them, to ensure adjacent tiles don't bleed together at the smaller mip levels.
\$\endgroup\$
  • \$\begingroup\$ I like the idea of texture arrays, but with my limited modelling knowledge, I do not know of any formats that can export 3 coordinates (UVW). OBJ file does have a materials file with it, but I am not sure if this file would be of any use. I will try to find one in the meantime, but thanks for this info! \$\endgroup\$ – Jason Apr 18 '17 at 20:36
  • \$\begingroup\$ As a crude hack you could say coordinates in the range -10...10 are brick, 10...30 are cobblestone, etc. Using a floor function in your loading script or your shader to compute the corresponding w component. That gives you room for 20x repeats across any single polygon. \$\endgroup\$ – DMGregory Apr 18 '17 at 20:46
0
\$\begingroup\$

I seem to have found probably the best answer for my case, it may be lazy, but if you have time, I would try and do what @DMGregory suggested, as it is probably more efficient.

So what I ended up doing was using the material file that is exported with the OBJ, which lets me define when a face should use a different texture. So I would render one texture, then swap textures, render the rest of faces with this texture, do that again and so on until the whole model was rendered. Of course this means many extra draw calls, but for me, this seemed to be the easiest option.

\$\endgroup\$

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.