summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2022-12-15 16:17:35 -0500
committerpommicket <pommicket@gmail.com>2022-12-15 16:17:35 -0500
commitae29a61c9917da5ad9fbb7a24151bff506669ffb (patch)
tree4d0d127880200d9b5245d57629fcf5a3e575091f
parent2f08c6ba1c98f271084799563529c43a32179676 (diff)
cool stuff
-rw-r--r--Cargo.toml2
-rw-r--r--src/main.rs97
-rw-r--r--src/sdf.rs150
-rw-r--r--src/win.rs44
4 files changed, 205 insertions, 88 deletions
diff --git a/Cargo.toml b/Cargo.toml
index bac29f0..d216d3c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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);
}
diff --git a/src/sdf.rs b/src/sdf.rs
index b27f43f..892b2b5 100644
--- a/src/sdf.rs
+++ b/src/sdf.rs
@@ -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);
}
}
+
+
diff --git a/src/win.rs b/src/win.rs
index 83e2e29..7115a43 100644
--- a/src/win.rs
+++ b/src/win.rs
@@ -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 {