Creating a scene
The goal of this section is to give a brief introduction to glre.js. We will start by setting up a scene, with a spinning cube.
Before we started
Before you can use glre.js, you need somewhere to display it. Save the following HTML to a file on your computer.
<canvas id="myCanvas"></canvas>
<script type="module">
import createGL from 'https://esm.sh/glre'
// Our GLSL and Javascript will go here.
const frag = `
// ...
`
function App() {
// ...
}
document.addEventListener('DOMContentLoaded', App)
</script>
Creating Fragment Shader
#version 300 es
precision highp float;
uniform vec2 iResolution; // canvas size
uniform vec3 up; // camera up direction
uniform vec3 eye; // camera position
uniform vec3 focus; // camera focus point
uniform float focal; // camera focal length
uniform float size; // object size
Creating Box
float boxSDF(vec3 p, float side) {
vec3 d = abs(p) - side;
return min(max(d.x, max(d.y, d.z)), 0.) + length(max(d, 0.));
}
Raymarch Setup
- Calculate this particular pixel's normalized coordinates
- Calculate the direction that the ray through this pixel goes
- Calculate eye directions
void main() {
/**
* Ray marching setup
*/
vec3 look = normalize(focus - eye);
vec3 right = normalize(cross(look, up));
vec2 scr = gl_FragCoord.xy - iResolution * .5;
vec3 dir = normalize(focal * look + scr.x * right + scr.y * up);
/**
* Ray marching
*/
vec3 p = eye + dir;
vec3 e = vec3(.0005, .0, .0);
float d = boxSDF(p, size);
for (int i = 0; i < 50; i++) {
if (d <= e.x) {
float dx = boxSDF(p + e.xyy, size) - d;
float dy = boxSDF(p + e.yxx, size) - d;
float dz = boxSDF(p + e.yyx, size) - d;
vec3 norm = normalize(vec3(dx, dy, dz));
fragColor = vec4(norm * .5 + .5, 1.);
return;
}
p = p + d * dir;
d = boxSDF(p, size);
}
}
Setup glre
function App() {
const { mount, size, uniform } = createGL({
el: document.getElementById('canvas'),
frag,
isWebGL: true,
render() {
const t = performance.now() / 1000
const x = 200 * Math.cos(t)
const z = 200 * Math.sin(t)
uniform({ eye: [x, 0, z] })
},
})
mount()
uniform({
up: [0, 1, 0],
focus: [0, 0, 0],
focal: 500,
size: 50,
})
}
document.addEventListener('DOMContentLoaded', App)
The result
<canvas id="id" style="top: 0; left: 0; position: fixed" />
<script type="module">
import createGL from 'https://esm.sh/glre'
const frag = `
#version 300 es
precision highp float;
uniform vec2 iResolution; // canvas size
uniform vec3 up; // camera up direction
uniform vec3 eye; // camera position
uniform vec3 focus; // camera focus point
uniform float focal; // camera focal length
uniform float size; // object size
out vec4 fragColor;
/**
* boxSDF
*/
float boxSDF(vec3 p, float side) {
vec3 d = abs(p) - side;
return min(max(d.x, max(d.y, d.z)), 0.) + length(max(d, 0.));
}
/**
* main
*/
void main() {
/**
* Ray marching setup
*/
vec3 look = normalize(focus - eye);
vec3 right = normalize(cross(look, up));
vec2 scr = gl_FragCoord.xy - iResolution * .5;
vec3 dir = normalize(focal * look + scr.x * right + scr.y * up);
/**
* Ray marching
*/
vec3 p = eye + dir;
vec3 e = vec3(.0005, 0., 0.);
float d = boxSDF(p, size);
for (int i = 0; i < 50; i++) {
if (d <= e.x) {
float dx = boxSDF(p + e.xyy, size) - d;
float dy = boxSDF(p + e.yxx, size) - d;
float dz = boxSDF(p + e.yyx, size) - d;
vec3 norm = normalize(vec3(dx, dy, dz));
fragColor = vec4(norm * .5 + .5, 1.);
return;
}
p = p + d * dir;
d = boxSDF(p, size);
}
}
`
export function App() {
const { mount, size, uniform } = createGL({
el: document.getElementById('canvas'),
frag,
isWebGL: true,
render() {
const t = performance.now() / 1000
const x = 200 * Math.cos(t)
const z = 200 * Math.sin(t)
uniform({ eye: [x, 0, z] })
},
})
mount()
uniform({
up: [0, 1, 0],
focus: [0, 0, 0],
focal: 500,
size: 50,
})
}
document.addEventListener('DOMContentLoaded', App)
</script>