use super::physics::*;
use crate::core::World;
use crate::math::{Mat4, Vec3};
use crate::renderer::{
components::{Camera, Material, Mesh, MeshRenderer},
Renderer,
};
use bytemuck;
use wgpu;
#[derive(Default)]
pub struct RenderCache {
pub(crate) batches: std::collections::HashMap<BatchKey, BatchData>,
pub instances: Vec<crate::renderer::gpu_types::InstanceRaw>,
pub draw_items: Vec<DrawItem>,
}
thread_local! {
static RENDER_CACHE: std::cell::RefCell<RenderCache> = std::cell::RefCell::new(RenderCache::default());
}
pub fn clear_render_cache() {
RENDER_CACHE.with(|rc| {
let mut cache = rc.borrow_mut();
cache.batches.clear();
cache.instances.clear();
cache.draw_items.clear();
});
}
#[derive(Clone)]
pub struct DrawItem {
vbuf: std::sync::Arc<wgpu::Buffer>,
vertex_count: u32,
bind_group: std::sync::Arc<wgpu::BindGroup>,
unlit: bool,
is_skybox: bool,
skeleton_bind_group: Option<std::sync::Arc<wgpu::BindGroup>>,
first_instance: u32,
instance_count: u32,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub(crate) struct BatchKey {
vbuf_id: usize,
mat_id: usize,
skeleton_id: Option<usize>,
}
pub(crate) struct BatchData {
vbuf: std::sync::Arc<wgpu::Buffer>,
bind_group: std::sync::Arc<wgpu::BindGroup>,
vertex_count: u32,
unlit: bool,
is_skybox: bool,
skeleton_bind_group: Option<std::sync::Arc<wgpu::BindGroup>>,
instances: Vec<crate::renderer::gpu_types::InstanceRaw>,
}
#[tracing::instrument(skip_all, name = "render_system")]
pub fn default_render_pass(
world: &mut World,
encoder: &mut wgpu::CommandEncoder,
view: &wgpu::TextureView,
renderer: &mut Renderer,
) {
let aspect = if renderer.size.height > 0 {
renderer.size.width as f32 / renderer.size.height as f32
} else {
1.0
};
let mut proj = Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, aspect, 0.1, 2000.0);
let mut view_mat = Mat4::from_translation(Vec3::ZERO);
let mut cam_pos = Vec3::ZERO;
let mut cam_forward = Vec3::new(0.0, 0.0, -1.0);
gpu_physics_submit_system(world, renderer);
gpu_physics_readback_system(world, renderer);
let cameras = world.borrow::<Camera>();
let transforms = world.borrow::<crate::physics::GlobalTransform>();
{
if let Some((active_cam, _)) = cameras.iter().next() {
if let (Some(cam), Some(trans)) = (cameras.get(active_cam), transforms.get(active_cam))
{
let (_, rot, pos) = trans.matrix.to_scale_rotation_translation();
proj = cam.get_projection(aspect);
view_mat = cam.get_view(pos);
cam_pos = pos;
cam_forward = rot * Vec3::new(0.0, 0.0, -1.0);
}
}
}
let unjittered_proj = proj;
if let Some(ref taa) = renderer.taa {
let jp = crate::renderer::taa::TaaState::get_jitter(taa.frame_index);
let jx = jp[0] * 2.0 / renderer.size.width as f32;
let jy = jp[1] * 2.0 / renderer.size.height as f32;
proj.z_axis.x -= jx;
proj.z_axis.y -= jy;
}
let view_proj = proj * view_mat; let unjittered_view_proj = unjittered_proj * view_mat;
let mut sun_dir = gizmo_math::Vec3::new(0.0, -1.0, 0.0);
let mut sun_col = gizmo_math::Vec4::new(1.0, 1.0, 1.0, 1.0);
if let Some(q) = world.query::<(
&crate::renderer::components::DirectionalLight,
&crate::physics::GlobalTransform,
)>() {
for (_id, (light, transform)) in q.iter() {
if light.role == crate::renderer::components::LightRole::Sun {
let (_, rot, _) = transform.matrix.to_scale_rotation_translation();
sun_dir = rot
.mul_vec3(gizmo_math::Vec3::new(0.0, 0.0, -1.0))
.normalize();
sun_col = gizmo_math::Vec4::new(
light.color.x,
light.color.y,
light.color.z,
light.intensity,
);
break;
}
}
}
let cascade_splits = [10.0f32, 50.0, 200.0, 2000.0];
let cascade_vp = crate::renderer::directional_cascade_view_projs(
cam_pos,
cam_forward,
aspect,
std::f32::consts::FRAC_PI_4,
0.1,
&cascade_splits,
sun_dir,
crate::renderer::SHADOW_MAP_RES,
);
let light_view_projs: [[[f32; 4]; 4]; 4] = cascade_vp.map(|m| m.to_cols_array_2d());
let mut lights_data = [crate::renderer::gpu_types::LightData {
position: [0.0; 4],
color: [0.0; 4],
direction: [0.0, -1.0, 0.0, 0.0],
params: [0.0; 4],
}; 10];
let mut num_lights = 0;
if let Some(q) = world.query::<(
&crate::renderer::components::PointLight,
&crate::physics::GlobalTransform,
)>() {
for (_id, (light, transform)) in q.iter() {
if num_lights >= 10 {
break;
}
let (_, _, pos) = transform.matrix.to_scale_rotation_translation();
lights_data[num_lights as usize] = crate::renderer::gpu_types::LightData {
position: [pos.x, pos.y, pos.z, light.intensity],
color: [light.color.x, light.color.y, light.color.z, light.radius],
direction: [0.0, -1.0, 0.0, 0.0],
params: [0.0, 0.0, 0.0, 0.0], };
num_lights += 1;
}
}
if let Some(q) = world.query::<(
&crate::renderer::components::SpotLight,
&crate::physics::GlobalTransform,
)>() {
for (_id, (light, transform)) in q.iter() {
if num_lights >= 10 {
break;
}
let (_, rot, pos) = transform.matrix.to_scale_rotation_translation();
let dir = rot
.mul_vec3(gizmo_math::Vec3::new(0.0, 0.0, -1.0))
.normalize();
lights_data[num_lights as usize] = crate::renderer::gpu_types::LightData {
position: [pos.x, pos.y, pos.z, light.intensity],
color: [light.color.x, light.color.y, light.color.z, light.radius],
direction: [dir.x, dir.y, dir.z, light.inner_angle],
params: [light.outer_angle, 1.0, 0.0, 0.0], };
num_lights += 1;
}
}
let scene_uniform_data = crate::renderer::gpu_types::SceneUniforms {
view_proj: view_proj.to_cols_array_2d(),
camera_pos: [cam_pos.x, cam_pos.y, cam_pos.z, 1.0],
sun_direction: [sun_dir.x, sun_dir.y, sun_dir.z, 1.0],
sun_color: [sun_col.x, sun_col.y, sun_col.z, sun_col.w],
lights: lights_data,
light_view_proj: light_view_projs,
cascade_splits,
camera_forward: [cam_forward.x, cam_forward.y, cam_forward.z, 0.0],
cascade_params: [0.1, 1.0 / crate::renderer::SHADOW_MAP_RES as f32, 0.0, 0.0],
num_lights,
_pre_align_pad: [0; 3],
_align_pad: [0; 3],
_post_align_pad: 0,
_pad_scene: [0; 3],
shading_mode: 0,
};
renderer.queue.write_buffer(
&renderer.scene.global_uniform_buffer,
0,
bytemuck::cast_slice(&[scene_uniform_data]),
);
for i in 0..crate::renderer::CASCADE_COUNT {
renderer.queue.write_buffer(
&renderer.scene.shadow_cascade_uniform_buffers[i],
0,
bytemuck::bytes_of(&crate::renderer::gpu_types::ShadowVsUniform {
light_view_proj: light_view_projs[i],
}),
);
}
if let Some(ref mut taa) = renderer.taa {
let jp = crate::renderer::taa::TaaState::get_jitter(taa.frame_index);
let jx = jp[0] * 2.0 / renderer.size.width as f32;
let jy = jp[1] * 2.0 / renderer.size.height as f32;
let alpha = if taa.frame_index == 0 { 1.0f32 } else { 0.1f32 };
taa.update_params(&renderer.queue, [jx, jy], alpha);
taa.store_prev_vp(unjittered_view_proj.to_cols_array_2d());
}
let renderers = world.borrow::<MeshRenderer>();
let frustum = crate::math::Frustum::from_matrix(&unjittered_view_proj);
let draw_items = RENDER_CACHE.with(|rc| {
let mut cache = rc.borrow_mut();
for batch in cache.batches.values_mut() {
batch.instances.clear();
}
cache.instances.clear();
cache.draw_items.clear();
let pooled_storage = world.borrow::<gizmo_core::pool::Pooled>();
macro_rules! process_mesh {
($e:expr, $mesh:expr, $trans:expr, $mat:expr, $skeleton:expr) => {
if renderers.get($e).is_none() {
continue;
}
if pooled_storage.get($e).is_some() {
continue;
}
let center_mat = Mat4::from_translation($mesh.center_offset);
let model = $trans.matrix * center_mat;
let local_cx = ($mesh.bounds.min.x + $mesh.bounds.max.x) * 0.5;
let local_cy = ($mesh.bounds.min.y + $mesh.bounds.max.y) * 0.5;
let local_cz = ($mesh.bounds.min.z + $mesh.bounds.max.z) * 0.5;
let world_c = model.transform_point3(Vec3::new(local_cx, local_cy, local_cz));
let hx = ($mesh.bounds.max.x - $mesh.bounds.min.x) * 0.5;
let hy = ($mesh.bounds.max.y - $mesh.bounds.min.y) * 0.5;
let hz = ($mesh.bounds.max.z - $mesh.bounds.min.z) * 0.5;
let local_r = (hx * hx + hy * hy + hz * hz).sqrt();
let sx = model.x_axis.truncate().length();
let sy = model.y_axis.truncate().length();
let sz = model.z_axis.truncate().length();
let world_r = local_r * sx.max(sy).max(sz);
if !frustum.intersects_sphere(world_c, world_r) {
continue;
}
let dist_to_cam = (world_c - cam_pos).length();
let use_lod1 = if !$mesh.lod_vbufs.is_empty() {
dist_to_cam > world_r * 15.0 } else {
false
};
let active_vbuf = if use_lod1 {
$mesh.lod_vbufs[0].clone()
} else {
$mesh.vbuf.clone()
};
let active_vertex_count = if use_lod1 {
$mesh.lod_vertex_counts[0]
} else {
$mesh.vertex_count
};
let instance_data = crate::renderer::gpu_types::InstanceRaw {
model: model.to_cols_array_2d(),
albedo_color: [$mat.albedo.x, $mat.albedo.y, $mat.albedo.z, $mat.albedo.w],
roughness: $mat.roughness,
metallic: $mat.metallic,
unlit: match $mat.material_type {
crate::renderer::components::MaterialType::Skybox => 2.0,
crate::renderer::components::MaterialType::Unlit => 1.0,
_ => 0.0,
},
_padding: 0.0,
};
let skel_bg = $skeleton.map(|s: &crate::renderer::components::Skeleton| s.bind_group.clone());
let key = BatchKey {
vbuf_id: std::sync::Arc::as_ptr(&active_vbuf) as usize,
mat_id: std::sync::Arc::as_ptr(&$mat.bind_group) as usize,
skeleton_id: skel_bg.as_ref().map(|bg| std::sync::Arc::as_ptr(bg) as usize),
};
let batch = cache.batches.entry(key).or_insert_with(|| BatchData {
vbuf: active_vbuf.clone(),
bind_group: $mat.bind_group.clone(),
vertex_count: active_vertex_count,
unlit: $mat.material_type == crate::renderer::components::MaterialType::Unlit
|| $mat.material_type == crate::renderer::components::MaterialType::Skybox,
is_skybox: $mat.material_type == crate::renderer::components::MaterialType::Skybox,
skeleton_bind_group: skel_bg,
instances: Vec::new(),
});
batch.instances.push(instance_data);
};
}
let skeletons = world.borrow::<crate::renderer::components::Skeleton>();
if let Some(mut q) = world.query::<(&Mesh, &crate::physics::GlobalTransform, &Material)>() {
for (e, (mesh, trans, mat)) in q.iter_mut() {
process_mesh!(e, mesh, trans, mat, skeletons.get(e));
}
}
let meshes = world.try_get_resource::<gizmo_core::asset::Assets<Mesh>>().ok();
let materials = world.try_get_resource::<gizmo_core::asset::Assets<Material>>().ok();
if let (Some(meshes), Some(materials)) = (meshes, materials) {
if let Some(mut q) = world.query::<(&gizmo_core::asset::Handle<Mesh>, &crate::physics::GlobalTransform, &gizmo_core::asset::Handle<Material>)>() {
for (e, (h_mesh, trans, h_mat)) in q.iter_mut() {
if let (Some(mesh), Some(mat)) = (meshes.get(h_mesh), materials.get(h_mat)) {
process_mesh!(e, mesh, trans, mat, skeletons.get(e));
}
}
}
}
let mut local_instances: Vec<crate::renderer::gpu_types::InstanceRaw> = std::mem::take(&mut cache.instances);
let mut local_draw_items: Vec<DrawItem> = std::mem::take(&mut cache.draw_items);
for (_, batch) in cache.batches.iter() {
if batch.instances.is_empty() { continue; }
let first_instance = local_instances.len() as u32;
let instance_count = batch.instances.len() as u32;
local_instances.extend(&batch.instances);
local_draw_items.push(DrawItem {
vbuf: batch.vbuf.clone(),
vertex_count: batch.vertex_count,
bind_group: batch.bind_group.clone(),
unlit: batch.unlit,
is_skybox: batch.is_skybox,
skeleton_bind_group: batch.skeleton_bind_group.clone(),
first_instance,
instance_count,
});
}
cache.instances = local_instances;
cache.draw_items = local_draw_items;
let max_instances = renderer.scene.instance_capacity;
let instances_slice = if cache.instances.len() > max_instances {
&cache.instances[..max_instances]
} else {
&cache.instances
};
if !instances_slice.is_empty() {
renderer.queue.write_buffer(
&renderer.scene.instance_buffer,
0,
bytemuck::cast_slice(instances_slice),
);
}
cache.draw_items.clone()
});
if let Some(physics) = &renderer.gpu_physics {
physics.request_readback(encoder);
physics.compute_pass(encoder);
physics.debug_compute_pass(encoder);
physics.cull_pass(encoder, &renderer.scene.global_bind_group);
}
let fluid_pos = Vec3::new(0.0, 5.0, 0.0);
let dist_to_fluid = (cam_pos - fluid_pos).length();
let fluid_lod = if dist_to_fluid < 40.0 {
1.0
} else if dist_to_fluid < 80.0 {
0.5
} else if dist_to_fluid < 150.0 {
0.1
} else {
0.0
};
let dist_to_origin = cam_pos.length();
let particle_lod = if dist_to_origin < 50.0 {
1.0
} else if dist_to_origin < 100.0 {
0.5
} else if dist_to_origin < 200.0 {
0.1
} else {
0.0
};
if let Some(fluid) = &renderer.gpu_fluid {
let active_fluid = (fluid.num_particles as f32 * fluid_lod) as u32;
fluid.compute_pass(encoder, &renderer.queue, true, active_fluid);
}
if let Some(particles) = &renderer.gpu_particles {
let active_parts = (particles.max_particles as f32 * particle_lod) as u32;
let dt = world
.get_resource::<gizmo_core::time::Time>()
.map(|t| t.time_scale() * 0.016)
.unwrap_or(0.016);
particles.update_params(&renderer.queue, dt); particles.compute_pass(encoder, active_parts);
}
if let Some(ref mut def) = renderer.deferred {
def.resize(&renderer.device, renderer.size.width, renderer.size.height);
}
{
let w = renderer.size.width;
let h = renderer.size.height;
if let (Some(ssao), Some(def)) = (&mut renderer.ssao, &renderer.deferred) {
if ssao.width != w || ssao.height != h {
ssao.resize(&renderer.device, def, w, h);
}
}
if let (Some(ssr), Some(def)) = (&mut renderer.ssr, &renderer.deferred) {
if ssr.width != w || ssr.height != h {
ssr.resize(&renderer.device, def, &renderer.post.hdr_texture_view, w, h);
}
}
if let (Some(volumetric), Some(def)) = (&mut renderer.volumetric, &renderer.deferred) {
if volumetric.width != w || volumetric.height != h {
volumetric.resize(&renderer.device, def, w, h);
}
}
}
{
let w = renderer.size.width;
let h = renderer.size.height;
if let (Some(taa), Some(def)) = (&mut renderer.taa, &renderer.deferred) {
if taa.width != w || taa.height != h {
taa.resize(
&renderer.device,
&renderer.post.hdr_texture_view,
&def.world_position_view,
w,
h,
);
}
}
}
for i in 0..crate::renderer::CASCADE_COUNT {
let mut shadow_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Shadow Pass"),
color_attachments: &[],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &renderer.scene.shadow_cascade_layer_views[i],
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
});
shadow_pass.set_pipeline(&renderer.scene.shadow_pipeline);
shadow_pass.set_bind_group(0, &renderer.scene.shadow_pass_bind_groups[i], &[]);
shadow_pass.set_bind_group(2, &renderer.scene.instance_bind_group, &[]);
for item in &draw_items {
if item.unlit {
continue;
}
let skel_bg = item
.skeleton_bind_group
.as_ref()
.unwrap_or(&renderer.scene.dummy_skeleton_bind_group);
shadow_pass.set_bind_group(1, skel_bg, &[]);
shadow_pass.set_vertex_buffer(0, item.vbuf.slice(..));
shadow_pass.draw(
0..item.vertex_count,
item.first_instance..(item.first_instance + item.instance_count),
);
}
}
if let Some(ref def) = renderer.deferred {
let mut z_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Z-Prepass"),
color_attachments: &[], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &renderer.depth_texture_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
});
z_pass.set_pipeline(&def.z_prepass_pipeline);
z_pass.set_bind_group(0, &renderer.scene.global_bind_group, &[]);
z_pass.set_bind_group(2, &renderer.scene.shadow_bind_group, &[]);
z_pass.set_bind_group(4, &renderer.scene.instance_bind_group, &[]);
for item in &draw_items {
if item.unlit || item.is_skybox {
continue;
}
let skel_bg = item
.skeleton_bind_group
.as_ref()
.unwrap_or(&renderer.scene.dummy_skeleton_bind_group);
z_pass.set_bind_group(3, skel_bg, &[]);
z_pass.set_bind_group(1, &item.bind_group, &[]);
z_pass.set_vertex_buffer(0, item.vbuf.slice(..));
z_pass.draw(
0..item.vertex_count,
item.first_instance..(item.first_instance + item.instance_count),
);
}
}
if let Some(ref def) = renderer.deferred {
let mut gbuf_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("G-Buffer Pass"),
color_attachments: &[
Some(wgpu::RenderPassColorAttachment {
view: &def.albedo_metallic_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
}),
Some(wgpu::RenderPassColorAttachment {
view: &def.normal_roughness_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
}),
Some(wgpu::RenderPassColorAttachment {
view: &def.world_position_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
}),
],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &renderer.depth_texture_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Load, store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
});
gbuf_pass.set_pipeline(&def.gbuffer_pipeline);
gbuf_pass.set_bind_group(0, &renderer.scene.global_bind_group, &[]);
gbuf_pass.set_bind_group(2, &renderer.scene.shadow_bind_group, &[]);
gbuf_pass.set_bind_group(4, &renderer.scene.instance_bind_group, &[]);
for item in &draw_items {
if item.unlit {
continue;
}
let skel_bg = item
.skeleton_bind_group
.as_ref()
.unwrap_or(&renderer.scene.dummy_skeleton_bind_group);
gbuf_pass.set_bind_group(3, skel_bg, &[]);
gbuf_pass.set_bind_group(1, &item.bind_group, &[]);
gbuf_pass.set_vertex_buffer(0, item.vbuf.slice(..));
gbuf_pass.draw(
0..item.vertex_count,
item.first_instance..(item.first_instance + item.instance_count),
);
}
}
let mut decal_draws = Vec::new();
if let Some(ref decal_state) = renderer.decal {
let decals = world.borrow::<crate::renderer::components::Decal>();
let transforms = world.borrow::<crate::physics::Transform>();
let mut uniform_data = Vec::new();
for (id, decal) in decals.iter() {
if let Some(trans) = transforms.get(id) {
let model = trans.local_matrix;
let inv_model = model.inverse();
uniform_data.push(crate::renderer::decal::DecalUniforms {
inv_model: inv_model.to_cols_array(),
model: model.to_cols_array(),
albedo_color: [decal.color.x, decal.color.y, decal.color.z, decal.color.w],
_pad: [0.0; 28],
});
decal_draws.push(decal.bind_group.clone());
if uniform_data.len() >= 1024 {
break;
} }
}
if !uniform_data.is_empty() {
renderer.queue.write_buffer(
&decal_state.uniform_buffer,
0,
bytemuck::cast_slice(&uniform_data),
);
}
}
if !decal_draws.is_empty() {
if let (Some(ref decal_state), Some(ref def)) = (&renderer.decal, &renderer.deferred) {
let mut decal_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Decal Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &def.albedo_metallic_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None, timestamp_writes: None,
occlusion_query_set: None,
});
decal_pass.set_pipeline(&decal_state.pipeline);
decal_pass.set_bind_group(0, &renderer.scene.global_bind_group, &[]);
decal_pass.set_bind_group(1, &decal_state.world_pos_bg, &[]);
decal_pass.set_vertex_buffer(0, decal_state.vertex_buffer.slice(..));
for (i, bind_group) in decal_draws.iter().enumerate() {
let offset = (i * 256) as u32;
decal_pass.set_bind_group(2, bind_group, &[]);
decal_pass.set_bind_group(3, &decal_state.decal_uniform_bg, &[offset]);
decal_pass.draw(0..36, 0..1);
}
}
}
if let Some(ref def) = renderer.deferred {
let mut light_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Deferred Lighting Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &renderer.post.hdr_texture_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
light_pass.set_pipeline(&def.lighting_pipeline);
light_pass.set_bind_group(0, &renderer.scene.global_bind_group, &[]);
light_pass.set_bind_group(1, &renderer.scene.shadow_bind_group, &[]);
light_pass.set_bind_group(2, &def.gbuffer_bind_group, &[]);
light_pass.draw(0..3, 0..1);
}
if let Some(ref ssao) = renderer.ssao {
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("SSAO Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &ssao.ao_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&ssao.ssao_pipeline);
pass.set_bind_group(0, &renderer.scene.global_bind_group, &[]);
pass.set_bind_group(1, &ssao.ssao_gbuf_bind_group, &[]);
pass.draw(0..3, 0..1);
}
if let Some(ref ssao) = renderer.ssao {
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("SSAO Blur Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &ssao.ao_blurred_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&ssao.blur_pipeline);
pass.set_bind_group(0, &ssao.blur_bind_group, &[]);
pass.draw(0..3, 0..1);
}
if let Some(ref ssao) = renderer.ssao {
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("SSAO Apply Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &renderer.post.hdr_texture_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&ssao.apply_pipeline);
pass.set_bind_group(0, &ssao.apply_bind_group, &[]);
pass.draw(0..3, 0..1);
}
{
let hdr_load = if renderer.deferred.is_some() {
wgpu::LoadOp::Load
} else {
wgpu::LoadOp::Clear(wgpu::Color {
r: 0.1,
g: 0.1,
b: 0.15,
a: 1.0,
})
};
let depth_load = if renderer.deferred.is_some() {
wgpu::LoadOp::Load
} else {
wgpu::LoadOp::Clear(1.0)
};
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Default Engine Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &renderer.post.hdr_texture_view,
resolve_target: None,
ops: wgpu::Operations {
load: hdr_load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &renderer.depth_texture_view,
depth_ops: Some(wgpu::Operations {
load: depth_load,
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
});
render_pass.set_bind_group(0, &renderer.scene.global_bind_group, &[]);
render_pass.set_bind_group(2, &renderer.scene.shadow_bind_group, &[]);
render_pass.set_bind_group(4, &renderer.scene.instance_bind_group, &[]);
for item in &draw_items {
let pipeline = if item.is_skybox {
&renderer.scene.sky_pipeline
} else if item.unlit {
&renderer.scene.unlit_pipeline
} else if renderer.deferred.is_none() {
&renderer.scene.render_pipeline
} else {
continue; };
render_pass.set_pipeline(pipeline);
let skel_bg = item
.skeleton_bind_group
.as_ref()
.unwrap_or(&renderer.scene.dummy_skeleton_bind_group);
render_pass.set_bind_group(1, &item.bind_group, &[]);
render_pass.set_bind_group(3, skel_bg, &[]);
render_pass.set_vertex_buffer(0, item.vbuf.slice(..));
render_pass.draw(
0..item.vertex_count,
item.first_instance..(item.first_instance + item.instance_count),
);
}
if let Some(physics) = &renderer.gpu_physics {
physics.render_pass(&mut render_pass, &renderer.scene.global_bind_group);
physics.debug_render_pass(&mut render_pass, &renderer.scene.global_bind_group);
}
if let Some(fluid) = &renderer.gpu_fluid {
fluid.render_pass(&mut render_pass, &renderer.scene.global_bind_group);
}
if let Some(particles) = &renderer.gpu_particles {
let active_parts = (particles.max_particles as f32 * particle_lod) as u32;
particles.render_pass(
&mut render_pass,
&renderer.scene.global_bind_group,
active_parts,
);
}
if let Some(gizmos) = world.get_resource::<crate::renderer::Gizmos>() {
if let Some(debug_renderer) = &mut renderer.debug_renderer {
debug_renderer.update(&renderer.queue, &gizmos);
debug_renderer.render(
&mut render_pass,
&renderer.scene.global_bind_group,
gizmos.depth_test,
);
}
}
}
if let Some(fluid) = &renderer.gpu_fluid {
let active_fluid = (fluid.num_particles as f32 * fluid_lod) as u32;
fluid.render_ssfr(
encoder,
&renderer.post.hdr_texture,
&renderer.post.hdr_texture_view,
&renderer.depth_texture_view,
&renderer.scene.global_bind_group,
active_fluid,
);
}
if let Some(mut gizmos) = world.get_resource_mut::<crate::renderer::Gizmos>() {
gizmos.clear();
}
if let Some(ref ssr) = renderer.ssr {
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("SSR Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &ssr.ssr_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&ssr.ssr_pipeline);
pass.set_bind_group(0, &renderer.scene.global_bind_group, &[]);
pass.set_bind_group(1, &ssr.ssr_bind_group, &[]);
pass.draw(0..3, 0..1);
}
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("SSR Apply Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &renderer.post.hdr_texture_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&ssr.apply_pipeline);
pass.set_bind_group(0, &ssr.apply_bind_group, &[]);
pass.draw(0..3, 0..1);
}
}
if let Some(ref ssgi) = renderer.ssgi {
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("SSGI Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &ssgi.ssgi_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&ssgi.ssgi_pipeline);
pass.set_bind_group(0, &renderer.scene.global_bind_group, &[]);
pass.set_bind_group(1, &ssgi.ssgi_bind_group, &[]);
pass.draw(0..3, 0..1);
}
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("SSGI Blur Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &ssgi.ssgi_blurred_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&ssgi.blur_pipeline);
pass.set_bind_group(0, &ssgi.blur_bind_group, &[]);
pass.draw(0..3, 0..1);
}
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("SSGI Apply Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &renderer.post.hdr_texture_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&ssgi.apply_pipeline);
pass.set_bind_group(0, &ssgi.apply_bind_group, &[]);
pass.draw(0..3, 0..1);
}
}
if let Some(ref vol) = renderer.volumetric {
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Volumetric Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &vol.volumetric_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&vol.volumetric_pipeline);
pass.set_bind_group(0, &renderer.scene.global_bind_group, &[]);
pass.set_bind_group(1, &renderer.scene.shadow_bind_group, &[]);
pass.set_bind_group(2, &vol.volumetric_bind_group, &[]);
pass.draw(0..3, 0..1);
}
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Volumetric Apply Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &renderer.post.hdr_texture_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&vol.apply_pipeline);
pass.set_bind_group(0, &vol.apply_bind_group, &[]);
pass.draw(0..3, 0..1);
}
}
if let Some(ref taa) = renderer.taa {
let (resolve_bg, output_view) = taa.current_resolve_inputs_output();
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("TAA Resolve Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: output_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&taa.resolve_pipeline);
pass.set_bind_group(0, resolve_bg, &[]);
pass.draw(0..3, 0..1);
}
if let Some(ref taa) = renderer.taa {
let blit_bg = taa.current_blit_bg();
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("TAA Blit Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &renderer.post.hdr_texture_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
pass.set_pipeline(&taa.blit_pipeline);
pass.set_bind_group(0, &taa.empty_bg, &[]);
pass.set_bind_group(1, blit_bg, &[]);
pass.draw(0..3, 0..1);
}
if let Some(ref mut taa) = renderer.taa {
taa.advance_frame();
}
renderer.run_post_processing(encoder, view);
}
pub trait RenderContextExt {
fn default_render(&mut self, world: &mut crate::core::World);
}
impl<'a> RenderContextExt for crate::renderer::RenderContext<'a> {
fn default_render(&mut self, world: &mut crate::core::World) {
let (encoder, view, renderer) = self.parts_mut();
default_render_pass(world, encoder, view, renderer);
}
}