Skip to content

Commit

Permalink
Color improvements (#20)
Browse files Browse the repository at this point in the history
* Rename

* Srgba functionality

* Rename

* Docs

* to_linear_srgba returns Vec4

* Rename to_linear_srgba to to_linear_srgb
  • Loading branch information
asny authored Jul 31, 2023
1 parent ed095d9 commit 5f8c675
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 122 deletions.
2 changes: 1 addition & 1 deletion src/geometry/point_cloud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub struct PointCloud {
/// The positions of the points.
pub positions: Positions,
/// The colors of the points.
pub colors: Option<Vec<Color>>,
pub colors: Option<Vec<Srgba>>,
}

impl std::fmt::Debug for PointCloud {
Expand Down
3 changes: 1 addition & 2 deletions src/geometry/tri_mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ pub struct TriMesh {
/// The uv coordinates of the vertices.
pub uvs: Option<Vec<Vec2>>,
/// The colors of the vertices.
/// The colors are assumed to be in linear space.
pub colors: Option<Vec<Color>>,
pub colors: Option<Vec<Srgba>>,
}

impl std::default::Default for TriMesh {
Expand Down
6 changes: 3 additions & 3 deletions src/io/gltf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ fn parse_model(mesh: &::gltf::mesh::Mesh, buffers: &[::gltf::buffer::Data]) -> R
let colors = reader.read_colors(0).map(|values| {
values
.into_rgba_u8()
.map(|c| Color::new(c[0], c[1], c[2], c[3]))
.map(|c| Srgba::new(c[0], c[1], c[2], c[3]))
.collect()
});

Expand Down Expand Up @@ -319,7 +319,7 @@ fn parse_material(
};
Ok(PbrMaterial {
name: material_name(material),
albedo: Color::from_rgba_slice(&color),
albedo: color.into(),
albedo_texture,
metallic: pbr.metallic_factor(),
roughness: pbr.roughness_factor(),
Expand All @@ -329,7 +329,7 @@ fn parse_material(
occlusion_texture,
occlusion_strength,
occlusion_metallic_roughness_texture: None,
emissive: Color::from_rgb_slice(&material.emissive_factor()),
emissive: material.emissive_factor().into(),
emissive_texture,
transmission: material
.transmission()
Expand Down
5 changes: 3 additions & 2 deletions src/io/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,13 @@ pub fn deserialize_obj(raw_assets: &mut RawAssets, path: &PathBuf) -> Result<Sce

materials.push(PbrMaterial {
name: material.name,
albedo: Color::from_rgba_slice(&[
albedo: [
color.r as f32,
color.g as f32,
color.b as f32,
material.alpha as f32,
]),
]
.into(),
albedo_texture,
metallic: ((material.color_specular.r
+ material.color_specular.g
Expand Down
2 changes: 1 addition & 1 deletion src/io/pcd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub fn deserialize_pcd(raw_assets: &mut RawAssets, path: &PathBuf) -> Result<Sce
pcd_rs::Field::F32(ref v) => v[0].to_ne_bytes(),
_ => unimplemented!(),
};
Color {
Srgba {
r: t[2],
g: t[1],
b: t[0],
Expand Down
16 changes: 8 additions & 8 deletions src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//!

#[doc(inline)]
pub use crate::{prelude::Color, texture::texture2d::*};
pub use crate::{prelude::Srgba, texture::texture2d::*};

/// Lighting models which specify how the lighting is computed when rendering a material.
/// This is a trade-off between how fast the computations are versus how physically correct they look.
Expand Down Expand Up @@ -47,9 +47,9 @@ pub enum NormalDistributionFunction {
pub struct PbrMaterial {
/// Name. Used for matching geometry and material.
pub name: String,
/// Albedo base color, also called diffuse color. Assumed to be in HDR or linear sRGB color space.
pub albedo: Color,
/// Texture with albedo base colors, also called diffuse color. Assumed to be in sRGB (`RgbU8`), sRGB with an alpha channel (`RgbaU8`) or HDR.
/// Albedo base color, also called diffuse color.
pub albedo: Srgba,
/// Texture with albedo base colors, also called diffuse colors.
pub albedo_texture: Option<Texture2D>,
/// A value in the range `[0..1]` specifying how metallic the material is.
pub metallic: f32,
Expand All @@ -74,8 +74,8 @@ pub struct PbrMaterial {
/// A tangent space normal map, also known as bump map.
pub normal_texture: Option<Texture2D>,
/// Color of light shining from an object.
pub emissive: Color,
/// Texture with color of light shining from an object. Assumed to be in sRGB (`RgbU8`), sRGB with an alpha channel (`RgbaU8`) or HDR.
pub emissive: Srgba,
/// Texture with color of light shining from an object.
pub emissive_texture: Option<Texture2D>,
/// Alpha cutout value for transparency in deferred rendering pipeline.
pub alpha_cutout: Option<f32>,
Expand All @@ -93,7 +93,7 @@ impl Default for PbrMaterial {
fn default() -> Self {
Self {
name: "default".to_string(),
albedo: Color::WHITE,
albedo: Srgba::WHITE,
albedo_texture: None,
occlusion_metallic_roughness_texture: None,
metallic_roughness_texture: None,
Expand All @@ -103,7 +103,7 @@ impl Default for PbrMaterial {
occlusion_strength: 1.0,
normal_texture: None,
normal_scale: 1.0,
emissive: Color::BLACK,
emissive: Srgba::BLACK,
emissive_texture: None,
index_of_refraction: 1.5,
transmission: 0.0,
Expand Down
200 changes: 141 additions & 59 deletions src/prelude/color.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use super::math::*;
use crate::prelude::*;

/// Represents a color composed of a red, green and blue component.
/// Represents a color composed of a red, green and blue component in the sRGB color space.
/// In addition, the alpha value determines the how transparent the color is (0 is fully transparent and 255 is fully opaque).
#[deprecated = "Renamed to Srgba"]
pub type Color = Srgba;

/// Represents a color composed of a red, green and blue component in the sRGB color space.
/// In addition, the alpha value determines the how transparent the color is (0 is fully transparent and 255 is fully opaque).
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Color {
pub struct Srgba {
/// Red component
pub r: u8,
/// Green component
Expand All @@ -15,97 +20,174 @@ pub struct Color {
pub a: u8,
}

impl Color {
impl Srgba {
///
/// Creates a new color with the given values.
/// Creates a new sRGBA color with the given values.
///
pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}

///
/// Creates a new color with the given red, green and blue values and an alpha value of 255.
/// Creates a new sRGB color with the given red, green and blue values and an alpha value of 255.
///
pub const fn new_opaque(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b, a: 255 }
}

///
/// Creates a new color from three float elements where each element are in the range `0.0..=1.0`.
/// Returns the color in linear sRGB color space.
///
pub fn from_rgb_slice(rgba: &[f32; 3]) -> Self {
pub fn to_linear_srgb(&self) -> Vec4 {
let convert = |c: u8| {
let c = c as f32 / 255.0;
if c < 0.04045 {
c / 12.92
} else {
((c + 0.055) / 1.055).powf(2.4)
}
};
vec4(
convert(self.r),
convert(self.g),
convert(self.b),
self.a as f32 / 255.0,
)
}

/// Opaque red
pub const RED: Self = Self::new_opaque(255, 0, 0);
/// Opaque green
pub const GREEN: Self = Self::new_opaque(0, 255, 0);
/// Opaque blue
pub const BLUE: Self = Self::new_opaque(0, 0, 255);
/// Opaque white
pub const WHITE: Self = Self::new_opaque(255, 255, 255);
/// Opaque black
pub const BLACK: Self = Self::new_opaque(0, 0, 0);
}

impl From<[f32; 3]> for Srgba {
fn from(value: [f32; 3]) -> Self {
Self {
r: (rgba[0] * 255.0) as u8,
g: (rgba[1] * 255.0) as u8,
b: (rgba[2] * 255.0) as u8,
..Default::default()
r: (value[0] * 255.0) as u8,
g: (value[1] * 255.0) as u8,
b: (value[2] * 255.0) as u8,
a: 255,
}
}
}

///
/// Creates a new color from four float elements where each element are in the range `0.0..=1.0`.
///
pub fn from_rgba_slice(rgba: &[f32; 4]) -> Self {
impl From<[f32; 4]> for Srgba {
fn from(value: [f32; 4]) -> Self {
Self {
r: (rgba[0] * 255.0) as u8,
g: (rgba[1] * 255.0) as u8,
b: (rgba[2] * 255.0) as u8,
a: (rgba[3] * 255.0) as u8,
r: (value[0] * 255.0) as u8,
g: (value[1] * 255.0) as u8,
b: (value[2] * 255.0) as u8,
a: (value[3] * 255.0) as u8,
}
}
}
impl From<Vec3> for Srgba {
fn from(value: Vec3) -> Self {
Self {
r: (value.x * 255.0) as u8,
g: (value.y * 255.0) as u8,
b: (value.z * 255.0) as u8,
a: 255,
}
}
}

/// Opaque red
pub const RED: Color = Color::new_opaque(255, 0, 0);
/// Opaque green
pub const GREEN: Color = Color::new_opaque(0, 255, 0);
/// Opaque blue
pub const BLUE: Color = Color::new_opaque(0, 0, 255);
/// Opaque white
pub const WHITE: Color = Color::new_opaque(255, 255, 255);
/// Opaque black
pub const BLACK: Color = Color::new_opaque(0, 0, 0);

/// Convert to [`Vec3`] by mapping the red, green and blue component to the range `0.0..=1.0`.
pub fn to_vec3(&self) -> Vec3 {
Vec3::new(
self.r as f32 / 255.0,
self.g as f32 / 255.0,
self.b as f32 / 255.0,
)
impl From<Vec4> for Srgba {
fn from(value: Vec4) -> Self {
Self {
r: (value.x * 255.0) as u8,
g: (value.y * 255.0) as u8,
b: (value.z * 255.0) as u8,
a: (value.w * 255.0) as u8,
}
}
}

/// Convert to [`Vec4`] by mapping each component to the range `0.0..=1.0`.
pub fn to_vec4(&self) -> Vec4 {
Vec4::new(
self.r as f32 / 255.0,
self.g as f32 / 255.0,
self.b as f32 / 255.0,
self.a as f32 / 255.0,
)
impl From<[u8; 3]> for Srgba {
fn from(value: [u8; 3]) -> Self {
Self {
r: value[0],
g: value[1],
b: value[2],
a: 255,
}
}
}

impl From<[u8; 4]> for Srgba {
fn from(value: [u8; 4]) -> Self {
Self {
r: value[0],
g: value[1],
b: value[2],
a: value[3],
}
}
}

/// Convert to a slice by mapping the red, green and blue component to the range `0.0..=1.0`.
pub fn to_rgb_slice(&self) -> [f32; 3] {
impl From<Srgba> for [f32; 3] {
fn from(value: Srgba) -> Self {
[
self.r as f32 / 255.0,
self.g as f32 / 255.0,
self.b as f32 / 255.0,
value.r as f32 / 255.0,
value.g as f32 / 255.0,
value.b as f32 / 255.0,
]
}
}

/// Convert to a slice by mapping each component to the range `0.0..=1.0`.
pub fn to_rgba_slice(&self) -> [f32; 4] {
impl From<Srgba> for [f32; 4] {
fn from(value: Srgba) -> Self {
[
self.r as f32 / 255.0,
self.g as f32 / 255.0,
self.b as f32 / 255.0,
self.a as f32 / 255.0,
value.r as f32 / 255.0,
value.g as f32 / 255.0,
value.b as f32 / 255.0,
value.a as f32 / 255.0,
]
}
}

impl Default for Color {
impl From<Srgba> for Vec3 {
fn from(value: Srgba) -> Self {
vec3(
value.r as f32 / 255.0,
value.g as f32 / 255.0,
value.b as f32 / 255.0,
)
}
}

impl From<Srgba> for Vec4 {
fn from(value: Srgba) -> Self {
vec4(
value.r as f32 / 255.0,
value.g as f32 / 255.0,
value.b as f32 / 255.0,
value.a as f32 / 255.0,
)
}
}

impl From<Srgba> for [u8; 3] {
fn from(value: Srgba) -> Self {
[value.r, value.g, value.b]
}
}

impl From<Srgba> for [u8; 4] {
fn from(value: Srgba) -> Self {
[value.r, value.g, value.b, value.a]
}
}

impl Default for Srgba {
fn default() -> Self {
Color::WHITE
Self::WHITE
}
}
Loading

0 comments on commit 5f8c675

Please sign in to comment.