Skip to content

Commit d088a84

Browse files
authored
Fix Transparency (#598)
1 parent 5aa9f4a commit d088a84

File tree

7 files changed

+173
-3
lines changed

7 files changed

+173
-3
lines changed

examples/src/scene_viewer/bistro.png

11.3 KB
Loading

rend3-routine/src/forward.rs

+104-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
//!
33
//! Will default to the PBR shader code if custom code is not specified.
44
5-
use std::{marker::PhantomData, sync::Arc};
5+
use std::{cmp::Ordering, marker::PhantomData, sync::Arc};
66

77
use arrayvec::ArrayVec;
88
use encase::{ShaderSize, StorageBuffer};
9+
use ordered_float::OrderedFloat;
910
use rend3::{
1011
graph::{DataHandle, NodeResourceUsage, RenderGraph, RenderPassTargets},
11-
types::{Material, SampleCount},
12+
managers::{CameraState, InternalObject, MaterialArchetypeView, TextureBindGroupIndex},
13+
types::{Material, RawObjectHandle, SampleCount, SortingOrder, SortingReason},
1214
util::bind_merge::BindGroupBuilder,
1315
ProfileData, Renderer, RendererDataCore, RendererProfile, ShaderPreProcessor,
1416
};
@@ -165,6 +167,8 @@ impl<M: Material> ForwardRoutine<M> {
165167
CameraSpecifier::Shadow(idx) => &ctx.eval_output.shadows[idx as usize].camera,
166168
};
167169

170+
let objects = sort(objects, archetype_view, self.material_key, camera);
171+
168172
let per_camera_uniform_values = PerCameraUniform {
169173
view: camera.view(),
170174
view_proj: camera.view_proj(),
@@ -230,6 +234,104 @@ impl<M: Material> ForwardRoutine<M> {
230234
}
231235
}
232236

237+
fn sort<'a, M, I>(
238+
objects: I,
239+
material_archetype: MaterialArchetypeView<'_, M>,
240+
requested_material_key: u64,
241+
camera: &CameraState,
242+
) -> Vec<(RawObjectHandle, &'a InternalObject<M>)>
243+
where
244+
M: Material,
245+
I: IntoIterator<Item = (RawObjectHandle, &'a InternalObject<M>)>,
246+
I::IntoIter: ExactSizeIterator,
247+
{
248+
let objects = objects.into_iter();
249+
250+
let mut sorted_objects = Vec::with_capacity(objects.len());
251+
{
252+
profiling::scope!("Sort Key Creation");
253+
for (raw_handle, object) in objects {
254+
let material = material_archetype.material(*object.material_handle);
255+
let object_material_key = material.inner.key();
256+
let sorting = material.inner.sorting();
257+
258+
if object_material_key != requested_material_key {
259+
continue;
260+
}
261+
262+
// Frustum culling
263+
if !camera.world_frustum().contains_sphere(object.inner.bounding_sphere) {
264+
continue;
265+
}
266+
267+
let bind_group_index = material.bind_group_index.map_gpu(|_| TextureBindGroupIndex::DUMMY).into_common();
268+
269+
let mut distance_sq = camera.location().distance_squared(object.location.into());
270+
271+
if sorting.order == SortingOrder::BackToFront {
272+
distance_sq = -distance_sq;
273+
}
274+
sorted_objects.push((
275+
ObjectSortingKey {
276+
bind_group_index,
277+
distance: OrderedFloat(distance_sq),
278+
sorting_reason: sorting.reason,
279+
},
280+
(raw_handle, object),
281+
))
282+
}
283+
}
284+
285+
{
286+
profiling::scope!("Sorting");
287+
sorted_objects.sort_unstable_by_key(|(k, _)| *k);
288+
}
289+
290+
sorted_objects.into_iter().map(|(_, o)| o).collect()
291+
}
292+
293+
#[derive(Debug, Clone, Copy, Eq)]
294+
pub(super) struct ObjectSortingKey {
295+
pub bind_group_index: TextureBindGroupIndex,
296+
pub distance: OrderedFloat<f32>,
297+
pub sorting_reason: SortingReason,
298+
}
299+
300+
impl PartialEq for ObjectSortingKey {
301+
fn eq(&self, other: &Self) -> bool {
302+
self.cmp(other).is_eq()
303+
}
304+
}
305+
306+
impl PartialOrd for ObjectSortingKey {
307+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
308+
Some(self.cmp(other))
309+
}
310+
}
311+
312+
impl Ord for ObjectSortingKey {
313+
fn cmp(&self, other: &Self) -> Ordering {
314+
match self.sorting_reason.cmp(&other.sorting_reason) {
315+
Ordering::Equal => {}
316+
ord => return ord,
317+
}
318+
// The above comparison means that both sides are equal
319+
if self.sorting_reason == SortingReason::Requirement {
320+
match self.distance.cmp(&other.distance) {
321+
Ordering::Equal => {}
322+
ord => return ord,
323+
}
324+
self.bind_group_index.cmp(&other.bind_group_index)
325+
} else {
326+
match self.bind_group_index.cmp(&other.bind_group_index) {
327+
Ordering::Equal => {}
328+
ord => return ord,
329+
}
330+
self.distance.cmp(&other.distance)
331+
}
332+
}
333+
}
334+
233335
fn build_forward_pipeline_inner<M: Material>(
234336
pll: &wgpu::PipelineLayout,
235337
args: &ForwardRoutineCreateArgs<'_, M>,

rend3-test/src/helpers.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::sync::Arc;
22

33
use glam::{Mat4, Vec3, Vec4};
44
use rend3::types::{DirectionalLightHandle, MaterialHandle, MeshBuilder, ObjectHandle};
5-
use rend3_routine::pbr::PbrMaterial;
5+
use rend3_routine::pbr::{PbrMaterial, Transparency};
66
use wgpu::Device;
77

88
use crate::TestRunner;
@@ -43,6 +43,15 @@ impl TestRunner {
4343
})
4444
}
4545

46+
pub fn add_transparent_material(&self, color: Vec4) -> MaterialHandle {
47+
self.renderer.add_material(PbrMaterial {
48+
albedo: rend3_routine::pbr::AlbedoComponent::Value(color),
49+
unlit: true,
50+
transparency: Transparency::Blend,
51+
..Default::default()
52+
})
53+
}
54+
4655
pub fn add_lit_material(&self, color: Vec4) -> MaterialHandle {
4756
self.renderer.add_material(PbrMaterial {
4857
albedo: rend3_routine::pbr::AlbedoComponent::Value(color),
455 Bytes
Loading

rend3-test/tests/root.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ mod msaa;
22
mod object;
33
mod shadow;
44
mod simple;
5+
mod transparency;

rend3-test/tests/transparency.rs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use anyhow::Context;
2+
use glam::{Mat4, Quat, Vec3, Vec4};
3+
use rend3::types::{Camera, Handedness};
4+
use rend3_test::{no_gpu_return, test_attr, FrameRenderSettings, TestRunner, Threshold};
5+
6+
/// Ensure that transparency is ordered correctly
7+
//
8+
// Todo: This test never fails, even if I remove the sort.
9+
#[test_attr]
10+
pub async fn transparency() -> anyhow::Result<()> {
11+
let iad = no_gpu_return!(rend3::create_iad(None, None, None, None).await)
12+
.context("InstanceAdapterDevice creation failed")?;
13+
14+
let Ok(runner) = TestRunner::builder().iad(iad.clone()).handedness(Handedness::Left).build().await else {
15+
return Ok(());
16+
};
17+
18+
runner.set_camera_data(Camera {
19+
projection: rend3::types::CameraProjection::Raw(Mat4::IDENTITY),
20+
view: Mat4::IDENTITY,
21+
});
22+
23+
let material1 = runner.add_transparent_material(Vec4::new(1.0, 0.0, 0.0, 0.5));
24+
let material2 = runner.add_transparent_material(Vec4::new(0.0, 1.0, 0.0, 0.5));
25+
let _object_left_1 = runner.plane(
26+
material1.clone(),
27+
Mat4::from_scale_rotation_translation(Vec3::new(-0.25, 0.25, 0.25), Quat::IDENTITY, Vec3::new(-0.5, 0.0, -0.5)),
28+
);
29+
30+
let _object_left_2 = runner.plane(
31+
material2.clone(),
32+
Mat4::from_scale_rotation_translation(Vec3::new(-0.25, 0.25, 0.25), Quat::IDENTITY, Vec3::new(-0.5, 0.0, 0.5)),
33+
);
34+
35+
let _object_right_2 = runner.plane(
36+
material2,
37+
Mat4::from_scale_rotation_translation(Vec3::new(-0.25, 0.25, 0.25), Quat::IDENTITY, Vec3::new(0.5, 0.0, 0.5)),
38+
);
39+
40+
let _object_right_1 = runner.plane(
41+
material1,
42+
Mat4::from_scale_rotation_translation(Vec3::new(-0.25, 0.25, 0.25), Quat::IDENTITY, Vec3::new(0.5, 0.0, -0.5)),
43+
);
44+
45+
runner
46+
.render_and_compare(FrameRenderSettings::new(), "tests/results/transparency/blending.png", Threshold::Mean(0.0))
47+
.await?;
48+
49+
Ok(())
50+
}

rend3/src/managers/material.rs

+8
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ pub struct MaterialArchetypeView<'a, M: Material> {
6565
data_vec: &'a [Option<InternalMaterial<M>>],
6666
}
6767

68+
impl<'a, M: Material> Copy for MaterialArchetypeView<'a, M> {}
69+
70+
impl<'a, M: Material> Clone for MaterialArchetypeView<'a, M> {
71+
fn clone(&self) -> Self {
72+
*self
73+
}
74+
}
75+
6876
impl<'a, M: Material> MaterialArchetypeView<'a, M> {
6977
pub fn buffer(&self) -> &'a Buffer {
7078
self.buffer

0 commit comments

Comments
 (0)