asny / three-d

2D/3D renderer - makes it simple to draw stuff across platforms (including web)
MIT License
1.29k stars 109 forks source link

How to control occlusion for 2D shapes? #438

Closed Bathtor closed 7 months ago

Bathtor commented 7 months ago

So, this maybe a n00b question, but: For 2D shapes with orthographic projection (like in the Shapes2D example), what controls which object gets rendered in front of the other? From playing with it, I guess it's still camera distance, but since z is always the same, it's the x-y distance? The example can't move the camera, only the objects, but I got a similar setup based on this where you can move camera as usual, and there basically changing where the camera is looking changes which of two overlapping shapes gets rendered and which gets cut.

So if I wanted to control that some shape always got rendered above another, how could I do so, without being able to specify some kind of z value when creating the shapes?

Nevsden commented 7 months ago

I guess it would then depend on the order in which you feed the objects into the rendering pipeline, no?

Bathtor commented 7 months ago

I...don't see how to control that. Changing the iteration order on the render call makes no difference. I also tried with two separate render calls. Still makes no difference. The only thing that seemed to matter regarding which overlapping shape got rendered and which cut seemed to be object placement and camera position.

Nevsden commented 7 months ago

Ah I think I may have gotten your request wrong in the first place.

So if I wanted to control that some shape always got rendered above another, how could I do so, without being able to specify some kind of z value when creating the shapes?

You basically have to control the z-component. This is the easiest solution. Check out this example: https://github.com/asny/three-d/blob/master/examples/instanced_draw_order/src/main.rs

Bathtor commented 7 months ago

You basically have to control the z-component. This is the easiest solution. Check out this example: https://github.com/asny/three-d/blob/maste

Aha, that is what I thought. I didn't realise the example applied, since it was 3D and around transparency, while the 2D shapes are opaque. But it helped me find a workaround for my problem. Something like

let mut circle_geom = Circle::new(&self.context, vec2(50.0, 50.0), 50.0);
let circle_transform = circle_geom.transformation();
let new_circle_transform = circle_transform * Mat4::from_translation(vec3(0.0, 0.0, 0.1));
circle_geom.set_transformation(new_circle_transform);

I can work with this, although it would be more convenient if the 2D shapes constructors had a convenient way to set a z-index that then either gets used directly as a z-coordinate or gets translated in some minimal z-coordinate based on the range of z-indices used like I do here manually with the 0.1 z-offset (or what you do in HTML/CSS with z-index).

Nevsden commented 7 months ago

I can understand your request, however in my opinion it would feel unnatural to add a z-component to a 2D shape, doesn't it? Also, you have found an easy way to set the z-compoonent through linear transformation. Can't you wrap this into a function?

Bathtor commented 7 months ago

Of course I can. That's what I'm doing now. I just thought it might help the next person not having to spend a lot of time researching if there were a simple, obvious way in the API to do it.

I don't disagree that a z-component in a 2D shape is weird. That's why I was thinking to have API more along the lines of the z-index, which people are likely familiar with from HTML/CSS, which is also just 2D. It does logically make sense to provide a way to describe which shapes should go in front and which behind in case of overlaps. The fact that this maps to a z-coordinate on a 2D shape transformation internally is...well, an implementation detail, imo.

asny commented 7 months ago

You can use z to define the order of your 2D shapes, but I would not recommend it. The problem is that the z value only makes sense relative to other shapes. So imagine you have a large application with layers at z index 1, 4, 5, 6, 7, 100, 101, 102, 103, 104 and 1000. You don't have this nice list, because these shapes are created at different places in the code. Now you want to render a new shape, that should render at exactly the right place in the order, so you need a correct z index. That is not easy to find and not super fun, trust me I've tried.

Instead, I'd recommend using the order of rendering, exactly as @Nevsden originally suggested. That way, it's just a matter of rendering your shapes from back to front. The reason it wasn't working is that you told the renderer to sort the shapes based on z. To disable this sorting, there's two options: