Add image accumulation
This commit is contained in:
@@ -0,0 +1,106 @@
|
|||||||
|
//! # Fractal flame
|
||||||
|
//!
|
||||||
|
//! The `flam3` sources include example parameters (`test.flam3`) to demonstrate the fractal flame algorithm,
|
||||||
|
//! which are copied here and used to generate an image that can be compared against the reference `flam3` renderer
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use enkou_shaders::chaos_game::ChaosGame;
|
||||||
|
use enkou_shaders::transform::Transform;
|
||||||
|
use enkou_shaders::variation::{Variation, VariationKind};
|
||||||
|
use glam::{UVec2, UVec3, UVec4, Vec2, Vec2Swizzles, Vec4};
|
||||||
|
use rand::SeedableRng;
|
||||||
|
use rand_xoshiro::Xoshiro256StarStar;
|
||||||
|
use enkou_shaders::camera::Camera;
|
||||||
|
use enkou_shaders::image::BlendMode;
|
||||||
|
|
||||||
|
const ITERATIONS_DISCARD: usize = 100;
|
||||||
|
const IMAGE_DIMENSIONS: UVec2 = UVec2::new(640, 480);
|
||||||
|
const IMAGE_QUALITY: f32 = 10.0;
|
||||||
|
|
||||||
|
// Palette data copied from `flam3-palettes.xml`.
|
||||||
|
// Palettes are ASCII hexadecimal strings; two characters form a byte,
|
||||||
|
// and bytes are grouped in triplets to get a single RGB color.
|
||||||
|
const PALETTE: &'static str = "\
|
||||||
|
00dadebc00eee6c500eef2ce00eef2cf00e6eee100eaeed800f2f1eb00f2f5d8
|
||||||
|
00e6f2ce00deeac500d6dac600ced2bc00c2caa900becaa000ced6aa00dee2c5
|
||||||
|
00eaedce00eaf2c500dee2c500c2caaa00aebeaa00a5b29600a2a98d0096a284
|
||||||
|
008d8d7a0085897100858d710085856700797d6700797d670071795e00656d55
|
||||||
|
004d5d4200344025003040250030381c002c3c1c002c341c00242c1200242400
|
||||||
|
00242c0900283409003840120030401c0040502f0055694200657555006c7d5e
|
||||||
|
00748d710074898400748d8400788d840079897a0079857100757d670071795e
|
||||||
|
006c715e006d705e006c795e0068755e00697155006d7555006d755500697155
|
||||||
|
0065715500696d550064715e006870670068706700686c67006c6c5e0071715e
|
||||||
|
0079796700818571007d91710085927a0085927a007d9284007992840078928d
|
||||||
|
00788d8d00748d84007492840075927a006c85670064795e0059694b00aa5700
|
||||||
|
0038441c00303c1c002c3c1c003440250050614b005d6d5e0064715e0060715e
|
||||||
|
0060755e0068755e006c795e006c795e0071796700707967006c7d6700687967
|
||||||
|
006c7967006c75670071755e0071755e0075795e00757d5e00818d5e008d925e
|
||||||
|
008d9267009a9a71009aa27a009aa27a009aa17a00929a71008992670081855e
|
||||||
|
007d7d550069794b00616d4200444c250038441c0040512500454d2500716d42
|
||||||
|
00797d4b00817d5500797955006d755500697d55006c795e006579540068795e
|
||||||
|
00647967006479670068755e0064715e00646c5e00656d55004d584200344025
|
||||||
|
002c381c0020281c001c1409001818000004140000081000000c1800001c2809
|
||||||
|
00243012003c4425005d6555007579550085895e008991710096a271009aa27a
|
||||||
|
009eaa7a009eaa7a00aaae7100a6aa7a00a2aa7a00a1a57a00969e7a0085967a
|
||||||
|
0081927a0078927a0075927a00758d7a00708167007d7d670089896700929a71
|
||||||
|
009eaa7a00aab68400b2b68d00b6ba9700c2ca9700b2be8d00b2b68d00aab28d
|
||||||
|
00a2ae84009aa67a00929e7a00859a7a007d967a007d927a007d9284007d9284
|
||||||
|
0081968400859684008596840081928400859a8400859a84008d9a8400929684
|
||||||
|
009ea98400aeb28400aaba8400b2be8d00b6c2a000c6caa000c6ceaa00d6dab3
|
||||||
|
00dae2c500d2d6bc00bec2a000aab68d009ea67a00929a710089897100817d67
|
||||||
|
007d7d6700817867007d7d5e0079795e0079815e00817d6700817d6700818167
|
||||||
|
008189710085917a0089927a00969d7a00969e7a0092968400969a8d00929284
|
||||||
|
0089918400819284007d928d0078928d0074928d0078928d007896970081968d
|
||||||
|
0081968d00819a8d00859a8d00899e8d00899e8d008da2970095a297008da297
|
||||||
|
0096a68d009aa18d009ea984009ea67a00a2a571009ea671009aa67100959d71";
|
||||||
|
|
||||||
|
/// Generate an image
|
||||||
|
pub fn main() -> Result<()> {
|
||||||
|
let transforms: &[Transform] = &[];
|
||||||
|
|
||||||
|
let weights = [0.25; 4];
|
||||||
|
|
||||||
|
let variations = [Variation::new(
|
||||||
|
VariationKind::Spherical,
|
||||||
|
1.0,
|
||||||
|
[0.0; 4].into(),
|
||||||
|
)];
|
||||||
|
|
||||||
|
let camera = Camera::new(IMAGE_DIMENSIONS, Vec2::ZERO, 0.0, Vec2::ZERO, (IMAGE_DIMENSIONS / 2).as_vec2());
|
||||||
|
|
||||||
|
let palette_chars: String = PALETTE.chars().filter(|c| c.is_alphanumeric()).collect();
|
||||||
|
let palette_bytes: Vec<u8> = (0..palette_chars.len())
|
||||||
|
.step_by(2)
|
||||||
|
.map(|i| {
|
||||||
|
let s = &palette_chars[i..i + 2];
|
||||||
|
let b = u8::from_str_radix(s, 16).expect("illegal palette value");
|
||||||
|
b
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let palette_colors: Vec<Vec4> = palette_bytes
|
||||||
|
.chunks(3)
|
||||||
|
.map(|chunk| [chunk[0] as f32, chunk[1] as f32, chunk[2] as f32, 255.0].into())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut rng = Xoshiro256StarStar::from_seed([4u8; 32]);
|
||||||
|
let chaos_game = ChaosGame::new(&mut rng, transforms, &weights, &variations);
|
||||||
|
|
||||||
|
let mut image_accum: Vec<Vec4> = Vec::new();
|
||||||
|
image_accum.resize(IMAGE_DIMENSIONS.element_product() as usize, Vec4::ZERO);
|
||||||
|
|
||||||
|
let iterations: usize = (IMAGE_DIMENSIONS.element_product() as f32 * IMAGE_QUALITY) as usize;
|
||||||
|
chaos_game.skip(ITERATIONS_DISCARD).take(iterations).for_each(|(ifs_point, ifs_color)| {
|
||||||
|
let pixel_point = camera.transform_point_to_image(ifs_point);
|
||||||
|
if pixel_point.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pixel_point = pixel_point.unwrap();
|
||||||
|
let pixel_index = pixel_point.y * IMAGE_DIMENSIONS.x + pixel_point.x;
|
||||||
|
|
||||||
|
let pixel_color = BlendMode::Linear.ifs_to_rgb(ifs_color, palette_colors.as_ref());
|
||||||
|
image_accum[pixel_index as usize] += pixel_color;
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -38,8 +38,8 @@ unsafe impl bytemuck::Pod for BlendMode {}
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use glam::Vec4;
|
|
||||||
use crate::image::BlendMode;
|
use crate::image::BlendMode;
|
||||||
|
use glam::Vec4;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn blend_linear() {
|
fn blend_linear() {
|
||||||
@@ -55,7 +55,12 @@ mod test {
|
|||||||
assert_eq!(ifs_to_rgb(0.5, palette), Vec4::splat(2.0));
|
assert_eq!(ifs_to_rgb(0.5, palette), Vec4::splat(2.0));
|
||||||
assert_eq!(ifs_to_rgb(1.0, palette), Vec4::splat(3.0));
|
assert_eq!(ifs_to_rgb(1.0, palette), Vec4::splat(3.0));
|
||||||
|
|
||||||
let palette = &[Vec4::splat(1.0), Vec4::splat(2.0), Vec4::splat(3.0), Vec4::splat(4.0)];
|
let palette = &[
|
||||||
|
Vec4::splat(1.0),
|
||||||
|
Vec4::splat(2.0),
|
||||||
|
Vec4::splat(3.0),
|
||||||
|
Vec4::splat(4.0),
|
||||||
|
];
|
||||||
assert_eq!(ifs_to_rgb(0.0, palette), Vec4::splat(1.0));
|
assert_eq!(ifs_to_rgb(0.0, palette), Vec4::splat(1.0));
|
||||||
assert_eq!(ifs_to_rgb(0.5, palette), Vec4::splat(2.5));
|
assert_eq!(ifs_to_rgb(0.5, palette), Vec4::splat(2.5));
|
||||||
assert_eq!(ifs_to_rgb(1.0, palette), Vec4::splat(4.0));
|
assert_eq!(ifs_to_rgb(1.0, palette), Vec4::splat(4.0));
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
pub mod camera;
|
pub mod camera;
|
||||||
pub mod chaos_game;
|
pub mod chaos_game;
|
||||||
|
pub mod image;
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
pub mod variation;
|
pub mod variation;
|
||||||
mod image;
|
|
||||||
|
|
||||||
use glam::Affine2;
|
use glam::Affine2;
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ impl From<[f32; 4]> for VariationParams {
|
|||||||
pub enum VariationKind {
|
pub enum VariationKind {
|
||||||
/// Identity variation, returns the point as-is
|
/// Identity variation, returns the point as-is
|
||||||
Linear = 0,
|
Linear = 0,
|
||||||
|
Spherical = 2,
|
||||||
Julia = 13,
|
Julia = 13,
|
||||||
Popcorn = 17,
|
Popcorn = 17,
|
||||||
Pdj = 24,
|
Pdj = 24,
|
||||||
@@ -87,6 +87,7 @@ impl Variation {
|
|||||||
) -> Vec2 {
|
) -> Vec2 {
|
||||||
(match self.kind {
|
(match self.kind {
|
||||||
VariationKind::Linear => transform_point_linear(point),
|
VariationKind::Linear => transform_point_linear(point),
|
||||||
|
VariationKind::Spherical => transform_point_spherical(point),
|
||||||
VariationKind::Julia => transform_point_julia(point, rng),
|
VariationKind::Julia => transform_point_julia(point, rng),
|
||||||
VariationKind::Popcorn => transform_point_popcorn(point, coefficients),
|
VariationKind::Popcorn => transform_point_popcorn(point, coefficients),
|
||||||
VariationKind::Pdj => transform_point_pdj(point, &self.params),
|
VariationKind::Pdj => transform_point_pdj(point, &self.params),
|
||||||
@@ -98,6 +99,12 @@ fn transform_point_linear(point: Vec2) -> Vec2 {
|
|||||||
point
|
point
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transform_point_spherical(point: Vec2) -> Vec2 {
|
||||||
|
let r2 = (point * point).element_sum();
|
||||||
|
let r2_inv = 1.0 / r2;
|
||||||
|
|
||||||
|
point * Vec2::splat(r2_inv)
|
||||||
|
}
|
||||||
fn transform_point_julia<R: Rng>(point: Vec2, rng: &mut R) -> Vec2 {
|
fn transform_point_julia<R: Rng>(point: Vec2, rng: &mut R) -> Vec2 {
|
||||||
let x2 = powf(point.x, 2.0);
|
let x2 = powf(point.x, 2.0);
|
||||||
let y2 = powf(point.y, 2.0);
|
let y2 = powf(point.y, 2.0);
|
||||||
|
|||||||
Reference in New Issue
Block a user