Skip to main content

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>