diff options
author | pommicket <pommicket@gmail.com> | 2022-12-15 16:17:35 -0500 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2022-12-15 16:17:35 -0500 |
commit | ae29a61c9917da5ad9fbb7a24151bff506669ffb (patch) | |
tree | 4d0d127880200d9b5245d57629fcf5a3e575091f | |
parent | 2f08c6ba1c98f271084799563529c43a32179676 (diff) |
cool stuff
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/main.rs | 97 | ||||
-rw-r--r-- | src/sdf.rs | 150 | ||||
-rw-r--r-- | src/win.rs | 44 |
4 files changed, 205 insertions, 88 deletions
@@ -8,6 +8,6 @@ edition = "2021" [dependencies] gl = "0.14" nalgebra = "0.31" -rand = "0.8" gen_random_proc_macro = { path = "./gen_random_proc_macro" } gen_random = { path = "./gen_random" } +rand = { version = "0.8", features = ["small_rng"] } diff --git a/src/main.rs b/src/main.rs index b8c4f54..41a6d7b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,26 +1,32 @@ /* @TODO: - auto-select level set by sampling a bunch of points +- bring time back, w pause/rewind/adjust time speed (start out paused?) - Params instead of depth +- seed control (maybe save seeds to a file then let user go back&forth through past sdfs) - fullscreen key - mathematical analysis - options for: - max framerate - mouse sensitivity + - fov, focal length - AA quality - # iterations, distance cutoff - documentation -- GenRandom integers (+ gen_random_scale_bias) +- GenRandom integers (+ gen_random_scale_bias + +----- +cool seeds: +18413841503509874975 +**17878446840930313726 */ -extern crate gen_random; extern crate nalgebra; pub mod sdf; mod sdl; pub mod win; -use gen_random::GenRandom; use nalgebra::{Matrix3, Matrix4, Rotation3, Vector3}; use std::time::Instant; @@ -72,17 +78,13 @@ impl View { } } -fn try_main() -> Result<(), String> { - let my_sdf = if false { - sdf::Sdf::sphere(1.0) - } else { - sdf::Sdf::gen_thread_random_max_depth(6) - }; - println!("{my_sdf:?}"); - - let mut window = win::Window::new("AutoSDF", 1280, 720, true) - .map_err(|e| format!("Error creating window: {e}"))?; - +fn gen_program_with_seed(window: &mut win::Window, program: &mut win::Program, seed: u64) -> Result<(), String> { + use rand::SeedableRng; + + let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); + let my_sdf = sdf::R3ToR::good_random(&mut rng, 6); + let color_function = sdf::R3ToR3::good_random(&mut rng, 7); + let mut fshader_source = String::new(); fshader_source.push_str( " @@ -93,11 +95,44 @@ uniform float u_time; uniform float u_fov; uniform float u_focal_length; uniform float u_level_set; +uniform int u_hsv; + +float smooth_min(float a, float b, float k) { + k = clamp(k, 0.0, 1.0); + float h = max(k-abs(a-b), 0.0)/k; + return min(a, b) - h*h*h*k*(1.0/6.0); +} ", ); - my_sdf.to_glsl(&mut fshader_source); + my_sdf.to_glsl_function("sdf", &mut fshader_source); + color_function.to_glsl_function("get_color_", &mut fshader_source); fshader_source.push_str( " + +// see https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB_alternative +float hsvf(float n, vec3 hsv) { + float k = mod(n + hsv.x * 6.0, 6.0); + return hsv.z - hsv.z * hsv.y * clamp(min(k, 4.0 - k), 0.0, 1.0); +} + +vec3 hsv_to_rgb(vec3 hsv) { + hsv.yz = clamp(hsv.yz, 0.0, 1.0); + return vec3(hsvf(5.0, hsv), hsvf(3.0, hsv), hsvf(1.0, hsv)); +} + +vec3 get_color(vec3 p) { + if (u_hsv != 0) { + vec3 hsv = get_color_(p); + // make sure object isn't too dark so we can actually see it + hsv.z = mix(hsv.z, 1.0, 0.5); + return hsv_to_rgb(hsv); + } else { + // in theory we should clamp this but it actually looks better if we don't + // (it makes the object glow) + return get_color_(p); + } +} + #define ITERATIONS 30 #define AA_X 1 #define AA_Y 1 @@ -134,7 +169,7 @@ void main() { p += u_translation; if (sdf(p) < 0.0) { // looking inside object - o_color = vec4(1.0, 0.0, 1.0, 1.0); + o_color = vec4(get_color(p), 1.0); return; } int i; @@ -159,7 +194,7 @@ void main() { float brightness = (1.0/threshold) * (threshold-min_dist); brightness = pow(brightness, 16.0); float L_ambient = 0.3; - vec3 color = vec3(1.0, 0.0, 0.0); + vec3 color = get_color(p); float specularity = 0.15; // strength of specular lighting final_color += brightness * mix(mix(L_diffuse, 1.0, L_ambient) * color, vec3(L_specular), specularity); break; @@ -173,10 +208,11 @@ void main() { ); //println!("{fshader_source}"); + println!("seed: {seed}"); - let program = window - .create_program( - "attribute vec2 v_pos; + window + .link_program(program, + "IN vec2 v_pos; OUT vec2 pos; uniform float u_aspect_ratio; @@ -187,6 +223,20 @@ void main() { &fshader_source, ) .map_err(|e| format!("Error compiling shader:\n{e}"))?; + Ok(()) +} + +fn gen_program(window: &mut win::Window, program: &mut win::Program) -> Result<(), String> { + let seed = rand::random::<u64>(); + gen_program_with_seed(window, program, seed) +} + +fn try_main() -> Result<(), String> { + + let mut window = win::Window::new("AutoSDF", 1280, 720, true) + .map_err(|e| format!("Error creating window: {e}"))?; + let mut program = window.new_program(); + gen_program(&mut window, &mut program)?; let mut buffer = window.create_buffer(); let data: &[[f32; 2]] = &[ @@ -219,8 +269,13 @@ void main() { match event { Quit | KeyDown(Escape) => break 'mainloop, KeyDown(F1) => show_debug_info = !show_debug_info, + KeyDown(R) => { + gen_program(&mut window, &mut program)?; + view.level_set = 0.0; + }, + KeyDown(N0) => view.level_set = 0.0, MouseMotion { xrel, yrel, .. } => { - let mouse_sensitivity = 0.33; + let mouse_sensitivity = 0.05; view.yaw_by(-xrel as f32 * mouse_sensitivity * frame_dt); view.pitch_by(-yrel as f32 * mouse_sensitivity * frame_dt); } @@ -4,6 +4,7 @@ extern crate rand; use gen_random::GenRandom; use gen_random_proc_macro::GenRandom; use std::fmt::{self, Display, Formatter, Write}; +use rand::Rng; // we're only writing numbers and strings so write! should never fail. macro_rules! write_str { @@ -199,11 +200,6 @@ impl VarCounter { Self { idx: 0 } } - fn prev(&self) -> Variable { - assert!(self.idx != 0); - Variable { id: self.idx - 1 } - } - fn next(&mut self) -> Variable { let ret = Variable { id: self.idx }; self.idx += 1; @@ -211,19 +207,77 @@ impl VarCounter { } } -#[derive(Debug)] -pub struct Sdf { - distance_function: R3ToR, +/// a type in GLSL. this doesn't have all the types. +enum GLSLType { + Float, + Vec3, +} + +impl fmt::Display for GLSLType { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + use GLSLType::*; + match self { + Float => write!(f, "float"), + Vec3 => write!(f, "vec3"), + } + } } -trait Function { +trait Function: Sized + GenRandom { /// appends `code` with glsl code to apply the function to the input variable. /// returns the output variable. #[must_use] fn to_glsl(&self, input: Variable, code: &mut String, var: &mut VarCounter) -> Variable; + + /// GLSL type which is the input to this function + fn input_type() -> GLSLType; + /// GLSL type which is the output of this function + fn output_type() -> GLSLType; + + /// adds GLSL code for function to `code`. + fn to_glsl_function(&self, name: &str, code: &mut String) { + let mut var = VarCounter::new(); + let input = var.next(); + write_str!( + code, + "{} {name}({} {input}) {{\n", + Self::output_type(), + Self::input_type() + ); + let output = self.to_glsl(input, code, &mut var); + write_str!( + code, + "return {output};\n}}\n\n"); + } + + fn good_random(rng: &mut impl Rng, max_depth: isize) -> Self { + // to make sure the function isn't too boring or too slow, + // we'll generate a bunch then take the one with the median code length. + let mut functions = vec![]; + for _i in 0..20 { + let f = Self::gen_random_max_depth(rng, max_depth); + let mut code = String::new(); + let mut var = VarCounter::new(); + let _ = f.to_glsl(var.next(), &mut code, &mut var); + let len = code.len(); + + functions.push((len, f)); + } + functions.sort_by_key(|x| x.0); + functions.remove(functions.len() / 2).1 + } + + fn good_thread_random(max_depth: isize) -> Self { + Self::good_random(&mut rand::thread_rng(), max_depth) + } } + + impl Function for RToR { + fn input_type() -> GLSLType { GLSLType::Float } + fn output_type() -> GLSLType { GLSLType::Float } + fn to_glsl(&self, input: Variable, code: &mut String, var: &mut VarCounter) -> Variable { use RToR::*; @@ -243,6 +297,9 @@ impl Function for RToR { } impl Function for R3ToR3 { + fn input_type() -> GLSLType { GLSLType::Vec3 } + fn output_type() -> GLSLType { GLSLType::Vec3 } + fn to_glsl(&self, input: Variable, code: &mut String, var: &mut VarCounter) -> Variable { use R3ToR3::*; @@ -308,6 +365,9 @@ impl Function for R3ToR3 { } impl Function for R3ToR { + fn input_type() -> GLSLType { GLSLType::Vec3 } + fn output_type() -> GLSLType { GLSLType::Float } + fn to_glsl(&self, input: Variable, code: &mut String, var: &mut VarCounter) -> Variable { use R3ToR::*; match self { @@ -355,7 +415,7 @@ impl Function for R3ToR { let k = 0.2; write_str!( code, - "float {output} = sdf_smooth_min({a_output}, {b_output}, {k});\n" + "float {output} = smooth_min({a_output}, {b_output}, {k});\n" ); output } @@ -368,57 +428,33 @@ impl Function for R3ToR { } } -impl GenRandom for Sdf { - fn gen_random_max_depth(rng: &mut impl rand::Rng, max_depth: isize) -> Self { - // to make sure the SDF isn't too boring or too slow, - // we'll generate a bunch then take the one with the median code length. - let mut distance_functions = vec![]; - for _i in 0..20 { - let f = R3ToR::gen_random_max_depth(rng, max_depth); - let mut code = String::new(); - let mut var = VarCounter::new(); - let _ = f.to_glsl(var.next(), &mut code, &mut var); - let len = code.len(); - - distance_functions.push((len, f)); - } - distance_functions.sort_by_key(|x| x.0); - let distance_function = distance_functions.remove(distance_functions.len() / 2).1; - - Sdf { - distance_function - } + +impl R3ToR { + pub fn good_random(rng: &mut impl Rng, max_depth: isize) -> Self { + <Self as Function>::good_random(rng, max_depth) + } + + pub fn good_thread_random(max_depth: isize) -> Self { + <Self as Function>::good_thread_random(max_depth) + } + + pub fn to_glsl_function(&self, name: &str, code: &mut String) { + <Self as Function>::to_glsl_function(self, name, code); } } -impl Sdf { - /// test sphere - pub fn sphere(r: f32) -> Self { - Self { - distance_function: R3ToR::Sphere(Constant::F32(r)), - } +impl R3ToR3 { + pub fn good_random(rng: &mut impl Rng, max_depth: isize) -> Self { + <Self as Function>::good_random(rng, max_depth) } - pub fn from_function(distance_function: R3ToR) -> Self { - Self { distance_function } - } - - /// appends some glsl code including a function `float sdf(vec3) { ... }` - pub fn to_glsl(&self, code: &mut String) { - code.push_str( - " -float sdf_smooth_min(float a, float b, float k) { - k = clamp(k, 0.0, 1.0); - float h = max(k-abs(a-b), 0.0)/k; - return min(a, b) - h*h*h*k*(1.0/6.0); -} -", - ); - code.push_str("float sdf(vec3 p) {\n"); - let mut var = VarCounter::new(); - write_str!(code, "vec3 {} = p;\n", var.next()); - let output = self.distance_function.to_glsl(var.prev(), code, &mut var); - write_str!(code, "return {output};\n"); - code.push('}'); + pub fn good_thread_random(max_depth: isize) -> Self { + <Self as Function>::good_thread_random(max_depth) + } + + pub fn to_glsl_function(&self, name: &str, code: &mut String) { + <Self as Function>::to_glsl_function(self, name, code); } } + + @@ -463,16 +463,20 @@ pub struct Program { } impl Program { - fn new(shaders: &[Shader]) -> Result<Self, String> { + fn new() -> Self { let id = unsafe { gl::CreateProgram() }; - let result = Self::new_with_id(id, shaders); - if result.is_err() { - unsafe { gl::DeleteShader(id) }; - } - result + Self { id } + } + + fn new_with_shaders(shaders: &[Shader]) -> Result<Self, String> { + let mut program = Self::new(); + program.relink(shaders)?; + Ok(program) } - fn new_with_id(id: GLuint, shaders: &[Shader]) -> Result<Self, String> { + + fn relink(&mut self, shaders: &[Shader]) -> Result<(), String> { + let id = self.id; for shader in shaders { unsafe { gl::AttachShader(id, shader.id) }; } @@ -496,8 +500,14 @@ impl Program { return Err("failed to link".to_string()); } } + + for shader in shaders { + unsafe { + gl::DetachShader(id, shader.id); + } + } - Ok(Self { id }) + Ok(()) } } @@ -740,6 +750,11 @@ impl Window { sdl::set_relative_mouse_mode(relative); } } + + /// new empty shader program + pub fn new_program(&mut self) -> Program { + Program::new() + } pub fn create_program( &mut self, @@ -748,7 +763,18 @@ impl Window { ) -> Result<Program, String> { let vshader = Shader::new(gl::VERTEX_SHADER, source_vshader)?; let fshader = Shader::new(gl::FRAGMENT_SHADER, source_fshader)?; - Program::new(&[vshader, fshader]) + Program::new_with_shaders(&[vshader, fshader]) + } + + pub fn link_program( + &mut self, + program: &mut Program, + source_vshader: &str, + source_fshader: &str, + ) -> Result<(), String> { + let vshader = Shader::new(gl::VERTEX_SHADER, source_vshader)?; + let fshader = Shader::new(gl::FRAGMENT_SHADER, source_fshader)?; + program.relink(&[vshader, fshader]) } pub fn create_buffer(&mut self) -> Buffer { |