<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Three.js + WebGL棒棒糖样式的三维螺旋隧道</title>
<style>
body {
margin: 0;
padding: 0;
}
#container {
position: fixed;
touch-action: none;
}
</style>
</head>
<body>
<script src="js/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;
const int octaves = 2;
const float seed = 43758.5453123;
const float seed2 = 73156.8473192;
// Epsilon value
const float eps = 0.005;
const vec3 ambientLight = 0.99 * vec3(1.0, 1.0, 1.0);
const vec3 light1Pos = vec3(10., 5.0, -25.0);
const vec3 light1Intensity = vec3(0.35);
const vec3 light2Pos = vec3(-20., -25.0, 85.0);
const vec3 light2Intensity = vec3(0.2);
// movement variables
vec3 movement = vec3(.0);
// Gloable variables for the raymarching algorithm.
const int maxIterations = 256;
const int maxIterationsShad = 16;
const float stepScale = .7;
const float stopThreshold = 0.001;
mat4 rotationMatrix(vec3 axis, float angle)
{
axis = normalize(axis);
float s = sin(angle);
float c = cos(angle);
float oc = 1.0 - c;
return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,
oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,
oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,
0.0, 0.0, 0.0, 1.0);
}
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 );
}
// smooth min
// reference: https://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);
}
vec3 path(in float delta) {
return vec3(cos(delta) * .2 + sin((u_time + delta) * .2) * .5, sin(delta * .5) * .4, delta);
}
// The world!
float world_sdf(in vec3 p) {
float world = 10.;
p.xy -= path(p.z).xy;
vec2 pol = vec2(length8(p.xy), atan(p.y, p.x) * 1. / 3.14159);
pol.y += p.z * .5;
pol = mod(pol, 1.0) - .5;
world = length(pol) - .2;
pol = vec2(length(p.xy), atan(p.y, p.x) * 1. / 3.14159);
pol.y += 3.14159 * 4. + p.z * .5;
pol = mod(pol, .5) - .25;
world = smin(length(pol) - .05, world, 10. + sin(u_time * .2) * 5.);
// world = min(length(pol) - .05, world);
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;
}
// Shadows
// Reference at: https://www.iquilezles.org/www/articles/rmshadows/rmshadows.htm
float softShadow(vec3 ro, vec3 lp, float k){
vec3 rd = (lp-ro); // Unnormalized direction ray.
float shade = 1.0;
float dist = 0.05;
float end = max(length(rd), 0.001);
rd /= end;
for (int i=0; i<maxIterationsShad; i++){
float h = world_sdf(ro + rd*dist);
shade = min(shade, k*h/dist);
dist += clamp(h, 0.01, 0.5);
if (h<0.001 || dist > end) break;
}
return shade;
}
// Based on original by IQ - optimized to remove a divide
float calculateAO(vec3 p, vec3 n)
{
const float AO_SAMPLES = 10.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);
}
/**
* Lighting
* This stuff is way way better than the model I was using.
* Courtesy Shane Warne
* Reference: https://raymarching.com/
* -------------------------------------
* */
// Lighting.
vec3 lighting( vec3 sp, vec3 camPos, int reflectionPass, float dist, float field, vec3 rd) {
// Start with black.
vec3 sceneColor = vec3(0.0);
vec2 pol = vec2(length(sp.xy), atan(sp.y, sp.x) * 1. / 3.14159 * 5.);
vec2 _pol = pol;
pol = mod(pol, 2.) - 1.;
float delta = 1. / pol.x; // The inverse distance from the centre
float col = sin(length(pol) + sp.z * 40.);
vec3 objColor = mix(vec3(1.0), vec3(0., 1., 0.), smoothstep(.5, .6, col));
objColor = mix(objColor, vec3(1., 0., 0.), smoothstep(-.5, -.6, col));
objColor *= vec3(1. / length(sp.xy));
// objColor = objColor.brg;
// Obtain the surface normal at the scene position "sp."
vec3 surfNormal = calculate_normal(sp);
// Lighting.
// lp - Light position. Keeping it in the vacinity of the camera, but away from the objects in the scene.
vec3 lp = path(camPos.z + 1.);
// ld - Light direction.
vec3 ld = lp-sp;
// lcolor - Light color.
vec3 lcolor = vec3(1.,1.,0.5) * .8;
// Light falloff (attenuation).
float len = length( ld ); // Distance from the light to the surface point.
ld /= len; // Normalizing the light-to-surface, aka light-direction, vector.
// float lightAtten = min( 1.0 / ( 0.15*len*len ), 1.0 ); // Removed light attenuation for this because I want the fade to white
float sceneLen = length(camPos - sp); // Distance of the camera to the surface point
float sceneAtten = min( 1.0 / ( 0.015*sceneLen*sceneLen ), 1.0 ); // Keeps things between 0 and 1.
// Obtain the reflected vector at the scene position "sp."
vec3 ref = reflect(-ld, surfNormal);
float ao = calcula