- ray marched bug
- First basic red sphere
- Final repeated, warped sphere
- for every pixel, start a ray from an origin, through the camera view plane, until it hits something
- draw?
- difference between marching / tracing
- determine if a given point is inside, on, or outside a geometry
- functions return negative values
- reference: http://iquilezles.org/www/articles/distfunctions/distfunctions.htm
- npm install glslify gl-toy glsl-raytrace glsl-camera-ray glsl-square-frame
app.js
const glslify = require('glslify')
const toy = require('gl-toy')
const shaderText = glslify(`precision mediump float;
vec2 doModel( vec3 p );
uniform vec2 uScreenSize;
uniform float uTime;
#pragma glslify: raytrace = require('glsl-raytrace', map=doModel, steps=50)
#pragma glslify: camera = require('glsl-camera-ray')
#pragma glslify: square = require('glsl-square-frame')
#pragma glslify: getNormal= require('glsl-sdf-normal', map=doModel)
vec2 doModel( vec3 p ) {
float radius = 1.;
float dist = length(p) - radius;
return vec2( dist, 0. );
}
void main() {
vec3 cameraPos = vec3( 0., 0., 5. );
vec3 cameraDir = vec3( 0., 0., 0. );
vec3 ray = camera( cameraPos, cameraDir, square( uScreenSize ), 2.0 );
vec2 t = raytrace( cameraPos, ray );
float color = t.x > -.5 ? 1. : 0.;
gl_FragColor = vec4( color, 0., 0., 1. );
}`)
const start = Date.now()
toy( shaderText, ( gl, shader ) => {
shader.uniforms.uScreenSize = [ gl.drawingBufferWidth, gl.drawingBufferHeight ]
shader.uniforms.uTime = ( Date.now() - start ) / 1000
})- npm install glsl-sdf-normal
- all of our javascript remains the same, only the main function of our shader will change. Only the shader code is provided here, just paste it into the glslify command.
precision mediump float;
vec2 doModel( vec3 p );
uniform vec2 uScreenSize;
uniform float uTime;
#pragma glslify: raytrace = require('glsl-raytrace', map=doModel, steps=50)
#pragma glslify: camera = require('glsl-camera-ray')
#pragma glslify: square = require('glsl-square-frame')
#pragma glslify: getNormal= require('glsl-sdf-normal', map=doModel)
vec2 doModel( vec3 p ) {
float radius = 1.;
float dist = length(p) - radius;
return vec2( dist, 0. );
}
void main() {
vec3 cameraPos = vec3( 0., 0., 5. );
vec3 cameraDir = vec3( 0., 0., 0. );
vec3 ray = camera( cameraPos, cameraDir, square( uScreenSize ), 2.0 );
vec2 t = raytrace( cameraPos, ray );
vec3 color = vec3(0.);
if( t.x > -.5 ) {
vec3 pos = cameraPos + t.x * ray;
vec3 normal = getNormal( pos );
color = normal;
}
gl_FragColor = vec4( color, 1. );
}- we'll add ambient and diffuse lighting for our sphere using the normal that we generate last time. We'll create a function,
lighting, that takes in our normal and calculates diffuse lighting based on its values.
precision mediump float;
vec2 doModel( vec3 p );
uniform vec2 uScreenSize;
uniform float uTime;
#pragma glslify: raytrace = require('glsl-raytrace', map=doModel, steps=50)
#pragma glslify: camera = require('glsl-camera-ray')
#pragma glslify: square = require('glsl-square-frame')
#pragma glslify: getNormal= require('glsl-sdf-normal', map=doModel)
vec2 doModel( vec3 p ) {
float radius = 1.;
float dist = length(p) - radius;
return vec2( dist, 0. );
}
vec3 lighting( vec3 normal ) {
vec3 lightDir = normalize(vec3(0, 1, 0));
vec3 lightColor = vec3(0.9, 0.5, 0.3);
vec3 diffuseAmt = lightColor * max(0.0, dot( lightDir, normal ));
vec3 ambientAmt = vec3(0.05);
return diffuseAmt + ambientAmt;
}
void main() {
vec3 cameraPos = vec3( 0., 0., 5. );
vec3 cameraDir = vec3( 0., 0., 0. );
vec3 ray = camera( cameraPos, cameraDir, square( uScreenSize ), 2.0 );
vec2 t = raytrace( cameraPos, ray );
vec3 color = vec3(0.);
// only perform lighting if we have a "hit"
if( t.x > -.5 ) {
vec3 pos = cameraPos + t.x * ray;
vec3 normal = getNormal( pos );
color = lighting( normal );
}
gl_FragColor = vec4( color, 1. );
}- 4D perlin noise enables us to include a time component (obtained as the uniform uTime)
- We just need to include one more glslify module (glsl-noise) and change the first line of our
doModelfunction to alter our sphere radius based on perlin noise. - The revised doModel function, and the call to import glsl-noise, is given below:
#pragma glslify: noise = require('glsl-noise/simplex/4d')
vec2 doModel(vec3 p) {
float radius = 1.0 + noise( vec4( p, uTime )) * 0.25;
float dist = length(p) - radius;
return vec2( dist, 0. );
}- You can see some fancier lighting here: http://glslb.in