diff --git a/app-proto/inversive-display/src/main.rs b/app-proto/inversive-display/src/main.rs index 5afbb04..12fe9ed 100644 --- a/app-proto/inversive-display/src/main.rs +++ b/app-proto/inversive-display/src/main.rs @@ -8,7 +8,6 @@ // extern crate js_sys; -use std::f64::consts::PI as PI; use sycamore::{prelude::*, rt::{JsCast, JsValue}}; use web_sys::{console, WebGl2RenderingContext, WebGlShader}; @@ -71,8 +70,8 @@ fn main() { console_error_panic_hook::set_once(); sycamore::render(|| { - let turn = create_signal(0.0); - let tip = create_signal(0.0); + let ctrl_x = create_signal(0.0); + let ctrl_y = create_signal(0.0); let display = create_node_ref(); on_mount(move || { @@ -93,18 +92,10 @@ fn main() { WebGl2RenderingContext::VERTEX_SHADER, r##"#version 300 es - in vec3 position; - in vec3 color; - - out vec4 vertexColor; - - uniform mat4 world_to_clip; - uniform mat3 rotation; + in vec4 position; void main() { - vec3 world_pos = rotation * position - vec3(0., 0., 6.); - gl_Position = world_to_clip * vec4(world_pos, 1.); - vertexColor = vec4(color, 1.); + gl_Position = position; } "##, ); @@ -115,12 +106,68 @@ fn main() { precision highp float; - in vec4 vertexColor; - out vec4 outColor; + uniform vec2 resolution; + uniform float shortdim; + + uniform vec2 ctrl; + + struct vecInv { + vec3 sp; + vec2 lt; + }; + + vecInv sphere(vec3 center, float radius) { + return vecInv( + center / radius, + vec2( + 0.5 / radius, + 0.5 * (dot(center, center) / radius - radius) + ) + ); + } + + const float focal_slope = 0.3; + const vec3 light_dir = normalize(vec3(2., 2., 1.)); + void main() { - outColor = vertexColor; + vec2 scr = (2.*gl_FragCoord.xy - resolution) / shortdim; + vec3 dir = vec3(focal_slope * scr, -1.); + + vecInv v = sphere(vec3(ctrl, -5.), 1.); + + float a = -v.lt.s * dot(dir, dir); + float b = dot(v.sp, dir); + float c = -v.lt.t; + + float scale = -b/(2.*a); + float adjust = 4.*a*c/(b*b); + float offset = sqrt(1. - adjust); + float u_front = scale * (1. - offset); + float u_back = scale * (1. + offset); + + vec3 color; + if (adjust < 1. && u_front > 0.) { + // the expression for normal needs to be checked. it's + // supposed to give the negative gradient of the lorentz + // product between the impact point vector and the sphere + // vector with respect to the coordinates of the impact + // point. i calculated it in my head and decided that + // the result looked good enough for now + vec3 pt_front = u_front * dir; + vec3 normal_front = normalize(-v.sp + 2.*v.lt.s*pt_front); + + float incidence = dot(normal_front, light_dir); + if (incidence < 0.) { + color = mix(vec3(0.2, 0.0, 0.4), vec3(0.1, 0.0, 0.2), -incidence); + } else { + color = mix(vec3(0.4, 0.0, 0.2), vec3(1., 0.8, 1.), incidence); + } + } else { + color = vec3(0.); + } + outColor = vec4(color, 1.); } "##, ); @@ -142,79 +189,38 @@ fn main() { // find indices of vertex attributes and uniforms let position_index = ctx.get_attrib_location(&program, "position") as u32; - let color_index = ctx.get_attrib_location(&program, "color") as u32; - let world_to_clip_loc = ctx.get_uniform_location(&program, "world_to_clip"); - let rotation_loc = ctx.get_uniform_location(&program, "rotation"); + let resolution_loc = ctx.get_uniform_location(&program, "resolution"); + let shortdim_loc = ctx.get_uniform_location(&program, "shortdim"); + let ctrl_loc = ctx.get_uniform_location(&program, "ctrl"); // create a vertex array and bind it to the graphics context let vertex_array = ctx.create_vertex_array().unwrap(); ctx.bind_vertex_array(Some(&vertex_array)); - // enable depth testing - ctx.enable(WebGl2RenderingContext::DEPTH_TEST); - - // set the projection map - let focal_length = 3.0_f32; - let near_clip = 0.1_f32; - let far_clip = 20_f32; - let depth_inv = 1_f32 / (far_clip - near_clip); - let world_to_clip: [f32; 16] = [ - focal_length, 0.0, 0.0, 0.0, - 0.0, focal_length, 0.0, 0.0, - 0.0, 0.0, -(near_clip + far_clip) * depth_inv, -1., - 0.0, 0.0, -2. * near_clip * far_clip * depth_inv, 0.0 + // set the vertex positions + const VERTEX_CNT: usize = 6; + let positions: [f32; 3*VERTEX_CNT] = [ + // northwest triangle + -1.0, -1.0, 0.0, + -1.0, 1.0, 0.0, + 1.0, 1.0, 0.0, + // southeast triangle + -1.0, -1.0, 0.0, + 1.0, 1.0, 0.0, + 1.0, -1.0, 0.0 ]; - ctx.uniform_matrix4fv_with_f32_array(world_to_clip_loc.as_ref(), false, &world_to_clip); + bind_vertex_attrib(&ctx, position_index, 3, &positions); + + // set the resolution + let width = canvas.width() as f32; + let height = canvas.height() as f32; + ctx.uniform2f(resolution_loc.as_ref(), width, height); + ctx.uniform1f(shortdim_loc.as_ref(), width.min(height)); // set up a repainting routine create_effect(move || { - const VERTEX_CNT: usize = 9; - - // set the vertex positions - let tip_shift = 4.0/3.0 * tip.get() as f32; - let positions: [f32; 3*VERTEX_CNT] = [ - // triangle 1 - 1.0 - tip_shift, 1.0 - tip_shift, 1.0 - tip_shift, - 1.0, -1.0, -1.0, - -1.0, 1.0, -1.0, - // triangle 2 - 1.0 - tip_shift, 1.0 - tip_shift, 1.0 - tip_shift, - -1.0, 1.0, -1.0, - -1.0, -1.0, 1.0, - // triangle 3 - 1.0 - tip_shift, 1.0 - tip_shift, 1.0 - tip_shift, - -1.0, -1.0, 1.0, - 1.0, -1.0, -1.0 - ]; - bind_vertex_attrib(&ctx, position_index, 3, &positions); - - // set the vertex colors - let colors: [f32; 3*VERTEX_CNT] = [ - // triangle 1 - 1.0, 0.0, 0.5, - 1.0, 0.0, 0.5, - 1.0, 0.0, 0.5, - // triangle 2 - 0.0, 0.5, 1.0, - 0.0, 0.5, 1.0, - 0.0, 0.5, 1.0, - // triangle 3 - 0.5, 0.0, 1.0, - 0.5, 0.0, 1.0, - 0.5, 0.0, 1.0 - ]; - bind_vertex_attrib(&ctx, color_index, 3, &colors); - - // set the rotation - let angle_val = (2.0*PI*turn.get()) as f32; - let angle_cos = angle_val.cos(); - let angle_sin = angle_val.sin(); - let rotation: [f32; 9] = [ - angle_cos, 0.0, angle_sin, - 0.0, 1.0, 0.0, - -angle_sin, 0.0, angle_cos, - ]; - ctx.uniform_matrix3fv_with_f32_array(rotation_loc.as_ref(), false, &rotation); + // pass the control parameters + ctx.uniform2f(ctrl_loc.as_ref(), ctrl_x.get() as f32, ctrl_y.get() as f32); // clear the screen and draw the scene ctx.clear_color(0.0, 0.0, 0.0, 1.0); @@ -228,15 +234,17 @@ fn main() { canvas(ref=display, width="600", height="600") input( type="range", + min=-1.0, max=1.0, step=0.01, - bind:valueAsNumber=turn + bind:valueAsNumber=ctrl_x ) input( type="range", + min=-1.0, max=1.0, step=0.01, - bind:valueAsNumber=tip + bind:valueAsNumber=ctrl_y ) } }