From 46cf1f19146b148e32688c8c23b8fb6a48076667 Mon Sep 17 00:00:00 2001 From: Raul Wagner Costa Date: Sat, 14 Oct 2023 18:10:35 +0000 Subject: [PATCH] Linearize light colors on the CPU --- CHANGELOG.md | 1 + assets/voxygen/shaders/include/light.glsl | 2 +- .../voxygen/shaders/include/point_glow.glsl | 2 +- common/benches/color_benchmark.rs | 4 ++-- common/src/util/color.rs | 24 +++++++++++++++---- voxygen/src/render/pipelines/mod.rs | 7 ++++-- voxygen/src/scene/figure/load.rs | 6 ++--- voxygen/src/ui/graphic/renderer.rs | 4 ++-- 8 files changed, 34 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 312df3c7af..8916bbad62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The ability limit for non-humanoids has been removed - Improved running, wielding, and riding animations - Fixed offset of items carried on backs when wearing cloaks and backpacks +- Linearize light colors on the CPU rather than in shaders on the GPU ### Removed - Medium and large potions from all loot tables diff --git a/assets/voxygen/shaders/include/light.glsl b/assets/voxygen/shaders/include/light.glsl index f391006b0a..191e64d206 100644 --- a/assets/voxygen/shaders/include/light.glsl +++ b/assets/voxygen/shaders/include/light.glsl @@ -84,7 +84,7 @@ vec3 light_at(vec3 wpos, vec3 wnorm) { float strength = attenuation_strength(difference); - vec3 color = srgb_to_linear(L.light_col.rgb) * strength; + vec3 color = L.light_col.rgb * strength; light += color * (max(0, max(dot(normalize(difference), wnorm), 0.15)) + LIGHT_AMBIANCE); } diff --git a/assets/voxygen/shaders/include/point_glow.glsl b/assets/voxygen/shaders/include/point_glow.glsl index 855e82d82b..6f190eeb42 100644 --- a/assets/voxygen/shaders/include/point_glow.glsl +++ b/assets/voxygen/shaders/include/point_glow.glsl @@ -33,7 +33,7 @@ void apply_point_glow_light(Light L, vec3 wpos, vec3 dir, float max_dist, inout strength *= clamp(cam_dist_2 / 9.0, 0.25, 1.0); #endif - vec3 light_color = srgb_to_linear(L.light_col.rgb) * strength; + vec3 light_color = L.light_col.rgb * strength; const float LIGHT_AMBIANCE = 0.025; color += light_color diff --git a/common/benches/color_benchmark.rs b/common/benches/color_benchmark.rs index 7ed2483d62..a722660437 100644 --- a/common/benches/color_benchmark.rs +++ b/common/benches/color_benchmark.rs @@ -1,13 +1,13 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use vek::*; -use veloren_common::util::{linear_to_srgb, srgb_to_linear}; +use veloren_common::util::{linear_to_srgb, srgb_to_linear_fast}; fn criterion_benchmark(c: &mut Criterion) { let mut c = c.benchmark_group("color"); c.bench_function("srgb to linear (0.5, 0.1, 0.5)", |b| { b.iter(|| { - black_box(srgb_to_linear(black_box(Rgb::new(0.5, 0.1, 0.5)))); + black_box(srgb_to_linear_fast(black_box(Rgb::new(0.5, 0.1, 0.5)))); }) }); c.bench_function("linear to srgb (0.5, 0.1, 0.5)", |b| { diff --git a/common/src/util/color.rs b/common/src/util/color.rs index 0ac8009f27..6fe1a2eefb 100644 --- a/common/src/util/color.rs +++ b/common/src/util/color.rs @@ -1,8 +1,9 @@ use vek::{Mat3, Rgb, Rgba, Vec3}; +/// This function is optimized for speed over perfect accuracy #[inline(always)] #[allow(clippy::excessive_precision)] -pub fn srgb_to_linear(col: Rgb) -> Rgb { +pub fn srgb_to_linear_fast(col: Rgb) -> Rgb { col.map(|c| { if c <= 0.104 { c * 0.08677088 @@ -12,6 +13,19 @@ pub fn srgb_to_linear(col: Rgb) -> Rgb { }) } +/// directly converted from 'vec3 srgb_to_linear(vec3 srgb)' function in +/// 'srgb.glsl' +#[inline(always)] +pub fn srgb_to_linear(col: Rgb) -> Rgb { + col.map(|c| { + if c <= 0.04045 { + c / 12.92 + } else { + f32::powf((c + 0.055) / 1.055, 2.4) + } + }) +} + #[inline(always)] #[allow(clippy::excessive_precision)] pub fn linear_to_srgb(col: Rgb) -> Rgb { @@ -29,7 +43,7 @@ pub fn linear_to_srgb(col: Rgb) -> Rgb { #[inline(always)] pub fn srgba_to_linear(col: Rgba) -> Rgba { - Rgba::from_translucent(srgb_to_linear(Rgb::from(col)), col.a) + Rgba::from_translucent(srgb_to_linear_fast(Rgb::from(col)), col.a) } #[inline(always)] @@ -133,7 +147,7 @@ pub fn xyy_to_rgb(xyy: Vec3) -> Rgb { // TO-DO: speed this up #[inline(always)] pub fn saturate_srgb(col: Rgb, value: f32) -> Rgb { - let mut hsv = rgb_to_hsv(srgb_to_linear(col)); + let mut hsv = rgb_to_hsv(srgb_to_linear_fast(col)); hsv.y *= 1.0 + value; linear_to_srgb(hsv_to_rgb(hsv).map(|e| e.clamp(0.0, 1.0))) } @@ -142,8 +156,8 @@ pub fn saturate_srgb(col: Rgb, value: f32) -> Rgb { /// other #[inline(always)] pub fn chromify_srgb(luma: Rgb, chroma: Rgb) -> Rgb { - let l = rgb_to_xyy(srgb_to_linear(luma)).z; - let mut xyy = rgb_to_xyy(srgb_to_linear(chroma)); + let l = rgb_to_xyy(srgb_to_linear_fast(luma)).z; + let mut xyy = rgb_to_xyy(srgb_to_linear_fast(chroma)); xyy.z = l; linear_to_srgb(xyy_to_rgb(xyy).map(|e| e.clamp(0.0, 1.0))) diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index 7fe4ba71ba..7b4d60518b 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -19,7 +19,7 @@ pub mod ui; use super::{Consts, Renderer, Texture}; use crate::scene::camera::CameraMode; use bytemuck::{Pod, Zeroable}; -use common::terrain::BlockKind; +use common::{terrain::BlockKind, util::srgb_to_linear}; use std::marker::PhantomData; use vek::*; @@ -246,9 +246,12 @@ impl Default for Globals { impl Light { pub fn new(pos: Vec3, col: Rgb, strength: f32) -> Self { + let linearized_col = srgb_to_linear(col); + Self { pos: Vec4::from(pos).into_array(), - col: (Rgba::new(col.r, col.g, col.b, 0.0) * strength).into_array(), + col: (Rgba::new(linearized_col.r, linearized_col.g, linearized_col.b, 0.0) * strength) + .into_array(), } } diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index 3d987939f0..5cabb93f1b 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -92,12 +92,12 @@ pub fn load_mesh(mesh_name: &str, position: Vec3) -> BoneMeshes { } fn recolor_grey(rgb: Rgb, color: Rgb) -> Rgb { - use common::util::{linear_to_srgb, srgb_to_linear}; + use common::util::{linear_to_srgb, srgb_to_linear_fast}; const BASE_GREY: f32 = 178.0; if rgb.r == rgb.g && rgb.g == rgb.b { - let c1 = srgb_to_linear(rgb.map(|e| e as f32 / BASE_GREY)); - let c2 = srgb_to_linear(color.map(|e| e as f32 / 255.0)); + let c1 = srgb_to_linear_fast(rgb.map(|e| e as f32 / BASE_GREY)); + let c2 = srgb_to_linear_fast(color.map(|e| e as f32 / 255.0)); linear_to_srgb(c1 * c2).map(|e| (e.clamp(0.0, 1.0) * 255.0) as u8) } else { diff --git a/voxygen/src/ui/graphic/renderer.rs b/voxygen/src/ui/graphic/renderer.rs index 477c0adb74..5430a0323a 100644 --- a/voxygen/src/ui/graphic/renderer.rs +++ b/voxygen/src/ui/graphic/renderer.rs @@ -1,6 +1,6 @@ use common::{ figure::Segment, - util::{linear_to_srgba, srgb_to_linear}, + util::{linear_to_srgba, srgb_to_linear_fast}, vol::{FilledVox, IntoFullVolIterator, ReadVol, SizedVol}, }; use euc::{buffer::Buffer2d, rasterizer, Pipeline}; @@ -97,7 +97,7 @@ impl Pipeline for Voxel { let diffuse = norm.dot(-self.light_dir).max(0.0); let brightness = 2.5; let light = Rgb::from(*ao_level as f32 / 4.0) * (diffuse + ambiance) * brightness; - let color = light * srgb_to_linear(*col); + let color = light * srgb_to_linear_fast(*col); let position = (self.mvp * Vec4::from_point(*pos)).into_array(); (position, VsOut(Rgba::from_opaque(color))) }