Added point lights

This commit is contained in:
Joshua Barretto 2019-07-21 16:04:36 +01:00
parent 1dc654dde7
commit b34d22565e
15 changed files with 171 additions and 32 deletions

View File

@ -1,7 +1,6 @@
#version 330 core
#include <globals.glsl>
#include <sky.glsl>
in vec3 f_pos;
in vec3 f_norm;
@ -23,6 +22,19 @@ uniform u_bones {
BoneData bones[16];
};
struct Light {
vec4 light_pos;
vec4 light_col;
};
layout (std140)
uniform u_lights {
Light lights[32];
};
#include <sky.glsl>
#include <light.glsl>
out vec4 tgt_color;
void main() {
@ -32,7 +44,7 @@ void main() {
vec4(f_norm, 0.0)
).xyz;
vec3 light = get_sun_diffuse(world_norm, time_of_day.x);
vec3 light = get_sun_diffuse(world_norm, time_of_day.x) + light_at(f_pos, f_norm);
vec3 surf_color = model_col.rgb * f_col * 2.0 * light;
float fog_level = fog(f_pos.xy, focus_pos.xy);

View File

@ -22,6 +22,16 @@ uniform u_bones {
BoneData bones[16];
};
struct Light {
vec4 light_pos;
vec4 light_col;
};
layout (std140)
uniform u_lights {
Light lights[32];
};
out vec3 f_pos;
out vec3 f_norm;
out vec3 f_col;

View File

@ -8,4 +8,5 @@ uniform u_globals {
vec4 time_of_day;
vec4 tick;
vec4 screen_res;
uvec4 light_count;
};

View File

@ -0,0 +1,22 @@
float attenuation_strength(vec3 rpos) {
return 1.0 / (rpos.x * rpos.x + rpos.y * rpos.y + rpos.z * rpos.z);
}
vec3 light_at(vec3 wpos, vec3 wnorm) {
const float LIGHT_AMBIENCE = 0.1;
vec3 light = vec3(0);
for (uint i = 0u; i < light_count.x; i ++) {
vec3 light_pos = lights[i].light_pos.xyz;
float strength = attenuation_strength(wpos - light_pos);
if (strength < 0.001) {
continue;
}
light += strength
* lights[i].light_col.rgb
* clamp(dot(normalize(light_pos - wpos), wnorm), LIGHT_AMBIENCE, 1.0);
}
return light;
}

View File

@ -1,7 +1,6 @@
#version 330 core
#include <globals.glsl>
#include <sky.glsl>
in vec3 f_pos;
flat in uint f_pos_norm;
@ -13,8 +12,21 @@ uniform u_locals {
vec3 model_offs;
};
struct Light {
vec4 light_pos;
vec4 light_col;
};
layout (std140)
uniform u_lights {
Light lights[32];
};
out vec4 tgt_color;
#include <sky.glsl>
#include <light.glsl>
void main() {
// Calculate normal from packed data
vec3 f_norm;
@ -28,7 +40,7 @@ void main() {
f_norm = vec3(0.0, 0.0, 1.0) * norm_dir;
}
vec3 light = get_sun_diffuse(f_norm, time_of_day.x) * f_light;
vec3 light = get_sun_diffuse(f_norm, time_of_day.x) * f_light + light_at(f_pos, f_norm);
vec3 surf_color = f_col * light;
float fog_level = fog(f_pos.xy, focus_pos.xy);

View File

@ -10,6 +10,16 @@ uniform u_locals {
vec3 model_offs;
};
struct Light {
vec4 light_pos;
vec4 light_col;
};
layout (std140)
uniform u_lights {
Light lights[32];
};
out vec3 f_pos;
flat out uint f_pos_norm;
out vec3 f_col;

View File

@ -5,7 +5,7 @@ use crate::{
Animation, Skeleton, SkeletonAttr,
},
render::{
create_pp_mesh, create_skybox_mesh, Consts, FigurePipeline, Globals, Model,
create_pp_mesh, create_skybox_mesh, Consts, FigurePipeline, Globals, Light, Model,
PostProcessLocals, PostProcessPipeline, Renderer, SkyboxLocals, SkyboxPipeline,
},
scene::{
@ -33,6 +33,7 @@ struct PostProcess {
pub struct Scene {
globals: Consts<Globals>,
lights: Consts<Light>,
camera: Camera,
skybox: Skybox,
@ -50,6 +51,7 @@ impl Scene {
Self {
globals: renderer.create_consts(&[Globals::default()]).unwrap(),
lights: renderer.create_consts(&[Light::default(); 32]).unwrap(),
camera: Camera::new(resolution.x / resolution.y),
skybox: Skybox {
@ -99,6 +101,7 @@ impl Scene {
55800.0,
client.state().get_time(),
renderer.get_resolution(),
0,
)],
) {
error!("Renderer failed to update: {:?}", err);
@ -139,6 +142,7 @@ impl Scene {
&self.globals,
self.figure_state.locals(),
self.figure_state.bone_consts(),
&self.lights,
);
renderer.render_figure(
@ -146,6 +150,7 @@ impl Scene {
&self.globals,
self.backdrop_state.locals(),
self.backdrop_state.bone_consts(),
&self.lights,
);
renderer.render_post_process(

View File

@ -22,7 +22,7 @@ pub use self::{
create_quad as create_ui_quad, create_tri as create_ui_tri, Locals as UiLocals,
Mode as UiMode, UiPipeline,
},
Globals,
Globals, Light,
},
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,
Globals, Light,
};
use gfx::{
self,
@ -37,6 +37,7 @@ gfx_defines! {
locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
bones: gfx::ConstantBuffer<BoneData> = "u_bones",
lights: gfx::ConstantBuffer<Light> = "u_lights",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
tgt_depth: gfx::DepthTarget<TgtDepthFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,

View File

@ -25,24 +25,16 @@ 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",
}
constant Light {
pos: [f32; 4] = "light_pos",
col: [f32; 4] = "light_col",
}
}
impl Globals {
/// Create global consts with default values.
pub fn default() -> Self {
Self {
view_mat: arr_to_mat(Mat4::identity().into_col_array()),
proj_mat: arr_to_mat(Mat4::identity().into_col_array()),
cam_pos: [0.0; 4],
focus_pos: [0.0; 4],
view_distance: [0.0; 4],
time_of_day: [0.0; 4],
tick: [0.0; 4],
screen_res: [800.0, 500.0, 0.0, 0.0],
}
}
/// Create global consts from the provided parameters.
pub fn new(
view_mat: Mat4<f32>,
@ -53,6 +45,7 @@ impl Globals {
time_of_day: f64,
tick: f64,
screen_res: Vec2<u16>,
light_count: usize,
) -> Self {
Self {
view_mat: arr_to_mat(view_mat.into_col_array()),
@ -63,6 +56,38 @@ 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],
}
}
}
impl Default for Globals {
fn default() -> Self {
Self::new(
Mat4::identity(),
Mat4::identity(),
Vec3::zero(),
Vec3::zero(),
0.0,
0.0,
0.0,
Vec2::new(800, 500),
0,
)
}
}
impl Light {
pub fn new(pos: Vec3<f32>, col: Rgb<f32>, strength: f32) -> Self {
Self {
pos: Vec4::from(pos).into_array(),
col: Rgba::new(col.r, col.g, col.b, strength).into_array(),
}
}
}
impl Default for Light {
fn default() -> Self {
Self::new(Vec3::zero(), Rgb::zero(), 0.0)
}
}

View File

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

View File

@ -3,7 +3,7 @@ use super::{
gfx_backend,
mesh::Mesh,
model::{DynamicModel, Model},
pipelines::{figure, postprocess, skybox, terrain, ui, Globals},
pipelines::{figure, postprocess, skybox, terrain, ui, Globals, Light},
texture::Texture,
Pipeline, RenderError,
};
@ -82,10 +82,15 @@ impl Renderer {
env!("CARGO_MANIFEST_DIR"),
"/shaders/include/sky.glsl"
));
let light = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/shaders/include/light.glsl"
));
let mut include_ctx = IncludeContext::new();
include_ctx.include("globals.glsl", globals);
include_ctx.include("sky.glsl", sky);
include_ctx.include("light.glsl", light);
// Construct a pipeline for rendering skyboxes
let skybox_pipeline = create_pipeline(
@ -389,6 +394,7 @@ impl Renderer {
globals: &Consts<Globals>,
locals: &Consts<figure::Locals>,
bones: &Consts<figure::BoneData>,
lights: &Consts<Light>,
) {
self.encoder.draw(
&model.slice,
@ -398,6 +404,7 @@ impl Renderer {
locals: locals.buf.clone(),
globals: globals.buf.clone(),
bones: bones.buf.clone(),
lights: lights.buf.clone(),
tgt_color: self.tgt_color_view.clone(),
tgt_depth: self.tgt_depth_view.clone(),
},
@ -410,6 +417,7 @@ impl Renderer {
model: &Model<terrain::TerrainPipeline>,
globals: &Consts<Globals>,
locals: &Consts<terrain::Locals>,
lights: &Consts<Light>,
) {
self.encoder.draw(
&model.slice,
@ -418,6 +426,7 @@ impl Renderer {
vbuf: model.vbuf.clone(),
locals: locals.buf.clone(),
globals: globals.buf.clone(),
lights: lights.buf.clone(),
tgt_color: self.tgt_color_view.clone(),
tgt_depth: self.tgt_depth_view.clone(),
},

View File

@ -5,7 +5,7 @@ use crate::{
},
mesh::Meshable,
render::{
Consts, FigureBoneData, FigureLocals, FigurePipeline, Globals, Mesh, Model, Renderer,
Consts, FigureBoneData, FigureLocals, FigurePipeline, Globals, Light, Mesh, Model, Renderer,
},
};
use client::Client;
@ -800,6 +800,7 @@ impl FigureMgr {
renderer: &mut Renderer,
client: &mut Client,
globals: &Consts<Globals>,
lights: &Consts<Light>,
) {
let tick = client.get_tick();
let ecs = client.state().ecs();
@ -855,7 +856,7 @@ impl FigureMgr {
.get_or_create_model(renderer, *body, tick)
.0;
renderer.render_figure(model, globals, locals, bone_consts);
renderer.render_figure(model, globals, locals, bone_consts, lights);
} else {
warn!("Body has no saved figure");
}

View File

@ -5,18 +5,21 @@ pub mod terrain;
use self::{camera::Camera, figure::FigureMgr, terrain::Terrain};
use crate::{
render::{
create_pp_mesh, create_skybox_mesh, Consts, Globals, Model, PostProcessLocals,
create_pp_mesh, create_skybox_mesh, Consts, Globals, Light, Model, PostProcessLocals,
PostProcessPipeline, Renderer, SkyboxLocals, SkyboxPipeline,
},
window::Event,
};
use client::Client;
use common::comp;
use specs::Join;
use vek::*;
// TODO: Don't hard-code this.
const CURSOR_PAN_SCALE: f32 = 0.005;
const LIGHT_DIST_RADIUS: f32 = 64.0; // The distance beyond which lights may not be visible
struct Skybox {
model: Model<SkyboxPipeline>,
locals: Consts<SkyboxLocals>,
@ -29,6 +32,7 @@ struct PostProcess {
pub struct Scene {
globals: Consts<Globals>,
lights: Consts<Light>,
camera: Camera,
skybox: Skybox,
@ -46,6 +50,7 @@ impl Scene {
Self {
globals: renderer.create_consts(&[Globals::default()]).unwrap(),
lights: renderer.create_consts(&[Light::default(); 32]).unwrap(),
camera: Camera::new(resolution.x / resolution.y),
skybox: Skybox {
@ -128,9 +133,27 @@ impl Scene {
let (view_mat, proj_mat, cam_pos) = self.camera.compute_dependents(client);
// Update chunk loaded distance smoothly for nice shader fog
let loaded_distance = client.loaded_distance().unwrap_or(0) as f32 * 32.0;
let loaded_distance = client.loaded_distance().unwrap_or(0) as f32 * 32.0; // TODO: No magic!
self.loaded_distance = (0.98 * self.loaded_distance + 0.02 * loaded_distance).max(0.01);
// Update light constants
let mut lights = (&client.state().ecs().read_storage::<comp::Pos>(),)
.join()
.filter(|(pos,)| {
(pos.0.distance_squared(player_pos) as f32)
< self.loaded_distance.powf(2.0) + LIGHT_DIST_RADIUS
})
.map(|(pos,)| pos.0)
.map(|pos| Light::new(pos + Vec3::unit_z(), Rgb::broadcast(1.0), 100.0)) // TODO: Don't add 1 to z!
.take(32)
.collect::<Vec<_>>();
lights.sort_by_key(|light| {
Vec3::from(Vec4::from(light.pos)).distance_squared(player_pos) as i32
});
renderer
.update_consts(&mut self.lights, &lights)
.expect("Failed to update light constants");
// Update global constants.
renderer
.update_consts(
@ -144,6 +167,7 @@ impl Scene {
client.state().get_time_of_day(),
client.state().get_time(),
renderer.get_resolution(),
lights.len(),
)],
)
.expect("Failed to update global constants");
@ -171,8 +195,9 @@ impl Scene {
renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals);
// Render terrain and figures.
self.terrain.render(renderer, &self.globals);
self.figure_mgr.render(renderer, client, &self.globals);
self.terrain.render(renderer, &self.globals, &self.lights);
self.figure_mgr
.render(renderer, client, &self.globals, &self.lights);
renderer.render_post_process(
&self.postprocess.model,

View File

@ -1,6 +1,6 @@
use crate::{
mesh::Meshable,
render::{Consts, Globals, Mesh, Model, Renderer, TerrainLocals, TerrainPipeline},
render::{Consts, Globals, Light, Mesh, Model, Renderer, TerrainLocals, TerrainPipeline},
};
use client::Client;
use common::{
@ -327,10 +327,15 @@ impl Terrain {
}
}
pub fn render(&self, renderer: &mut Renderer, globals: &Consts<Globals>) {
pub fn render(
&self,
renderer: &mut Renderer,
globals: &Consts<Globals>,
lights: &Consts<Light>,
) {
for (_pos, chunk) in &self.chunks {
if chunk.visible {
renderer.render_terrain_chunk(&chunk.model, globals, &chunk.locals);
renderer.render_terrain_chunk(&chunk.model, globals, &chunk.locals, lights);
}
}
}