[personal profile] lexpartizan
Creating games is a struggle with the limitations of real time. The game should work incredibly fast. One of the main constraints for rendering is the amount of materials on which the number of drawcalls depends. We try to use atlases, sacrifice the quality of textures and use different techniques to reduce the amount of materials at least a little.

But there is a solution.

What if I tell you that it is possible to create one material for all?

Well, for almost everything. We will still have special materials with shaders, like water, transparent materials and others. Which it makes no sense to do in one piece about the reasons for performance limitations. But for almost everything else, we will be able to make one material.

There is a rather old technical solution called Texture Array. It's very cool, but almost no one talks about it and they consider it a solution for very outdated graphics with tiles. In fact, it is. But the new is the well-forgotten old. We just need to look at it in a new way.

I started with the fact that I had to make models of houses. And in architecture, we usually have large surfaces. To cover them, you need to use tile materials.

And there should be a lot of them. Roof, walls, fences, much more. This is a lot of materials. But I'm sorry to use a lot of materials. I'll settle for one.

There are a lot of materials that use tiling, so it is impossible to combine them into an atlas. But it can be combined into a texture array.

Its meaning is that a large texture is cut into tiles and each of these tiles is perceived as a separate material and can be tiled without restrictions. To access the desired texture tile in shader, its sequence number is used.

Now, before we dive into the technical wilds, let's talk about the limitations of this method. Perhaps you just won't need it.

So our approach is to create a bank of different texture surfaces and give yourself as many artistic possibilities as possible, using only these surfaces and nothing else. We cannot use the normal map, which was baked from a highly polygonal model. Just because using a unique texture is another material. So, we need to achieve good shading and chamfering using geometry, because you can't fix anything with a normal map.

Surfaces will have normal maps, but not models. For example, there will be a normal map of a tree, but there will not be a normal map of a wooden statue. The statue will have to be made with geometry so that all the chamfers conceived are geometry. So that the model has an even adequate shading. Use both chamfers and sharp edges for this. So your model will no longer be as low-poly as possible with unique and individual texturing.

You will not be able to texture the models in the way it is customary in AAA games. You won't be able to create scuffs where you want. Or dust. Or other traces. However, we will talk later about how this is still possible, albeit limited.

But you should be prepared for the fact that you will not be able to create an AAA quality model in this way and replenish your portfolio. This is the way for indies and mobiles.

And now that the professionals have left here, we can continue our journey down the rabbit hole.

So, we have the idea to store many textures in one texture. And thus a lot of materials in one. But we need to somehow tell the game engine which tile we want to use. If you do this from the code, then each model will need its own unique materials, otherwise the "switch" will act on everything. And in one model there can be many different surfaces from our texture bank. It will be almost impossible to manage them from the outside. This is usually the end of all the lessons on the text of array that I could find on the Internet. They just drive textures using an external switch. Like, look, here's the whole glass, here it's cracked, here it's broken, but it's not there at all.

Not everything is so simple, you need to be able to cook array textures :-). They were not invented for this.

Therefore, the necessary information must be stored in the model itself. Where can we store information in a 3D model? In vertex colors and additional UV channels.

In godot 3, we had only two UV channels, one of which was intended for lightmaps, but could be used in any way. In godot 4 we have 8 channels. And it's just amazing. But for now, two are enough for us to explain the concept and an example.

So, we will transmit information about the tile number in an additional UV channel and read it in the shader.We also need to make sure that the very process of specifying this information takes place in a blender and is intuitive and convenient. Because the convenience of the workflow is very important. And it seems to me that I have solved this problem.

Working with the model looks like this. We, as usual, arrange the seams and unwrap the model. We arrange the seams so that the surfaces of different types, which are located on different stalls, are located on different uv islands. Then we arrange these islands according to the squares of the tiles. And we do another unwrap, where we scale these islands to a result that suits us (for example, the number of bricks in the wall). It helps a lot that the blender copies the previous unwrap to the new one. A significant plus is that since we are dealing with files, we do not need to try to fit everything into a square, we do not care about the density and efficiency of the scan and other things that take a lot of time. We don't need to pack islands. It saves a lot of time.

This is about how it works in a blender.


This is what the code for the shader looks like in a blender. Connect the output to the UV channel of the texture.



godot-import
Blender UV-shader

In the texture itself , do not forget to set interpolation mode "closest", since the blender does not have array textures and smoothes the entire texture entirely, which gives stripes on the edges.

8 is the number of rows and columns of tiles in the texture. This means that there will be as many as 64 (8X8) different materials on our texture! If you need a different quantity, then just change this number. I use 64 materials with 512X512 quality in one 4k texture.

Finished with the blender, we begin to move on to godot.

Finished with the blender, we begin to move on to godot. Import it as a Texture Array and specify how we want to cut it. In our case 8X8


This is what the code for the shader looks like in a godot.


shader_type spatial;
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx;
uniform sampler2DArray texture_albedo : hint_albedo;
uniform sampler2DArray texture_ORM : hint_white;
uniform sampler2DArray texture_normal : hint_normal;
varying lowp vec3 vertex_color;
varying lowp vec3 uv;
uniform lowp float subdivision: hint_range (1.0,16.0,1.0);

void vertex() {
vertex_color = COLOR.rgb;
vec2 tile_offset = trunc(UV2*subdivision);
lowp float tile = tile_offset.x+subdivision*(tile_offset.y);
uv= vec3 (UV*subdivision,tile);
}

void fragment() {
ALBEDO = texture(texture\_albedo,uv).rgb\*vertex\_color;
NORMALMAP =(texture(texture\_normal,uv).rgb);
vec3 ORM = texture(texture\_ORM,uv).rgb;
METALLIC = ORM.b;
ROUGHNESS = ORM.g;
}



Since the tile number is calculated in vertexes shader, the difference with the usual material in speed is minimal.

Here you can find a scene with an example in the Godot.

https://disk.yandex.ru/d/1_xlAcNSNhe6nw

And here download an example for windows (the same as in the scene.)

https://disk.yandex.ru/d/EyDYWOjXK99Mfg

I also want to note the difference in UV channels for Godot and for blender.

Blender focuses on UV channels by name. And godot in the order of creation. Therefore, before importing, I create a cube, quickly unfold it and name all its UV channels, as it should be in a blender, but in order, as it should be in a year. And then I make a join of my model to it, so that its UV channels are arranged as it is done with the cube.. I remove the vertices of the cube and export the model already with the correct channel order.

The main part of this article is over.

But what if we go further? How can we expand the artistic possibilities, because in fact 64 materials is not enough to create a diversity? There are several ways.

There are simple flat materials for which only roughness and metal or not metal are important. I call them single-pixel. Because one pixel is enough for them. So I created a small palette with different roughness values for metals and nonmetals.

R-height,G-Roughness, B-metallic

I have textures of different wallpapers in this example. But the wallpaper material is actually the same, it's just paper, but the patterns are different. But all these patterns occupy a precious place among these 64 materials. It would be necessary to take them out of there and at the same time make it possible to set any color. And now I'm already creating a new texture atlas. Color atlas. Where I throw different color patterns for clothes, wallpaper, etc. And also throw a texture with a color circle. This is such a palette organized in a circle, it allows artists to easily search for complementary colors, for example. I also placed brightness gradations, a stepwise and a solid gradient there. And the colors of popular metals, like gold, copper, etc.


color circle

Just like before, I'm creating two more UV channels, now for the color texture array. And I put uv islands on the color or pattern that I need. And now I already have a red and green chair. Since all our materials are white by default, we can simply multiply the colors from the new texture. Now we have 64 materials that can be combined with 64 color patterns. And this greatly expands our artistic possibilities.

It is important that if the UV channel does not exist, then there will be no error, and as UV coordinates we will get (0,0), lower left corner. So this corner should be white to avoid unnecessary work. I just don't create extra UV channels if the base material suits me (why paint wood, for example?)

I still have few artistic opportunities.

What else can we do? And we can actually still mix materials. For example, bricks can come through plaster. We can apply decals. We can overlay text. We can apply all sorts of normalmap patterns to create new materials. And so that there are many and different deсals, what are we doing? That's right, we are creating another texture array, with which we will mix what we have turned out. We make 2 UV channels for decals.

Materials are usually mixed using vertex color. By default, vertex - color is white (1,1,1).

So we think that 1 is the basic material. And 0 is the material of the parts. In addition, there is a transparency mask in the material of the parts itself. In addition, to increase the variety, we have 3 color channels. So I mix by R albedo, G - roughness&metal, B -normalmap. It gives me more options.

As an example, this garbage bag.


garbage bag

I used a 1-pixel shiny material on the first basic texture array. Multiplied it by the black color from the second color texture array. And then, using vertex color, I lowered only the color channel B (1.0, 1.0, 0.0) so that the normalmap from the third decals texture array with wrinkles, which I usually use for fabric sheets, would appear))

Since the color of the vertices is white by default, only the vertices that I paint are important on the scan. The rest does not matter where they are, since the base material will be on top by default. This also saves time, I need to deploy on this texture array only those polygons that I want to mix with decals.

I also use decals with different fonts to make inscriptions.


R -opacity, G-Roughness,B-Metallic

We see this approach force us use more polygons. Each letter for the inscription requires own polygon.


And I use another UV channel for the color of the inscriptions. In this case, I use the above texture with a color circle.
No longer specifying the tile number, but using it hard-coded. Why? Because the UV channels have already ended. Such sad.

1 channel for lightmaps.

2 channels for the base material.

2 channels for color pattern.

2 channels for decals.

1 channel for the color of decals.

In total, 8 UV channels and vertex colors were used. There's nothing else left, otherwise I would have thought of something more.

Now let's see what I've made of this stuff.

What do they have in common? One material. Like hundreds of other objects I've made.







Profile

lexpartizan

May 2022

S M T W T F S
1234567
891011121314
15161718192021
22232425262728
2930 31    

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 27th, 2025 01:14 pm
Powered by Dreamwidth Studios