Get a WebGL canvas working in Sycamore

This commit is contained in:
Aaron Fenyes 2024-08-21 13:01:33 -07:00
parent c48d685ad6
commit fd3cbae1b4
5 changed files with 224 additions and 0 deletions

View File

@ -0,0 +1,3 @@
target
dist
Cargo.lock

View File

@ -0,0 +1,37 @@
[package]
name = "inversive-display"
version = "0.1.0"
authors = ["Aaron"]
edition = "2021"
[features]
default = ["console_error_panic_hook"]
[dependencies]
js-sys = "0.3.70"
sycamore = "0.9.0-beta.2"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.7", optional = true }
[dependencies.web-sys]
version = "0.3.69"
features = [
'HtmlCanvasElement',
'WebGl2RenderingContext',
'WebGlBuffer',
'WebGlProgram',
'WebGlShader',
'WebGlVertexArrayObject',
'Window'
]
[dev-dependencies]
wasm-bindgen-test = "0.3.34"
[profile.release]
opt-level = "s" # optimize for small code size
debug = true # include debug symbols

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Inversive display</title>
<link data-trunk rel="css" href="main.css"/>
</head>
<body></body>
</html>

View File

@ -0,0 +1,23 @@
body {
margin-left: 20px;
margin-top: 20px;
color: #fcfcfc;
background-color: #202020;
}
#app {
display: flex;
flex-direction: column;
width: 600px;
}
canvas {
float: left;
background-color: #020202;
border-radius: 10px;
margin-top: 5px;
}
input {
margin-top: 5px;
}

View File

@ -0,0 +1,152 @@
// based on the WebGL example in the `wasm-bindgen` guide
//
// https://rustwasm.github.io/wasm-bindgen/examples/webgl.html
//
extern crate js_sys;
use std::f64::consts::PI as PI;
use sycamore::{prelude::*, rt::{JsCast, JsValue}};
use web_sys::{console, WebGl2RenderingContext, WebGlProgram, WebGlShader};
fn compile_shader(
context: &WebGl2RenderingContext,
shader_type: u32,
source: &str,
) -> WebGlShader {
let shader = context.create_shader(shader_type).unwrap();
context.shader_source(&shader, source);
context.compile_shader(&shader);
shader
}
fn main() {
// set up a config option that forwards panic messages to `console.error`
#[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once();
sycamore::render(|| {
let tip = create_signal(0.0);
let display = create_node_ref();
on_mount(move || {
// get the display canvas
let canvas = display
.get::<DomNode>()
.unchecked_into::<web_sys::HtmlCanvasElement>();
let ctx = canvas
.get_context("webgl2")
.unwrap()
.unwrap()
.dyn_into::<WebGl2RenderingContext>()
.unwrap();
// load the vertex and fragment shaders
let vertex_shader = compile_shader(
&ctx,
WebGl2RenderingContext::VERTEX_SHADER,
r##"#version 300 es
in vec4 position;
void main() {
gl_Position = position;
}
"##,
);
let fragment_shader = compile_shader(
&ctx,
WebGl2RenderingContext::FRAGMENT_SHADER,
r##"#version 300 es
precision highp float;
out vec4 outColor;
void main() {
outColor = vec4(gl_FragCoord.xy / 600., 1., 1.);
}
"##,
);
let program = ctx.create_program().unwrap();
ctx.attach_shader(&program, &vertex_shader);
ctx.attach_shader(&program, &fragment_shader);
ctx.link_program(&program);
let link_status = ctx
.get_program_parameter(&program, WebGl2RenderingContext::LINK_STATUS)
.as_bool()
.unwrap();
let link_msg = if link_status {
"Linked successfully"
} else {
"Linking failed"
};
console::log_1(&JsValue::from(link_msg));
ctx.use_program(Some(&program));
// set up a repainting routine
create_effect(move || {
// list the vertices
let vertices: [f32; 9] = [
-0.9, 0.9, 0.0,
-0.9, -0.9, 0.0,
0.9*(tip.get() as f32), 0.0, 0.0
];
// create a vertex buffer and bind it to ARRAY_BUFFER
let position_attribute_location = ctx.get_attrib_location(&program, "position");
let buffer = ctx.create_buffer().unwrap();
ctx.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, Some(&buffer));
// load the vertex list into the vertex buffer. the function
// `Float32Array::view` creates a raw view into our module's
// `WebAssembly.Memory` buffer. allocating more memory will
// change the buffer, invalidating the view. that means we have
// to make sure we don't allocate any memory until the view is
// dropped
unsafe {
ctx.buffer_data_with_array_buffer_view(
WebGl2RenderingContext::ARRAY_BUFFER,
&js_sys::Float32Array::view(&vertices),
WebGl2RenderingContext::STATIC_DRAW,
);
}
// create a vertex array and bind it to the graphics context
let vtx_array_obj = ctx.create_vertex_array().unwrap();
ctx.bind_vertex_array(Some(&vtx_array_obj));
// use whatever is bound to ARRAY_BUFFER---here, the vertex
// buffer created above---as the vertex attribute array
//
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/vertexAttribPointer
//
ctx.vertex_attrib_pointer_with_i32(
position_attribute_location as u32,
3, // dimension
WebGl2RenderingContext::FLOAT, // type
false, // normalized?
0, // stride
0, // offset
);
ctx.enable_vertex_attrib_array(position_attribute_location as u32);
// clear the screen and draw the scene
let vert_count = (vertices.len() / 3) as i32;
ctx.clear_color(0.0, 0.0, 0.0, 1.0);
ctx.clear(WebGl2RenderingContext::COLOR_BUFFER_BIT);
ctx.draw_arrays(WebGl2RenderingContext::TRIANGLES, 0, vert_count);
});
});
view! {
div(id="app") {
canvas(ref=display, width="600", height="600")
input(
type="range",
max=1.0,
step=0.01,
bind:valueAsNumber=tip
)
}
}
});
}