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):
|
||||
color_type = EnumProperty(
|
||||
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"
|
||||
)
|
||||
|
||||
color_blackbody_temp = FloatProperty(
|
||||
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
|
||||
|
@ -107,7 +111,11 @@ class PsychopathMaterial(bpy.types.PropertyGroup):
|
|||
|
||||
color_type = EnumProperty(
|
||||
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"
|
||||
)
|
||||
|
||||
|
@ -120,7 +128,7 @@ class PsychopathMaterial(bpy.types.PropertyGroup):
|
|||
|
||||
color_blackbody_temp = FloatProperty(
|
||||
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(
|
||||
|
|
|
@ -218,6 +218,8 @@ class SphereLamp:
|
|||
self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
|
||||
elif self.ob.data.psychopath.color_type == 'Blackbody':
|
||||
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]
|
||||
|
||||
|
@ -234,6 +236,8 @@ class SphereLamp:
|
|||
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
|
||||
elif col[0] == 'Blackbody':
|
||||
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:
|
||||
w.write("Radius [%f]\n" % rad)
|
||||
|
||||
|
@ -257,6 +261,8 @@ class RectLamp:
|
|||
self.time_col += [('Rec709', self.ob.data.color * self.ob.data.energy)]
|
||||
elif self.ob.data.psychopath.color_type == 'Blackbody':
|
||||
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':
|
||||
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]))
|
||||
elif col[0] == 'Blackbody':
|
||||
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:
|
||||
w.write("Dimensions [%f %f]\n" % dim)
|
||||
|
||||
|
@ -340,6 +348,11 @@ class Material:
|
|||
self.mat.psychopath.color_blackbody_temp,
|
||||
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':
|
||||
w.write("Type [Lambert]\n")
|
||||
if self.mat.psychopath.color_type == 'Rec709':
|
||||
|
@ -352,6 +365,11 @@ class Material:
|
|||
self.mat.psychopath.color_blackbody_temp,
|
||||
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':
|
||||
w.write("Type [GGX]\n")
|
||||
if self.mat.psychopath.color_type == 'Rec709':
|
||||
|
@ -364,6 +382,11 @@ class Material:
|
|||
self.mat.psychopath.color_blackbody_temp,
|
||||
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("Fresnel [%f]\n" % self.mat.psychopath.fresnel)
|
||||
else:
|
||||
|
|
|
@ -130,7 +130,7 @@ class DATA_PT_psychopath_lamp(PsychopathPanel, bpy.types.Panel):
|
|||
|
||||
if ob.data.psychopath.color_type == 'Rec709':
|
||||
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, "energy")
|
||||
|
@ -254,7 +254,7 @@ class MATERIAL_PT_psychopath_surface(PsychopathPanel, bpy.types.Panel):
|
|||
col.prop(mat.psychopath, "color_type")
|
||||
if mat.psychopath.color_type == 'Rec709':
|
||||
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")
|
||||
|
||||
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)]
|
||||
elif self.ob.data.psychopath.color_type == 'Blackbody':
|
||||
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]
|
||||
|
||||
|
@ -151,6 +153,8 @@ class DistantDiskLamp:
|
|||
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
|
||||
elif col[0] == 'Blackbody':
|
||||
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:
|
||||
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
|
||||
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 {
|
||||
|
@ -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 {
|
||||
let wls = wavelengths(hero_wavelength);
|
||||
match self {
|
||||
|
@ -84,6 +98,21 @@ impl Color {
|
|||
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_.
|
||||
pub fn approximate_energy(self) -> f32 {
|
||||
// TODO: better approximation for Blackbody.
|
||||
// TODO: better approximation for Blackbody and Temperature.
|
||||
match self {
|
||||
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 {
|
||||
match self {
|
||||
Color::XYZ(x, y, z) => Color::XYZ(x * rhs, y * rhs, z * rhs),
|
||||
|
||||
Color::Blackbody {
|
||||
temperature,
|
||||
factor,
|
||||
|
@ -112,6 +151,14 @@ impl Mul<f32> for Color {
|
|||
temperature: temperature,
|
||||
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),
|
||||
(z1 * inv_alpha) + (z2 * alpha),
|
||||
),
|
||||
|
||||
(
|
||||
Color::Blackbody {
|
||||
temperature: tmp1,
|
||||
|
@ -153,6 +201,21 @@ impl Lerp for Color {
|
|||
temperature: (tmp1 * inv_alpha) + (tmp2 * 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."),
|
||||
}
|
||||
}
|
||||
|
@ -199,6 +262,14 @@ fn plancks_law(temperature: f32, wavelength: f32) -> f32 {
|
|||
(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)]
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user