-
-
Notifications
You must be signed in to change notification settings - Fork 116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compute InstancedMesh' InstanceBuffers at render time based on distance from camera. #297
Conversation
That's definitely a problem, but one I deliberately chose not to solve at the moment. Did you test the performance impact of your solution? To me it sounds very expensive to create and fill all of the instance buffers each frame. |
Ah, I see, after seeing #154 I expected it was just a matter of "didn't get to" yet.
I did not, but I was already calling I first tried to solve it where I generate the instances. But the problem is that I didn't have the camera pose there. Then, as I was changing my signatures to propagate the camera I realised that this would break down if I ever needed two camera's. So then tried to fix it 'in the proper place' (from the view of my usage at least). It's easy to make this behaviour optional and opt-in though, we could set a boolean called |
Yeah, well it's also a matter of limited time, but I also didn't have a good solution and I also thought that having transparent instances where a bit of a special case. I actually think your solution is the right solution - just want to make that absolutely clear - my main concern is performance. But it might not be a big issue, I don't know, it's always good to be challenged about your assumptions. The problem is that it can be difficult to test, because it also depends on hardware. Also, performance wouldn't be a problem if OpenGL supported that you could set the order of instances, like an index buffer but for instances instead of vertices. However, I don't think it does, at least I couldn't find it, it only support to specify the number of instances that you want to draw. Anyway, to render correctly when the instances are transparent, we need to sort the instances back to front and that need to happen every frame (in the So I think the right solution in most cases is to either make the objects not transparent (transparency always cause troubles in a rasteriser) or use individual objects instead of instances (the |
Good morning, thanks for the detailed response.
I also searched, and likewise couldn't find anything for it.
Yeah, I'm slowly starting to understand that transparency in renderers is a 'thing'... I never really gave it any thought. Reading a bit more it seems like a very complex topic. I think we're fine with 'good enough' and just treating the elements in this
I may be misunderstanding this sentence. But aren't there valid cases where nothing has changed in this
That wasn't performant enough, I'm trying to make an effect where my large cube falls apart into many smaller cubes and rendering each small cube as individual objects would be too slow. I had to switch to the
Ok, I think that's a good approach. Some thoughts on this approach:
|
No problem, thanks for your involvement 💪
Yes, it is definitely a thing, which is not a thing if you do ray tracing - but that's another story 🙂 I agree that we should just make it as good as possible, and then be open about the limitations with regards to transparency.
Sorry, wasn't really clear. Just wanted to mention that we shouldn't worry about whether or not the
That's fair, just wanted to add that as a possibility.
Agree 👍 However, I didn't think about that the instances are also transparent if the alpha value of the per-instance colour is not 255. So the instances must be sorted if the material is transparent or if the colour alpha value is less than 255.
I also agree with you here; either have two sets of buffers, one for rendering it with opaque materials and one that's created whenever it needs to be rendered with a transparent material, or use a
Yeah, you are right. If the material is transparent, then all of them should be treated as transparent and sorted back to front. If the material is not transparent, but the per-instance colour has an alpha value less than 255, then you should sort the instances such that the instances with a==255 is rendered first, and then the instances with a < 255 sorted back to front. It's getting complex, but hope it makes sense 🙂 A few additional notes: I actually think the I've started slowly working on animation support, that should hopefully make it possible to avoid having to call |
7539c7c
to
dc74034
Compare
@@ -90,24 +130,189 @@ impl InstancedMesh { | |||
/// Update the instances. | |||
/// | |||
pub fn set_instances(&mut self, instances: &Instances) { | |||
// For code review; should this be here > I dev with --release, that hides this and then | |||
// it fails elsewhere. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, this is something I ran into several times now, I always build with --release
, because otherwise everything is slow, but that hides this assertion. Then it fails elsewhere, without a nice convenient print that's like; your color vector isn't equal size. What's the actual cost of doing this assertion and panicking with a nice message?
Though, now that I think of it it's probably possible to compile with optimisations and debug assertions. Yes, may be worth documenting somewhere?
@@ -334,11 +555,20 @@ impl Geometry for InstancedMesh { | |||
color_texture: Option<ColorTexture>, | |||
depth_texture: Option<DepthTexture>, | |||
) { | |||
// Update the instance buffers if required. | |||
let is_material_transparent = false; // is this correct? PostMaterial doesn't provide this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need an expert opinion here, I don't know enough about what a PostMaterial
is, or how it is used to determine whether we should default to transparent or opaque.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A PostMaterial
is similar to a material but applied one at a time after everything else has been rendered, because it needs the intermediate rendered image. For example a blur effect applied on top of the rendered image. Since it's applied one at a time, I thought I didn't need the material type, but I didn't think of this case. So that needs to be added, but I can do that later, just assume it's not transparent for now.
Ok, I spent some time coding this all up. And - indeed - it's gotten a bit complex 😬 So I started the evening by making a new test example. It can be found here and currently lives on branch instanced-transparency-test. I'm happy to add it to this branch if that makes testing easier. The current output of that example in master is: Left row is transparent, right row is opaque materials, all equal size cubes sized (1.0, 1.0, 0.1), all plane offsets 1 unit apart (away from camera) and drawn in the following order: Color::new(0, 255, 0, 255), // green, closest, should obscure everything.
Color::new(255, 0, 255, 255), // purple, behind green, second opaque plane.
Color::new(255, 0, 0, 128), // Red, third plane, should be behind two opaques, blend in front of blue.
Color::new(0, 0, 255, 128), // Furthest, blue layer. It is surprising to me that the transparent materials (but While implementing I ran into the
Well, I for one have difficulty thinking of a material that is not transparent, but the instances then still have an alpha that is non 255 and a useful value?
I tried to stick as close as possible to these few sentences in the implementation, I split out the decision whether we should update and what the drawing order then should be. The ordering itself happens here, especially the I don't really know how to test that For the other case, the transparent material the new example, with this branch at dc74034 gives: and rotating the camera to the other side also looks as I would expect. I modified the example a bit while writing up this comment to also have the non-transparent material and non-transparent instances in it to test the instance count method there. |
Again, sorry for the late reply. I think this is getting a bit too complex, luckily I realised that I was wrong. The instances only need to be sorted if the material is transparent, it doesn't matter if the alpha value is less than 255, since that is not used if the blend state doesn't use it. Therefore it should be sorted if the material is transparent and not sorted if the material is not transparent. Should make things a bit more simple. Also, I would really like to avoid I hopefully have time to look more into it soon, but at least you have some more input 🙂 |
Haha, yes... I was thinking it was getting complicated when I was coding it, fun challenge though. Anyways, glad you realised it could be simpler, it's always nice to be able to remove complexity.
Yep, 'opaque material & transparent instances' was the scenario that made things the most complicated.
I didn't see how to avoid re-creating the buffers each frame if the camera stays the same without a state (or at least, camera position), but given that the complexity is now much lower I just went with a
I think I actually managed to accommodate it neatly in the updated code. Since we only have to worry about this for transparent materials, we can just check it whenever we check the camera pose and we can check it by looking at any length of Simplified code is pushed, I did keep the unit test for the ordering function. I also noticed |
Super nice 💪 I'll be happy to merge it as it is, so let me know if you don't think this is fun anymore or want to do other stuff 😄
Glad you feel that way, I like to remove code as well, but not all like to remove what they have spend time doing, which is understandable 🙂
I think your solution is perfectly all right, so we can keep it like that, no problem 🙂 If you want to spend more time on it, I had an idea of storing the instance indices instead of the camera position, and then in each render call check if it's sorted (do something like this). If it's not sorted, then sort the indices and update the stored set of indices and update the buffers. What do you think?
Great, keep it if it's not complicating things, but if it significantly complicates things, I'm ok with removing it 🙂
Great with a unit test 👍 You could also sort and update the instance buffers in the draw method instead of in both the render methods, but it's a minor improvement. Do you also want to add the example you created for this? Finally, I was wondering why rendering instances was so much more performant so I did a profiling and turns out that all of my string handling and hashmap lookups is the bottleneck 😬 So the problem is CPU side and can be fixed. Will look into it as soon as I have time 💪 |
I'm not sure actually, comparing camera position is trivial, it is always three float comparisons, regardless of how many instances we have. Checking if the indices are ordered requires calculating their distances and then comparing them, so that's linear with the number of instances. I don't know much about graphics 'things', but you said that the transfer of the buffers is expensive, so I can see value in checking if we have to do the transfer at all by seeing if the index ordering has changed, but I would do so in addition to the camera check, not instead of, just because checking if it is ordered is much more expensive than checking just the camera pose. I'm fine adding it, but I'm just not too sure if we need this optimisation just yet, like I didn't notice it when my buffers updated every frame. Thinking about this some more - at the cost of high bookkeeping - you could also check the largest consecutive non-ordered section in the buffer and only rewrite that. Because it is very unlikely the ordering of objects far away from the camera changes if the camera moves just a little bit. Technically, that can all live in one place in the buffer object and that can do a delta update. We definitely don't need this, just musing.
Yeah, that's better, that moves that action into one location, I'll give
Yeah sure, I currently have the transparency_draw_order one, which I've used for screenshots in this PR; it has a row of cubes with transparency and a row of opaque ones (and the - to be removed - opaque material, transparent instances). Is that what you were thinking? Or do you want an example that shows the limitations of the current approach? Like two intersecting instances, which will render correctly only from one side, or two overlapping |
Did you use anything specific to Rust for this? Or just the usual tooling one would use for any program on linux? Familiar with the latter, so just wondering if there's anything worth knowing specifically for rust.
Heh, we missed that we also pass the In 1f0d504, I added an example. But obviously there's no 'without reordering' anymore, so to make it more insightful, I added a situation where the current approach always breaks: Also changed the camera to orbit, such that it is easy to rotate around it. With that, unless you have any other outstanding remarks you'd like to see addressed, I think this is good to go in. |
5a42d3b
to
c93ab42
Compare
Good point 👍
Yes 🥳
I'm on mac, so I used the cpu profiler in "Instruments", it's a general profiler, so not specific to Rust 🙂
Arh, yeah, I didn't think of that.
Perfect 👍
I have nothing else, thanks for your contribution 🚀 🥳 🎉 |
Hi again!
So, I ran into a rendering problem with
InstancedMesh
displaying cubes with transparency. The rendering order of this was based on the order in which the instances are assigned, and this results in some undesirable visualisations.I was working on an effect to 'deconstruct' a bunch of cubes into smaller cubes that fall to the ground and then fade out. The problem is that some of these instances were being drawn in front of instances they should have been behind: vehicle_before. The tracks should clearly be hidden behind the instances that make up the green body of the vehicle.
Searching in the issues, I think #154 relates to this problem. Currently all objects are sorted by bounding box to determine the rendering order, but for the individual instances in the
InstancedMesh
this is not done.In 2759377 I tried to modify the instanced shapes example to show the problem, it's on branch transparent-instancing-ordering-example, the before screenshot of the example:

It's not super clear, but the green cube (behind the front cube) is drawn later than the front cube and it all ends up looking off.
With the changes proposed in this branch that example ends up looking like this:

The same vehicle screenshot from earlier ends up looking correct: vehicle_after
This works by:
instances
object as a private variable.instance_buffers
fromInstancedMesh
.instance_buffers
to a private function that also takes the camera.InstancedMesh
objects that overlap. While not perfect, it's an improvement over the current state.draw
,render_with_material
andrender_with_postmaterial
we first calculate the instance buffers based on our camera position, allowing us to order the instances.