Search Unity

Complete novice, probably wanting to do something overly complex...

Discussion in 'Shaders' started by ecutruin, Jan 26, 2016.

  1. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    Hey guys,

    I'm attempting to achieve a layered ground effect similar to the game Don't Starve's Turf system. I have a conceptual idea that I think will work, but I'm not quite sure how to implement it as I've never written a shader in my life.

    Essentially I want to provide a set of textures, which represent the terrain type atlases, and then use an additional texture as a map which tells the shader which tile to draw from which atlas in which order.

    This map would use alpha to determine the order and the color to determine the tile. The resulting texture would then be drawn to the quad (one large quad representing the entire area the player is in).

    Example of a terrain atlas:

    I want to be able to have multiple instances of these and have them just draw over each other, in order. There should also be a base atlas that is drawn first as the background as well.

    Not asking someone to write the code for me, but as a novice in shader development, I am looking for some insight into learning what I would need to learn to make this happen.

    Thanks,
    Ecu
     

    Attached Files:

  2. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    First you should look at how Unity default terrain shader works. That's close to what you just proposed(it doesn't have texture with data, but it doesn't matter) It's much simpler, works with just 4 (+4 for each additional pass) textures and blends them when needed. You proposed essentially same, but instead of using vertex data directly pack all textures into one and pack vertex uvs into texture.
    But it won't work like you've drawn in your example. You will paint a vertex (a corner of tile in most examples) which just can't produce quad-like result you've drawn. It will draw a circle around corner point with smooth blending of one tile into another. In don't starve I think they use more-than-4-vertice tiles with vertex displacement (random I think) and vertex UV coordinates without any additional texture like you propose. More to it, to me it seems each material they use (speaking in Unity terms) has its own texture and they don't blend at all and each texture 'continues' beyond their tile so they don't use texture atlases at all.
     
  3. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    I will take a look into Unity's default shader. As for how it works, I don't see why you could not combine multiple textures one on top the other based on a set of layers. Conceptually it should work. Then again, I'm a compete novice to shader development, so I'm not sure what they can/cannot do. However, I've seen this which shows using a shader to build a tile map.

    I have to imagine you could not do something similar to achieve the effect I desire. However, I'm not sure how many textures you can pass a shader before issues arise.
     
  4. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    It will work, but why bother with writing shader for packing/unpacking when even without it everything works fine? Besides, either way you will need to manually create tilemap mesh and at that time you can just write UVs for tile itself. It's simpler to make, it's comparable in terms of performance and user won't notice difference anyway. Besides your last link's way won't work in don't starve because don't starve uses a one large texture for each material of ground (that's why it doesn't look tiled and all those lines on ground continue even past tile) while tilemap can't do that trick without losing performance.
    Besides you've provided link for strict tiles, but before that you were showing screenshot of non-strict tiles (larger/smaller than square), pick one first...
     
  5. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    I can write UVs for a tile for a single texture, when I generate the mesh. This is indeed correct, but creating UVs that layer multiple textures on top of each other? Can UVs even receive multiple textures? I mean, if that is feasible, I would gladly create a mesh with a bunch of UVs for each tile mapped to different textures. It indeed would be a simple solution.

    However, my understanding was that a mesh has a single material and the shader needs to reference multiple textures for this to work the way I want. Am I incorrect?

    Edit:
    As for the comment about strict tiles, you seem to be misunderstanding or something. Don't Starve does have strict tiles. Its a series of tilesets layered on top of each other in a set order.
     
    Last edited: Jan 27, 2016
  6. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    see uv2, uv3, uv4... Yes, using them requires you to have up to 4 textures in your shader. But you can just not assign/use them for now. I doubt you will need more than 4 layers for just terrain. And if you want more, you can use Texture3D with your packing... But why? What do you want? Epic performance? Then don't use Unity. It will never beat raw C or C++. Make a game? Then don't think about optimizing before you can actually play your game and see that your problem is in performance. It's not a game before you can play it.
    In data array - Sure. In drawing - Wrong. It's several materials each with its own texture and displacement of vertices to create non-even edges. If you have two materials on screen, that's two materials used. One with plains texture, another with marsh texture. If there's 50 of them on screen, that's 50 materials. That's 50 draw calls. Before 200 draw calls there's no performance drop even on mobile systems, so I don't see why would they even think about tilemaps.
    Look at don't starve screenshot again. Look at monotone plains. Do you see it tiling? Because I don't. That means copying it with tiles is inefficient!
    Each submesh has single material, not mesh. Yes, they will need to reference different textures. Yes, each material will cause one more drawcall. And what about it? If you're making less than 200 draw calls for mobile or 1000 for PC - why would that matter? If you make each single tile a separate material - it might become slow (but don't starve has very limited view radius and culling works fine).

    P.S. Stop doing preemptive optimization and just do it with way you can develop fastest. And only when that way will slow down target platform - optimize. Not visa versa. If you really want those optimizations, prepare interface which would support both ways and just do fastest dev-time first and do optimizations later.
     
  7. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    Hrm,

    I suppose you're right about optimizations. I'm going to try out just generating a mesh per 'layer' of the visible area, controlled by a parent and mess with spacing between these layers to get something that looks nice. This should keep the draw calls relatively low still (1 per layer from my understanding), and I can just use a different material per texture.

    Combine that with a nice editor script to manipulate the layers, it should be decent.

    I appreciate ya knocking some sense into me about optimization. I always end up trying to researching different methods of building an idea and getting wrapped up in the best way to do it.

    I'll update the thread with the result once I have the energy to sit down and write it all.
     
  8. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    Welp, I tried it the straight-forward way, generating a quad per tile, with rotations. It works well, but is rather limiting due to vertex limitations (I can only have about a 100x100 map). Now I could go about and break things into chunks to handle larger areas, however that would add an additional manager that would be needed to manage chunks.

    Is there a better way to generate a 2D tilemap with different layers?
     
  9. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Vertex limitation is 64k (maximum of 16 bit unsigned integer to be precise) vertices, there's no way around it other then subdividing mesh. Rendering a tile with a quad is usual practice as it makes it easy to use tile atlases. So yes, you would need additional manager to manage chunks, check when you should load another and when you should not, when it's already cached, etc.

    Unity has 2D tilemaps planned(in roadmap), right now it's listed as long and undetermined.
     
  10. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    I understand that all (thanks for the heads up about 2D tilemaps being on the roadmap though). I am curious if a shader solution could make the process better. Use a shader to render on a single quad instead, feeding the shader a 'map' texture and an atlas. Or...even better, compress multiple atlas's into a 3D Texture as a whole tileset, and feed a 3D map texture to draw each layer.

    I know this is technically possible, however, being a novice with how shaders are coded, I'm not sure where to start looking. I'm also not sure what kind of limitations the shader solution would had (as I'm sure it has texture memory size limitations).

    Any advice about going down this avenue you could offer would be greatly appreciated.
     
  11. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    It is possible. It will require more from GPU per frame and still limit your tilemap to size of texture (some platforms support up to 1024x1024, average non-gaming PCs are limited to 2048x2048, gaming PCs are limited to 4096x4096, performance-wise see below). Also don't forget to turn interpolation of texture to GL_NEAREST or it won't work at all. Also you will have artifacts if you will want to rotate your quad or use non-2D camera and rotate it.

    Performance notes: can and most likely will stutter on Texture.Apply() so runtime update with Texture larger than 128x128 or 256x256 will be out of question anyway if you need to change tiles runtime. Also need to make sure never update texture more than once per frame(requires manager anyway): for me doing that brought FPS down to 30 on texture 512x512 on gaming PC. For some it brings it down to 10 FPS. GPU-wise should be fine as I don't see any need for if statements(slowest op on GPU) or any heavy math. But maybe coordinate unpacking will require them - depends on how you do it.

    Developing difficulty: packing UVs into color and unpacking it in shader will be tricky. Everything else is easy enough as long as you know what you're using and doing. Bugs possible are hard hitting and shaders are very bad at debugging though.

    What to do in simple case:
    Shader:
    First make simple shader with two textures (one for tilemap, one for tile atlas). Make a vertex shader that outputs world position into fragment shader and applies usual vertex transformations to quad itself. Create additional function which gets UV coordinate of tile in atlas from texture color and world position. Add offset inside tile and then return tex2D from tile atlas with coordinates you get from that function as color.

    Script:
    In script you create tilemap texture with GL_NEAREST interpolation (Sorry, don't remember how it's called in Unity. I remember it from OpenGL. Any other will break your packing) and without compression (packing breaker too) and size of square with power of 2 (like 4x4, 128x128, etc - for performance reasons on some systems). Fill out array that texture will contain (pack tile UVs into Color) then fill it with values you need using SetPixels and Apply.

    Also problem you might encounter with texture interpolation: even with GL_NEAREST you might need to offset texture probing by (-0.5,-0.5)*tileSize in world coordinates.

    To pack tile UVs into texture you can for example write Color with rgba, then merge r,g,b,a to get int and deduce UVs from that int with simple math.

    I'm not sure why you want it though. If you plan on changing tiles runtime, I advise against it. Just make a manager that is easy to debug and forget it.
     
    Last edited: Feb 10, 2016
  12. Marrt

    Marrt

    Joined:
    Feb 7, 2012
    Posts:
    613
  13. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    It will hit exactly the
    as there's no subdivision into several Meshes there. It will create small enough tilemaps normally, but will crash if you request it to make that 1024x512 mesh they promise in a single call. And as far as I understand, that's exactly what OP wants (not sure why though).
     
  14. ecutruin

    ecutruin

    Joined:
    Aug 19, 2014
    Posts:
    39
    Yeah, I already have something quite similar to this, except mine can rotate tiles by shifting UV order for a tile and layers multiple meshes on top of each other to say have grass on top of dirt (leaving a incredibly small gap between the layers).

    It basically has a limit of like 128x128 tiles roughly (I set a limit on mine of 100x100). In the end, it may just be better to implement a chunking system on top of this layer system, essentially building a 'flattened' Minecraft-esk engine.