Skip to content
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

[re_renderer] Enable 4x MSAA #276

Merged
merged 12 commits into from
Nov 8, 2022
31 changes: 21 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ egui-wgpu = { git = "https://github.com/emilk/egui", rev = "8c76b8caff32e47e4419
# Because gltf hasn't published a new version: https://github.com/gltf-rs/gltf/issues/357
gltf = { git = "https://github.com/rerun-io/gltf", rev = "3c14ded73755d1ce9e47010edb06db63cb7e2cca" }

# 2022-10-12 - Eq for DepthBiasState and DepthStencilState
wgpu = { git = "https://github.com/gfx-rs/wgpu.git", ref = "aed3b1ae59ee097901b852d934493c7ec167d344" }
wgpu-core = { git = "https://github.com/gfx-rs/wgpu.git", ref = "aed3b1ae59ee097901b852d934493c7ec167d344" }
# 2022-10-12 - Alpha to coverage support for GLES
wgpu = { git = "https://github.com/gfx-rs/wgpu.git", ref = "a377ae2b7fe6c1c9412751166f0917e617164e49" }
wgpu-core = { git = "https://github.com/gfx-rs/wgpu.git", ref = "a377ae2b7fe6c1c9412751166f0917e617164e49" }
#wgpu = { path = "../wgpu/wgpu" }
4 changes: 2 additions & 2 deletions crates/re_renderer/examples/renderer_standalone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,15 +507,15 @@ struct AppState {
impl AppState {
fn new(re_ctx: &mut RenderContext) -> Self {
let mut rnd = <rand::rngs::StdRng as rand::SeedableRng>::seed_from_u64(42);
let random_point_range = -2.0_f32..2.0_f32;
let random_point_range = -5.0_f32..5.0_f32;
let random_points = (0..500000)
.map(|_| PointCloudPoint {
position: glam::vec3(
rnd.gen_range(random_point_range.clone()),
rnd.gen_range(random_point_range.clone()),
rnd.gen_range(random_point_range.clone()),
),
radius: rnd.gen_range(0.005..0.025),
radius: rnd.gen_range(0.005..0.05),
srgb_color: [rnd.gen(), rnd.gen(), rnd.gen(), 255],
})
.collect_vec();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ struct VertexOutput {
};

@group(1) @binding(0)
var hdr_texture: texture_2d<f32>;
var input_texture: texture_2d<f32>;

@fragment
fn main(in: VertexOutput) -> @location(0) Vec4 {
// Note that we can't use a simple textureLoad using @builtin(position) here despite the lack of filtering.
// The issue is that positions provided by @builtin(position) are not dependent on the set viewport,
// but are about the location of the texel in the target texture.
let hdr = textureSample(hdr_texture, nearest_sampler, in.texcoord).rgb;
let input = textureSample(input_texture, nearest_sampler, in.texcoord).rgb;
// TODO(andreas): Do something meaningful with values above 1
let hdr = clamp(hdr, vec3<f32>(0.0), vec3<f32>(1.0));
return Vec4(srgb_from_linear(hdr), 1.0);
let input = clamp(input, vec3<f32>(0.0), vec3<f32>(1.0));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let input = clamp(input, vec3<f32>(0.0), vec3<f32>(1.0));
let input = clamp(input, Vec3(0.0), Vec3(1.0));

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't work gfx-rs/naga#2105
tbh I'm not so sure about our typedefs

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dare I even say:

Suggested change
let input = clamp(input, vec3<f32>(0.0), vec3<f32>(1.0));
let input = clamp(input, ZERO, ONE);

👀

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

witchcraft!


// Convert to srgb - this is necessary since the final eframe output does *not* have an srgb format.
// Note that the input here is assumed to be linear - if the input texture was an srgb texture it would have been converted on load.
return Vec4(srgb_from_linear(input), 1.0);
}
2 changes: 2 additions & 0 deletions crates/re_renderer/shader/global_bindings.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ struct FrameUniformBuffer {

camera_position: vec3<f32>,
top_right_screen_corner_in_view: vec2<f32>,
/// Multiply this with a camera distance to get a measure of how wide a pixel is in world units.
pixel_world_size_from_camera_distance: f32,
};
@group(0) @binding(0)
var<uniform> frame: FrameUniformBuffer;
Expand Down
4 changes: 2 additions & 2 deletions crates/re_renderer/shader/lines.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,6 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
@fragment
fn fs_main(in: VertexOut) -> @location(0) Vec4 {
// TODO(andreas): Rounded caps, proper shading/lighting, etc.
let shading = 1.2 - length(in.position_world - in.position_world_line) / in.line_radius;
return in.color * shading ;
let shading = max(0.2, 1.2 - length(in.position_world - in.position_world_line) / in.line_radius);
return in.color * shading;
}
43 changes: 27 additions & 16 deletions crates/re_renderer/shader/point_cloud.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
// "unnecessary" overlaps. So instead, we change the size _and_ move the sphere closer (using math!)
let radius_sq = point_data.radius * point_data.radius;
let camera_offset = radius_sq * distance_to_camera_inv;
let modified_radius = point_data.radius * distance_to_camera_inv * sqrt(distance_to_camera_sq - radius_sq);
let pos = point_data.pos + pos_in_quad * modified_radius + camera_offset * quad_normal;
var modified_radius = point_data.radius * distance_to_camera_inv * sqrt(distance_to_camera_sq - radius_sq);
// We're computing a coverage mask in the fragment shader - make sure the quad doesn't cut off our antialiasing.
// It's fairly subtle but if we don't do this our spheres look slightly squarish
modified_radius += frame.pixel_world_size_from_camera_distance / distance_to_camera_inv;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good comments ⭐ ⭐ ⭐

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... I love Iq's work, but took me ages to understand his sphere AA shadertoy and then a bit more to understand "custom" pieces like this one :)

let pos = point_data.pos + pos_in_quad * modified_radius * 1.0 + camera_offset * quad_normal;
// normal billboard (spheres are cut off!):
// pos = point_data.pos + pos_in_quad * point_data.radius;
// only enlarged billboard (works but requires z care even for non-overlapping spheres):
Expand All @@ -83,31 +86,39 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
return out;
}

// Return how far the closest intersection point is from ray_origin.
// Returns -1.0 if no intersection happend
fn sphere_intersect(sphere_pos: Vec3, radius_sq: f32, ray_origin: Vec3, ray_dir: Vec3) -> f32 {
let sphere_to_origin = ray_origin - sphere_pos;

// Returns distance to sphere surface (x) and distance to of closest ray hit (y)
// Via https://iquilezles.org/articles/spherefunctions/ but with more verbose names.
fn sphere_distance(ray_origin: Vec3, ray_dir: Vec3, sphere_origin: Vec3, sphere_radius: f32) -> Vec2 {
let sphere_radius_sq = sphere_radius * sphere_radius;
let sphere_to_origin = ray_origin - sphere_origin;
let b = dot(sphere_to_origin, ray_dir);
let c = dot(sphere_to_origin, sphere_to_origin) - radius_sq;
let discriminant = b * b - c;
if (discriminant < 0.0) {
return -1.0;
}
return -b - sqrt(discriminant);
let c = dot(sphere_to_origin, sphere_to_origin) - sphere_radius_sq;
let h = b * b - c;
let d = sqrt(max(0.0, sphere_radius_sq - h)) - sphere_radius;
return Vec2(d, -b - sqrt(max(h, 0.0)));
}


@fragment
fn fs_main(in: VertexOut) -> @location(0) Vec4 {
// TODO(andreas): Pass around squared radius instead.
let ray_dir = normalize(in.world_position - frame.camera_position);
if sphere_intersect(in.point_center, in.radius * in.radius, frame.camera_position, ray_dir) < 0.0 {

// Sphere intersection with anti-aliasing as described by Iq here
// https://www.shadertoy.com/view/MsSSWV
// (but rearranged and labled to it's easier to understand!)
let d = sphere_distance(frame.camera_position, ray_dir, in.point_center, in.radius);
let smallest_distance_to_sphere = d.x;
let closest_ray_dist = d.y;
let pixel_world_size = closest_ray_dist * frame.pixel_world_size_from_camera_distance;
if smallest_distance_to_sphere > pixel_world_size {
discard;
}
let coverage = 1.0 - clamp(smallest_distance_to_sphere / pixel_world_size, 0.0, 1.0);

// TODO(andreas): Do we want manipulate the depth buffer depth to actually render spheres?

// TODO(andreas): Proper shading
let shading = min(1.0, 1.2 - distance(in.point_center, in.world_position) / in.radius); // quick and dirty coloring)
return in.color * shading;
let shading = max(0.2, 1.2 - distance(in.point_center, in.world_position) / in.radius); // quick and dirty coloring)
return vec4(in.color.rgb * shading, coverage);
}
2 changes: 1 addition & 1 deletion crates/re_renderer/shader/test_triangle.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var<private> v_colors: array<Vec4, 3> = array<Vec4, 3>(
fn vs_main(@builtin(vertex_index) v_idx: u32) -> VertexOut {
var out: VertexOut;

out.position = frame.projection_from_world * Vec4(v_positions[v_idx], 0.0, 1.0);
out.position = frame.projection_from_world * Vec4(v_positions[v_idx] * 5.0, 0.0, 1.0);
out.color = v_colors[v_idx];

return out;
Expand Down
5 changes: 4 additions & 1 deletion crates/re_renderer/src/global_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ pub(crate) struct FrameUniformBuffer {
pub camera_position: wgpu_buffer_types::Vec3,

/// View space coordinates of the top right screen corner.
pub top_right_screen_corner_in_view: wgpu_buffer_types::Vec2Padded,
pub top_right_screen_corner_in_view: wgpu_buffer_types::Vec2,
/// Multiply this with a camera distance to get a measure of how wide a pixel is in world units.
pub pixel_world_size_from_camera_distance: f32,
pub _padding: f32,
}

pub(crate) struct GlobalBindings {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,42 @@ use super::*;

use smallvec::smallvec;

pub struct Tonemapper {
pub struct Compositor {
render_pipeline: GpuRenderPipelineHandle,
bind_group_layout: GpuBindGroupLayoutHandle,
}

#[derive(Clone)]
pub struct TonemapperDrawable {
/// [`GpuBindGroup`] pointing at the current HDR source and
/// a uniform buffer for describing a tonemapper configuration.
hdr_target_bind_group: GpuBindGroupHandleStrong,
pub struct CompositorDrawable {
/// [`GpuBindGroup`] pointing at the current image source and
/// a uniform buffer for describing a tonemapper/compositor configuration.
bind_group: GpuBindGroupHandleStrong,
}

impl Drawable for TonemapperDrawable {
type Renderer = Tonemapper;
impl Drawable for CompositorDrawable {
type Renderer = Compositor;
}

impl TonemapperDrawable {
impl CompositorDrawable {
pub fn new(
ctx: &mut RenderContext,
device: &wgpu::Device,
hdr_target: &GpuTextureHandleStrong,
target: &GpuTextureHandleStrong,
) -> Self {
let pools = &mut ctx.resource_pools;
let tonemapper = ctx.renderers.get_or_create::<_, Tonemapper>(
let compositor = ctx.renderers.get_or_create::<_, Compositor>(
&ctx.shared_renderer_data,
pools,
device,
&mut ctx.resolver,
);
TonemapperDrawable {
hdr_target_bind_group: pools.bind_groups.alloc(
CompositorDrawable {
bind_group: pools.bind_groups.alloc(
device,
&BindGroupDesc {
label: "tonemapping".into(),
entries: smallvec![BindGroupEntry::DefaultTextureView(**hdr_target)],
layout: tonemapper.bind_group_layout,
label: "compositor".into(),
entries: smallvec![BindGroupEntry::DefaultTextureView(**target)],
layout: compositor.bind_group_layout,
},
&pools.bind_group_layouts,
&pools.textures,
Expand All @@ -58,8 +58,8 @@ impl TonemapperDrawable {
}
}

impl Renderer for Tonemapper {
type DrawData = TonemapperDrawable;
impl Renderer for Compositor {
type DrawData = CompositorDrawable;

fn create_renderer<Fs: FileSystem>(
shared_data: &SharedRendererData,
Expand All @@ -70,12 +70,12 @@ impl Renderer for Tonemapper {
let bind_group_layout = pools.bind_group_layouts.get_or_create(
device,
&BindGroupLayoutDesc {
label: "tonemapping".into(),
label: "compositor".into(),
entries: vec![wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::default(),
sample_type: wgpu::TextureSampleType::Float { filterable: false },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
Expand All @@ -87,11 +87,11 @@ impl Renderer for Tonemapper {
let render_pipeline = pools.render_pipelines.get_or_create(
device,
&RenderPipelineDesc {
label: "tonemapping".into(),
label: "compositor".into(),
pipeline_layout: pools.pipeline_layouts.get_or_create(
device,
&PipelineLayoutDesc {
label: "tonemapping".into(),
label: "compositor".into(),
entries: vec![shared_data.global_bindings.layout, bind_group_layout],
},
&pools.bind_group_layouts,
Expand All @@ -111,7 +111,7 @@ impl Renderer for Tonemapper {
resolver,
&ShaderModuleDesc {
label: "tonemap (fragment)".into(),
source: include_file!("../../shader/tonemap.wgsl"),
source: include_file!("../../shader/composite.wgsl"),
},
),
vertex_buffers: smallvec![],
Expand All @@ -124,7 +124,7 @@ impl Renderer for Tonemapper {
&pools.shader_modules,
);

Tonemapper {
Compositor {
render_pipeline,
bind_group_layout,
}
Expand All @@ -134,12 +134,10 @@ impl Renderer for Tonemapper {
&self,
pools: &'a WgpuResourcePools,
pass: &mut wgpu::RenderPass<'a>,
draw_data: &TonemapperDrawable,
draw_data: &CompositorDrawable,
) -> anyhow::Result<()> {
let pipeline = pools.render_pipelines.get_resource(self.render_pipeline)?;
let bind_group = pools
.bind_groups
.get_resource(&draw_data.hdr_target_bind_group)?;
let bind_group = pools.bind_groups.get_resource(&draw_data.bind_group)?;

pass.set_pipeline(&pipeline.pipeline);
pass.set_bind_group(1, &bind_group.bind_group, &[]);
Expand Down
Loading