Enabled AO in darkness, better light attenuation

This commit is contained in:
Joshua Barretto 2020-04-16 14:42:20 +01:00
parent 525cba5029
commit c4879e991d
8 changed files with 65 additions and 39 deletions

View File

@ -25,7 +25,9 @@ vec3 illuminate(vec3 color, vec3 light, vec3 diffuse, vec3 ambience) {
} }
float attenuation_strength(vec3 rpos) { float attenuation_strength(vec3 rpos) {
return 0.3 / pow(rpos.x * rpos.x + rpos.y * rpos.y + rpos.z * rpos.z, 0.5); // This is not how light attenuation works at all, but it produces visually pleasing and mechanically useful properties
float d2 = rpos.x * rpos.x + rpos.y * rpos.y + rpos.z * rpos.z;
return max(2.0 / pow(d2 + 10, 0.35) - pow(d2 / 50000.0, 0.8), 0.0);
} }
vec3 light_at(vec3 wpos, vec3 wnorm) { vec3 light_at(vec3 wpos, vec3 wnorm) {
@ -43,7 +45,7 @@ vec3 light_at(vec3 wpos, vec3 wnorm) {
// Pre-calculate difference between light and fragment // Pre-calculate difference between light and fragment
vec3 difference = light_pos - wpos; vec3 difference = light_pos - wpos;
float strength = pow(attenuation_strength(difference), 0.6); float strength = attenuation_strength(difference);
// Multiply the vec3 only once // Multiply the vec3 only once
vec3 color = srgb_to_linear(L.light_col.rgb) * (strength * L.light_col.a); vec3 color = srgb_to_linear(L.light_col.rgb) * (strength * L.light_col.a);

View File

@ -6,6 +6,7 @@ in vec3 f_pos;
flat in uint f_pos_norm; flat in uint f_pos_norm;
in vec3 f_col; in vec3 f_col;
in float f_light; in float f_light;
in float f_ao;
layout (std140) layout (std140)
uniform u_locals { uniform u_locals {
@ -29,14 +30,18 @@ void main() {
// Use an array to avoid conditional branching // Use an array to avoid conditional branching
vec3 f_norm = normals[(f_pos_norm >> 29) & 0x7u]; vec3 f_norm = normals[(f_pos_norm >> 29) & 0x7u];
float ao = pow(f_ao, 0.5) * 0.9 + 0.1;
vec3 light, diffuse_light, ambient_light; vec3 light, diffuse_light, ambient_light;
get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0); get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0);
float point_shadow = shadow_at(f_pos, f_norm); float point_shadow = shadow_at(f_pos, f_norm);
diffuse_light *= f_light * point_shadow; diffuse_light *= point_shadow;
ambient_light *= f_light * point_shadow; ambient_light *= point_shadow;
vec3 point_light = light_at(f_pos, f_norm); vec3 point_light = light_at(f_pos, f_norm);
light += point_light; light += point_light;
diffuse_light += point_light; ambient_light *= min(f_light, ao);
diffuse_light *= min(f_light, ao);
diffuse_light += point_light * ao;
vec3 surf_color = illuminate(srgb_to_linear(f_col), light, diffuse_light, ambient_light); vec3 surf_color = illuminate(srgb_to_linear(f_col), light, diffuse_light, ambient_light);
float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x); float fog_level = fog(f_pos.xyz, focus_pos.xyz, medium.x);

View File

@ -16,6 +16,7 @@ out vec3 f_pos;
flat out uint f_pos_norm; flat out uint f_pos_norm;
out vec3 f_col; out vec3 f_col;
out float f_light; out float f_light;
out float f_ao;
const float EXTRA_NEG_Z = 65536.0; const float EXTRA_NEG_Z = 65536.0;
@ -27,7 +28,8 @@ void main() {
f_col = vec3((uvec3(v_col_light) >> uvec3(8, 16, 24)) & uvec3(0xFFu)) / 255.0; f_col = vec3((uvec3(v_col_light) >> uvec3(8, 16, 24)) & uvec3(0xFFu)) / 255.0;
f_light = float(v_col_light & 0xFFu) / 255.0; f_light = float(v_col_light & 0x3Fu) / 64.0;
f_ao = float((v_col_light >> 6u) & 3u) / 4.0;
f_pos_norm = v_pos_norm; f_pos_norm = v_pos_norm;

View File

@ -31,6 +31,7 @@ impl Meshable<FigurePipeline, FigurePipeline> for Segment {
offs + pos.map(|e| e as f32), offs + pos.map(|e| e as f32),
&[[[Rgba::from_opaque(col); 3]; 3]; 3], &[[[Rgba::from_opaque(col); 3]; 3]; 3],
|origin, norm, col, ao, light| { |origin, norm, col, ao, light| {
let ao = ao * 0.95 + 0.05;
FigureVertex::new( FigureVertex::new(
origin, origin,
norm, norm,
@ -39,7 +40,7 @@ impl Meshable<FigurePipeline, FigurePipeline> for Segment {
) )
}, },
&{ &{
let mut ls = [[[0.0; 3]; 3]; 3]; let mut ls = [[[None; 3]; 3]; 3];
for x in 0..3 { for x in 0..3 {
for y in 0..3 { for y in 0..3 {
for z in 0..3 { for z in 0..3 {
@ -48,9 +49,9 @@ impl Meshable<FigurePipeline, FigurePipeline> for Segment {
.map(|v| v.is_empty()) .map(|v| v.is_empty())
.unwrap_or(true) .unwrap_or(true)
{ {
1.0 Some(1.0)
} else { } else {
0.0 None
}; };
} }
} }
@ -84,6 +85,7 @@ impl Meshable<SpritePipeline, SpritePipeline> for Segment {
offs + pos.map(|e| e as f32), offs + pos.map(|e| e as f32),
&[[[Rgba::from_opaque(col); 3]; 3]; 3], &[[[Rgba::from_opaque(col); 3]; 3]; 3],
|origin, norm, col, ao, light| { |origin, norm, col, ao, light| {
let ao = ao * 0.95 + 0.05;
SpriteVertex::new( SpriteVertex::new(
origin, origin,
norm, norm,
@ -91,7 +93,7 @@ impl Meshable<SpritePipeline, SpritePipeline> for Segment {
) )
}, },
&{ &{
let mut ls = [[[0.0; 3]; 3]; 3]; let mut ls = [[[None; 3]; 3]; 3];
for x in 0..3 { for x in 0..3 {
for y in 0..3 { for y in 0..3 {
for z in 0..3 { for z in 0..3 {
@ -100,9 +102,9 @@ impl Meshable<SpritePipeline, SpritePipeline> for Segment {
.map(|v| v.is_empty()) .map(|v| v.is_empty())
.unwrap_or(true) .unwrap_or(true)
{ {
1.0 Some(1.0)
} else { } else {
0.0 None
}; };
} }
} }

View File

@ -28,7 +28,7 @@ impl Blendable for BlockKind {
fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>( fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
bounds: Aabb<i32>, bounds: Aabb<i32>,
vol: &VolGrid2d<V>, vol: &VolGrid2d<V>,
) -> impl Fn(Vec3<i32>) -> f32 { ) -> impl FnMut(Vec3<i32>) -> Option<f32> + '_ {
const UNKNOWN: u8 = 255; const UNKNOWN: u8 = 255;
const OPAQUE: u8 = 254; const OPAQUE: u8 = 254;
const SUNLIGHT: u8 = 24; const SUNLIGHT: u8 = 24;
@ -189,12 +189,20 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
} }
move |wpos| { move |wpos| {
if vol_cached
.get(wpos)
.map(|block| block.is_opaque())
.unwrap_or(false)
{
None
} else {
let pos = wpos - outer.min; let pos = wpos - outer.min;
light_map Some(light_map
.get(lm_idx(pos.x, pos.y, pos.z)) .get(lm_idx(pos.x, pos.y, pos.z))
.filter(|l| **l != OPAQUE && **l != UNKNOWN) .filter(|l| **l != OPAQUE && **l != UNKNOWN)
.map(|l| *l as f32 / SUNLIGHT as f32) .map(|l| *l as f32 / SUNLIGHT as f32)
.unwrap_or(0.0) .unwrap_or(0.0))
}
} }
} }
@ -212,7 +220,7 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
let mut opaque_mesh = Mesh::new(); let mut opaque_mesh = Mesh::new();
let mut fluid_mesh = Mesh::new(); let mut fluid_mesh = Mesh::new();
let light = calc_light(range, self); let mut light = calc_light(range, self);
let mut lowest_opaque = range.size().d; let mut lowest_opaque = range.size().d;
let mut highest_opaque = 0; let mut highest_opaque = 0;
@ -292,7 +300,7 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
.min(range.size().d - 1); .min(range.size().d - 1);
for x in 1..range.size().w - 1 { for x in 1..range.size().w - 1 {
for y in 1..range.size().w - 1 { for y in 1..range.size().w - 1 {
let mut lights = [[[0.0; 3]; 3]; 3]; let mut lights = [[[None; 3]; 3]; 3];
for i in 0..3 { for i in 0..3 {
for j in 0..3 { for j in 0..3 {
for k in 0..3 { for k in 0..3 {
@ -373,8 +381,10 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
faces_to_make(&blocks, false, |vox| !vox.is_opaque()), faces_to_make(&blocks, false, |vox| !vox.is_opaque()),
offs, offs,
&colors, &colors,
|pos, norm, col, ao, light| { |pos, norm, col, light, ao| {
let light = (light.min(ao) * 255.0) as u32; //let light = (light.min(ao) * 255.0) as u32;
let light = (light * 255.0) as u32;
let ao = (ao * 255.0) as u32;
let norm = if norm.x != 0.0 { let norm = if norm.x != 0.0 {
if norm.x < 0.0 { 0 } else { 1 } if norm.x < 0.0 { 0 } else { 1 }
} else if norm.y != 0.0 { } else if norm.y != 0.0 {
@ -382,7 +392,7 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
} else { } else {
if norm.z < 0.0 { 4 } else { 5 } if norm.z < 0.0 { 4 } else { 5 }
}; };
TerrainVertex::new(norm, light, pos, col) TerrainVertex::new(norm, light, ao, pos, col)
}, },
&lights, &lights,
); );
@ -392,7 +402,7 @@ impl<V: RectRasterableVol<Vox = Block> + ReadVol + Debug> Meshable<TerrainPipeli
faces_to_make(&blocks, false, |vox| vox.is_air()), faces_to_make(&blocks, false, |vox| vox.is_air()),
offs, offs,
&colors, &colors,
|pos, norm, col, _ao, light| { |pos, norm, col, light, _ao| {
FluidVertex::new(pos, norm, col, light, 0.3) FluidVertex::new(pos, norm, col, light, 0.3)
}, },
&lights, &lights,

View File

@ -12,7 +12,7 @@ use crate::render::{
fn get_ao_quad( fn get_ao_quad(
shift: Vec3<i32>, shift: Vec3<i32>,
dirs: &[Vec3<i32>], dirs: &[Vec3<i32>],
darknesses: &[[[f32; 3]; 3]; 3], darknesses: &[[[Option<f32>; 3]; 3]; 3],
) -> Vec4<(f32, f32)> { ) -> Vec4<(f32, f32)> {
dirs.windows(2) dirs.windows(2)
.map(|offs| { .map(|offs| {
@ -23,7 +23,7 @@ fn get_ao_quad(
.get_unchecked(pos.z) .get_unchecked(pos.z)
.get_unchecked(pos.y) .get_unchecked(pos.y)
.get_unchecked(pos.x) .get_unchecked(pos.x)
<= &0.0 .is_none()
} }
}; };
@ -41,17 +41,21 @@ fn get_ao_quad(
); );
let mut darkness = 0.0; let mut darkness = 0.0;
let mut total = 0.0f32;
for x in 0..2 { for x in 0..2 {
for y in 0..2 { for y in 0..2 {
let dark_pos = shift + offs[0] * x + offs[1] * y + 1; let dark_pos = shift + offs[0] * x + offs[1] * y + 1;
darkness += unsafe { if let Some(dark) = unsafe { darknesses
darknesses
.get_unchecked(dark_pos.z as usize) .get_unchecked(dark_pos.z as usize)
.get_unchecked(dark_pos.y as usize) .get_unchecked(dark_pos.y as usize)
.get_unchecked(dark_pos.x as usize) .get_unchecked(dark_pos.x as usize) }
} / 4.0; {
darkness += dark;
total += 1.0;
} }
} }
}
let darkness = darkness / total.max(1.0);
( (
darkness, darkness,
@ -60,7 +64,7 @@ fn get_ao_quad(
} else { } else {
let corner = vox_opaque(shift + offs[0] + offs[1]); let corner = vox_opaque(shift + offs[0] + offs[1]);
// Map both 1 and 2 neighbors to 0.5 occlusion. // Map both 1 and 2 neighbors to 0.5 occlusion.
if s1 || s2 || corner { 0.5 } else { 1.0 } if s1 || s2 || corner { 0.4 } else { 1.0 }
}, },
) )
}) })
@ -112,7 +116,7 @@ fn create_quad<P: Pipeline, F: Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>, f32, f32) -> P
let darkness = darkness_ao.map(|e| e.0); let darkness = darkness_ao.map(|e| e.0);
let ao = darkness_ao.map(|e| e.1); let ao = darkness_ao.map(|e| e.1);
let ao_map = ao * 0.85 + 0.15; let ao_map = ao;
if ao[0].min(ao[2]).min(darkness[0]).min(darkness[2]) if ao[0].min(ao[2]).min(darkness[0]).min(darkness[2])
< ao[1].min(ao[3]).min(darkness[1]).min(darkness[3]) < ao[1].min(ao[3]).min(darkness[1]).min(darkness[3])
@ -151,7 +155,7 @@ pub fn push_vox_verts<P: Pipeline>(
offs: Vec3<f32>, offs: Vec3<f32>,
cols: &[[[Rgba<u8>; 3]; 3]; 3], cols: &[[[Rgba<u8>; 3]; 3]; 3],
vcons: impl Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>, f32, f32) -> P::Vertex, vcons: impl Fn(Vec3<f32>, Vec3<f32>, Rgb<f32>, f32, f32) -> P::Vertex,
darknesses: &[[[f32; 3]; 3]; 3], darknesses: &[[[Option<f32>; 3]; 3]; 3],
) { ) {
let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z()); let (x, y, z) = (Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z());

View File

@ -37,7 +37,7 @@ gfx_defines! {
} }
impl Vertex { impl Vertex {
pub fn new(norm_bits: u32, light: u32, pos: Vec3<f32>, col: Rgb<f32>) -> Self { pub fn new(norm_bits: u32, light: u32, ao: u32, pos: Vec3<f32>, col: Rgb<f32>) -> Self {
const EXTRA_NEG_Z: f32 = 65536.0; const EXTRA_NEG_Z: f32 = 65536.0;
Self { Self {
@ -50,7 +50,8 @@ impl Vertex {
| ((col.r.mul(255.0) as u32) & 0xFF) << 8 | ((col.r.mul(255.0) as u32) & 0xFF) << 8
| ((col.g.mul(255.0) as u32) & 0xFF) << 16 | ((col.g.mul(255.0) as u32) & 0xFF) << 16
| ((col.b.mul(255.0) as u32) & 0xFF) << 24 | ((col.b.mul(255.0) as u32) & 0xFF) << 24
| (light & 0xFF) << 0, | (ao >> 6) << 6
| ((light >> 2) & 0x3F) << 0,
} }
} }
} }

View File

@ -216,7 +216,7 @@ impl Floor {
// Ensure no overlap // Ensure no overlap
if self.rooms if self.rooms
.iter() .iter()
.any(|r| r.collides_with_rect(room_border))// || r.contains_point(self.stair_tile)) .any(|r| r.collides_with_rect(room_border) || r.contains_point(self.stair_tile))
{ {
return None; return None;
} }