287 lines
8.1 KiB
HTML
287 lines
8.1 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html lang="en" >
|
||
|
<head>
|
||
|
<meta charset="UTF-8">
|
||
|
<title>Control 2</title>
|
||
|
<link rel="stylesheet" href="./style.css">
|
||
|
|
||
|
</head>
|
||
|
<body>
|
||
|
|
||
|
<canvas id="webgl" width="500" height="1758"></canvas>
|
||
|
|
||
|
<script id="vertexShader" type="x-shader/x-vertex">
|
||
|
attribute vec4 a_position;
|
||
|
|
||
|
uniform mat4 u_modelViewMatrix;
|
||
|
uniform mat4 u_projectionMatrix;
|
||
|
|
||
|
void main() {
|
||
|
gl_Position = a_position;
|
||
|
}
|
||
|
</script>
|
||
|
<script id="fragmentShader" type="x-shader/x-fragment">
|
||
|
precision highp float;
|
||
|
precision highp int;
|
||
|
|
||
|
uniform vec2 u_resolution;
|
||
|
uniform vec2 u_mouse;
|
||
|
uniform float u_time;
|
||
|
uniform sampler2D u_noise;
|
||
|
|
||
|
// movement variables
|
||
|
vec3 movement = vec3(.0);
|
||
|
|
||
|
const int maxIterations = 256;
|
||
|
const float stopThreshold = 0.002;
|
||
|
const float stepScale = .5;
|
||
|
const float eps = 0.002;
|
||
|
const vec3 clipColour = vec3(1.);
|
||
|
const vec3 fogColour = vec3(1.);
|
||
|
|
||
|
const vec3 light1_position = vec3(0, 1., -1.);
|
||
|
const vec3 light1_colour = vec3(.8, .8, .85);
|
||
|
|
||
|
struct Surface {
|
||
|
int object_id;
|
||
|
float distance;
|
||
|
vec3 position;
|
||
|
vec3 onsurface_position;
|
||
|
vec3 colour;
|
||
|
float steps;
|
||
|
float ambient;
|
||
|
float spec;
|
||
|
float fog;
|
||
|
};
|
||
|
|
||
|
// Distance function copyright Inigo Quilez
|
||
|
float opExtrusion( in vec3 p, in float primitive, in float h ) {
|
||
|
float d = primitive;
|
||
|
vec2 w = vec2( d, abs(p.z) - h );
|
||
|
return min(max(w.x,w.y),0.0) + length(max(w,0.0));
|
||
|
}
|
||
|
float sdBox( in vec2 p, in vec2 b ) {
|
||
|
vec2 d = abs(p)-b;
|
||
|
return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
|
||
|
}
|
||
|
|
||
|
vec3 path(float z) {
|
||
|
// return vec3(0,0,0.);
|
||
|
return vec3(sin(z * .1) * 4., sin(z * .05) * 10., z);
|
||
|
}
|
||
|
|
||
|
float getBlock(vec3 position, inout int object_id, inout vec3 p) {
|
||
|
// position.z = fract(position.z) - .5;
|
||
|
// object_id = int(id);
|
||
|
float id = float(object_id);
|
||
|
float r = sin(id * .3 + u_time) * .5;
|
||
|
r = sin(texture2D(u_noise, vec2((id*2.)/255.)).x * 2. + u_time - id) * .25;
|
||
|
float rw = texture2D(u_noise, vec2((id*2.)/255.)).x - .5;
|
||
|
// // position.xy += r;
|
||
|
float s = sin(r);
|
||
|
float c = cos(r);
|
||
|
position.xy *= mat2(c, -s, s, c);
|
||
|
|
||
|
p = position;
|
||
|
|
||
|
float box = sdBox(position.xy, vec2(1.5 + rw, 1.)) * -1.;
|
||
|
float world = opExtrusion(position, box, .45) - .005;
|
||
|
// world += smoothstep(.04, 0., abs(sin(p.x * 4.))) * .01;
|
||
|
// world += smoothstep(.02, 0., abs(sin(p.y * 2.))) * .01;
|
||
|
return world;
|
||
|
}
|
||
|
|
||
|
// This function describes the world in distances from any given 3 dimensional point in space
|
||
|
float world(in vec3 position, inout int object_id, inout vec3 p) {
|
||
|
|
||
|
position.xy -= path(position.z).xy;
|
||
|
|
||
|
float id = floor(position.z);
|
||
|
position.z = fract(position.z) - .5;
|
||
|
|
||
|
int oid1 = int(id);
|
||
|
vec3 p1;
|
||
|
float block1 = getBlock(position, oid1, p1);
|
||
|
int oid2 = int(id+1.);
|
||
|
vec3 p2;
|
||
|
float block2 = getBlock(position + vec3(0,0,-1), oid2, p2);
|
||
|
|
||
|
object_id = oid1;
|
||
|
p = p1;
|
||
|
|
||
|
if(block2 < block1) {
|
||
|
block1 = block2;
|
||
|
object_id = oid2;
|
||
|
p = p2;
|
||
|
}
|
||
|
|
||
|
return block1;
|
||
|
}
|
||
|
float world(in vec3 position, inout int object_id) {
|
||
|
vec3 p;
|
||
|
return world(position, object_id, p);
|
||
|
}
|
||
|
float world(in vec3 position) {
|
||
|
int dummy = 0;
|
||
|
return world(position, dummy);
|
||
|
}
|
||
|
|
||
|
Surface getSurface(int object_id, float rayDepth, vec3 sp, float steps, vec3 onsurface_pos, float fog) {
|
||
|
return Surface(
|
||
|
object_id,
|
||
|
rayDepth,
|
||
|
sp,
|
||
|
onsurface_pos,
|
||
|
vec3(1.),
|
||
|
steps,
|
||
|
.5,
|
||
|
1000.,
|
||
|
fog);
|
||
|
}
|
||
|
|
||
|
// The raymarch loop
|
||
|
Surface rayMarch(vec3 ro, vec3 rd, float start, float end) {
|
||
|
float sceneDist = 1e4;
|
||
|
float rayDepth = start;
|
||
|
int object_id = 0;
|
||
|
float steps = 0.;
|
||
|
float fog = 0.;
|
||
|
vec3 p;
|
||
|
for(int i = 0; i < maxIterations; i++) {
|
||
|
sceneDist = world(ro + rd * rayDepth, object_id, p);
|
||
|
rd += (texture2D(u_noise, (p.xy)*255.).rgb-.5)*.0005;
|
||
|
steps++;
|
||
|
fog += max(sceneDist, 0.);
|
||
|
|
||
|
if(sceneDist < stopThreshold || rayDepth > end) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
rayDepth += sceneDist * stepScale;
|
||
|
}
|
||
|
|
||
|
return getSurface(object_id, rayDepth, ro + rd * rayDepth, steps, p, fog);
|
||
|
}
|
||
|
|
||
|
// Calculated the normal of any given point in space. Intended to be cast from the point of a surface
|
||
|
vec3 calculate_normal(in vec3 position) {
|
||
|
vec3 grad = vec3(
|
||
|
world(vec3(position.x + eps, position.y, position.z)) - world(vec3(position.x - eps, position.y, position.z)),
|
||
|
world(vec3(position.x, position.y + eps, position.z)) - world(vec3(position.x, position.y - eps, position.z)),
|
||
|
world(vec3(position.x, position.y, position.z + eps)) - world(vec3(position.x, position.y, position.z - eps))
|
||
|
);
|
||
|
|
||
|
return normalize(grad);
|
||
|
}
|
||
|
|
||
|
vec3 lighting(Surface surface_object, vec3 cam) {
|
||
|
|
||
|
// start with black
|
||
|
vec3 sceneColour = vec3(0);
|
||
|
|
||
|
// Surface normal
|
||
|
vec3 normal = calculate_normal(surface_object.position);
|
||
|
normal += smoothstep(.02, 0., abs(sin(surface_object.onsurface_position.x * 4.))) * .5;
|
||
|
normal += smoothstep(.01, 0., abs(sin(surface_object.onsurface_position.y * 2.))) * .5;
|
||
|
|
||
|
// Light position
|
||
|
vec3 lp = path(u_time*3.+15.);
|
||
|
// Light direction
|
||
|
vec3 ld = lp - surface_object.position;
|
||
|
|
||
|
// light attenuation
|
||
|
// For brightly lit scenes or global illumination (like sunlit), this can be limited to just normalizing the ld
|
||
|
float len = length( ld );
|
||
|
ld = normalize(ld);
|
||
|
float lightAtten = min( 1.0 / ( 0.15*len ), 1.0 );
|
||
|
lightAtten = 1.;
|
||
|
|
||
|
// The surface's light reflection normal
|
||
|
vec3 reflection_normal = reflect(-ld, normal);
|
||
|
|
||
|
// Ambient Occlusion
|
||
|
|
||
|
float ao = (surface_object.steps*.01*(1./(surface_object.fog*.07)));
|
||
|
ao *= ao*2.;
|
||
|
ao = clamp(
|
||
|
((1.-ao)+.3)
|
||
|
, 0., 1.);
|
||
|
// ao -= surface_object.steps*.005;
|
||
|
|
||
|
// Object surface properties
|
||
|
float diffuse = max(0., dot(normal, ld));
|
||
|
float specular = max(0., dot( reflection_normal, normalize(cam - surface_object.position) ));
|
||
|
|
||
|
// Bringing all of the lighting components together
|
||
|
vec3 tp = surface_object.onsurface_position * 2.;
|
||
|
vec3 c = texture2D(u_noise, tp.xy + tp.zx).rrr + texture2D(u_noise, tp.zy + tp.yx).rrr;
|
||
|
tp = surface_object.onsurface_position * vec3(.8, .8, 1.);
|
||
|
c += texture2D(u_noise, tp.xy + tp.zx).rrr + texture2D(u_noise, tp.zy + tp.yx).rrr;
|
||
|
tp = surface_object.onsurface_position * vec3(1., .4, .4);
|
||
|
c += texture2D(u_noise, tp.xy + tp.zx).rrr + texture2D(u_noise, tp.zy + tp.yx).rrr;
|
||
|
c *= .125;
|
||
|
c *= .5 + .5;
|
||
|
c *= vec3(1,.98,.90);
|
||
|
sceneColour += ( c * (diffuse + specular )) * light1_colour * lightAtten * ao;
|
||
|
|
||
|
// adding fog
|
||
|
float fogl = surface_object.fog*.04;
|
||
|
fogl *= smoothstep(-.5, 1.8, fogl);
|
||
|
sceneColour = mix( sceneColour, fogColour, fogl );
|
||
|
|
||
|
return sceneColour;
|
||
|
}
|
||
|
|
||
|
void main() {
|
||
|
vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / min(u_resolution.y, u_resolution.x);
|
||
|
|
||
|
float t = u_time * 3.;
|
||
|
|
||
|
// movement
|
||
|
movement = path(t);
|
||
|
|
||
|
// Camera and look-at
|
||
|
vec3 cam = vec3(0,0,-2);
|
||
|
vec3 lookAt = vec3(0,-.5,-2.);
|
||
|
|
||
|
// add movement
|
||
|
lookAt = path(t+3.);
|
||
|
cam = movement;
|
||
|
// cam.y += abs(sin(u_time*20.)*.02);
|
||
|
|
||
|
// Unit vectors
|
||
|
vec3 forward = normalize(lookAt - cam);
|
||
|
vec3 right = normalize(vec3(forward.z, 0., -forward.x));
|
||
|
vec3 up = normalize(cross(forward, right));
|
||
|
|
||
|
// FOV
|
||
|
float FOV = 1.4;
|
||
|
|
||
|
// Ray origin and ray direction
|
||
|
vec3 ro = cam;
|
||
|
vec3 rd = normalize(forward + FOV * uv.x * right + FOV * uv.y * up);
|
||
|
rd.y -= (movement.y - lookAt.y) * .04;
|
||
|
|
||
|
// Ray marching
|
||
|
const float clipNear = 0.;
|
||
|
const float clipFar = 20.;
|
||
|
Surface objectSurface = rayMarch(ro, rd, clipNear, clipFar);
|
||
|
if(objectSurface.distance > clipFar) {
|
||
|
gl_FragColor = vec4(clipColour, 1.);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
vec3 sceneColour = lighting(objectSurface, cam);
|
||
|
|
||
|
gl_FragColor = vec4(sceneColour, 1.);
|
||
|
// gl_FragColor = vec4(vec3(objectSurface.fog*.03), 1.);
|
||
|
// gl_FragColor.rgb *= objectSurface.fog*.05;
|
||
|
}
|
||
|
|
||
|
</script>
|
||
|
|
||
|
<script src="./script.js"></script>
|
||
|
|
||
|
</body>
|
||
|
</html>
|