Added another color temperature based way of specifying color.
This uses a normalized version of blackbody radiation, so the colors still vary but the brightness doesn't vary nearly as wildly as with genuine blackbody radiation.
This commit is contained in:
parent
53754b956c
commit
1cd5d28767
|
@ -81,13 +81,17 @@ class PsychopathCamera(bpy.types.PropertyGroup):
|
||||||
class PsychopathLight(bpy.types.PropertyGroup):
|
class PsychopathLight(bpy.types.PropertyGroup):
|
||||||
color_type = EnumProperty(
|
color_type = EnumProperty(
|
||||||
name="Color Type", description="",
|
name="Color Type", description="",
|
||||||
items=[('Rec709', 'Rec709', ""), ('Blackbody', 'Blackbody', "")],
|
items=[
|
||||||
|
('Rec709', 'Rec709', ""),
|
||||||
|
('Blackbody', 'Blackbody', ""),
|
||||||
|
('ColorTemperature', 'ColorTemperature', "Same as Blackbody, except with brightness kept more even."),
|
||||||
|
],
|
||||||
default="Rec709"
|
default="Rec709"
|
||||||
)
|
)
|
||||||
|
|
||||||
color_blackbody_temp = FloatProperty(
|
color_blackbody_temp = FloatProperty(
|
||||||
name="Temperature", description="Blackbody temperature in kelvin",
|
name="Temperature", description="Blackbody temperature in kelvin",
|
||||||
min=0.0, max=32000.0, soft_min=800.0, soft_max=6500.0, default=1200.0
|
min=0.0, soft_min=800.0, soft_max=6500.0, default=1200.0
|
||||||
)
|
)
|
||||||
|
|
||||||
# Custom Mesh properties
|
# Custom Mesh properties
|
||||||
|
@ -107,7 +111,11 @@ class PsychopathMaterial(bpy.types.PropertyGroup):
|
||||||
|
|
||||||
color_type = EnumProperty(
|
color_type = EnumProperty(
|
||||||
name="Color Type", description="",
|
name="Color Type", description="",
|
||||||
items=[('Rec709', 'Rec709', ""), ('Blackbody', 'Blackbody', "")],
|
items=[
|
||||||
|
('Rec709', 'Rec709', ""),
|
||||||
|
('Blackbody', 'Blackbody', ""),
|
||||||
|
('ColorTemperature', 'ColorTemperature', "Same as Blackbody, except with brightness kept more even."),
|
||||||
|
],
|
||||||
default="Rec709"
|
default="Rec709"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -120,7 +128,7 @@ class PsychopathMaterial(bpy.types.PropertyGroup):
|
||||||
|
|
||||||
color_blackbody_temp = FloatProperty(
|
color_blackbody_temp = FloatProperty(
|
||||||
name="Temperature", description="Blackbody temperature in kelvin",
|
name="Temperature", description="Blackbody temperature in kelvin",
|
||||||
min=0.0, max=32000.0, soft_min=800.0, soft_max=6500.0, default=1200.0
|
min=0.0, soft_min=800.0, soft_max=6500.0, default=1200.0
|
||||||
)
|
)
|
||||||
|
|
||||||
roughness = FloatProperty(
|
roughness = FloatProperty(
|
||||||
|
|
|
@ -218,6 +218,8 @@ class SphereLamp:
|
||||||
self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
|
self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
|
||||||
elif self.ob.data.psychopath.color_type == 'Blackbody':
|
elif self.ob.data.psychopath.color_type == 'Blackbody':
|
||||||
self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
|
self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
|
||||||
|
elif self.ob.data.psychopath.color_type == 'ColorTemperature':
|
||||||
|
self.time_col += [('ColorTemperature', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
|
||||||
|
|
||||||
self.time_rad += [self.ob.data.shadow_soft_size]
|
self.time_rad += [self.ob.data.shadow_soft_size]
|
||||||
|
|
||||||
|
@ -234,6 +236,8 @@ class SphereLamp:
|
||||||
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
|
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
|
||||||
elif col[0] == 'Blackbody':
|
elif col[0] == 'Blackbody':
|
||||||
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
|
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
|
||||||
|
elif col[0] == 'ColorTemperature':
|
||||||
|
w.write("Color [color_temperature, %f %f]\n" % (col[1], col[2]))
|
||||||
for rad in self.time_rad:
|
for rad in self.time_rad:
|
||||||
w.write("Radius [%f]\n" % rad)
|
w.write("Radius [%f]\n" % rad)
|
||||||
|
|
||||||
|
@ -257,6 +261,8 @@ class RectLamp:
|
||||||
self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
|
self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
|
||||||
elif self.ob.data.psychopath.color_type == 'Blackbody':
|
elif self.ob.data.psychopath.color_type == 'Blackbody':
|
||||||
self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
|
self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
|
||||||
|
elif self.ob.data.psychopath.color_type == 'ColorTemperature':
|
||||||
|
self.time_col += [('ColorTemperature', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
|
||||||
|
|
||||||
if self.ob.data.shape == 'RECTANGLE':
|
if self.ob.data.shape == 'RECTANGLE':
|
||||||
self.time_dim += [(self.ob.data.size, self.ob.data.size_y)]
|
self.time_dim += [(self.ob.data.size, self.ob.data.size_y)]
|
||||||
|
@ -276,6 +282,8 @@ class RectLamp:
|
||||||
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
|
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
|
||||||
elif col[0] == 'Blackbody':
|
elif col[0] == 'Blackbody':
|
||||||
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
|
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
|
||||||
|
elif col[0] == 'ColorTemperature':
|
||||||
|
w.write("Color [color_temperature, %f %f]\n" % (col[1], col[2]))
|
||||||
for dim in self.time_dim:
|
for dim in self.time_dim:
|
||||||
w.write("Dimensions [%f %f]\n" % dim)
|
w.write("Dimensions [%f %f]\n" % dim)
|
||||||
|
|
||||||
|
@ -340,6 +348,11 @@ class Material:
|
||||||
self.mat.psychopath.color_blackbody_temp,
|
self.mat.psychopath.color_blackbody_temp,
|
||||||
1.0,
|
1.0,
|
||||||
))
|
))
|
||||||
|
elif self.mat.psychopath.color_type == 'ColorTemperature':
|
||||||
|
w.write("Color [color_temperature, %f %f]\n" % (
|
||||||
|
self.mat.psychopath.color_blackbody_temp,
|
||||||
|
1.0,
|
||||||
|
))
|
||||||
elif self.mat.psychopath.surface_shader_type == 'Lambert':
|
elif self.mat.psychopath.surface_shader_type == 'Lambert':
|
||||||
w.write("Type [Lambert]\n")
|
w.write("Type [Lambert]\n")
|
||||||
if self.mat.psychopath.color_type == 'Rec709':
|
if self.mat.psychopath.color_type == 'Rec709':
|
||||||
|
@ -352,6 +365,11 @@ class Material:
|
||||||
self.mat.psychopath.color_blackbody_temp,
|
self.mat.psychopath.color_blackbody_temp,
|
||||||
1.0,
|
1.0,
|
||||||
))
|
))
|
||||||
|
elif self.mat.psychopath.color_type == 'ColorTemperature':
|
||||||
|
w.write("Color [color_temperature, %f %f]\n" % (
|
||||||
|
self.mat.psychopath.color_blackbody_temp,
|
||||||
|
1.0,
|
||||||
|
))
|
||||||
elif self.mat.psychopath.surface_shader_type == 'GGX':
|
elif self.mat.psychopath.surface_shader_type == 'GGX':
|
||||||
w.write("Type [GGX]\n")
|
w.write("Type [GGX]\n")
|
||||||
if self.mat.psychopath.color_type == 'Rec709':
|
if self.mat.psychopath.color_type == 'Rec709':
|
||||||
|
@ -364,6 +382,11 @@ class Material:
|
||||||
self.mat.psychopath.color_blackbody_temp,
|
self.mat.psychopath.color_blackbody_temp,
|
||||||
1.0,
|
1.0,
|
||||||
))
|
))
|
||||||
|
elif self.mat.psychopath.color_type == 'ColorTemperature':
|
||||||
|
w.write("Color [color_temperature, %f %f]\n" % (
|
||||||
|
self.mat.psychopath.color_blackbody_temp,
|
||||||
|
1.0,
|
||||||
|
))
|
||||||
w.write("Roughness [%f]\n" % self.mat.psychopath.roughness)
|
w.write("Roughness [%f]\n" % self.mat.psychopath.roughness)
|
||||||
w.write("Fresnel [%f]\n" % self.mat.psychopath.fresnel)
|
w.write("Fresnel [%f]\n" % self.mat.psychopath.fresnel)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -130,7 +130,7 @@ class DATA_PT_psychopath_lamp(PsychopathPanel, bpy.types.Panel):
|
||||||
|
|
||||||
if ob.data.psychopath.color_type == 'Rec709':
|
if ob.data.psychopath.color_type == 'Rec709':
|
||||||
col.prop(ob.data, "color")
|
col.prop(ob.data, "color")
|
||||||
elif ob.data.psychopath.color_type == 'Blackbody':
|
elif ob.data.psychopath.color_type == 'Blackbody' or ob.data.psychopath.color_type == 'ColorTemperature':
|
||||||
col.prop(ob.data.psychopath, "color_blackbody_temp")
|
col.prop(ob.data.psychopath, "color_blackbody_temp")
|
||||||
|
|
||||||
col.prop(ob.data, "energy")
|
col.prop(ob.data, "energy")
|
||||||
|
@ -254,7 +254,7 @@ class MATERIAL_PT_psychopath_surface(PsychopathPanel, bpy.types.Panel):
|
||||||
col.prop(mat.psychopath, "color_type")
|
col.prop(mat.psychopath, "color_type")
|
||||||
if mat.psychopath.color_type == 'Rec709':
|
if mat.psychopath.color_type == 'Rec709':
|
||||||
col.prop(mat.psychopath, "color")
|
col.prop(mat.psychopath, "color")
|
||||||
elif mat.psychopath.color_type == 'Blackbody':
|
elif mat.psychopath.color_type == 'Blackbody' or mat.psychopath.color_type == 'ColorTemperature':
|
||||||
col.prop(mat.psychopath, "color_blackbody_temp")
|
col.prop(mat.psychopath, "color_blackbody_temp")
|
||||||
|
|
||||||
if mat.psychopath.surface_shader_type == 'GTR':
|
if mat.psychopath.surface_shader_type == 'GTR':
|
||||||
|
|
|
@ -137,6 +137,8 @@ class DistantDiskLamp:
|
||||||
self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
|
self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
|
||||||
elif self.ob.data.psychopath.color_type == 'Blackbody':
|
elif self.ob.data.psychopath.color_type == 'Blackbody':
|
||||||
self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
|
self.time_col += [('Blackbody', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
|
||||||
|
elif self.ob.data.psychopath.color_type == 'ColorTemperature':
|
||||||
|
self.time_col += [('ColorTemperature', self.ob.data.psychopath.color_blackbody_temp, self.ob.data.energy)]
|
||||||
|
|
||||||
self.time_rad += [self.ob.data.shadow_soft_size]
|
self.time_rad += [self.ob.data.shadow_soft_size]
|
||||||
|
|
||||||
|
@ -151,6 +153,8 @@ class DistantDiskLamp:
|
||||||
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
|
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
|
||||||
elif col[0] == 'Blackbody':
|
elif col[0] == 'Blackbody':
|
||||||
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
|
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
|
||||||
|
elif col[0] == 'ColorTemperature':
|
||||||
|
w.write("Color [color_temperature, %f %f]\n" % (col[1], col[2]))
|
||||||
for rad in self.time_rad:
|
for rad in self.time_rad:
|
||||||
w.write("Radius [%f]\n" % rad)
|
w.write("Radius [%f]\n" % rad)
|
||||||
|
|
||||||
|
|
75
src/color.rs
75
src/color.rs
|
@ -46,6 +46,12 @@ pub enum Color {
|
||||||
temperature: f32, // In kelvin
|
temperature: f32, // In kelvin
|
||||||
factor: f32, // Brightness multiplier
|
factor: f32, // Brightness multiplier
|
||||||
},
|
},
|
||||||
|
// Same as Blackbody except with the spectrum's energy roughly
|
||||||
|
// normalized.
|
||||||
|
Temperature {
|
||||||
|
temperature: f32, // In kelvin
|
||||||
|
factor: f32, // Brightness multiplier
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
|
@ -62,6 +68,14 @@ impl Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new_temperature(temp: f32, fac: f32) -> Self {
|
||||||
|
Color::Temperature {
|
||||||
|
temperature: temp,
|
||||||
|
factor: fac,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_spectral_sample(self, hero_wavelength: f32) -> SpectralSample {
|
pub fn to_spectral_sample(self, hero_wavelength: f32) -> SpectralSample {
|
||||||
let wls = wavelengths(hero_wavelength);
|
let wls = wavelengths(hero_wavelength);
|
||||||
match self {
|
match self {
|
||||||
|
@ -84,6 +98,21 @@ impl Color {
|
||||||
hero_wavelength,
|
hero_wavelength,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Color::Temperature {
|
||||||
|
temperature,
|
||||||
|
factor,
|
||||||
|
} => {
|
||||||
|
SpectralSample::from_parts(
|
||||||
|
// TODO: make this SIMD
|
||||||
|
Float4::new(
|
||||||
|
plancks_law_normalized(temperature, wls.get_0()) * factor,
|
||||||
|
plancks_law_normalized(temperature, wls.get_1()) * factor,
|
||||||
|
plancks_law_normalized(temperature, wls.get_2()) * factor,
|
||||||
|
plancks_law_normalized(temperature, wls.get_3()) * factor,
|
||||||
|
),
|
||||||
|
hero_wavelength,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,10 +120,19 @@ impl Color {
|
||||||
///
|
///
|
||||||
/// Note: this really is very _approximate_.
|
/// Note: this really is very _approximate_.
|
||||||
pub fn approximate_energy(self) -> f32 {
|
pub fn approximate_energy(self) -> f32 {
|
||||||
// TODO: better approximation for Blackbody.
|
// TODO: better approximation for Blackbody and Temperature.
|
||||||
match self {
|
match self {
|
||||||
Color::XYZ(_, y, _) => y,
|
Color::XYZ(_, y, _) => y,
|
||||||
Color::Blackbody { factor, .. } => factor,
|
|
||||||
|
Color::Blackbody {
|
||||||
|
temperature,
|
||||||
|
factor,
|
||||||
|
} => {
|
||||||
|
let t2 = temperature * temperature;
|
||||||
|
t2 * t2 * factor
|
||||||
|
}
|
||||||
|
|
||||||
|
Color::Temperature { factor, .. } => factor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,6 +143,7 @@ impl Mul<f32> for Color {
|
||||||
fn mul(self, rhs: f32) -> Self {
|
fn mul(self, rhs: f32) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Color::XYZ(x, y, z) => Color::XYZ(x * rhs, y * rhs, z * rhs),
|
Color::XYZ(x, y, z) => Color::XYZ(x * rhs, y * rhs, z * rhs),
|
||||||
|
|
||||||
Color::Blackbody {
|
Color::Blackbody {
|
||||||
temperature,
|
temperature,
|
||||||
factor,
|
factor,
|
||||||
|
@ -112,6 +151,14 @@ impl Mul<f32> for Color {
|
||||||
temperature: temperature,
|
temperature: temperature,
|
||||||
factor: factor * rhs,
|
factor: factor * rhs,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Color::Temperature {
|
||||||
|
temperature,
|
||||||
|
factor,
|
||||||
|
} => Color::Temperature {
|
||||||
|
temperature: temperature,
|
||||||
|
factor: factor * rhs,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,6 +187,7 @@ impl Lerp for Color {
|
||||||
(y1 * inv_alpha) + (y2 * alpha),
|
(y1 * inv_alpha) + (y2 * alpha),
|
||||||
(z1 * inv_alpha) + (z2 * alpha),
|
(z1 * inv_alpha) + (z2 * alpha),
|
||||||
),
|
),
|
||||||
|
|
||||||
(
|
(
|
||||||
Color::Blackbody {
|
Color::Blackbody {
|
||||||
temperature: tmp1,
|
temperature: tmp1,
|
||||||
|
@ -153,6 +201,21 @@ impl Lerp for Color {
|
||||||
temperature: (tmp1 * inv_alpha) + (tmp2 * alpha),
|
temperature: (tmp1 * inv_alpha) + (tmp2 * alpha),
|
||||||
factor: (fac1 * inv_alpha) + (fac2 * alpha),
|
factor: (fac1 * inv_alpha) + (fac2 * alpha),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
(
|
||||||
|
Color::Temperature {
|
||||||
|
temperature: tmp1,
|
||||||
|
factor: fac1,
|
||||||
|
},
|
||||||
|
Color::Temperature {
|
||||||
|
temperature: tmp2,
|
||||||
|
factor: fac2,
|
||||||
|
},
|
||||||
|
) => Color::Temperature {
|
||||||
|
temperature: (tmp1 * inv_alpha) + (tmp2 * alpha),
|
||||||
|
factor: (fac1 * inv_alpha) + (fac2 * alpha),
|
||||||
|
},
|
||||||
|
|
||||||
_ => panic!("Cannot lerp colors with different representations."),
|
_ => panic!("Cannot lerp colors with different representations."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,6 +262,14 @@ fn plancks_law(temperature: f32, wavelength: f32) -> f32 {
|
||||||
(energy * 1.0e-6).max(0.0)
|
(energy * 1.0e-6).max(0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same as above, except normalized to keep roughly equal spectral
|
||||||
|
/// energy across temperatures. This makes it easier to use for
|
||||||
|
/// choosing colors without making brightness explode.
|
||||||
|
fn plancks_law_normalized(temperature: f32, wavelength: f32) -> f32 {
|
||||||
|
let t2 = temperature * temperature;
|
||||||
|
plancks_law(temperature, wavelength) * 4.0e7 / (t2 * t2)
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
|
|
@ -606,6 +606,16 @@ pub fn parse_color(contents: &str) -> Result<Color, PsyParseError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"color_temperature" => {
|
||||||
|
if let IResult::Done(_, (temperature, factor)) =
|
||||||
|
closure!(tuple!(ws_f32, ws_f32))(items[1].as_bytes())
|
||||||
|
{
|
||||||
|
return Ok(Color::new_temperature(temperature, factor));
|
||||||
|
} else {
|
||||||
|
return Err(PsyParseError::UnknownError(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ => return Err(PsyParseError::UnknownError(0)),
|
_ => return Err(PsyParseError::UnknownError(0)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user