Added rain occlusion

This commit is contained in:
IsseW 2022-03-15 19:04:21 +01:00
parent b3200ed89f
commit 79cac935c8
26 changed files with 1046 additions and 92 deletions

View File

@ -28,19 +28,19 @@
// This *MUST* come after `cloud.glsl`: it contains a function that depends on `cloud.glsl` when clouds are enabled
#include <point_glow.glsl>
layout(set = 1, binding = 0)
layout(set = 2, binding = 0)
uniform texture2D t_src_color;
layout(set = 1, binding = 1)
layout(set = 2, binding = 1)
uniform sampler s_src_color;
layout(set = 1, binding = 2)
layout(set = 2, binding = 2)
uniform texture2D t_src_depth;
layout(set = 1, binding = 3)
layout(set = 2, binding = 3)
uniform sampler s_src_depth;
layout(location = 0) in vec2 uv;
layout (std140, set = 1, binding = 4)
layout (std140, set = 2, binding = 4)
uniform u_locals {
mat4 proj_mat_inv;
mat4 view_mat_inv;
@ -112,40 +112,37 @@ void main() {
vec2 view_pos = vec2(atan2(dir_2d.x, dir_2d.y), z);
vec3 cam_wpos = cam_pos.xyz + focus_off.xyz;
float rain_density = rain_density_at(cam_wpos.xy) * 10.0;
if (rain_density > 0) {
float rain_dist = 150.0;
for (int i = 0; i < 7; i ++) {
rain_dist *= 0.3;
float rain_dist = 150.0;
for (int i = 0; i < 7; i ++) {
rain_dist *= 0.3;
vec3 rpos = vec3(vec2(dir_2d), view_pos.y) * rain_dist;
float dist_to_rain = length(rpos);
if (dist < dist_to_rain || cam_wpos.z + rpos.z > CLOUD_AVG_ALT) {
vec3 rpos = vec3(vec2(dir_2d), view_pos.y) * rain_dist;
float dist_to_rain = length(rpos);
if (dist < dist_to_rain || cam_wpos.z + rpos.z > CLOUD_AVG_ALT) {
continue;
}
if (dot(rpos * vec3(1, 1, 0.5), rpos) < 1.0) {
break;
}
vec2 drop_density = vec2(30, 1);
vec2 drop_size = vec2(0.0008, 0.05);
vec2 rain_pos = (view_pos * rain_dist);
rain_pos += vec2(0, tick.x * fall_rate + cam_wpos.z);
vec2 cell = floor(rain_pos * drop_density) / drop_density;
if (fract(hash(fract(vec4(cell, rain_dist, 0) * 0.01))) > rain_density) {
continue;
}
vec2 near_drop = cell + (vec2(0.5) + (vec2(hash(vec4(cell, 0, 0)), 0.5) - 0.5) * vec2(2, 0)) / drop_density;
float avg_alpha = (drop_size.x * drop_size.y) * 10 / 1;
float alpha = sign(max(1 - length((rain_pos - near_drop) / drop_size * 0.1), 0));
float light = sqrt(dot(old_color, vec3(1))) + (get_sun_brightness() + get_moon_brightness()) * 0.01;
color.rgb = mix(color.rgb, vec3(0.3, 0.4, 0.5) * light, mix(avg_alpha, alpha, min(1000 / dist_to_rain, 1)) * 0.25);
if (dot(rpos * vec3(1, 1, 0.5), rpos) < 1.0) {
break;
}
float rain_density = rain_density_at(cam_wpos.xy + rpos.xy) * rain_occlusion_at(cam_pos.xyz + rpos.xyz) * 10.0;
vec2 drop_density = vec2(30, 1);
vec2 drop_size = vec2(0.0008, 0.05);
vec2 rain_pos = (view_pos * rain_dist);
rain_pos += vec2(0, tick.x * fall_rate + cam_wpos.z);
vec2 cell = floor(rain_pos * drop_density) / drop_density;
if (fract(hash(fract(vec4(cell, rain_dist, 0) * 0.01))) > rain_density) {
continue;
}
vec2 near_drop = cell + (vec2(0.5) + (vec2(hash(vec4(cell, 0, 0)), 0.5) - 0.5) * vec2(2, 0)) / drop_density;
float avg_alpha = (drop_size.x * drop_size.y) * 10 / 1;
float alpha = sign(max(1 - length((rain_pos - near_drop) / drop_size * 0.1), 0));
float light = sqrt(dot(old_color, vec3(1))) + (get_sun_brightness() + get_moon_brightness()) * 0.01;
color.rgb = mix(color.rgb, vec3(0.3, 0.4, 0.5) * light, mix(avg_alpha, alpha, min(1000 / dist_to_rain, 1)) * 0.25);
}
#endif

View File

@ -150,7 +150,7 @@ void main() {
);
#ifdef EXPERIMENTAL_RAIN
float rain_density = rain_density_at(cam_pos.xy + focus_off.xy) * 50.0;
float rain_density = rain_density_at(f_pos.xy + focus_off.xy) * rain_occlusion_at(f_pos.xyz) * 50.0;
if (rain_density > 0 && surf_norm.z > 0.5) {
vec3 drop_density = vec3(2, 2, 2);
vec3 drop_pos = wave_pos + vec3(0, 0, -time_of_day.x * 0.025);

View File

@ -0,0 +1,36 @@
#ifndef RAIN_OCCLUSION_GLSL
#define RAIN_OCCLUSION_GLSL
// Use with sampler2DShadow
layout(set = 1, binding = 4)
uniform texture2D t_directed_occlusion_maps;
layout(set = 1, binding = 5)
uniform samplerShadow s_directed_occlusion_maps;
layout (std140, set = 0, binding = 14)
uniform u_rain_occlusion {
mat4 occlusionMatrices;
mat4 occlusion_texture_mat;
};
float rain_occlusion_at(in vec3 fragPos)
{
float bias = 0.000;
float diskRadius = 0.01;
const vec3 sampleOffsetDirections[20] = vec3[]
(
vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1),
vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1),
vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0),
vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1),
vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1)
);
vec4 rain_pos = occlusion_texture_mat * vec4(fragPos, 1.0);
float visibility = textureProj(sampler2DShadow(t_directed_occlusion_maps, s_directed_occlusion_maps), rain_pos);
return visibility;
}
#endif

View File

@ -5,6 +5,7 @@
#include <srgb.glsl>
#include <shadows.glsl>
#include <globals.glsl>
#include <rain_occlusion.glsl>
// Information about an approximately directional light, like the sun or moon.
struct DirectionalLight {
@ -121,8 +122,9 @@ float cloud_tendency_at(vec2 pos) {
}
const float RAIN_CLOUD = 0.05;
float rain_density_at(vec2 pos) {
return sample_weather(pos).g;
return 1.0; //sample_weather(pos).g;
//return clamp((cloud_tendency_at(pos) - RAIN_CLOUD) * 10, 0, 1);
}

View File

@ -0,0 +1,61 @@
#version 420 core
// #extension ARB_texture_storage : enable
#include <constants.glsl>
#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION
#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
#if (FLUID_MODE == FLUID_MODE_CHEAP)
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
#elif (FLUID_MODE == FLUID_MODE_SHINY)
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
#endif
#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET
#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN
#define HAS_SHADOW_MAPS
// Currently, we only need globals for focus_off.
#include <globals.glsl>
// For shadow locals.
// #include <shadows.glsl>
layout (std140, set = 0, binding = 14)
uniform u_rain_occlusion {
mat4 rainOcclusionMatrices;
mat4 texture_mat;
};
/* Accurate packed shadow maps for many lights at once!
*
* Ideally, we would just write to a bitmask...
*
* */
layout(location = 0) in uint v_pos_norm;
// in uint v_col_light;
// in vec4 v_pos;
// layout(location = 1) in uint v_atlas_pos;
// Light projection matrices.
layout (std140, set = 1, binding = 0)
uniform u_locals {
vec3 model_offs;
float load_time;
ivec4 atlas_offs;
};
// out vec4 shadowMapCoord;
const float EXTRA_NEG_Z = 32768.0;
void main() {
vec3 f_chunk_pos = vec3(v_pos_norm & 0x3Fu, (v_pos_norm >> 6) & 0x3Fu, float((v_pos_norm >> 12) & 0xFFFFu) - EXTRA_NEG_Z);
vec3 f_pos = f_chunk_pos + (model_offs - focus_off.xyz);
gl_Position = rainOcclusionMatrices * vec4(f_pos, 1.0);
}

View File

@ -0,0 +1,82 @@
#version 420 core
// #extension ARB_texture_storage : enable
#define FIGURE_SHADER
#include <constants.glsl>
#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION
#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
#if (FLUID_MODE == FLUID_MODE_CHEAP)
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
#elif (FLUID_MODE == FLUID_MODE_SHINY)
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
#endif
#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET
#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN
#define HAS_SHADOW_MAPS
// Currently, we only need globals for focus_off.
#include <globals.glsl>
// For shadow locals.
// #include <shadows.glsl>
layout (std140, set = 0, binding = 14)
uniform u_rain_occlusion {
mat4 rainOcclusionMatrices;
mat4 texture_mat;
};
/* Accurate packed shadow maps for many lights at once!
*
* Ideally, we would just write to a bitmask...
*
* */
layout(location = 0) in uint v_pos_norm;
layout(location = 1) in uint v_atlas_pos;
// in uint v_col_light;
// in vec4 v_pos;
layout (std140, set = 1, binding = 0)
uniform u_locals {
mat4 model_mat;
vec4 highlight_col;
vec4 model_light;
vec4 model_glow;
ivec4 atlas_offs;
vec3 model_pos;
// bit 0 - is player
// bit 1-31 - unused
int flags;
};
struct BoneData {
mat4 bone_mat;
mat4 normals_mat;
};
layout (std140, set = 1, binding = 1)
uniform u_bones {
// Warning: might not actually be 16 elements long. Don't index out of bounds!
BoneData bones[16];
};
// out vec4 shadowMapCoord;
void main() {
uint bone_idx = (v_pos_norm >> 27) & 0xFu;
vec3 pos = (vec3((uvec3(v_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0;
vec3 f_pos = (
bones[bone_idx].bone_mat *
vec4(pos, 1.0)
).xyz + (model_pos - focus_off.xyz/* + vec3(0.0, 0.0, 0.0001)*/);
gl_Position = rainOcclusionMatrices * vec4(f_pos, 1.0);
}

View File

@ -30,7 +30,7 @@ layout(location = 7) in float inst_glow;
layout(location = 8) in float model_wind_sway; // NOTE: this only varies per model
layout(location = 9) in float model_z_scale; // NOTE: this only varies per model
layout(set = 0, binding = 14) restrict readonly buffer sprite_verts {
layout(set = 0, binding = 15) restrict readonly buffer sprite_verts {
uvec2 verts[];
};

View File

@ -232,9 +232,11 @@ void main() {
vec3 k_s = vec3(R_s);
#ifdef EXPERIMENTAL_RAIN
float rain_density = rain_density_at(cam_pos.xy + focus_off.xy) * 50.0;
vec3 pos = f_pos + focus_off.xyz;
float rain_density = rain_density_at(pos.xy) * rain_occlusion_at(f_pos.xyz) * 50.0;
// tgt_color = vec4(rain_occlusion_at(f_pos.xyz), 0.0, 0.0, 1.0);
// return;
if (rain_density > 0 && !faces_fluid && f_norm.z > 0.5) {
vec3 pos = f_pos + focus_off.xyz;
vec3 drop_density = vec3(2, 2, 2);
vec3 drop_pos = pos + vec3(pos.zz, 0) + vec3(0, 0, -tick.x * 1.0);
drop_pos.z += noise_2d(floor(drop_pos.xy * drop_density.xy) * 13.1) * 10;

View File

@ -166,11 +166,13 @@ impl WeatherLerp {
fn update(&mut self) {
let old = &self.old.0;
let new = &self.new.0;
if old.size() != new.size() {
if self.current.size() != new.size() {
self.current = new.clone();
}
} else {
if new.size() == Vec2::zero() {
return;
}
if self.current.size() != new.size() {
self.current = new.clone();
}
if old.size() == new.size() {
// Assume updates are regular
let t = (self.new.1.elapsed().as_secs_f32()
/ self.new.1.duration_since(self.old.1).as_secs_f32())

View File

@ -186,7 +186,9 @@ pub fn handle_mine_block(
) {
let skill_group = SkillGroupKind::Weapon(tool);
let outcome_bus = state.ecs().read_resource::<EventBus<Outcome>>();
if let Some(level_outcome) = skillset.add_experience(skill_group, exp_reward) {
if let Some(level_outcome) =
skillset.add_experience(skill_group, exp_reward)
{
outcome_bus.emit_now(Outcome::SkillPointGain {
uid,
skill_tree: skill_group,

View File

@ -13,8 +13,8 @@ use common::{
};
use common_state::State;
use serde::Deserialize;
use strum::IntoEnumIterator;
use std::time::Instant;
use strum::IntoEnumIterator;
use tracing::warn;
use vek::*;
@ -66,14 +66,17 @@ impl AmbientMgr {
if index >= audio.ambient_channels.len() {
println!("Creating audio channel with this tag: {:?}", tag);
audio.new_ambient_channel(tag);
break
break;
}
}
// even if the conditions don't warrant the creation of a channel
// with that tag, but a channel with that tag remains
// nonetheless, run the code
// even if the conditions don't warrant the creation of a
// channel with that tag, but a channel with
// that tag remains nonetheless, run the code
} else if audio.get_ambient_channel(tag).is_some() {
println!("Channel for {:?} is actually present, performing volume code", tag);
println!(
"Channel for {:?} is actually present, performing volume code",
tag
);
for index in 0..AmbientChannelTag::iter().len() {
// update with sfx volume
audio.ambient_channels[index].set_volume(sfx_volume);
@ -121,7 +124,11 @@ impl AmbientMgr {
// remove channel if not playing
if audio.ambient_channels[index].get_multiplier() == 0.0 {
println!("Removing channel {:?} with tag {:?}", index, audio.ambient_channels[index].get_tag());
println!(
"Removing channel {:?} with tag {:?}",
index,
audio.ambient_channels[index].get_tag()
);
audio.ambient_channels[index].stop();
audio.ambient_channels.remove(index);
};
@ -182,8 +189,8 @@ impl AmbientMgr {
// Tree density factors into wind volume. The more trees,
// the lower wind volume. The trees make more of an impact
// the closer the camera is to the ground.
let tree_multiplier =
1.0 - (((1.0 - tree_density) + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)).min(1.0));
let tree_multiplier = 1.0
- (((1.0 - tree_density) + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)).min(1.0));
return tree_multiplier > 0.05;
}
@ -264,10 +271,13 @@ impl AmbientChannel {
let tree_multiplier =
((1.0 - tree_density) + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)).min(1.0);
// Lastly, we of course have to take into account actual wind speed from weathersim
// Lastly, we of course have to take into account actual wind speed from
// weathersim
let wind_speed_multiplier = (client_wind_speed_sq / 30.0_f32.powi(2)).min(1.0);
return alt_multiplier * tree_multiplier * (wind_speed_multiplier + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)).min(1.0);
return alt_multiplier
* tree_multiplier
* (wind_speed_multiplier + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)).min(1.0);
}
fn get_rain_volume(&mut self, client: &Client) -> f32 {
@ -301,8 +311,8 @@ impl AmbientChannel {
// Tree density factors into wind volume. The more trees,
// the lower wind volume. The trees make more of an impact
// the closer the camera is to the ground.
let tree_multiplier =
1.0 - (((1.0 - tree_density) + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)).min(1.0));
let tree_multiplier = 1.0
- (((1.0 - tree_density) + ((cam_pos.z - terrain_alt).abs() / 150.0).powi(2)).min(1.0));
if tree_multiplier > 0.05 {
tree_multiplier

View File

@ -22,8 +22,8 @@ use crate::audio::{
};
use rodio::{OutputStreamHandle, Sample, Sink, Source, SpatialSink};
use serde::Deserialize;
use strum::EnumIter;
use std::time::Instant;
use strum::EnumIter;
use tracing::warn;
use vek::*;

View File

@ -1,6 +1,6 @@
use crate::render::{
GlobalModel, Globals, GlobalsBindGroup, Light, LodData, PointLightMatrix, Renderer, Shadow,
ShadowLocals,
GlobalModel, Globals, GlobalsBindGroup, Light, LodData, PointLightMatrix, RainOcclusionLocals,
Renderer, Shadow, ShadowLocals,
};
pub struct Scene {
@ -14,6 +14,8 @@ impl Scene {
lights: renderer.create_consts(&[Light::default(); 32]),
shadows: renderer.create_consts(&[Shadow::default(); 32]),
shadow_mats: renderer.create_shadow_bound_locals(&[ShadowLocals::default()]),
rain_occlusion_mats: renderer
.create_rain_occlusion_bound_locals(&[RainOcclusionLocals::default()]),
point_light_matrices: Box::new([PointLightMatrix::default(); 126]),
};

View File

@ -30,6 +30,7 @@ pub use self::{
lod_terrain::{LodData, Vertex as LodTerrainVertex},
particle::{Instance as ParticleInstance, Vertex as ParticleVertex},
postprocess::Locals as PostProcessLocals,
rain_occlusion::Locals as RainOcclusionLocals,
shadow::{Locals as ShadowLocals, PointLightMatrix},
skybox::{create_mesh as create_skybox_mesh, Vertex as SkyboxVertex},
sprite::{
@ -122,6 +123,10 @@ pub enum CloudMode {
High,
}
impl CloudMode {
pub fn is_enabled(&self) -> bool { *self != CloudMode::None }
}
impl Default for CloudMode {
fn default() -> Self { CloudMode::High }
}
@ -398,7 +403,7 @@ impl RenderMode {
#[derive(PartialEq, Clone, Debug)]
pub struct PipelineModes {
aa: AaMode,
cloud: CloudMode,
pub cloud: CloudMode,
fluid: FluidMode,
lighting: LightingMode,
pub shadow: ShadowMode,

View File

@ -153,7 +153,11 @@ impl CloudsPipeline {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Clouds pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &layout.layout],
bind_group_layouts: &[
&global_layout.globals,
&global_layout.shadow_textures,
&layout.layout,
],
});
let samples = match aa_mode {

View File

@ -8,6 +8,7 @@ pub mod lod_object;
pub mod lod_terrain;
pub mod particle;
pub mod postprocess;
pub mod rain_occlusion;
pub mod shadow;
pub mod skybox;
pub mod sprite;
@ -254,6 +255,7 @@ pub struct GlobalModel {
pub lights: Consts<Light>,
pub shadows: Consts<Shadow>,
pub shadow_mats: shadow::BoundLocals,
pub rain_occlusion_mats: rain_occlusion::BoundLocals,
pub point_light_matrices: Box<[shadow::PointLightMatrix; 126]>,
}
@ -425,6 +427,18 @@ impl GlobalsLayouts {
},
count: None,
},
// rain occlusion
wgpu::BindGroupLayoutEntry {
binding: 14,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
// TODO: is this relevant?
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
]
}
@ -503,6 +517,26 @@ impl GlobalsLayouts {
},
count: None,
},
// Rain occlusion maps
wgpu::BindGroupLayoutEntry {
binding: 4,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Depth,
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 5,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: true,
},
count: None,
},
],
});
@ -584,6 +618,11 @@ impl GlobalsLayouts {
binding: 13,
resource: wgpu::BindingResource::Sampler(&lod_data.weather.sampler),
},
// rain occlusion
wgpu::BindGroupEntry {
binding: 14,
resource: global_model.rain_occlusion_mats.buf().as_entire_binding(),
},
]
}
@ -608,6 +647,7 @@ impl GlobalsLayouts {
device: &wgpu::Device,
point_shadow_map: &Texture,
directed_shadow_map: &Texture,
rain_occlusion_map: &Texture,
) -> ShadowTexturesBindGroup {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
@ -629,6 +669,14 @@ impl GlobalsLayouts {
binding: 3,
resource: wgpu::BindingResource::Sampler(&directed_shadow_map.sampler),
},
wgpu::BindGroupEntry {
binding: 4,
resource: wgpu::BindingResource::TextureView(&rain_occlusion_map.view),
},
wgpu::BindGroupEntry {
binding: 5,
resource: wgpu::BindingResource::Sampler(&rain_occlusion_map.sampler),
},
],
});

View File

@ -0,0 +1,213 @@
use super::super::{
AaMode, Bound, Consts, FigureLayout, GlobalsLayouts, TerrainLayout, TerrainVertex,
};
use bytemuck::{Pod, Zeroable};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
shadow_matrices: [[f32; 4]; 4],
texture_mats: [[f32; 4]; 4],
}
impl Locals {
pub fn new(shadow_mat: Mat4<f32>, texture_mat: Mat4<f32>) -> Self {
Self {
shadow_matrices: shadow_mat.into_col_arrays(),
texture_mats: texture_mat.into_col_arrays(),
}
}
pub fn default() -> Self { Self::new(Mat4::identity(), Mat4::identity()) }
}
pub type BoundLocals = Bound<Consts<Locals>>;
pub struct RainOcclusionLayout {
pub locals: wgpu::BindGroupLayout,
}
impl RainOcclusionLayout {
pub fn new(device: &wgpu::Device) -> Self {
Self {
locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
}),
}
}
pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts<Locals>) -> BoundLocals {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.locals,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: locals.buf().as_entire_binding(),
}],
});
BoundLocals {
bind_group,
with: locals,
}
}
}
pub struct RainOcclusionFigurePipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl RainOcclusionFigurePipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
figure_layout: &FigureLayout,
aa_mode: AaMode,
) -> Self {
common_base::span!(_guard, "new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Directed figure shadow pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &figure_layout.locals],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Directed shadow figure pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[TerrainVertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
clamp_depth: true,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24Plus,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: None,
});
Self {
pipeline: render_pipeline,
}
}
}
pub struct RainOcclusionPipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl RainOcclusionPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
terrain_layout: &TerrainLayout,
aa_mode: AaMode,
) -> Self {
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Rain occlusion pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &terrain_layout.locals],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Rain occlusion pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[TerrainVertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Front),
clamp_depth: true,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24Plus,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: None,
});
Self {
pipeline: render_pipeline,
}
}
}

View File

@ -176,11 +176,11 @@ pub struct SpriteLayout {
impl SpriteLayout {
pub fn new(device: &wgpu::Device) -> Self {
let mut entries = GlobalsLayouts::base_globals_layout();
debug_assert_eq!(14, entries.len()); // To remember to adjust the bindings below
debug_assert_eq!(15, entries.len()); // To remember to adjust the bindings below
entries.extend_from_slice(&[
// sprite_verts
wgpu::BindGroupLayoutEntry {
binding: 14,
binding: 15,
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
@ -214,7 +214,7 @@ impl SpriteLayout {
entries.extend_from_slice(&[
// sprite_verts
wgpu::BindGroupEntry {
binding: 14,
binding: 15,
resource: sprite_verts.0.buf.as_entire_binding(),
},
]);

View File

@ -3,6 +3,7 @@ pub(super) mod drawer;
// Consts and bind groups for post-process and clouds
mod locals;
mod pipeline_creation;
mod rain_occlusion_map;
mod screenshot;
mod shaders;
mod shadow_map;
@ -14,6 +15,8 @@ use pipeline_creation::{
use shaders::Shaders;
use shadow_map::{ShadowMap, ShadowMapRenderer};
use self::{pipeline_creation::RainOcclusionPipelines, rain_occlusion_map::RainOcclusionMap};
use super::{
buffer::Buffer,
consts::Consts,
@ -21,8 +24,8 @@ use super::{
mesh::Mesh,
model::{DynamicModel, Model},
pipelines::{
blit, bloom, clouds, debug, figure, postprocess, shadow, sprite, terrain, ui,
GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup,
blit, bloom, clouds, debug, figure, postprocess, rain_occlusion, shadow, sprite, terrain,
ui, GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup,
},
texture::Texture,
AaMode, AddressMode, FilterMode, OtherModes, PipelineModes, RenderError, RenderMode,
@ -54,6 +57,7 @@ struct ImmutableLayouts {
debug: debug::DebugLayout,
figure: figure::FigureLayout,
shadow: shadow::ShadowLayout,
rain_occlusion: rain_occlusion::RainOcclusionLayout,
sprite: sprite::SpriteLayout,
terrain: terrain::TerrainLayout,
clouds: clouds::CloudsLayout,
@ -90,6 +94,7 @@ struct Views {
/// Shadow rendering textures, layouts, pipelines, and bind groups
struct Shadow {
rain_map: RainOcclusionMap,
map: ShadowMap,
bind: ShadowTexturesBindGroup,
}
@ -104,6 +109,7 @@ enum State {
Interface {
pipelines: InterfacePipelines,
shadow_views: Option<(Texture, Texture)>,
rain_occlusion_view: Option<Texture>,
// In progress creation of the remaining pipelines in the background
creating: PipelineCreation<IngameAndShadowPipelines>,
},
@ -117,6 +123,7 @@ enum State {
(
Pipelines,
ShadowPipelines,
RainOcclusionPipelines,
Arc<postprocess::PostProcessLayout>,
),
RenderError,
@ -359,6 +366,16 @@ impl Renderer {
})
.ok();
let rain_occlusion_view = RainOcclusionMap::create_view(
&device,
(dims.width, dims.height),
&ShadowMapMode::try_from(pipeline_modes.shadow).unwrap_or_default(),
)
.map_err(|err| {
warn!("Could not create rain occlusion map views: {:?}", err);
})
.ok();
let shaders = Shaders::load_expect("");
let shaders_watcher = shaders.reload_watcher();
@ -368,6 +385,7 @@ impl Renderer {
let debug = debug::DebugLayout::new(&device);
let figure = figure::FigureLayout::new(&device);
let shadow = shadow::ShadowLayout::new(&device);
let rain_occlusion = rain_occlusion::RainOcclusionLayout::new(&device);
let sprite = sprite::SpriteLayout::new(&device);
let terrain = terrain::TerrainLayout::new(&device);
let clouds = clouds::CloudsLayout::new(&device);
@ -385,6 +403,7 @@ impl Renderer {
debug,
figure,
shadow,
rain_occlusion,
sprite,
terrain,
clouds,
@ -417,6 +436,7 @@ impl Renderer {
let state = State::Interface {
pipelines: interface_pipelines,
shadow_views,
rain_occlusion_view,
creating,
};
@ -680,34 +700,49 @@ impl Renderer {
// Get mutable reference to shadow views out of the current state
let shadow_views = match &mut self.state {
State::Interface { shadow_views, .. } => {
shadow_views.as_mut().map(|s| (&mut s.0, &mut s.1))
},
State::Interface {
shadow_views,
rain_occlusion_view,
..
} => shadow_views
.as_mut()
.map(|s| (&mut s.0, &mut s.1))
.zip(rain_occlusion_view.as_mut()),
State::Complete {
shadow:
Shadow {
map: ShadowMap::Enabled(shadow_map),
rain_map: RainOcclusionMap::Enabled(rain_occlusion_map),
..
},
..
} => Some((&mut shadow_map.point_depth, &mut shadow_map.directed_depth)),
} => Some((
(&mut shadow_map.point_depth, &mut shadow_map.directed_depth),
&mut rain_occlusion_map.depth,
)),
State::Complete { .. } => None,
State::Nothing => None, // Should never hit this
};
if let (Some((point_depth, directed_depth)), ShadowMode::Map(mode)) =
if let (Some(((point_depth, directed_depth), rain_depth)), ShadowMode::Map(mode)) =
(shadow_views, self.pipeline_modes.shadow)
{
match ShadowMap::create_shadow_views(&self.device, (dims.x, dims.y), &mode) {
Ok((new_point_depth, new_directed_depth)) => {
match (
ShadowMap::create_shadow_views(&self.device, (dims.x, dims.y), &mode),
RainOcclusionMap::create_view(&self.device, (dims.x, dims.y), &mode),
) {
(Ok((new_point_depth, new_directed_depth)), Ok(new_rain_depth)) => {
*point_depth = new_point_depth;
*directed_depth = new_directed_depth;
*rain_depth = new_rain_depth;
// Recreate the shadow bind group if needed
if let State::Complete {
shadow:
Shadow {
bind,
map: ShadowMap::Enabled(shadow_map),
rain_map: RainOcclusionMap::Enabled(rain_occlusion_map),
..
},
..
@ -717,11 +752,17 @@ impl Renderer {
&self.device,
&shadow_map.point_depth,
&shadow_map.directed_depth,
&rain_occlusion_map.depth,
);
}
},
Err(err) => {
warn!("Could not create shadow map views: {:?}", err);
(shadow, rain) => {
if let Err(err) = shadow {
warn!("Could not create shadow map views: {:?}", err);
}
if let Err(err) = rain {
warn!("Could not create rain occlusion map view: {:?}", err);
}
},
}
}
@ -951,12 +992,17 @@ impl Renderer {
self.state = if let State::Interface {
pipelines: interface,
shadow_views,
rain_occlusion_view,
creating,
} = state
{
match creating.try_complete() {
Ok(pipelines) => {
let IngameAndShadowPipelines { ingame, shadow } = pipelines;
let IngameAndShadowPipelines {
ingame,
shadow,
rain_occlusion,
} = pipelines;
let pipelines = Pipelines::consolidate(interface, ingame);
@ -969,14 +1015,26 @@ impl Renderer {
shadow_views,
);
let rain_occlusion_map = RainOcclusionMap::new(
&self.device,
&self.queue,
rain_occlusion.terrain,
rain_occlusion.figure,
rain_occlusion_view,
);
let shadow_bind = {
let (point, directed) = shadow_map.textures();
self.layouts
.global
.bind_shadow_textures(&self.device, point, directed)
self.layouts.global.bind_shadow_textures(
&self.device,
point,
directed,
rain_occlusion_map.texture(),
)
};
let shadow = Shadow {
rain_map: rain_occlusion_map,
map: shadow_map,
bind: shadow_bind,
};
@ -991,6 +1049,7 @@ impl Renderer {
Err(creating) => State::Interface {
pipelines: interface,
shadow_views,
rain_occlusion_view,
creating,
},
}
@ -1002,7 +1061,12 @@ impl Renderer {
} = state
{
match pipeline_creation.try_complete() {
Ok(Ok((pipelines, shadow_pipelines, postprocess_layout))) => {
Ok(Ok((
pipelines,
shadow_pipelines,
rain_occlusion_pipelines,
postprocess_layout,
))) => {
if let (
Some(point_pipeline),
Some(terrain_directed_pipeline),
@ -1019,6 +1083,19 @@ impl Renderer {
shadow_map.figure_directed_pipeline = figure_directed_pipeline;
}
if let (
Some(terrain_directed_pipeline),
Some(figure_directed_pipeline),
RainOcclusionMap::Enabled(rain_occlusion_map),
) = (
rain_occlusion_pipelines.terrain,
rain_occlusion_pipelines.figure,
&mut shadow.rain_map,
) {
rain_occlusion_map.terrain_pipeline = terrain_directed_pipeline;
rain_occlusion_map.figure_pipeline = figure_directed_pipeline;
}
self.pipeline_modes = new_pipeline_modes;
self.layouts.postprocess = postprocess_layout;
// TODO: we have the potential to skip recreating bindings / render targets on

View File

@ -1,3 +1,5 @@
use crate::render::pipelines::rain_occlusion;
use super::{
super::{
pipelines::{
@ -74,6 +76,16 @@ impl Renderer {
self.layouts.shadow.bind_locals(&self.device, locals)
}
pub fn create_rain_occlusion_bound_locals(
&mut self,
locals: &[rain_occlusion::Locals],
) -> rain_occlusion::BoundLocals {
let locals = self.create_consts(locals);
self.layouts
.rain_occlusion
.bind_locals(&self.device, locals)
}
pub fn figure_bind_col_light(&self, col_light: Texture) -> ColLights<figure::Locals> {
self.layouts.global.bind_col_light(&self.device, col_light)
}

View File

@ -9,6 +9,7 @@ use super::{
ShadowTexturesBindGroup,
},
},
rain_occlusion_map::{RainOcclusionMap, RainOcclusionMapRenderer},
Renderer, ShadowMap, ShadowMapRenderer,
};
use core::{num::NonZeroU32, ops::Range};
@ -135,6 +136,46 @@ impl<'frame> Drawer<'frame> {
/// Get the pipeline modes.
pub fn pipeline_modes(&self) -> &super::super::PipelineModes { self.borrow.pipeline_modes }
/// Returns None if the shadow renderer is not enabled at some level or the
/// pipelines are not available yet
pub fn rain_occlusion_pass(&mut self) -> Option<RainOcclusionPassDrawer> {
if !self.borrow.pipeline_modes.cloud.is_enabled() {
return None;
}
if let RainOcclusionMap::Enabled(ref rain_occlusion_renderer) = self.borrow.shadow?.rain_map
{
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut render_pass = encoder.scoped_render_pass(
"rain_occlusion_pass",
device,
&wgpu::RenderPassDescriptor {
label: Some("rain occlusion pass"),
color_attachments: &[],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &rain_occlusion_renderer.depth.view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
},
);
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
Some(RainOcclusionPassDrawer {
render_pass,
borrow: &self.borrow,
rain_occlusion_renderer,
})
} else {
None
}
}
/// Returns None if the shadow renderer is not enabled at some level or the
/// pipelines are not available yet
pub fn shadow_pass(&mut self) -> Option<ShadowPassDrawer> {
@ -216,6 +257,8 @@ impl<'frame> Drawer<'frame> {
/// Returns None if the clouds pipeline is not available
pub fn second_pass(&mut self) -> Option<SecondPassDrawer> {
let pipelines = &self.borrow.pipelines.all()?;
let shadow = self.borrow.shadow?;
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut render_pass =
@ -237,6 +280,7 @@ impl<'frame> Drawer<'frame> {
});
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
render_pass.set_bind_group(1, &shadow.bind.bind_group, &[]);
Some(SecondPassDrawer {
render_pass,
@ -639,6 +683,37 @@ impl<'pass> ShadowPassDrawer<'pass> {
}
}
#[must_use]
pub struct RainOcclusionPassDrawer<'pass> {
render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
borrow: &'pass RendererBorrow<'pass>,
rain_occlusion_renderer: &'pass RainOcclusionMapRenderer,
}
impl<'pass> RainOcclusionPassDrawer<'pass> {
pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> {
let mut render_pass = self
.render_pass
.scope("direcred_figure_rain_occlusion", self.borrow.device);
render_pass.set_pipeline(&self.rain_occlusion_renderer.figure_pipeline.pipeline);
set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);
FigureShadowDrawer { render_pass }
}
pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> {
let mut render_pass = self
.render_pass
.scope("direcred_terrain_rain_occlusion", self.borrow.device);
render_pass.set_pipeline(&self.rain_occlusion_renderer.terrain_pipeline.pipeline);
set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, self.borrow);
TerrainShadowDrawer { render_pass }
}
}
#[must_use]
pub struct FigureShadowDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
@ -970,7 +1045,7 @@ impl<'pass> SecondPassDrawer<'pass> {
self.render_pass
.set_pipeline(&self.clouds_pipeline.pipeline);
self.render_pass
.set_bind_group(1, &self.borrow.locals.clouds_bind.bind_group, &[]);
.set_bind_group(2, &self.borrow.locals.clouds_bind.bind_group, &[]);
self.render_pass.draw(0..3, 0..1);
}

View File

@ -1,3 +1,5 @@
use crate::render::pipelines::rain_occlusion;
use super::{
super::{
pipelines::{
@ -60,9 +62,16 @@ pub struct ShadowPipelines {
pub figure: Option<shadow::ShadowFigurePipeline>,
}
pub struct RainOcclusionPipelines {
pub terrain: Option<rain_occlusion::RainOcclusionPipeline>,
pub figure: Option<rain_occlusion::RainOcclusionFigurePipeline>,
}
// TODO: Find a better name for this?
pub struct IngameAndShadowPipelines {
pub ingame: IngamePipelines,
pub shadow: ShadowPipelines,
pub rain_occlusion: RainOcclusionPipelines,
}
/// Pipelines neccesary to display the UI and take screenshots
@ -131,6 +140,8 @@ struct ShaderModules {
point_light_shadows_vert: wgpu::ShaderModule,
light_shadows_directed_vert: wgpu::ShaderModule,
light_shadows_figure_vert: wgpu::ShaderModule,
rain_occlusion_directed_vert: wgpu::ShaderModule,
rain_occlusion_figure_vert: wgpu::ShaderModule,
}
impl ShaderModules {
@ -151,6 +162,7 @@ impl ShaderModules {
let random = shaders.get("include.random").unwrap();
let lod = shaders.get("include.lod").unwrap();
let shadows = shaders.get("include.shadows").unwrap();
let rain_occlusion = shaders.get("include.rain_occlusion").unwrap();
let point_glow = shaders.get("include.point_glow").unwrap();
// We dynamically add extra configuration settings to the constants file.
@ -252,6 +264,7 @@ impl ShaderModules {
"constants.glsl" => constants.clone(),
"globals.glsl" => globals.0.to_owned(),
"shadows.glsl" => shadows.0.to_owned(),
"rain_occlusion.glsl" => rain_occlusion.0.to_owned(),
"sky.glsl" => sky.0.to_owned(),
"light.glsl" => light.0.to_owned(),
"srgb.glsl" => srgb.0.to_owned(),
@ -332,6 +345,14 @@ impl ShaderModules {
"light-shadows-figure-vert",
ShaderKind::Vertex,
)?,
rain_occlusion_directed_vert: create_shader(
"rain-occlusion-directed-vert",
ShaderKind::Vertex,
)?,
rain_occlusion_figure_vert: create_shader(
"rain-occlusion-figure-vert",
ShaderKind::Vertex,
)?,
})
}
}
@ -422,7 +443,7 @@ fn create_ingame_and_shadow_pipelines(
needs: PipelineNeeds,
pool: &rayon::ThreadPool,
// TODO: Reduce the boilerplate in this file
tasks: [Task; 16],
tasks: [Task; 18],
) -> IngameAndShadowPipelines {
prof_span!(_guard, "create_ingame_and_shadow_pipelines");
@ -454,6 +475,8 @@ fn create_ingame_and_shadow_pipelines(
point_shadow_task,
terrain_directed_shadow_task,
figure_directed_shadow_task,
terrain_directed_rain_occlusion_task,
figure_directed_rain_occlusion_task,
] = tasks;
// TODO: pass in format of target color buffer
@ -739,6 +762,36 @@ fn create_ingame_and_shadow_pipelines(
"figure directed shadow pipeline creation",
)
};
// Pipeline for rendering directional light terrain rain occlusion maps.
let create_terrain_directed_rain_occlusion = || {
terrain_directed_rain_occlusion_task.run(
|| {
rain_occlusion::RainOcclusionPipeline::new(
device,
&shaders.rain_occlusion_directed_vert,
&layouts.global,
&layouts.terrain,
pipeline_modes.aa,
)
},
"terrain directed rain occlusion pipeline creation",
)
};
// Pipeline for rendering directional light figure rain occlusion maps.
let create_figure_directed_rain_occlusion = || {
figure_directed_rain_occlusion_task.run(
|| {
rain_occlusion::RainOcclusionFigurePipeline::new(
device,
&shaders.rain_occlusion_figure_vert,
&layouts.global,
&layouts.figure,
pipeline_modes.aa,
)
},
"figure directed rain occlusion pipeline creation",
)
};
let j1 = || pool.join(create_debug, || pool.join(create_skybox, create_figure));
let j2 = || pool.join(create_terrain, || pool.join(create_fluid, create_bloom));
@ -755,7 +808,14 @@ fn create_ingame_and_shadow_pipelines(
create_figure_directed_shadow,
)
};
let j7 = create_lod_object;
let j7 = || {
pool.join(create_lod_object, || {
pool.join(
create_terrain_directed_rain_occlusion,
create_figure_directed_rain_occlusion,
)
})
};
// Ignore this
let (
@ -765,7 +825,7 @@ fn create_ingame_and_shadow_pipelines(
),
(
((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)),
lod_object,
(lod_object, (terrain_directed_rain_occlusion, figure_directed_rain_occlusion)),
),
) = pool.join(
|| pool.join(|| pool.join(j1, j2), || pool.join(j3, j4)),
@ -795,6 +855,10 @@ fn create_ingame_and_shadow_pipelines(
directed: Some(terrain_directed_shadow),
figure: Some(figure_directed_shadow),
},
rain_occlusion: RainOcclusionPipelines {
terrain: Some(terrain_directed_rain_occlusion),
figure: Some(figure_directed_rain_occlusion),
},
}
}
@ -887,6 +951,7 @@ pub(super) fn recreate_pipelines(
(
Pipelines,
ShadowPipelines,
RainOcclusionPipelines,
Arc<postprocess::PostProcessLayout>,
),
RenderError,
@ -952,14 +1017,18 @@ pub(super) fn recreate_pipelines(
let interface = create_interface_pipelines(needs, pool, interface_tasks);
// Create the rest of the pipelines
let IngameAndShadowPipelines { ingame, shadow } =
create_ingame_and_shadow_pipelines(needs, pool, ingame_and_shadow_tasks);
let IngameAndShadowPipelines {
ingame,
shadow,
rain_occlusion,
} = create_ingame_and_shadow_pipelines(needs, pool, ingame_and_shadow_tasks);
// Send them
result_send
.send(Ok((
Pipelines::consolidate(interface, ingame),
shadow,
rain_occlusion,
layouts.postprocess,
)))
.expect("Channel disconnected");

View File

@ -0,0 +1,226 @@
use crate::render::pipelines::rain_occlusion;
use super::{
super::{texture::Texture, RenderError, ShadowMapMode},
Renderer,
};
use vek::*;
/// A type that holds shadow map data. Since shadow mapping may not be
/// supported on all platforms, we try to keep it separate.
pub struct RainOcclusionMapRenderer {
pub depth: Texture,
pub terrain_pipeline: rain_occlusion::RainOcclusionPipeline,
pub figure_pipeline: rain_occlusion::RainOcclusionFigurePipeline,
pub layout: rain_occlusion::RainOcclusionLayout,
}
pub enum RainOcclusionMap {
Enabled(RainOcclusionMapRenderer),
/// Dummy texture
Disabled(Texture),
}
impl RainOcclusionMap {
pub fn new(
device: &wgpu::Device,
queue: &wgpu::Queue,
directed: Option<rain_occlusion::RainOcclusionPipeline>,
figure: Option<rain_occlusion::RainOcclusionFigurePipeline>,
view: Option<Texture>,
) -> Self {
if let (Some(terrain_pipeline), Some(figure_pipeline), Some(depth)) =
(directed, figure, view)
{
let layout = rain_occlusion::RainOcclusionLayout::new(device);
Self::Enabled(RainOcclusionMapRenderer {
depth,
terrain_pipeline,
figure_pipeline,
layout,
})
} else {
Self::Disabled(Self::create_dummy_tex(device, queue))
}
}
fn create_dummy_tex(device: &wgpu::Device, queue: &wgpu::Queue) -> Texture {
let tex = {
let tex = wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: 4,
height: 4,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth24Plus,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT,
};
let view = wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Depth24Plus),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::DepthOnly,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
};
let sampler_info = wgpu::SamplerDescriptor {
label: None,
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Nearest,
compare: Some(wgpu::CompareFunction::LessEqual),
..Default::default()
};
Texture::new_raw(device, &tex, &view, &sampler_info)
};
// Clear to 1.0
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Dummy rain occlusion tex clearing encoder"),
});
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Clear dummy rain occlusion texture"),
color_attachments: &[],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &tex.view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
});
queue.submit(std::iter::once(encoder.finish()));
tex
}
/// Create texture and view for rain ocllusion maps.
/// Returns (point, directed)
pub(super) fn create_view(
device: &wgpu::Device,
size: (u32, u32),
mode: &ShadowMapMode,
) -> Result<Texture, RenderError> {
// (Attempt to) apply resolution factor to rain occlusion map resolution.
let resolution_factor = mode.resolution.clamped(0.25, 4.0);
let max_texture_size = Renderer::max_texture_size_raw(device);
// Limit to max texture size, rather than erroring.
let size = Vec2::new(size.0, size.1).map(|e| {
let size = e as f32 * resolution_factor;
// NOTE: We know 0 <= e since we clamped the resolution factor to be between
// 0.25 and 4.0.
if size <= max_texture_size as f32 {
size as u32
} else {
max_texture_size
}
});
let levels = 1;
// Limit to max texture size rather than erroring.
let two_size = size.map(|e| {
u32::checked_next_power_of_two(e)
.filter(|&e| e <= max_texture_size)
.unwrap_or(max_texture_size)
});
let min_size = size.reduce_min();
let max_size = size.reduce_max();
let _min_two_size = two_size.reduce_min();
let _max_two_size = two_size.reduce_max();
// For rotated shadow maps, the maximum size of a pixel along any axis is the
// size of a diagonal along that axis.
let diag_size = size.map(f64::from).magnitude();
let diag_cross_size = f64::from(min_size) / f64::from(max_size) * diag_size;
let (diag_size, _diag_cross_size) =
if 0.0 < diag_size && diag_size <= f64::from(max_texture_size) {
// NOTE: diag_cross_size must be non-negative, since it is the ratio of a
// non-negative and a positive number (if max_size were zero,
// diag_size would be 0 too). And it must be <= diag_size,
// since min_size <= max_size. Therefore, if diag_size fits in a
// u16, so does diag_cross_size.
(diag_size as u32, diag_cross_size as u32)
} else {
// Limit to max texture resolution rather than error.
(max_texture_size as u32, max_texture_size as u32)
};
let diag_two_size = u32::checked_next_power_of_two(diag_size)
.filter(|&e| e <= max_texture_size)
// Limit to max texture resolution rather than error.
.unwrap_or(max_texture_size)
// Make sure we don't try to create a zero sized texture (divided by 4 below)
.max(4);
let rain_occlusion_tex = wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: diag_two_size,
height: diag_two_size,
depth_or_array_layers: 1,
},
mip_level_count: levels,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth24Plus,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::RENDER_ATTACHMENT,
};
let rain_occlusion_view = wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Depth24Plus),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::DepthOnly,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
};
let sampler_info = wgpu::SamplerDescriptor {
label: None,
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Nearest,
compare: Some(wgpu::CompareFunction::LessEqual),
..Default::default()
};
let rain_occlusion_tex = Texture::new_raw(
device,
&rain_occlusion_tex,
&rain_occlusion_view,
&sampler_info,
);
Ok(rain_occlusion_tex)
}
pub fn texture(&self) -> &Texture {
match self {
Self::Enabled(renderer) => &renderer.depth,
Self::Disabled(dummy) => dummy,
}
}
pub fn is_enabled(&self) -> bool { matches!(self, Self::Enabled(_)) }
}

View File

@ -34,6 +34,7 @@ impl assets::Compound for Shaders {
"include.random",
"include.lod",
"include.shadows",
"include.rain_occlusion",
"include.point_glow",
"antialias.none",
"antialias.fxaa",
@ -45,6 +46,8 @@ impl assets::Compound for Shaders {
"figure-vert",
"light-shadows-figure-vert",
"light-shadows-directed-vert",
"rain-occlusion-figure-vert",
"rain-occlusion-directed-vert",
"point-light-shadows-vert",
"skybox-vert",
"skybox-frag",

View File

@ -22,8 +22,8 @@ use crate::{
audio::{ambient, ambient::AmbientMgr, music::MusicMgr, sfx::SfxMgr, AudioFrontend},
render::{
create_skybox_mesh, CloudsLocals, Consts, Drawer, GlobalModel, Globals, GlobalsBindGroup,
Light, Model, PointLightMatrix, PostProcessLocals, Renderer, Shadow, ShadowLocals,
SkyboxVertex,
Light, Model, PointLightMatrix, PostProcessLocals, RainOcclusionLocals, Renderer, Shadow,
ShadowLocals, SkyboxVertex,
},
settings::Settings,
window::{AnalogGameInput, Event},
@ -282,6 +282,8 @@ impl Scene {
lights: renderer.create_consts(&[Light::default(); MAX_LIGHT_COUNT]),
shadows: renderer.create_consts(&[Shadow::default(); MAX_SHADOW_COUNT]),
shadow_mats: renderer.create_shadow_bound_locals(&[ShadowLocals::default()]),
rain_occlusion_mats: renderer
.create_rain_occlusion_bound_locals(&[RainOcclusionLocals::default()]),
point_light_matrices: Box::new([PointLightMatrix::default(); MAX_LIGHT_COUNT * 6 + 6]),
};
@ -1010,6 +1012,13 @@ impl Scene {
);
renderer.update_consts(&mut self.data.shadow_mats, &[shadow_locals]);
let rain_occlusion_locals = RainOcclusionLocals::new(
directed_proj_mat * shadow_all_mat,
directed_texture_proj_mat * shadow_all_mat,
);
renderer
.update_consts(&mut self.data.rain_occlusion_mats, &[rain_occlusion_locals]);
}
directed_shadow_mats.push(light_view_mat);
// This leaves us with five dummy slots, which we push as defaults.
@ -1139,6 +1148,21 @@ impl Scene {
self.terrain.chunks_for_point_shadows(focus_pos),
)
}
// Render rain occlusion texture
{
prof_span!("rain occlusion");
if let Some(mut occlusion_pass) = drawer.rain_occlusion_pass() {
self.terrain
.render_shadows(&mut occlusion_pass.draw_terrain_shadows(), focus_pos);
self.figure_mgr.render_shadows(
&mut occlusion_pass.draw_figure_shadows(),
state,
tick,
camera_data,
);
}
}
}
prof_span!(guard, "main pass");

View File

@ -2,8 +2,8 @@ use crate::{
mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_terrain},
render::{
create_skybox_mesh, BoneMeshes, Consts, FigureModel, FirstPassDrawer, GlobalModel, Globals,
GlobalsBindGroup, Light, LodData, Mesh, Model, PointLightMatrix, Renderer, Shadow,
ShadowLocals, SkyboxVertex, TerrainVertex,
GlobalsBindGroup, Light, LodData, Mesh, Model, PointLightMatrix, RainOcclusionLocals,
Renderer, Shadow, ShadowLocals, SkyboxVertex, TerrainVertex,
},
scene::{
camera::{self, Camera, CameraMode},
@ -113,6 +113,8 @@ impl Scene {
lights: renderer.create_consts(&[Light::default(); 20]),
shadows: renderer.create_consts(&[Shadow::default(); 24]),
shadow_mats: renderer.create_shadow_bound_locals(&[ShadowLocals::default()]),
rain_occlusion_mats: renderer
.create_rain_occlusion_bound_locals(&[RainOcclusionLocals::default()]),
point_light_matrices: Box::new([PointLightMatrix::default(); 126]),
};
let lod = LodData::dummy(renderer);