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

Merge final outline render into composite step in order to fix blending #1629

Merged
merged 5 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions crates/re_renderer/shader/composite.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,57 @@
#import <./global_bindings.wgsl>
#import <./screen_triangle_vertex.wgsl>

struct CompositeUniformBuffer {
outline_color_layer_a: Vec4,
outline_color_layer_b: Vec4,
outline_radius_pixel: f32,
};
@group(1) @binding(0)
var input_texture: texture_2d<f32>;
var<uniform> uniforms: CompositeUniformBuffer;

@group(1) @binding(1)
var color_texture: texture_2d<f32>;

@group(1) @binding(2)
var outline_voronoi_texture: texture_2d<f32>;

@fragment
fn main(in: FragmentInput) -> @location(0) Vec4 {
let resolution = Vec2(textureDimensions(color_texture).xy);
let pixel_coordinates = resolution * in.texcoord;

// 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.
var input = textureSample(input_texture, nearest_sampler, in.texcoord).rgb;
var color = textureSample(color_texture, nearest_sampler, in.texcoord).rgb;
// TODO(andreas): Do something meaningful with values above 1
input = clamp(input, ZERO.xyz, ONE.xyz);
color = clamp(color, ZERO.xyz, ONE.xyz);

// Outlines
{
let closest_positions = textureSample(outline_voronoi_texture, nearest_sampler, in.texcoord);

let distance_pixel_a = distance(pixel_coordinates, closest_positions.xy);
let distance_pixel_b = distance(pixel_coordinates, closest_positions.zw);

let sharpness = 1.0; // Fun to play around with, but not exposed yet.
let outline_a = saturate((uniforms.outline_radius_pixel - distance_pixel_a) * sharpness);
let outline_b = saturate((uniforms.outline_radius_pixel - distance_pixel_b) * sharpness);

let outline_color_a = outline_a * uniforms.outline_color_layer_a;
let outline_color_b = outline_b * uniforms.outline_color_layer_b;

// Blend outlines with screen color.
color = color * (1.0 - outline_color_a.a) + outline_color_a.rgb;
color = color * (1.0 - outline_color_b.a) + outline_color_b.rgb;

// Show only the outline. Useful for debugging.
//color = outline_color_a.rgb;

// Show the raw voronoi texture. Useful for debugging.
//color = Vec3(closest_positions.xy / resolution, 0.0);
}

// 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);
// Apply srgb gamma curve - this is necessary since the final eframe output does *not* have an srgb format.
return Vec4(srgb_from_linear(color), 1.0);
}
44 changes: 0 additions & 44 deletions crates/re_renderer/shader/outlines/outlines_from_voronoi.wgsl

This file was deleted.

107 changes: 93 additions & 14 deletions crates/re_renderer/src/renderer/compositor.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
use crate::{
allocator::create_and_fill_uniform_buffer,
context::SharedRendererData,
include_file,
wgpu_resources::{
BindGroupDesc, BindGroupEntry, BindGroupLayoutDesc, GpuBindGroup, GpuBindGroupLayoutHandle,
GpuRenderPipelineHandle, GpuTexture, PipelineLayoutDesc, RenderPipelineDesc,
ShaderModuleDesc, WgpuResourcePools,
},
Rgba,
};

use super::{
screen_triangle_vertex_shader, DrawData, DrawPhase, FileResolver, FileSystem, RenderContext,
Renderer,
screen_triangle_vertex_shader, DrawData, DrawPhase, FileResolver, FileSystem, OutlineConfig,
RenderContext, Renderer,
};

use smallvec::smallvec;

mod gpu_data {
use crate::wgpu_buffer_types;

/// Keep in sync with `composite.wgsl`
#[repr(C, align(256))]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub struct CompositeUniformBuffer {
pub outline_color_layer_a: wgpu_buffer_types::Vec4,
pub outline_color_layer_b: wgpu_buffer_types::Vec4,
pub outline_radius_pixel: wgpu_buffer_types::F32RowPadded,
pub end_padding: [wgpu_buffer_types::PaddingRow; 16 - 3],
}
}

pub struct Compositor {
render_pipeline: GpuRenderPipelineHandle,
bind_group_layout: GpuBindGroupLayoutHandle,
Expand All @@ -32,21 +48,58 @@ impl DrawData for CompositorDrawData {
}

impl CompositorDrawData {
pub fn new(ctx: &mut RenderContext, target: &GpuTexture) -> Self {
pub fn new(
ctx: &mut RenderContext,
color_texture: &GpuTexture,
outline_final_voronoi: Option<&GpuTexture>,
outline_config: &Option<OutlineConfig>,
) -> Self {
let mut renderers = ctx.renderers.write();
let compositor = renderers.get_or_create::<_, Compositor>(
&ctx.shared_renderer_data,
&mut ctx.gpu_resources,
&ctx.device,
&mut ctx.resolver,
);

let outline_config = outline_config.clone().unwrap_or(OutlineConfig {
outline_radius_pixel: 0.0,
color_layer_a: Rgba::TRANSPARENT,
color_layer_b: Rgba::TRANSPARENT,
});

let uniform_buffer_binding = create_and_fill_uniform_buffer(
ctx,
"CompositorDrawData".into(),
gpu_data::CompositeUniformBuffer {
outline_color_layer_a: outline_config.color_layer_a.into(),
outline_color_layer_b: outline_config.color_layer_b.into(),
outline_radius_pixel: outline_config.outline_radius_pixel.into(),
end_padding: Default::default(),
},
);

let outline_final_voronoi_handle = outline_final_voronoi.map_or_else(
|| {
ctx.texture_manager_2d
.get(ctx.texture_manager_2d.white_texture_handle())
.expect("white fallback texture missing")
.handle
},
|t| t.handle,
);

CompositorDrawData {
bind_group: ctx.gpu_resources.bind_groups.alloc(
&ctx.device,
&ctx.gpu_resources,
&BindGroupDesc {
label: "compositor".into(),
entries: smallvec![BindGroupEntry::DefaultTextureView(target.handle)],
entries: smallvec![
uniform_buffer_binding,
BindGroupEntry::DefaultTextureView(color_texture.handle),
BindGroupEntry::DefaultTextureView(outline_final_voronoi_handle)
],
layout: compositor.bind_group_layout,
},
),
Expand All @@ -66,17 +119,43 @@ impl Renderer for Compositor {
let bind_group_layout = pools.bind_group_layouts.get_or_create(
device,
&BindGroupLayoutDesc {
label: "compositor".into(),
entries: vec![wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: false },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
label: "Compositor::bind_group_layout".into(),
entries: vec![
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: std::num::NonZeroU64::new(std::mem::size_of::<
gpu_data::CompositeUniformBuffer,
>(
)
as _),
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: false },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
count: None,
}],
],
},
);

Expand Down
Loading