<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>3D无限圆柱体Canvas特效 - 【更多源码:www.96flw.com】</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<canvas></canvas>
<script type="x-shader/vertex">
precision mediump float;
attribute vec2 position;
void main () {
gl_Position = vec4(position, 0, 1.0);
}
</script>
<script type="x-shader/fragment">
precision highp float;
#define ITERS 64
uniform float width;
uniform float height;
uniform float time;
// Calculate cameras "orthonormal basis", i.e. its transform matrix components
vec3 getCameraRayDir(vec2 uv, vec3 camPos, vec3 camTarget) {
vec3 camForward = normalize(camTarget - camPos);
vec3 camRight = normalize(cross(vec3(0.0, 1.0, 0.0), camForward));
vec3 camUp = normalize(cross(camForward, camRight));
float fPersp = 2.0;
vec3 vDir = normalize(uv.x * camRight + uv.y * camUp + camForward * fPersp);
return vDir;
}
// distance function for a sphere
float sphere(vec3 p, float r) {
return length(p) - r;
}
// capped cylinder
float cappedCylinder(vec3 p, float h, float r)
{
vec2 d = abs(vec2(length(p.xz),p.y)) - vec2(h,r);
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
float plane(vec3 p) {
return p.y;
}
float addToScene(float a, float b) {
return min(a, b);
}
float deformation(in vec3 pos) {
return .0125 *
sin(-time + pos.x * 35.0) *
sin(-time * 2.0 - pos.z * 37.0);
}
vec3 repeatXZ(in vec3 pos) {
return vec3( fract(pos.x+0.5)-0.5, pos.y, fract(pos.z+0.5) - 0.5);
}
float scene(in vec3 pos) {
float t = 1e7;
vec3 rpos = repeatXZ(pos);
t = addToScene(t, cappedCylinder(rpos, .3, .5 + sin(time * .2) * .25));
t = addToScene(t, plane(pos));
return t;
}
// cast a ray along a direction and return
// the distance to the first thing it hits
// if nothing was hit, return -1
float castRay(vec3 rayOrigin, vec3 rayDir)
{
float t = 0.0; // Stores current distance along ray
for (int i = 0; i < ITERS; i++)
{
float res = scene(rayOrigin + rayDir * t);
if (res < (0.0005*t))
{
return t;
}
t += res;
}
return -1.0;
}
// calculate normal:
vec3 calcNormal(vec3 pos) {
// Center sample
float c = scene(pos);
// Use offset samples to compute gradient / normal
vec2 eps_zero = vec2(0.001, 0.0);
return normalize(vec3( scene(pos + eps_zero.xyy), scene(pos + eps_zero.yxy), scene(pos + eps_zero.yyx) ) - c);
}
// gamma correction
vec3 gammaCorrect(vec3 col) {
return pow(col, vec3(1.0 / 2.2));
}
// https://www.iquilezles.org/www/articles/rmshadows/rmshadows.htm
float softshadow(in vec3 ro, in vec3 rd, float mint, float maxt, float k) {
float res = 1.0;
float ph = 1e10;
float t = mint;
for (int i=0; i < ITERS; i++) {
float h = scene(ro + rd * t);
float y = h * h / (2.0 * ph);
float d = sqrt(h * h - y * y);
res = min(res, k * d / max(0.0, t - y) );
ph = h;
if (res < 5e-4 || t >= maxt) {
break;
}
t += h;
}
return clamp(res, 0.0, 1.0);
}
// calculate ambient occlusion
float ambientOcclusion( in vec3 pos, in vec3 nor )
{
float occ = 0.0;
float sca = 1.0;
for(int i=0; i<5; i++)
{
float h = 0.001 + 0.15*float(i)/4.0;
float d = scene( pos + h*nor );
occ += (h-d)*sca;
sca *= 0.95;
}
return clamp( 1.0 - 1.5*occ, 0.0, 1.0 );
}
// Visualize depth based on the distance
vec3 render(vec3 rayOrigin, vec3 rayDir)
{
float t = castRay(rayOrigin, rayDir);
if (t == -1.0) {
return vec3(0, 0, 0);
}
vec3 material = vec3(1.0, .5, 0.0);
// key light
vec3 pos = rayOrigin + rayDir * t;
vec3 nor = calcNormal(pos);
vec3 lig = normalize(vec3(sin(time * .01) * 5.0, 10.0, 5.6));
float dif = clamp(dot(nor, lig), 0.0, 1.0) * softshadow(pos, lig, 0.00001, 3.0, 32.0);
vec3 col = material * 4.0 * dif * vec3(1.00,1.00,1.0);
// ambient light
float occ = ambientOcclusion(pos, nor);
float amb = clamp( 0.5+0.5*nor.y, 0.0, 1.0 );
col += material * amb * occ * vec3(0.04, 0.04, 0.04);
// fog
col *= exp( -0.0005*t*t*t );
return col;
}
// normalize coords and correct for aspect ratio
vec2 normalizeScreenCoords() {
float aspectRatio = width / height;
vec2 result = 2.0 * (gl_FragCoord.xy / vec2(width, height) - 0.5);
result.x *= aspectRatio;
return result;
}
float rand() {
return fract(sin(dot(gl_FragCoord.xy + sin(time),vec2(12.9898,78.233))) * 43758.5453);
}
void main() {
vec3 camPos = vec3(3.5 + sin(time * .1) * 4.0, 6.0, -3.0);
vec3 camTarget = vec3(0);
vec2 uv = normalizeScreenCoords();
vec3 rayDir = getCameraRayDir(uv, camPos, camTarget);
vec3 col = render(camPos, rayDir);
gl_FragColor = vec4(gammaCorrect(mix(col, vec3(rand()), .0125)), 1.0);
}
</script>
<!-- partial -->
<script type="module" src="js/script.js"></script>
</body>
</html>