Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: How to add a depth buffer to isometric textures?  (Read 2884 times)

0 Members and 1 Guest are viewing this topic.

YesBox

  • Newbie
  • *
  • Posts: 11
    • View Profile
How to add a depth buffer to isometric textures?
« on: October 28, 2022, 07:34:29 pm »
Hello, I'm creating an isometric city builder pixel art game. You can see examples on reddit.com/r/Archapolis for reference.

I figured out how to implement depth sorting via an algorithm, and it solves 95-99% of the cases (with some hacks). The general solution is to sort everything by the bottom of every textures screen.y position, but I have walls that are just the edge of the isometric cube (so no depth), so I needed to add some workarounds for objects/unit in buildings. I hit a hard limit with hallways (very complicated to explain).

I'd prefer to hit 100% with no hacks, and from the little I know about graphics programming, depth sorting appears to be the answer.

It seems possible to add this to SFML, but Im not sure how I would do that, and I am not sure how I would assign a depth to each pixel for the GPU.

Anyone know how I can experiment with this method?

fallahn

  • Hero Member
  • *****
  • Posts: 507
  • Buns.
    • View Profile
    • Trederia
Re: How to add a depth buffer to isometric textures?
« Reply #1 on: October 28, 2022, 10:52:25 pm »
It's possible to add depth to SFML drawables, but it depends on your definition of 'hack' ;) It also requires a bit of knowledge about OpenGL, particularly shaders. The gist is that normally in the vertex shader you take the coordinates of each vertex of a drawable and convert them, usually through a world/view/projection matrix, into your display's coordinates so that the drawable is rendered on screen in the correct place. This is done automatically by SFML, but it is possible to override the behaviour with custom shaders. SFML is by nature 2D so it makes sense that at the vertex shader stage only the x and y coordinates of the vertices are provided. However, with a bit of experimenting I was able to store z/depth data in the vertex colour property. Here's the result:



Constructing a vertex position from this data in the vertex shader essentially makes the vertex 3D, allowing OpenGL to do the depth testing for you. Of course this doesn't necessarily mean that your game has to be 3D - the 2D output can be maintained using an orthographic projection matrix (see link above). The source is available on github if you're curious (there's a link in the video), with a bit of work it might help sort your drawables if you're willing to give up some of your colour data to depth data :)

YesBox

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: How to add a depth buffer to isometric textures?
« Reply #2 on: October 29, 2022, 04:59:48 pm »
Thanks, this is very interesting but I currently use the vertex color for my game.

I'm hoping what I can do is essentially loop through every column of pixels, loop through every pixel in the column, and draw assets based on which texture's pixel is closer to the camera (based on the bottom pixel i.e. screen.y position). Essentially do what I'm doing now, but on a pixel scale instead of texture/rectangle scale. Pretty sure this would solve the problem I have with walls having no depth.

I just dont know how I would implement that in a shader and pull the proper info from the sf::VertexArray

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: How to add a depth buffer to isometric textures?
« Reply #3 on: November 02, 2022, 06:16:53 pm »
it depends on your definition of 'hack' ;)

Exactly this:
store z/depth data in the vertex colour property.
;D

draw assets based on which texture's pixel is closer to the camera (based on the bottom pixel i.e. screen.y position).
Sounds like a fragment shader but the shader works per pixel and doesn't know about the bottom of the rest of the asset. You would need to find a way to store that information somewhere to pass to the shader; colour, as mentioned above, is available to use.
Note that one way to use colour for both depth and colour is to use the alpha channel for depth information; this allows you to continue using colour while using depth. The only thing you're giving up is transparency.
If that's absolutely not possible, you can still do it with a palette: use one colour channel for depth and another one (or two or three) to reference a predetermined colour (including alpha).
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

YesBox

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: How to add a depth buffer to isometric textures?
« Reply #4 on: November 03, 2022, 07:50:08 pm »
Thanks! Palettes make perfect sense, as I am using the alpha channel.

I implemented a basic fragment shader for the VertexArray storing the sorted textures. Now I can change the color... wooh!

So:
1.) Use the color channels to store depth, and the alpha channel to store 256 unique colors/opacities (I wont need more). Translate the alpha to a color in the shader.

2.) Question: can I calculate and store depth in the vertex shader? I probably cannot do this with the CPU for every pixel since each zoom level in my game uses a different sized tiles/tile atlas, and because there could be many units moving around screen (since it could be possible to store the position once, and pass the tile size to update them in the shader if the tile atlas changes...).

3.) Do you think I will be able to calculate the depth by finding the first/bottom most pixel in the texture that has an alpha value, adding the pixel texture.y position to the texture.y position, and then copying that value to the rest of the pixels in that column in that texture?

Edit: and by texture, I mean the 32 x 32 texture bounds defined in the vertex array for the render texture used.

Double Edit: Actually, the bottom most pixel per column wont work for everything, but I can get around that so long as I can label textures. E.g. a human against the wall with their arm extended would fail. The other option is to calculate z-depth per pixel, but there seems to be a multitude ways of doing that so I would have to experiment.
« Last Edit: November 03, 2022, 08:44:31 pm by YesBox »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: How to add a depth buffer to isometric textures?
« Reply #5 on: November 03, 2022, 08:57:17 pm »
A vertex shader only calculates once per vertex. A fragment shader calculates once per pixel.

I'm not 100% on how you wanted to use the depth so I can't answer exactly what you need to do.

That said, one thing you do need to be aware of is that you can't reliably use multiple colour channels to represent a single value and have them interpolated automatically. Trust me; I've tried to do exactly that and it looks fine... until it doesn't and you wonder what went wrong!. So, if you need depth to be interpolated, you should use just one channel. With that said, however, if you are using multiple integer values for depths - without interpolation - it should be ok; the values just represent a specific depth.

If what I understand about your intention is correct, you can assign a depth to the colour channel to each 'thing' you draw by setting the colour of all of their vertices to the same colour (just the channel(s) you are using for depth). You would need to do this for each vertex of each 'thing'. You would determine the depth to apply by reading its lowest vertex's y position and just set the colour to match.
Then, when you draw the 'thing', it (the fragment shader - per pixel) can compare the depth (colour channel) of what you are drawing with what is below it and decide which one (pixel colour) should be there based on which depth is higher/lower.

Note that this means you are calculating the depth per 'thing' manually and then setting its colour (channel(s)) to represent this depth but only per vertex, not per pixel, and you can, at this point, access the lowest vertex to see which depth it should have.

Hope that helps and it's somewhere near what you are going for. Or, at least, may spark some other idea! :)
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

YesBox

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: How to add a depth buffer to isometric textures?
« Reply #6 on: November 06, 2022, 02:01:05 am »
First, a friend sent me this wonderful article which adds depth sorting by extending some SFML classes: https://www.jordansavant.com/book/graphics/sfml/sfml2_depth_buffering.md

Though Im still figuring out if this is what I need. Will report back later.

Assigning the depth to the color channel for each thing (at the vertex level) would recreate the solution I have now, but in the GPU, and it wouldn't solve any edge cases. Computing this on the CPU is not a bottleneck because I only draw (thus sort) what is visible on screen, and I was able to implement logic for all but one edge case.



YesBox

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: How to add a depth buffer to isometric textures?
« Reply #7 on: January 14, 2023, 03:39:50 am »
It works, though the link is incomplete.

In addition to what's in the tutorial, you'll need to define the depth bits in the window's context settings, clear the depth buffer every frame, and draw an empty sf::VertexArray before drawing all the Vertex3Arrays (so some backend OpenGL stuff is set properly), and draw an empty sf::Shape before drawing any sf::Sprites (again, so some backend OpenGL parameters are set properly).

Its a bit hacky, and Im sure if I took the time to read to SFML's source code, you could avoid the empty draw calls and set the OpenGL parameters in the extended class.

Lastly, to draw alpha textures, just draw them last (the order matters if you want alpha stacking to work with multiple alpha vertex arrays)