feat: add classic colors in 3d
This commit is contained in:
+26
-14
@@ -39,8 +39,13 @@ const INTERACT_DOWNSCALE: u32 = 2;
|
|||||||
const INTERACT_SETTLE: f64 = 0.12;
|
const INTERACT_SETTLE: f64 = 0.12;
|
||||||
/// Palette names; index maps to `palette_id` in the shader.
|
/// Palette names; index maps to `palette_id` in the shader.
|
||||||
const PALETTE_NAMES: &[&str] = &["Amber", "Rainbow", "Ember", "Lime", "Grayscale"];
|
const PALETTE_NAMES: &[&str] = &["Amber", "Rainbow", "Ember", "Lime", "Grayscale"];
|
||||||
/// Shadow palette names; index maps to `palette_id` in the shader.
|
/// Shadow palette names; index maps to `shadow_palette_id` in the shader.
|
||||||
const SHADOW_PALETTE_NAMES: &[&str] = &["Grayscale", "Red & Blue", "Custom lights"];
|
/// Append new entries: share links store the index.
|
||||||
|
const SHADOW_PALETTE_NAMES: &[&str] = &["Grayscale", "Red & Blue", "Custom lights", "Classic"];
|
||||||
|
/// Shadow palette lit by the user's `lights` list.
|
||||||
|
const SHADOW_PALETTE_CUSTOM_LIGHTS: u32 = 2;
|
||||||
|
/// Shadow palette that paints the classic escape-time palette, lit.
|
||||||
|
const SHADOW_PALETTE_CLASSIC: u32 = 3;
|
||||||
/// Buddhabrot tonemap style names; index maps to `BuddhabrotUniforms::palette`.
|
/// Buddhabrot tonemap style names; index maps to `BuddhabrotUniforms::palette`.
|
||||||
const BUDDHA_PALETTE_NAMES: &[&str] = &["Nebula", "Yellow", "Grayscale"];
|
const BUDDHA_PALETTE_NAMES: &[&str] = &["Nebula", "Yellow", "Grayscale"];
|
||||||
|
|
||||||
@@ -992,6 +997,13 @@ impl FractalApp {
|
|||||||
self.last_request = Some(key);
|
self.last_request = Some(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the classic escape-time palette (and its scale / offset /
|
||||||
|
/// palette controls) is in use: always in classic mode, and in shadow/3D
|
||||||
|
/// modes under the "Classic" shading palette.
|
||||||
|
fn uses_classic_palette(&self) -> bool {
|
||||||
|
self.rendering_mode == 0 || self.shadow_palette == SHADOW_PALETTE_CLASSIC
|
||||||
|
}
|
||||||
|
|
||||||
/// The rendering mode the shaders should use this frame: 3D for as long
|
/// The rendering mode the shaders should use this frame: 3D for as long
|
||||||
/// as the 2D <-> 3D camera transition is in flight (the raymarcher, and
|
/// as the 2D <-> 3D camera transition is in flight (the raymarcher, and
|
||||||
/// the DE heights it reads, stay on until the camera is back top-down),
|
/// the DE heights it reads, stay on until the camera is back top-down),
|
||||||
@@ -1801,7 +1813,16 @@ impl FractalApp {
|
|||||||
ui.separator();
|
ui.separator();
|
||||||
ui.add_space(4.);
|
ui.add_space(4.);
|
||||||
|
|
||||||
if self.rendering_mode == 0 {
|
if self.rendering_mode != 0 {
|
||||||
|
egui::ComboBox::from_label("shading")
|
||||||
|
.selected_text(SHADOW_PALETTE_NAMES[self.shadow_palette as usize])
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for (i, name) in SHADOW_PALETTE_NAMES.iter().enumerate() {
|
||||||
|
ui.selectable_value(&mut self.shadow_palette, i as u32, *name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if self.uses_classic_palette() {
|
||||||
ui.add(
|
ui.add(
|
||||||
egui::Slider::new(&mut self.color_scale, 0.01..=1.0)
|
egui::Slider::new(&mut self.color_scale, 0.01..=1.0)
|
||||||
.text("color scale")
|
.text("color scale")
|
||||||
@@ -1815,17 +1836,8 @@ impl FractalApp {
|
|||||||
ui.selectable_value(&mut self.palette, i as u32, *name);
|
ui.selectable_value(&mut self.palette, i as u32, *name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
egui::ComboBox::from_label("palette")
|
|
||||||
.selected_text(SHADOW_PALETTE_NAMES[self.shadow_palette as usize])
|
|
||||||
.show_ui(ui, |ui| {
|
|
||||||
for (i, name) in SHADOW_PALETTE_NAMES.iter().enumerate() {
|
|
||||||
ui.selectable_value(&mut self.shadow_palette, i as u32, *name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if self.rendering_mode > 0 && self.shadow_palette as usize == SHADOW_PALETTE_NAMES.len() - 1
|
if self.rendering_mode != 0 && self.shadow_palette == SHADOW_PALETTE_CUSTOM_LIGHTS {
|
||||||
{
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label("lights:");
|
ui.label("lights:");
|
||||||
if ui.button("+").clicked() {
|
if ui.button("+").clicked() {
|
||||||
@@ -1848,7 +1860,7 @@ impl FractalApp {
|
|||||||
ui.add_space(4.);
|
ui.add_space(4.);
|
||||||
|
|
||||||
ui.collapsing("Animation", |ui| {
|
ui.collapsing("Animation", |ui| {
|
||||||
if self.rendering_mode == 0 {
|
if self.uses_classic_palette() {
|
||||||
ui.checkbox(&mut self.anim.color, "Cycle colours")
|
ui.checkbox(&mut self.anim.color, "Cycle colours")
|
||||||
.on_hover_text("Scroll the palette offset over time.");
|
.on_hover_text("Scroll the palette offset over time.");
|
||||||
if self.anim.color {
|
if self.anim.color {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ fn shadow_fragment(pos: vec2<f32>) -> vec4<f32> {
|
|||||||
let size = textureDimensions(data_tex);
|
let size = textureDimensions(data_tex);
|
||||||
let here = textureLoad(data_tex, vec2<i32>(x, y), 0);
|
let here = textureLoad(data_tex, vec2<i32>(x, y), 0);
|
||||||
if here.b != 0. {
|
if here.b != 0. {
|
||||||
return vec4<f32>(0.1, 0.1, 0.1, 1.0);
|
return vec4<f32>(shadow_interior_color(), 1.0);
|
||||||
}
|
}
|
||||||
// Forward differences, except on the last column/row where x+1 / y+1
|
// Forward differences, except on the last column/row where x+1 / y+1
|
||||||
// is off the texture: fall back to a backward difference, mirrored
|
// is off the texture: fall back to a backward difference, mirrored
|
||||||
@@ -45,7 +45,7 @@ fn shadow_fragment(pos: vec2<f32>) -> vec4<f32> {
|
|||||||
h2 = 2.0 * h0 - textureLoad(data_tex, vec2<i32>(x, y - 1), 0).g;
|
h2 = 2.0 * h0 - textureLoad(data_tex, vec2<i32>(x, y - 1), 0).g;
|
||||||
}
|
}
|
||||||
let normal = normal_from_heights(h0, h1, h2);
|
let normal = normal_from_heights(h0, h1, h2);
|
||||||
return vec4<f32>(shadow_color(normal), 1.0);
|
return vec4<f32>(shadow_color(normal, here.r), 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
|
|||||||
@@ -141,10 +141,12 @@ fn normal_from_heights(h0: f32, h1: f32, h2: f32) -> vec3<f32> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shade a DE-derived surface normal per `u.shadow_palette_id`: 0 = grayscale
|
// Shade a DE-derived surface normal per `u.shadow_palette_id`: 0 = grayscale
|
||||||
// key light, 1 = red/blue two-tone, 2 = the user's custom `lights` list.
|
// key light, 1 = red/blue two-tone, 2 = the user's custom `lights` list,
|
||||||
|
// 3 = the classic escape-time palette at `ci` (the smoothed iteration count),
|
||||||
|
// lit by the grayscale key light.
|
||||||
// Shared by the interactive shadow pass (colorize.wgsl) and the PNG-export
|
// Shared by the interactive shadow pass (colorize.wgsl) and the PNG-export
|
||||||
// shadow path (mandelbrot.wgsl's `fs_color`), which must render identically.
|
// shadow path (mandelbrot.wgsl's `fs_color`), which must render identically.
|
||||||
fn shadow_color(normal: vec3<f32>) -> vec3<f32> {
|
fn shadow_color(normal: vec3<f32>, ci: f32) -> vec3<f32> {
|
||||||
var color: vec3<f32>;
|
var color: vec3<f32>;
|
||||||
if u.shadow_palette_id == 0u {
|
if u.shadow_palette_id == 0u {
|
||||||
color = compute_light(normal, vec3<f32>(0.57735027, 0.57735027, 0.57735027)) + vec3<f32>(0.58, 0.85, 1.) * 0.2;
|
color = compute_light(normal, vec3<f32>(0.57735027, 0.57735027, 0.57735027)) + vec3<f32>(0.58, 0.85, 1.) * 0.2;
|
||||||
@@ -155,6 +157,14 @@ fn shadow_color(normal: vec3<f32>) -> vec3<f32> {
|
|||||||
color = compute_light(normal, vec3<f32>(0., 0.70710678, 0.70710678)) * vec3<f32>(1., 0.5, 0.5) + compute_light(normal, vec3<f32>(0.70710678, 0., 0.70710678)) * vec3<f32>(0.5, 1., 1.);
|
color = compute_light(normal, vec3<f32>(0., 0.70710678, 0.70710678)) * vec3<f32>(1., 0.5, 0.5) + compute_light(normal, vec3<f32>(0.70710678, 0., 0.70710678)) * vec3<f32>(0.5, 1., 1.);
|
||||||
|
|
||||||
color = filmic(color, 4.2);
|
color = filmic(color, 4.2);
|
||||||
|
} else if u.shadow_palette_id == 3u {
|
||||||
|
// No DE darkening as in `classic_color`: in shadow/3D modes the DE
|
||||||
|
// is a height (clamped to 1000, not 1), and the lighting already
|
||||||
|
// shows the relief.
|
||||||
|
let t = fract(ci * u.color_scale + u.color_offset);
|
||||||
|
let ambient = 0.25;
|
||||||
|
let light = compute_light(normal, vec3<f32>(0.57735027, 0.57735027, 0.57735027));
|
||||||
|
color = palette(u.palette_id, t) * (ambient + (1.0 - ambient) * light);
|
||||||
} else {
|
} else {
|
||||||
color = vec3<f32>(0);
|
color = vec3<f32>(0);
|
||||||
let light_count = min(u.light_count, 16u);
|
let light_count = min(u.light_count, 16u);
|
||||||
@@ -167,3 +177,12 @@ fn shadow_color(normal: vec3<f32>) -> vec3<f32> {
|
|||||||
}
|
}
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Colour of an interior (non-escaped) pixel in shadow/3D modes: black under
|
||||||
|
// the classic palette, like classic 2D mode, otherwise a dark gray plateau.
|
||||||
|
fn shadow_interior_color() -> vec3<f32> {
|
||||||
|
if u.shadow_palette_id == 3u {
|
||||||
|
return vec3<f32>(0.0);
|
||||||
|
}
|
||||||
|
return vec3<f32>(0.1);
|
||||||
|
}
|
||||||
|
|||||||
@@ -595,12 +595,12 @@ fn fs_color(in: VsOut) -> @location(0) vec4<f32> {
|
|||||||
// neighbours a `dx`/`dy` step away.
|
// neighbours a `dx`/`dy` step away.
|
||||||
let here = aggregate_sample(base, dx, dy, px, aa);
|
let here = aggregate_sample(base, dx, dy, px, aa);
|
||||||
if here.z != 0.0 {
|
if here.z != 0.0 {
|
||||||
return vec4<f32>(0.1, 0.1, 0.1, 1.0);
|
return vec4<f32>(shadow_interior_color(), 1.0);
|
||||||
}
|
}
|
||||||
let right = aggregate_sample(base + dx, dx, dy, px, aa);
|
let right = aggregate_sample(base + dx, dx, dy, px, aa);
|
||||||
let down = aggregate_sample(base + dy, dx, dy, px, aa);
|
let down = aggregate_sample(base + dy, dx, dy, px, aa);
|
||||||
let normal = normal_from_heights(here.y, right.y, down.y);
|
let normal = normal_from_heights(here.y, right.y, down.y);
|
||||||
return vec4<f32>(shadow_color(normal), 1.0);
|
return vec4<f32>(shadow_color(normal, here.x), 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let inv = 1.0 / f32(aa);
|
let inv = 1.0 / f32(aa);
|
||||||
|
|||||||
Reference in New Issue
Block a user