- Home /
How can I use a texture atlas to supply textures for a material?
There are so many questions and answers on this topic, but my head has exploded trying to understand them, because none seem to be related to my issue.
My situation is my game is suffering from draw call issues as it is a building game. Occlusion culling doesn't work, as it is an outdoor game, so I need to look at reducing draw calls, and one idea I had was a texture atlas.
As you can see I have a lot of trees on the screen. These are not tree models, but are instead made up of individual blocks. These are buildable prefabs in my game. There are over 5000 individual models here but as you can see there are basically just 2 materials in use in this particular scene, the brown trunk and the green leaves. These materials both use the same normal map, the only difference being the individual 128x128 texture used.
My plan is to create a texture atlas which the material can use to select textures from. In this example, there are only 2 textures, but there are currently 16 default textures that would be assigned to the atlas. (More than likely increasing in the future).
Here is the default atlas I have made:
I can very easily work out the positions of the UV for each texture, as they are all pure power of 2, the question is, how can I make a material use a texture atlas in this regard?
Being able to atlas textures in my specific instance would hugely reduce the number of draw calls required, but I cannot see anything on having this implemented in a 3d game in this way.
Any thoughts tips or suggestions?
-Edit-
I did the test again, but found much better use of batching this time. The problem gets much worse the more of the different textures I use. Here is a quick breakdown of the issue, oddly, this time, I notice that with far fewer verts (1.3m in this video compared with 2.3m in another test) the fps is much lower.
Any help with suggestions which could narrow down where I should be looking to investigate optimisation opportunities would be welcome.
https://www.youtube.com/watch?v=V5yVnvNE-7o
(please wait for video to be processed)
Here is a screenshot of the drawcalls at the end of the video, as this bit is blurry on the youtube video:
Answer by Tekksin · Dec 04, 2015 at 10:32 PM
well if they're just going to be solid blocks, why not forgo any texture at all, and instead just tint the object?
There's a tiny parameter on regular diffuse materials that can be changed at runtime (in your case, perhaps at "Start"?) by writing:
//javascript
GetComponent(Renderer).material.color = Color.red;
//C#
GetComponent().material.color = Color.red;
The color.red can be any normal color (color.green, blue, white, black, etc) but if you want a specific color, just go to the color picker in unity, and select your color. then take the 3 numbers and divide them by 255.
so.. R: 107 G: 200 B: 56
divided all by 255:
//js
var alpha : float;
Color(0.42, 0.78, 0.22, alpha); //<--some puke green.
more info here: http://docs.unity3d.com/ScriptReference/Color.html
edit: it should be noted 3D objects use textures, not sprites, so setting up an atlas is not possible. Your best bet is to take advantage of tinting wherever you can. And you can do that with a black and white texture, should you desire a mincraft-type of rocky texture for everything.
@Tekksin That is a great great answer for this specific example, but what about extending that when I use brick textures etc?
I tried to keep the example simple, and you have provided an excellent answer to that. Can you provide a solution for when non plain textures are used?
I have awarded you with a reputation point (sadly I do not have many), but will not answer this question just yet. If I get an answer to help with the non-plain textures, I will make yours as an answer as well, as it is very helpful.
Yeah I believe you'll be stuck with the draw calls, if they aren't batching (batching usually gets wonky the more vertices that are being rendered). What you can try to do is shut off the objects that are not in the camera's view. This is a feature of unity pro, but you can do some rudimentary scripting to emulate the effect.
Doing this is described in great detail in this post: http://answers.unity3d.com/questions/8003/how-can-i-know-if-a-gameobject-is-seen-by-a-partic.html
This is why you see a lot of pop in even in triple A games. Things too far or off screen are shut off (as far as rendering goes) and turned on when the camera is actually on them.
Also, a popular technique to reduce draw calls is to make 2D quads with a similar texture on them, in place of the 3D model (which is giving you all those draw calls because of their many vertices). Then, when the player is close enough, switch the quads to the 3D objects.
You can update the objects based on the distance the player is from them.
This is a good exmaple: http://answers.unity3d.com/questions/536563/besy-way-to-check-distance-between-player-and-obje.html
You can do what Tekksin describes with LODGroup component, it should be much more efficient than a custom script to check the distance from the camera, I think it is available in the free version of Unity 5.
http://docs.unity3d.com/$$anonymous$$anual/LevelOfDetail.html
http://docs.unity3d.com/$$anonymous$$anual/class-LODGroup.html
that's awesome! Definitely bookmarking that. I didn't know unity had something like that built in. I usually stick to 2D, so I never did much research on scenes with lots of geometry. It'll surely be handy info for when I do, though.
Also, I mentioned lower in the comments when I finally saw the video. When I wrote the other info u noted, I didn't base it off of the material soulrider posted outside of the pics here :x
What I was looking at was a manual way of doing texture atlasing. A friend built a mod for Natural Selection 2 that used atlasing of textures to enable model texture customisation, so while Unity has no in-built atlasing support for textures, I am looking for a potential solution to get me on the track to make my own solution.
You can play with the tiling and offset to show sections of a texture. This has nothing to do with sprite atlases. It'll just be a regular texture, and you can open the material parameters up to change the offset and tiling to render the part of the texture that you want.
Do you know how to do this?
no, playing with the rendering was not something we were generally able to do when I was modding NS2, so texture manipulation etc is very much new territory for me at the moment.
I understand about offsets etc for the Texture 'atlas', but not sure how to implement it directly within the material parameters.
I use atlas to refer to a collection of textures, i use sprite sheet as a term more when talking about sprites :)
Any assistance you can give to help me would be very appreciated.
I'll mark this as answered as you have given more than enough help already to get me on the route to solving the problems I am looking for. It will definitely be a combination of many elements which reduce my issues.
So I just finished testing it. You can use all the sections of your current atlas by setting the tiling to 0.25 for the x and y axis, and then just changing the offset.
You can mix and match 0, 0.25, 0.5, 0.75, and 1.0 on both the x and y offset to access each square of your atlas.
I would suggest making those sections of the atlas black and white textures, so you can overlay color with the aforementioned color tint method I mentioned. this way you can increase your variance in objects. But that's up to you. Consider replacing far off objects if you can, though, because that's what's impacting your performance the most.
Thanks again.
The current solution I am ai$$anonymous$$g for is using a color picker, and enabling freedom of choice on the color, using tinting to set the color. On items like trees etc I will set the color manually when making the prefab, and it can then be changed at runtime if the user wishes to edit the color.
I will definitely use your method suggested above about tiling and offstes for the non-plain textures I will need though.
As regards the objects, I am definitely going to look at methods to reduce complexity of objects when at distance. Initially, I am going to play with objects outside max build range, ie, cannot be affected or edited. In these cases I will have a version with stretched blocks, ins$$anonymous$$d of multiple separate blocks, and I will remove unnecessary/unseen blocks.
Once further out of range, i will then look at planes etc, if the need still arises.
I cannot thank you enough for the information and inspiration you have provided with your answers :)