Pass logical view size to outline shader.
This commit is contained in:
parent
e13eef50cb
commit
a960850611
|
@ -9,6 +9,7 @@ use bevy_mod_rounded_box::*;
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.insert_resource(Msaa { samples: 4 })
|
.insert_resource(Msaa { samples: 4 })
|
||||||
|
.insert_resource(ClearColor(Color::BLACK))
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.add_plugin(OutlinePlugin)
|
.add_plugin(OutlinePlugin)
|
||||||
.add_startup_system(setup)
|
.add_startup_system(setup)
|
||||||
|
@ -44,7 +45,7 @@ fn setup(
|
||||||
})
|
})
|
||||||
.insert(outlines.add(Outline {
|
.insert(outlines.add(Outline {
|
||||||
colour: Color::rgba(0.0, 1.0, 0.0, 0.5),
|
colour: Color::rgba(0.0, 1.0, 0.0, 0.5),
|
||||||
width: 50.0,
|
width: 25.0,
|
||||||
}))
|
}))
|
||||||
.insert(TheCube());
|
.insert(TheCube());
|
||||||
commands.spawn_bundle(PointLightBundle {
|
commands.spawn_bundle(PointLightBundle {
|
||||||
|
|
138
src/lib.rs
138
src/lib.rs
|
@ -7,6 +7,7 @@ use bevy::pbr::{
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::reflect::TypeUuid;
|
use bevy::reflect::TypeUuid;
|
||||||
|
use bevy::render::camera::{ActiveCamera, Camera3d};
|
||||||
use bevy::render::mesh::{MeshVertexBufferLayout, PrimitiveTopology};
|
use bevy::render::mesh::{MeshVertexBufferLayout, PrimitiveTopology};
|
||||||
use bevy::render::render_asset::{RenderAsset, RenderAssetPlugin, RenderAssets};
|
use bevy::render::render_asset::{RenderAsset, RenderAssetPlugin, RenderAssets};
|
||||||
use bevy::render::render_component::ExtractComponentPlugin;
|
use bevy::render::render_component::ExtractComponentPlugin;
|
||||||
|
@ -18,10 +19,10 @@ use bevy::render::render_resource::std140::{AsStd140, Std140};
|
||||||
use bevy::render::render_resource::{
|
use bevy::render::render_resource::{
|
||||||
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
|
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
|
||||||
BindGroupLayoutEntry, BindingType, Buffer, BufferBindingType, BufferInitDescriptor, BufferSize,
|
BindGroupLayoutEntry, BindingType, Buffer, BufferBindingType, BufferInitDescriptor, BufferSize,
|
||||||
BufferUsages, Face, PipelineCache, RenderPipelineDescriptor, ShaderStages,
|
BufferUsages, DynamicUniformVec, Face, PipelineCache, RenderPipelineDescriptor, ShaderStages,
|
||||||
SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines,
|
SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines,
|
||||||
};
|
};
|
||||||
use bevy::render::renderer::RenderDevice;
|
use bevy::render::renderer::{RenderDevice, RenderQueue};
|
||||||
use bevy::render::view::ExtractedView;
|
use bevy::render::view::ExtractedView;
|
||||||
use bevy::render::{RenderApp, RenderStage};
|
use bevy::render::{RenderApp, RenderStage};
|
||||||
use libm::nextafterf;
|
use libm::nextafterf;
|
||||||
|
@ -37,7 +38,7 @@ const OUTLINE_SHADER_HANDLE: HandleUntyped =
|
||||||
pub struct Outline {
|
pub struct Outline {
|
||||||
/// Colour of the outline
|
/// Colour of the outline
|
||||||
pub colour: Color,
|
pub colour: Color,
|
||||||
/// Width of the outline in pixels
|
/// Width of the outline in logical pixels
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ impl RenderAsset for Outline {
|
||||||
});
|
});
|
||||||
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
label: Some("outline_bind_group"),
|
label: Some("outline_bind_group"),
|
||||||
layout: &outline_pipeline.bind_group_layout,
|
layout: &outline_pipeline.outline_bind_group_layout,
|
||||||
entries: &[
|
entries: &[
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
|
@ -97,6 +98,26 @@ impl RenderAsset for Outline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Component, AsStd140)]
|
||||||
|
struct ViewSizeUniform {
|
||||||
|
logical_size: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ViewSizeUniforms {
|
||||||
|
pub uniforms: DynamicUniformVec<ViewSizeUniform>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct ViewSizeUniformOffset {
|
||||||
|
pub offset: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct GpuViewSize {
|
||||||
|
bind_group: BindGroup,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, AsStd140)]
|
#[derive(Clone, AsStd140)]
|
||||||
struct VertexStageData {
|
struct VertexStageData {
|
||||||
width: f32,
|
width: f32,
|
||||||
|
@ -133,6 +154,9 @@ impl Plugin for OutlinePlugin {
|
||||||
.add_render_command::<Transparent3d, DrawOutline>()
|
.add_render_command::<Transparent3d, DrawOutline>()
|
||||||
.init_resource::<OutlinePipeline>()
|
.init_resource::<OutlinePipeline>()
|
||||||
.init_resource::<SpecializedMeshPipelines<OutlinePipeline>>()
|
.init_resource::<SpecializedMeshPipelines<OutlinePipeline>>()
|
||||||
|
.init_resource::<ViewSizeUniforms>()
|
||||||
|
.add_system_to_stage(RenderStage::Extract, extract_view_size_uniforms)
|
||||||
|
.add_system_to_stage(RenderStage::Prepare, prepare_view_size_uniforms)
|
||||||
.add_system_to_stage(RenderStage::Queue, queue_outline);
|
.add_system_to_stage(RenderStage::Queue, queue_outline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,10 +165,29 @@ type DrawOutline = (
|
||||||
SetItemPipeline,
|
SetItemPipeline,
|
||||||
SetMeshViewBindGroup<0>,
|
SetMeshViewBindGroup<0>,
|
||||||
SetMeshBindGroup<1>,
|
SetMeshBindGroup<1>,
|
||||||
SetOutlineBindGroup<2>,
|
SetOutlineViewBindGroup<2>,
|
||||||
|
SetOutlineBindGroup<3>,
|
||||||
DrawMesh,
|
DrawMesh,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
struct SetOutlineViewBindGroup<const I: usize>();
|
||||||
|
|
||||||
|
impl<const I: usize> EntityRenderCommand for SetOutlineViewBindGroup<I> {
|
||||||
|
type Param = SQuery<(Read<ViewSizeUniformOffset>, Read<GpuViewSize>)>;
|
||||||
|
#[inline]
|
||||||
|
fn render<'w>(
|
||||||
|
view: Entity,
|
||||||
|
_item: Entity,
|
||||||
|
view_query: SystemParamItem<'w, '_, Self::Param>,
|
||||||
|
pass: &mut TrackedRenderPass<'w>,
|
||||||
|
) -> RenderCommandResult {
|
||||||
|
let (view_size_uniform, gpu_view_size) = view_query.get_inner(view).unwrap();
|
||||||
|
pass.set_bind_group(I, &gpu_view_size.bind_group, &[view_size_uniform.offset]);
|
||||||
|
|
||||||
|
RenderCommandResult::Success
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SetOutlineBindGroup<const I: usize>();
|
struct SetOutlineBindGroup<const I: usize>();
|
||||||
|
|
||||||
impl<const I: usize> EntityRenderCommand for SetOutlineBindGroup<I> {
|
impl<const I: usize> EntityRenderCommand for SetOutlineBindGroup<I> {
|
||||||
|
@ -162,6 +205,62 @@ impl<const I: usize> EntityRenderCommand for SetOutlineBindGroup<I> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract_view_size_uniforms(
|
||||||
|
mut commands: Commands,
|
||||||
|
windows: Res<Windows>,
|
||||||
|
images: Res<Assets<Image>>,
|
||||||
|
active_camera: Res<ActiveCamera<Camera3d>>,
|
||||||
|
query: Query<&Camera, With<Camera3d>>,
|
||||||
|
) {
|
||||||
|
if let Some(entity) = active_camera.get() {
|
||||||
|
if let Ok(camera) = query.get(entity) {
|
||||||
|
if let Some(size) = camera.target.get_logical_size(&windows, &images) {
|
||||||
|
commands
|
||||||
|
.get_or_spawn(entity)
|
||||||
|
.insert(ViewSizeUniform { logical_size: size });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_view_size_uniforms(
|
||||||
|
mut commands: Commands,
|
||||||
|
render_device: Res<RenderDevice>,
|
||||||
|
render_queue: Res<RenderQueue>,
|
||||||
|
outline_pipeline: Res<OutlinePipeline>,
|
||||||
|
mut view_size_uniforms: ResMut<ViewSizeUniforms>,
|
||||||
|
views: Query<(Entity, &ViewSizeUniform)>,
|
||||||
|
) {
|
||||||
|
view_size_uniforms.uniforms.clear();
|
||||||
|
for (entity, view_size_uniform) in views.iter() {
|
||||||
|
let view_size_uniforms = ViewSizeUniformOffset {
|
||||||
|
offset: view_size_uniforms.uniforms.push(view_size_uniform.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
commands.entity(entity).insert(view_size_uniforms);
|
||||||
|
}
|
||||||
|
|
||||||
|
view_size_uniforms
|
||||||
|
.uniforms
|
||||||
|
.write_buffer(&render_device, &render_queue);
|
||||||
|
|
||||||
|
if let Some(view_binding) = view_size_uniforms.uniforms.binding() {
|
||||||
|
let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||||
|
entries: &[BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: view_binding.clone(),
|
||||||
|
}],
|
||||||
|
label: Some("outline_view_size_bind_group"),
|
||||||
|
layout: &outline_pipeline.view_size_bind_group_layout,
|
||||||
|
});
|
||||||
|
for (entity, _) in views.iter() {
|
||||||
|
commands.entity(entity).insert(GpuViewSize {
|
||||||
|
bind_group: bind_group.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn queue_outline(
|
fn queue_outline(
|
||||||
opaque_3d_draw_functions: Res<DrawFunctions<Opaque3d>>,
|
opaque_3d_draw_functions: Res<DrawFunctions<Opaque3d>>,
|
||||||
|
@ -232,7 +331,8 @@ fn queue_outline(
|
||||||
|
|
||||||
pub struct OutlinePipeline {
|
pub struct OutlinePipeline {
|
||||||
mesh_pipeline: MeshPipeline,
|
mesh_pipeline: MeshPipeline,
|
||||||
bind_group_layout: BindGroupLayout,
|
view_size_bind_group_layout: BindGroupLayout,
|
||||||
|
outline_bind_group_layout: BindGroupLayout,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromWorld for OutlinePipeline {
|
impl FromWorld for OutlinePipeline {
|
||||||
|
@ -240,9 +340,25 @@ impl FromWorld for OutlinePipeline {
|
||||||
let world = world.cell();
|
let world = world.cell();
|
||||||
let mesh_pipeline = world.get_resource::<MeshPipeline>().unwrap().clone();
|
let mesh_pipeline = world.get_resource::<MeshPipeline>().unwrap().clone();
|
||||||
let render_device = world.get_resource::<RenderDevice>().unwrap();
|
let render_device = world.get_resource::<RenderDevice>().unwrap();
|
||||||
let bind_group_layout =
|
let view_size_bind_group_layout =
|
||||||
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||||
label: Some("outline_pipeline_bind_group_layout"),
|
label: Some("outline_view_size_bind_group_layout"),
|
||||||
|
entries: &[BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: ShaderStages::VERTEX,
|
||||||
|
ty: BindingType::Buffer {
|
||||||
|
ty: BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: true,
|
||||||
|
min_binding_size: BufferSize::new(
|
||||||
|
ViewSizeUniform::std140_size_static() as u64
|
||||||
|
),
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
let outline_bind_group_layout =
|
||||||
|
render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||||
|
label: Some("outline_bind_group_layout"),
|
||||||
entries: &[
|
entries: &[
|
||||||
BindGroupLayoutEntry {
|
BindGroupLayoutEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
|
@ -272,7 +388,8 @@ impl FromWorld for OutlinePipeline {
|
||||||
});
|
});
|
||||||
OutlinePipeline {
|
OutlinePipeline {
|
||||||
mesh_pipeline,
|
mesh_pipeline,
|
||||||
bind_group_layout,
|
view_size_bind_group_layout,
|
||||||
|
outline_bind_group_layout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,7 +409,8 @@ impl SpecializedMeshPipeline for OutlinePipeline {
|
||||||
descriptor.layout = Some(vec![
|
descriptor.layout = Some(vec![
|
||||||
self.mesh_pipeline.view_layout.clone(),
|
self.mesh_pipeline.view_layout.clone(),
|
||||||
self.mesh_pipeline.mesh_layout.clone(),
|
self.mesh_pipeline.mesh_layout.clone(),
|
||||||
self.bind_group_layout.clone(),
|
self.view_size_bind_group_layout.clone(),
|
||||||
|
self.outline_bind_group_layout.clone(),
|
||||||
]);
|
]);
|
||||||
Ok(descriptor)
|
Ok(descriptor)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,10 @@ struct VertexOutput {
|
||||||
[[builtin(position)]] clip_position: vec4<f32>;
|
[[builtin(position)]] clip_position: vec4<f32>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ViewSizeUniforms {
|
||||||
|
logical_size: vec2<f32>;
|
||||||
|
};
|
||||||
|
|
||||||
struct VertexStageData {
|
struct VertexStageData {
|
||||||
width: f32;
|
width: f32;
|
||||||
};
|
};
|
||||||
|
@ -22,9 +26,12 @@ struct FragmentStageData {
|
||||||
var<uniform> mesh: Mesh;
|
var<uniform> mesh: Mesh;
|
||||||
|
|
||||||
[[group(2), binding(0)]]
|
[[group(2), binding(0)]]
|
||||||
|
var<uniform> view_size: ViewSizeUniforms;
|
||||||
|
|
||||||
|
[[group(3), binding(0)]]
|
||||||
var<uniform> vstage: VertexStageData;
|
var<uniform> vstage: VertexStageData;
|
||||||
|
|
||||||
[[group(2), binding(1)]]
|
[[group(3), binding(1)]]
|
||||||
var<uniform> fstage: FragmentStageData;
|
var<uniform> fstage: FragmentStageData;
|
||||||
|
|
||||||
fn mat4to3(m: mat4x4<f32>) -> mat3x3<f32> {
|
fn mat4to3(m: mat4x4<f32>) -> mat3x3<f32> {
|
||||||
|
@ -38,7 +45,7 @@ fn vertex(vertex: Vertex) -> VertexOutput {
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
var clip_pos = view.view_proj * (mesh.model * vec4<f32>(vertex.position, 1.0));
|
var clip_pos = view.view_proj * (mesh.model * vec4<f32>(vertex.position, 1.0));
|
||||||
var clip_norm = mat4to3(view.view_proj) * (mat4to3(mesh.model) * normalize(vertex.normal));
|
var clip_norm = mat4to3(view.view_proj) * (mat4to3(mesh.model) * normalize(vertex.normal));
|
||||||
var clip_delta = vec4<f32>(vstage.width * normalize(clip_norm.xy) * clip_pos.w / vec2<f32>(view.width, view.height), 0.0, 0.0);
|
var clip_delta = vec4<f32>(2.0 * vstage.width * normalize(clip_norm.xy) * clip_pos.w / view_size.logical_size, 0.0, 0.0);
|
||||||
out.clip_position = clip_pos + clip_delta;
|
out.clip_position = clip_pos + clip_delta;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue