Added entity shadows

This commit is contained in:
Joshua Barretto 2019-09-25 13:00:00 +01:00
parent 50daf185a8
commit 499344ccab
20 changed files with 174 additions and 42 deletions

View File

@ -30,6 +30,9 @@ out vec4 tgt_color;
void main() {
vec3 light, diffuse_light, ambient_light;
get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light);
float point_shadow = shadow_at(f_pos, f_norm);
diffuse_light *= point_shadow;
ambient_light *= point_shadow;
vec3 point_light = light_at(f_pos, f_norm);
light += point_light;
diffuse_light += point_light;

View File

@ -40,8 +40,9 @@ void main() {
vec3 light, diffuse_light, ambient_light;
get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light);
diffuse_light *= f_light;
ambient_light *= f_light;
float point_shadow = shadow_at(f_pos, f_norm);
diffuse_light *= f_light * point_shadow;
ambient_light *= f_light, point_shadow;
vec3 point_light = light_at(f_pos, f_norm);
light += point_light;
diffuse_light += point_light;

View File

@ -8,6 +8,6 @@ uniform u_globals {
vec4 time_of_day;
vec4 tick;
vec4 screen_res;
uvec4 light_count;
uvec4 light_shadow_count;
uvec4 medium;
};

View File

@ -8,6 +8,15 @@ uniform u_lights {
Light lights[32];
};
struct Shadow {
vec4 shadow_pos_radius;
};
layout (std140)
uniform u_shadows {
Shadow shadows[24];
};
#include <srgb.glsl>
vec3 illuminate(vec3 color, vec3 light, vec3 diffuse, vec3 ambience) {
@ -24,7 +33,7 @@ vec3 light_at(vec3 wpos, vec3 wnorm) {
vec3 light = vec3(0);
for (uint i = 0u; i < light_count.x; i++) {
for (uint i = 0u; i < light_shadow_count.x; i ++) {
// Only access the array once
Light L = lights[i];
@ -39,16 +48,30 @@ vec3 light_at(vec3 wpos, vec3 wnorm) {
// Multiply the vec3 only once
vec3 color = srgb_to_linear(L.light_col.rgb) * (strength * L.light_col.a);
// This is commented out to avoid conditional branching. See here: https://community.khronos.org/t/glsl-float-multiply-by-zero/104391
// if (max(max(color.r, color.g), color.b) < 0.002) {
// continue;
// }
// Old: light += color * clamp(dot(normalize(difference), wnorm), LIGHT_AMBIENCE, 1.0);
// The dot product cannot be greater than one, so no need to clamp max value
// Also, rather than checking if it is smaller than LIGHT_AMBIENCE, add LIGHT_AMBIENCE instead
light += color * (max(0, dot(normalize(difference), wnorm)) + LIGHT_AMBIENCE);
}
return light;
}
float shadow_at(vec3 wpos, vec3 wnorm) {
float shadow = 1.0;
for (uint i = 0u; i < light_shadow_count.y; i ++) {
// Only access the array once
Shadow S = shadows[i];
vec3 shadow_pos = S.shadow_pos_radius.xyz;
float radius = S.shadow_pos_radius.w;
vec3 diff = shadow_pos - wpos;
if (diff.z >= 0.0) {
diff.z = diff.z * 0.1;
}
float shade = max(pow(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z, 0.25) / pow(radius * radius * 0.5, 0.25), 0.5);
shadow = min(shadow, shade);
}
return min(shadow, 1.0);
}

View File

@ -18,8 +18,9 @@ const float FADE_DIST = 32.0;
void main() {
vec3 light, diffuse_light, ambient_light;
get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light);
diffuse_light *= f_light;
ambient_light *= f_light;
float point_shadow = shadow_at(f_pos, f_norm);
diffuse_light *= f_light * point_shadow;
ambient_light *= f_light, point_shadow;
vec3 point_light = light_at(f_pos, f_norm);
light += point_light;
diffuse_light += point_light;

View File

@ -21,8 +21,9 @@ out vec4 tgt_color;
void main() {
vec3 light, diffuse_light, ambient_light;
get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light);
diffuse_light *= f_light;
ambient_light *= f_light;
float point_shadow = shadow_at(f_pos, f_norm);
diffuse_light *= f_light * point_shadow;
ambient_light *= f_light * point_shadow;
vec3 point_light = light_at(f_pos, f_norm);
light += point_light;
diffuse_light += point_light;

View File

@ -6,7 +6,7 @@ use crate::{
},
render::{
create_pp_mesh, create_skybox_mesh, Consts, FigurePipeline, Globals, Light, Model,
PostProcessLocals, PostProcessPipeline, Renderer, SkyboxLocals, SkyboxPipeline,
PostProcessLocals, PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline,
},
scene::{
camera::{Camera, CameraMode},
@ -36,6 +36,7 @@ struct PostProcess {
pub struct Scene {
globals: Consts<Globals>,
lights: Consts<Light>,
shadows: Consts<Shadow>,
camera: Camera,
skybox: Skybox,
@ -57,6 +58,7 @@ impl Scene {
Self {
globals: renderer.create_consts(&[Globals::default()]).unwrap(),
lights: renderer.create_consts(&[Light::default(); 32]).unwrap(),
shadows: renderer.create_consts(&[Shadow::default(); 32]).unwrap(),
camera: Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson),
skybox: Skybox {
@ -134,6 +136,7 @@ impl Scene {
client.state().get_time(),
renderer.get_resolution(),
0,
0,
BlockKind::Air,
)],
) {
@ -193,6 +196,7 @@ impl Scene {
self.figure_state.locals(),
self.figure_state.bone_consts(),
&self.lights,
&self.shadows,
);
renderer.render_figure(
@ -201,6 +205,7 @@ impl Scene {
self.backdrop_state.locals(),
self.backdrop_state.bone_consts(),
&self.lights,
&self.shadows,
);
renderer.render_post_process(

View File

@ -26,7 +26,7 @@ pub use self::{
create_quad as create_ui_quad, create_tri as create_ui_tri, Locals as UiLocals,
Mode as UiMode, UiPipeline,
},
Globals, Light,
Globals, Light, Shadow,
},
renderer::{Renderer, TgtColorFmt, TgtDepthFmt, WinColorFmt, WinDepthFmt},
texture::Texture,

View File

@ -1,6 +1,6 @@
use super::{
super::{util::arr_to_mat, Pipeline, TgtColorFmt, TgtDepthFmt},
Globals, Light,
Globals, Light, Shadow,
};
use gfx::{
self,
@ -38,6 +38,7 @@ gfx_defines! {
globals: gfx::ConstantBuffer<Globals> = "u_globals",
bones: gfx::ConstantBuffer<BoneData> = "u_bones",
lights: gfx::ConstantBuffer<Light> = "u_lights",
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,

View File

@ -1,6 +1,6 @@
use super::{
super::{Pipeline, TerrainLocals, TgtColorFmt, TgtDepthFmt},
Globals, Light,
Globals, Light, Shadow,
};
use gfx::{
self,
@ -27,6 +27,7 @@ gfx_defines! {
locals: gfx::ConstantBuffer<TerrainLocals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
lights: gfx::ConstantBuffer<Light> = "u_lights",
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA),
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_TEST,

View File

@ -28,7 +28,7 @@ gfx_defines! {
time_of_day: [f32; 4] = "time_of_day", // TODO: Make this f64.
tick: [f32; 4] = "tick",
screen_res: [f32; 4] = "screen_res",
light_count: [u32; 4] = "light_count",
light_shadow_count: [u32; 4] = "light_shadow_count",
medium: [u32; 4] = "medium",
}
@ -36,6 +36,10 @@ gfx_defines! {
pos: [f32; 4] = "light_pos",
col: [f32; 4] = "light_col",
}
constant Shadow {
pos_radius: [f32; 4] = "shadow_pos_radius",
}
}
impl Globals {
@ -50,6 +54,7 @@ impl Globals {
tick: f64,
screen_res: Vec2<u16>,
light_count: usize,
shadow_count: usize,
medium: BlockKind,
) -> Self {
Self {
@ -61,7 +66,7 @@ impl Globals {
time_of_day: [time_of_day as f32; 4],
tick: [tick as f32; 4],
screen_res: Vec4::from(screen_res.map(|e| e as f32)).into_array(),
light_count: [light_count as u32; 4],
light_shadow_count: [light_count as u32, shadow_count as u32, 0, 0],
medium: [if medium.is_fluid() { 1 } else { 0 }; 4],
}
}
@ -79,6 +84,7 @@ impl Default for Globals {
0.0,
Vec2::new(800, 500),
0,
0,
BlockKind::Air,
)
}
@ -91,6 +97,10 @@ impl Light {
col: Rgba::new(col.r, col.g, col.b, strength).into_array(),
}
}
pub fn get_pos(&self) -> Vec3<f32> {
Vec3::new(self.pos[0], self.pos[1], self.pos[2])
}
}
impl Default for Light {
@ -98,3 +108,21 @@ impl Default for Light {
Self::new(Vec3::zero(), Rgb::zero(), 0.0)
}
}
impl Shadow {
pub fn new(pos: Vec3<f32>, radius: f32) -> Self {
Self {
pos_radius: [pos.x, pos.y, pos.z, radius],
}
}
pub fn get_pos(&self) -> Vec3<f32> {
Vec3::new(self.pos_radius[0], self.pos_radius[1], self.pos_radius[2])
}
}
impl Default for Shadow {
fn default() -> Self {
Self::new(Vec3::zero(), 0.0)
}
}

View File

@ -1,6 +1,6 @@
use super::{
super::{util::arr_to_mat, Pipeline, TgtColorFmt, TgtDepthFmt},
Globals, Light,
Globals, Light, Shadow,
};
use gfx::{
self,
@ -36,6 +36,7 @@ gfx_defines! {
globals: gfx::ConstantBuffer<Globals> = "u_globals",
lights: gfx::ConstantBuffer<Light> = "u_lights",
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA),
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,

View File

@ -1,6 +1,6 @@
use super::{
super::{Pipeline, TgtColorFmt, TgtDepthFmt},
Globals, Light,
Globals, Light, Shadow,
};
use gfx::{
self,
@ -32,6 +32,7 @@ gfx_defines! {
locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
lights: gfx::ConstantBuffer<Light> = "u_lights",
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,

View File

@ -4,7 +4,7 @@ use super::{
instances::Instances,
mesh::Mesh,
model::{DynamicModel, Model},
pipelines::{figure, fluid, postprocess, skybox, sprite, terrain, ui, Globals, Light},
pipelines::{figure, fluid, postprocess, skybox, sprite, terrain, ui, Globals, Light, Shadow},
texture::Texture,
Pipeline, RenderError,
};
@ -392,6 +392,7 @@ impl Renderer {
locals: &Consts<figure::Locals>,
bones: &Consts<figure::BoneData>,
lights: &Consts<Light>,
shadows: &Consts<Shadow>,
) {
self.encoder.draw(
&gfx::Slice {
@ -408,6 +409,7 @@ impl Renderer {
globals: globals.buf.clone(),
bones: bones.buf.clone(),
lights: lights.buf.clone(),
shadows: shadows.buf.clone(),
tgt_color: self.tgt_color_view.clone(),
tgt_depth: self.tgt_depth_view.clone(),
},
@ -421,6 +423,7 @@ impl Renderer {
globals: &Consts<Globals>,
locals: &Consts<terrain::Locals>,
lights: &Consts<Light>,
shadows: &Consts<Shadow>,
) {
self.encoder.draw(
&gfx::Slice {
@ -436,6 +439,7 @@ impl Renderer {
locals: locals.buf.clone(),
globals: globals.buf.clone(),
lights: lights.buf.clone(),
shadows: shadows.buf.clone(),
tgt_color: self.tgt_color_view.clone(),
tgt_depth: self.tgt_depth_view.clone(),
},
@ -449,6 +453,7 @@ impl Renderer {
globals: &Consts<Globals>,
locals: &Consts<terrain::Locals>,
lights: &Consts<Light>,
shadows: &Consts<Shadow>,
) {
self.encoder.draw(
&gfx::Slice {
@ -464,6 +469,7 @@ impl Renderer {
locals: locals.buf.clone(),
globals: globals.buf.clone(),
lights: lights.buf.clone(),
shadows: shadows.buf.clone(),
tgt_color: self.tgt_color_view.clone(),
tgt_depth: self.tgt_depth_view.clone(),
},
@ -477,6 +483,7 @@ impl Renderer {
globals: &Consts<Globals>,
instances: &Instances<sprite::Instance>,
lights: &Consts<Light>,
shadows: &Consts<Shadow>,
) {
self.encoder.draw(
&gfx::Slice {
@ -492,6 +499,7 @@ impl Renderer {
ibuf: instances.ibuf.clone(),
globals: globals.buf.clone(),
lights: lights.buf.clone(),
shadows: shadows.buf.clone(),
tgt_color: self.tgt_color_view.clone(),
tgt_depth: self.tgt_depth_view.clone(),
},

View File

@ -9,7 +9,7 @@ use crate::{
self, character::CharacterSkeleton, object::ObjectSkeleton, quadruped::QuadrupedSkeleton,
quadrupedmedium::QuadrupedMediumSkeleton, Animation, Skeleton,
},
render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer},
render::{Consts, FigureBoneData, FigureLocals, Globals, Light, Renderer, Shadow},
scene::camera::{Camera, CameraMode},
};
use client::Client;
@ -408,6 +408,7 @@ impl FigureMgr {
client: &mut Client,
globals: &Consts<Globals>,
lights: &Consts<Light>,
shadows: &Consts<Shadow>,
camera: &Camera,
) {
let tick = client.get_tick();
@ -480,7 +481,7 @@ impl FigureMgr {
)
.0;
renderer.render_figure(model, globals, locals, bone_consts, lights);
renderer.render_figure(model, globals, locals, bone_consts, lights, shadows);
} else {
debug!("Body has no saved figure");
}

View File

@ -13,7 +13,7 @@ use crate::{
audio::AudioFrontend,
render::{
create_pp_mesh, create_skybox_mesh, Consts, Globals, Light, Model, PostProcessLocals,
PostProcessPipeline, Renderer, SkyboxLocals, SkyboxPipeline,
PostProcessPipeline, Renderer, Shadow, SkyboxLocals, SkyboxPipeline,
},
window::Event,
};
@ -30,7 +30,10 @@ use vek::*;
const CURSOR_PAN_SCALE: f32 = 0.005;
const MAX_LIGHT_COUNT: usize = 32;
const LIGHT_DIST_RADIUS: f32 = 64.0; // The distance beyond which lights may not be visible
const MAX_SHADOW_COUNT: usize = 24;
const LIGHT_DIST_RADIUS: f32 = 64.0; // The distance beyond which lights may not emit light from their origin
const SHADOW_DIST_RADIUS: f32 = 8.0;
const SHADOW_MAX_DIST: f32 = 96.0; // The distance beyond which shadows may not be visible
struct Skybox {
model: Model<SkyboxPipeline>,
@ -45,6 +48,7 @@ struct PostProcess {
pub struct Scene {
globals: Consts<Globals>,
lights: Consts<Light>,
shadows: Consts<Shadow>,
camera: Camera,
skybox: Skybox,
@ -63,7 +67,12 @@ impl Scene {
Self {
globals: renderer.create_consts(&[Globals::default()]).unwrap(),
lights: renderer.create_consts(&[Light::default(); 32]).unwrap(),
lights: renderer
.create_consts(&[Light::default(); MAX_LIGHT_COUNT])
.unwrap(),
shadows: renderer
.create_consts(&[Shadow::default(); MAX_SHADOW_COUNT])
.unwrap(),
camera: Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson),
skybox: Skybox {
@ -200,14 +209,30 @@ impl Scene {
)
})
.collect::<Vec<_>>();
lights.sort_by_key(|light| {
Vec3::from(Vec4::from(light.pos)).distance_squared(player_pos) as i32
});
lights.sort_by_key(|light| light.get_pos().distance_squared(player_pos) as i32);
lights.truncate(MAX_LIGHT_COUNT);
renderer
.update_consts(&mut self.lights, &lights)
.expect("Failed to update light constants");
// Update shadow constants
let mut shadows = (
&client.state().ecs().read_storage::<comp::Pos>(),
client.state().ecs().read_storage::<comp::Scale>().maybe(),
)
.join()
.filter(|(pos, _)| {
(pos.0.distance_squared(player_pos) as f32)
< self.loaded_distance.powf(2.0).min(SHADOW_MAX_DIST) + SHADOW_DIST_RADIUS
})
.map(|(pos, scale)| Shadow::new(pos.0, scale.map(|s| s.0).unwrap_or(1.0)))
.collect::<Vec<_>>();
shadows.sort_by_key(|shadow| shadow.get_pos().distance_squared(player_pos) as i32);
shadows.truncate(MAX_SHADOW_COUNT);
renderer
.update_consts(&mut self.shadows, &shadows)
.expect("Failed to update light constants");
// Update global constants.
renderer
.update_consts(
@ -222,6 +247,7 @@ impl Scene {
client.state().get_time(),
renderer.get_resolution(),
lights.len(),
shadows.len(),
client
.state()
.terrain()
@ -258,12 +284,19 @@ impl Scene {
renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals);
// Render terrain and figures.
self.figure_mgr
.render(renderer, client, &self.globals, &self.lights, &self.camera);
self.figure_mgr.render(
renderer,
client,
&self.globals,
&self.lights,
&self.shadows,
&self.camera,
);
self.terrain.render(
renderer,
&self.globals,
&self.lights,
&self.shadows,
self.camera.get_focus_pos(),
);

View File

@ -1,8 +1,8 @@
use crate::{
mesh::Meshable,
render::{
Consts, FluidPipeline, Globals, Instances, Light, Mesh, Model, Renderer, SpriteInstance,
SpritePipeline, TerrainLocals, TerrainPipeline,
Consts, FluidPipeline, Globals, Instances, Light, Mesh, Model, Renderer, Shadow,
SpriteInstance, SpritePipeline, TerrainLocals, TerrainPipeline,
},
};
@ -894,12 +894,19 @@ impl<V: RectRasterableVol> Terrain<V> {
renderer: &mut Renderer,
globals: &Consts<Globals>,
lights: &Consts<Light>,
shadows: &Consts<Shadow>,
focus_pos: Vec3<f32>,
) {
// Opaque
for (_, chunk) in &self.chunks {
if chunk.visible {
renderer.render_terrain_chunk(&chunk.opaque_model, globals, &chunk.locals, lights);
renderer.render_terrain_chunk(
&chunk.opaque_model,
globals,
&chunk.locals,
lights,
shadows,
);
}
}
@ -919,6 +926,7 @@ impl<V: RectRasterableVol> Terrain<V> {
globals,
&instances,
lights,
shadows,
);
}
}
@ -928,7 +936,13 @@ impl<V: RectRasterableVol> Terrain<V> {
// Translucent
for (_, chunk) in &self.chunks {
if chunk.visible {
renderer.render_fluid_chunk(&chunk.fluid_model, globals, &chunk.locals, lights);
renderer.render_fluid_chunk(
&chunk.fluid_model,
globals,
&chunk.locals,
lights,
shadows,
);
}
}
}

View File

@ -54,7 +54,11 @@ impl<'a> BlockGen<'a> {
cache,
Vec2::from(*cliff_pos),
) {
Some(cliff_sample) if cliff_sample.is_cliffs && cliff_sample.spawn_rate > 0.5 => {
Some(cliff_sample)
if cliff_sample.is_cliffs
&& cliff_sample.spawn_rate > 0.5
&& cliff_sample.spawn_rules.cliffs =>
{
let cliff_pos3d = Vec3::from(*cliff_pos);
let height = (RandomField::new(seed + 1).get(cliff_pos3d) % 64) as f32

View File

@ -10,11 +10,15 @@ use vek::*;
#[derive(Copy, Clone, Debug)]
pub struct SpawnRules {
pub trees: bool,
pub cliffs: bool,
}
impl Default for SpawnRules {
fn default() -> Self {
Self { trees: true }
Self {
trees: true,
cliffs: true,
}
}
}
@ -22,6 +26,7 @@ impl SpawnRules {
pub fn and(self, other: Self) -> Self {
Self {
trees: self.trees && other.trees,
cliffs: self.cliffs && other.cliffs,
}
}
}

View File

@ -106,6 +106,7 @@ impl<'a> Generator<'a, TownState> for TownGen {
fn spawn_rules(&self, town: &'a TownState, wpos: Vec2<i32>) -> SpawnRules {
SpawnRules {
trees: wpos.distance_squared(town.center.into()) > (town.radius + 32).pow(2),
cliffs: false,
}
}
}