Endless Hallway 403 Forbidden Page developed using CSS, HTML, Three.js and JavaScript. Demo and download avilable.
HTML Snippet
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
void main() {
gl_Position = vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec2 u_resolution;
uniform float u_time;
uniform vec2 u_mouse;
uniform sampler2D u_plate;
const int octaves = 2;
const float seed = 43758.5453123;
const float seed2 = 73156.8473192;
// Epsilon value
const float eps = 0.005;
// movement variables
vec3 movement = vec3(.0);
// Gloable variables for the raymarching algorithm.
const int maxIterations = 256;
const float stepScale = .7;
const float stopThreshold = 0.001;
const float PI = 3.14159;
vec3 path(float delta) {
return vec3(0., 0., -delta*3.);
}
float length2( vec2 p )
{
return sqrt( p.x*p.x + p.y*p.y );
}
float length6( vec2 p )
{
p = p*p*p; p = p*p;
return pow( p.x + p.y, 1.0/6.0 );
}
float length8( vec2 p )
{
p = p*p; p = p*p; p = p*p;
return pow( p.x + p.y, 1.0/8.0 );
}
// Distance function primitives
// Reference: http://iquilezles.org/www/articles/distfunctions/distfunctions.htm
float sdBox( vec3 p, vec3 b )
{
vec3 d = abs(p) - b;
return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0));
}
float udBox( vec3 p, vec3 b )
{
return length(max(abs(p)-b,0.0));
}
float udRoundBox( vec3 p, vec3 b, float r )
{
return length(max(abs(p)-b,0.0))-r;
}
float sdSphere( vec3 p, float s )
{
return length(p)-s;
}
float sdCylinder( vec3 p, vec3 c )
{
return length(p.xz-c.xy)-c.z;
}
float sdCappedCylinder( vec3 p, vec2 h )
{
vec2 d = abs(vec2(length(p.xz),p.y)) - h;
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
float sdTorus82( vec3 p, vec2 t )
{
vec2 q = vec2(length2(p.xz)-t.x,p.y);
return length8(q)-t.y;
}
float sdPlane( vec3 p)
{
return p.y;
}
// smooth min
// reference: http://iquilezles.org/www/articles/smin/smin.htm
float smin(float a, float b, float k) {
float res = exp(-k*a) + exp(-k*b);
return -log(res)/k;
}
vec3 random3( vec3 p ) {
return fract(sin(vec3(dot(p,vec3(127.1,311.7,319.8)),dot(p,vec3(269.5,183.3, 415.2)),dot(p,vec3(362.9,201.5,134.7))))*43758.5453);
}
vec2 random2( vec2 p ) {
return fract(sin(vec2(dot(p,vec2(127.1,311.7)),dot(p,vec2(269.5,183.3))))*43758.5453);
}
// The world!
float world_sdf(in vec3 p) {
float world = 10.;
vec2 pxy = p.xy;
float pz = p.z;
float delta = pz * .3;
float s = sin(delta);
float c = cos(delta);
mat2 rot = mat2(c, -s, s, c);
p.z = mod(p.z, 1.5) - .75;
p.xy *= rot;
// p.z = mod(p.z, 2.) - 1.;
float path = path(u_time).z;
float animation = smoothstep(-2., -1.5, path - pz + p.z); // Adding the p.z here to make the doors appear solid
const float wallWidth = .02;
const float doorWidth = .23;
world = udBox(p, vec3(5.,10.,wallWidth)); // wall
world = min(world, udBox(p + vec3(0., .5, 0.), vec3(5., .05, wallWidth + .015))); // baseboard
// world = min(world, length(max(abs(p.yz)-vec2(.05, wallWidth + .015),0.0)));
world = max(world, -sdBox(p, vec3(doorWidth,.5,1.))); // doorframe
world = min(world, p.y + .5); // floor
// world = min(world, length(p.zy + vec2(.2))-.05);
const float RAD90 = 1.5708;
const float cospi = cos(PI);
const float sinpi = sin(PI);
const float cosrad90 = cos(RAD90);
const float sinrad90 = sin(RAD90*1.1);
c = mix(cospi, cosrad90, animation);
s = mix(sinpi, sinrad90, animation);
// Door
vec3 doorP = p;
doorP.xz *= mat2(c, -s, s, c);
doorP.x += mix(0.001, doorWidth - .005, animation);
doorP.z += mix(0., doorWidth - .005, animation);
world = min(world, sdBox(doorP, vec3(doorWidth -.005,.5-.005,wallWidth)));
// Plate
vec3 plateoffset = vec3(0., -.2, 0.);
vec2 plateUV = doorP.xy * vec2(-3., 3.) + .5 + plateoffset.xy * 3.;
float t = 1. - texture2D(u_plate, plateUV).x;
world = min(world, sdBox(doorP + plateoffset, vec3(doorWidth*.8,.1,wallWidth+.01 * t + .005)));
// Door handle
doorP.x += .12;
doorP.z -= .05;
doorP.y += .05;
world = min(world, sdSphere(doorP, .03));
return world;
}
// Fuck yeah, normals!
vec3 calculate_normal(in vec3 p)
{
const vec3 small_step = vec3(0.0001, 0.0, 0.0);
float gradient_x = world_sdf(vec3(p.x + eps, p.y, p.z)) - world_sdf(vec3(p.x - eps, p.y, p.z));
float gradient_y = world_sdf(vec3(p.x, p.y + eps, p.z)) - world_sdf(vec3(p.x, p.y - eps, p.z));
float gradient_z = world_sdf(vec3(p.x, p.y, p.z + eps)) - world_sdf(vec3(p.x, p.y, p.z - eps));
vec3 normal = vec3(gradient_x, gradient_y, gradient_z);
return normalize(normal);
}
// Raymarching.
float rayMarching( vec3 origin, vec3 dir, float start, float end, inout float field ) {
float sceneDist = 1e4;
float rayDepth = start;
for ( int i = 0; i < maxIterations; i++ ) {
sceneDist = world_sdf( origin + dir * rayDepth ); // Distance from the point along the ray to the nearest surface point in the scene.
if (( sceneDist < stopThreshold ) || (rayDepth >= end)) {
break;
}
// We haven't hit anything, so increase the depth by a scaled factor of the minimum scene distance.
rayDepth += sceneDist * stepScale;
}
if ( sceneDist >= stopThreshold ) rayDepth = end;
else rayDepth += sceneDist;
// We've used up our maximum iterations. Return the maximum distance.
return rayDepth;
}
// Based on original by IQ - optimized to remove a divide
float calculateAO(vec3 p, vec3 n)
{
const float AO_SAMPLES = 5.0;
float r = 0.0;
float w = 1.0;
for (float i=1.0; i<=AO_SAMPLES; i++)
{
float d0 = i * 0.15; // 1.0/AO_SAMPLES
r += w * (d0 - world_sdf(p + n * d0));
w *= 0.5;
}
return 1.0-clamp(r,0.0,1.0);
}
var container = void 0;
var camera = void 0,scene = void 0,renderer = void 0;
var uniforms = void 0;
var loader = new THREE.TextureLoader();
var texture = void 0,plate = void 0;
loader.setCrossOrigin("anonymous");
loader.load(
'https://s3-us-west-2.amazonaws.com/s.cdpn.io/982762/noise.png',
function (tex) {
texture = tex;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.minFilter = THREE.LinearFilter;
loader.load('https://s3-us-west-2.amazonaws.com/s.cdpn.io/982762/403-2.png', function (tex) {
plate = tex;
init();
animate();
});
});
function init() {
container = document.getElementById('container');
camera = new THREE.Camera();
camera.position.z = 1;
scene = new THREE.Scene();
var geometry = new THREE.PlaneBufferGeometry(2, 2);
uniforms = {
u_time: { type: "f", value: 1.0 },
u_resolution: { type: "v2", value: new THREE.Vector2() },
u_noise: { type: "t", value: texture },
u_plate: { type: "t", value: plate },
u_mouse: { type: "v2", value: new THREE.Vector2() } };
var material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent });
material.extensions.derivatives = true;
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(renderer.domElement);
onWindowResize();
window.addEventListener('resize', onWindowResize, false);
document.addEventListener('pointermove', function (e) {
var ratio = window.innerHeight / window.innerWidth;
uniforms.u_mouse.value.x = (e.pageX - window.innerWidth / 2) / window.innerWidth / ratio;
uniforms.u_mouse.value.y = (e.pageY - window.innerHeight / 2) / window.innerHeight * -1;
e.preventDefault();
});
}
function onWindowResize(event) {
renderer.setSize(window.innerWidth, window.innerHeight);
uniforms.u_resolution.value.x = renderer.domElement.width;
uniforms.u_resolution.value.y = renderer.domElement.height;
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
uniforms.u_time.value += 0.01;
renderer.render(scene, camera);
}
Preview
Leave a Reply