christmas tree
raymarching
happy new year
license: gpl-3.0 | |
height: 600 | |
scrolling: no | |
border: yes |
vcs.xml | |
.idea/ |
const float PI =3.141592; | |
const float PI2=6.2831853; | |
const float maxd=256.0; //Max depth | |
float nearestD = maxd; | |
vec3 color = vec3(0.0,0.0,1.0); | |
float flakes(vec3 p) { | |
const float snowflakeMaxDist = 20.0; | |
if ( (abs(p.x) > snowflakeMaxDist) || | |
(abs(p.y) > snowflakeMaxDist) || | |
(abs(p.z) > snowflakeMaxDist) ) | |
return 9999.9; | |
float snowPush = 0.25*time; | |
p.x += snowPush*-3.0; | |
p.y += snowPush*1.5; | |
p.z += snowPush*-0.25; | |
const float modDist = 2.0; | |
float stepX = floor(p.x/modDist); | |
float stepY = floor(p.y/modDist); | |
float stepZ = floor(p.z/modDist); | |
vec3 flakeP = vec3( | |
mod(p.x,modDist), | |
mod(p.y,modDist), | |
mod(p.z,modDist) | |
); | |
vec3 flakePos = vec3(modDist*0.5); | |
flakePos.x += sin(snowPush+stepY*1.0)*(2.0/5.0)*modDist; | |
flakePos.y += sin(snowPush+stepZ*1.3)*(2.0/5.0)*modDist; | |
flakePos.z += sin(snowPush+stepX*1.7)*(2.0/5.0)*modDist; | |
return sdSphere(flakeP- flakePos, 0.03); | |
} | |
<!DOCTYPE html> | |
<html> | |
<style> | |
body { | |
margin: 0; | |
overflow: hidden | |
} | |
canvas { | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
} | |
</style> | |
<body> | |
<script src="https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/js/glx.js"></script> | |
<script src="https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/js/ShaderLoader.js"></script> | |
<script src="https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/js/FullScreenTriangle.js"></script> | |
<script src="https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/js/mouse3d.js"></script> | |
<script> | |
ShaderLoader.load('tree.glsl', init); | |
function init(fragCode) { | |
let fst = FullScreenTriangle(fragCode); | |
let resolution = fst.triangleProgram.uniform('2f', 'resolution'); | |
let lightPos = fst.triangleProgram.uniform('3f', 'lightPos'); | |
let time = fst.triangleProgram.uniform('1f', 'time'); | |
let eye = fst.triangleProgram.uniform('3f', 'eye'); | |
let lookAt = fst.triangleProgram.uniform('3f', 'lookAt'); | |
let started = new Date().getTime(); | |
Mouse3D.init(fst.canvas, 12); | |
animate(); | |
function animate() { | |
requestAnimationFrame(animate); | |
drawFrame(); | |
} | |
function drawFrame() { | |
let t = (new Date().getTime() - started)/1000; | |
resolution.set(fst.gl.drawingBufferWidth, fst.gl.drawingBufferHeight); | |
time.set(t); | |
lightPos.set(Math.cos(t/10)*10,10,Math.sin(t/10)*10); | |
eye.set(Mouse3D.eye[0], Mouse3D.eye[1], Mouse3D.eye[2]); | |
lookAt.set(Mouse3D.lookAt[0], Mouse3D.lookAt[1], Mouse3D.lookAt[2]); | |
fst.draw(); | |
} | |
} | |
</script> | |
</body> | |
</html> |
#pragma import https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/glsl/lighting/phong.glsl | |
/** | |
* Lighting via Phong illumination. | |
* | |
* The vec3 returned is the RGB color of that point after lighting is applied. | |
* k_a: Ambient color | |
* k_d: Diffuse color | |
* k_s: Specular color | |
* alpha: Shininess coefficient | |
* p: position of point being lit | |
* eye: the position of the camera | |
* | |
* See https://en.wikipedia.org/wiki/Phong_reflection_model#Description | |
*/ | |
vec3 phongIllumination(vec3 k_a, vec3 k_d, vec3 k_s, float alpha, vec3 p, vec3 nor, vec3 eye, vec3 materialColor) { | |
const vec3 lightIntensity = vec3(0.7, 0.7, 0.7); | |
vec3 color = materialColor * k_a; | |
color += phongContribForLight(k_d, k_s, alpha, p, nor, eye, lightPos, lightIntensity); | |
//color += phongContribForLight(k_d, k_s, alpha, p, eye, light2Pos, light2Intensity); | |
return color; | |
} | |
vec3 decodeMaterial(float material) { | |
if (material < 0.) { | |
return vec3(0.,1.,0.); | |
} | |
float r = material * 10.; | |
r = r - fract(r); | |
r = r/10.; | |
float g = material * 100. - r * 100.; | |
g = g - fract(g); | |
g = g/10.; | |
float b = material * 1000. - r * 1000. - g * 100.; | |
b = b - fract(b); | |
b = b/10.; | |
return vec3(r, g, b); | |
} | |
vec3 phong(vec3 p, vec3 nor, vec3 eye, float material) { | |
const vec3 K_a = vec3(.3, .2, .2); | |
const vec3 K_d = vec3(.2, .4, .7); | |
const vec3 K_s = vec3(.1, .1, .1); | |
const float shininess = 3.5; | |
vec3 materialColor = decodeMaterial(material); | |
return phongIllumination(K_a, K_d, K_s, shininess, p, nor, eye, materialColor); | |
} |
const float VERY_FAR = 1e11; | |
float ground(vec3 p){ | |
float d = -0.50 ; | |
p.y += noise(p)*.33 ; | |
d = sdPlane(p, d); | |
return d; | |
} | |
vec2 star(vec3 p) { | |
p.y -=1.5; | |
p.xy = repeatAng(p.xy, 5.0); // 3. Clone five cornders radially about Z axis | |
p.xz = abs(p.xz); // 2. Symmetrical about XoY and ZoY | |
vec3 n = vec3(0.5, 0.25, 0.8); | |
float d = plane(p, normalize(n), 0.065); // 1. A plane cutting the corner of X+Y+Z+ | |
return vec2(d, .900 ); | |
} | |
vec2 green(vec3 p) { | |
float d = VERY_FAR; | |
float k = .12; | |
d = sdCappedCone( p - vec3(.0, .99, .0), .4, 0.45, .0); | |
d = opSmoothU(d, sdCappedCone( p - vec3(.0, .55, .0), .5, 0.55, .0), k); | |
d = opSmoothU(d, sdCappedCone( p - vec3(.0, .11, .0), .6, 0.65, .0), k); | |
return vec2( d, -2. ); | |
} | |
vec2 trunk(vec3 p) { | |
float d = VERY_FAR; | |
d = sdCylinder(p-vec3(.0, -.5,.0), vec2(0.1, 0.6)); | |
return vec2( d, 0.000 ); | |
} | |
vec2 toy(vec3 p, float col) { | |
float d = VERY_FAR; | |
d = sdSphere(p, 0.07); | |
return vec2( d, col ); | |
} | |
vec2 toys(vec3 p) { | |
vec2 res = vec2(VERY_FAR, -1); | |
float r = .42; | |
float h = -0.57; | |
res = opU( res, toy(p-vec3(-r, h, -r), 0.990) ); | |
res = opU( res, toy(p-vec3(-r, h, r), 0.099) ); | |
res = opU( res, toy(p-vec3( r, h, -r), 0.999) ); | |
res = opU( res, toy(p-vec3( r, h, r), 0.555) ); | |
r = .3; | |
h = 0.5; | |
res = opU( res, toy(p-vec3(-r, h, -r), 0.990) ); | |
res = opU( res, toy(p-vec3(-r, h, r), 0.099) ); | |
res = opU( res, toy(p-vec3( r, h, -r), 0.999) ); | |
res = opU( res, toy(p-vec3( r, h, r), 0.555) ); | |
r = .5; | |
h = -0.05; | |
res = opU( res, toy(p-vec3(-r, h, 0.), 0.990) ); | |
res = opU( res, toy(p-vec3( 0, h, -r), 0.099) ); | |
res = opU( res, toy(p-vec3( r, h, 0.), 0.999) ); | |
res = opU( res, toy(p-vec3( 0, h, r), 0.555) ); | |
return res; | |
} | |
vec2 tree(vec3 p) { | |
vec2 res = vec2(VERY_FAR, -1); | |
res = opU( res, green(p) ); | |
res = opU( res, trunk(p) ); | |
res = opU( res, star(p) ); | |
res = opU( res, toys(p) ); | |
// res = opU( res, lamps(p) ); | |
return res; | |
} | |
float snowmans(vec3 p) { | |
// mirror 3 | |
p.xz = repeatAng(p.xz, 3.0); | |
p.xz = abs(p.xz); | |
float k = .12; | |
float d = sdSphere( p - vec3(1.5, 0.7, 3.0), .2); | |
d = opSmoothU(d, sdSphere( p - vec3(1.5, 0.2, 3.0), .3), k); | |
d = opSmoothU(d, sdSphere( p - vec3(1.5, -0.4, 3.0), .4), k); | |
return d; | |
} | |
float snowfall(vec3 p) { | |
vec3 q = vec3(mod(p.x, 3.0) - 1.5, p.yz); | |
return sdSphere( q - vec3(1.5, 0.7, 3.0), .02); | |
} | |
vec2 snow(vec3 p) { | |
float k = .12; | |
float d = ground(p); | |
d = opSmoothU(d, snowmans( p ), k); | |
// d = opSmoothU(d, snowfall( p ), k); | |
d = opSmoothU( d, flakes(p), k ); | |
return vec2( d, .999 ); | |
} | |
vec2 snowmans_accesories(vec3 p) { | |
// mirror 3 | |
p.xz = repeatAng(p.xz, 3.0); | |
p.xz = abs(p.xz); | |
float k = .12; | |
vec2 res = vec2(sdSphere( p - vec3(1.50, 0.70, 3.20), .02), 0.00 ); | |
res = opU(res, vec2(sdSphere( p - vec3(1.44, 0.75, 3.18), .02), 0.00 )); | |
res = opU(res, vec2(sdSphere( p - vec3(1.56, 0.75, 3.18), .02), 0.00 )); | |
res = opU(res, vec2(sdSphere( p - vec3(1.50, 0.25, 3.30), .02), 0.00 )); | |
res = opU(res, vec2(sdSphere( p - vec3(1.50, -0.40, 3.40), .02), 0.00 )); | |
return res; | |
} | |
vec2 map(vec3 p) { | |
vec2 res = vec2(VERY_FAR, -1); | |
res = opU( res, snow(p) ); | |
res = opU( res, snowmans_accesories(p) ); | |
res = opU( res, tree(p) ); | |
return res; | |
} |
// distance to bezier curve | |
float det( vec2 a, vec2 b ) { | |
return a.x*b.y-b.x*a.y; | |
} | |
vec3 getClosest( vec2 b0, vec2 b1, vec2 b2 ) { | |
float a = det(b0,b2); | |
float b = det(b1,b0)*2.; | |
float d = det(b2,b1)*2.; | |
float f = b*d - a*a; | |
vec2 d21 = b2-b1; | |
vec2 d10 = b1-b0; | |
vec2 d20 = b2-b0; | |
vec2 gf = 2.0*(b*d21+d*d10+a*d20); gf = vec2(gf.y,-gf.x); | |
vec2 pp = -f*gf/dot(gf,gf); | |
vec2 d0p = b0-pp; | |
float ap = det(d0p,d20); | |
float bp = 2.0*det(d10,d0p); | |
float t = clamp( (ap+bp)/(2.0*a+b+d), 0.0 ,1.0 ); | |
return vec3( mix(mix(b0,b1,t), mix(b1,b2,t),t), t ); | |
} | |
vec2 sdBezier( vec3 a, vec3 b, vec3 c, vec3 p, float thickness ) { | |
vec3 w = normalize( cross( c-b, a-b ) ); | |
vec3 u = normalize( c-b ); | |
vec3 v = normalize( cross( w, u ) ); | |
vec2 a2 = vec2( dot(a-b,u), dot(a-b,v) ); | |
vec2 b2 = vec2( 0.0 ); | |
vec2 c2 = vec2( dot(c-b,u), dot(c-b,v) ); | |
vec3 p3 = vec3( dot(p-b,u), dot(p-b,v), dot(p-b,w) ); | |
vec3 cp = getClosest( a2-p3.xy, b2-p3.xy, c2-p3.xy ); | |
return vec2( 0.85*(sqrt(dot(cp.xy,cp.xy)+p3.z*p3.z) - thickness), cp.z ); | |
} |
// https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm | |
// distance functions | |
float dot2( in vec2 v ) { | |
return dot(v,v); | |
} | |
float sdCappedCone( in vec3 p, in float h, in float r1, in float r2 ){ | |
vec2 q = vec2( length(p.xz), p.y ); | |
vec2 k1 = vec2(r2,h); | |
vec2 k2 = vec2(r2-r1,2.0*h); | |
vec2 ca = vec2(q.x-min(q.x,(q.y < 0.0)?r1:r2), abs(q.y)-h); | |
vec2 cb = q - k1 + k2*clamp( dot(k1-q,k2)/dot2(k2), 0.0, 1.0 ); | |
float s = (cb.x < 0.0 && ca.y < 0.0) ? -1.0 : 1.0; | |
return s*sqrt( min(dot2(ca),dot2(cb)) ); | |
} | |
// vertical | |
float sdCylinder( 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 sdSphere( vec3 p, float s ){ | |
return length(p)-s; | |
} | |
// horizontal | |
float sdPlane( vec3 p , float down){ | |
return p.y - down; | |
} | |
float plane(vec3 p, vec3 n, float offs) { | |
return dot(p, n) - offs; | |
} | |
// distance functions operations | |
float opS( float d1, float d2 ){ | |
return max(-d2,d1); | |
} | |
vec2 opU( vec2 d1, vec2 d2 ){ | |
return (d1.x<d2.x) ? d1 : d2; | |
} | |
vec3 opRep( vec3 p, vec3 c ){ | |
return mod(p,c)-0.5*c; | |
} | |
vec3 opTwist( vec3 p ){ | |
float c = cos(10.0*p.y+10.0); | |
float s = sin(10.0*p.y+10.0); | |
mat2 m = mat2(c,-s,s,c); | |
return vec3(m*p.xz,p.y); | |
} | |
float opSmoothU( float d1, float d2, float k ) { | |
float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 ); | |
return mix( d2, d1, h ) - k*h*(1.0-h); | |
} | |
vec2 rotate(vec2 p, float ang) { | |
float c = cos(ang), s = sin(ang); | |
return vec2(p.x*c - p.y*s, p.x*s + p.y*c); | |
} | |
vec2 repeatAng(vec2 p, float n) { | |
float ang = 2.0*3.14/n; | |
float sector = floor(atan(p.x, p.y)/ang + 0.5); | |
p = rotate(p, sector*ang); | |
return p; | |
} |
precision highp float; | |
// christmas tree | |
// by stranger in the q | |
// at 2018-dec-29 | |
uniform vec2 resolution; | |
uniform vec3 eye; | |
uniform vec3 lookAt; | |
uniform vec3 lightPos; | |
uniform float time; | |
#pragma import https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/glsl/noise.glsl | |
#pragma import sdf.glsl | |
#pragma import flakes.glsl | |
#pragma import scene.glsl | |
#pragma import https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/glsl/rayDirection.glsl | |
#pragma import https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/glsl/viewMatrix.glsl | |
#pragma import https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/glsl/castRay.glsl | |
#pragma import https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/glsl/normal.glsl | |
#pragma import phong.glsl | |
#pragma import https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/glsl/softShadow.glsl | |
#pragma import https://cdn.jsdelivr.net/gh/strangerintheq/[email protected]/glsl/ao.glsl | |
void drawSky() { | |
gl_FragColor = vec4(0.9, 0.9, 0.9, 1.0); | |
} | |
void drawScene(vec3 pt, float material) { | |
vec3 nor = estimateNormal( pt ); | |
float occ = ao( pt, nor ); | |
float shadow = softShadow( pt, normalize(lightPos-pt), 0.1, 22.2); | |
vec3 color = phong(pt, nor, eye, material)*sqrt(occ); | |
color += color * shadow; | |
gl_FragColor = vec4(color, 1.0); | |
} | |
void main(void) { | |
vec3 direction = rayDirection(60.0, resolution); | |
mat4 viewToWorld = viewMatrix(eye, lookAt, vec3(0.0, 1.0, 0.0)); | |
vec3 worldDir = (viewToWorld * vec4(direction, 0.0)).xyz; | |
vec2 dist = castRay(eye, worldDir); | |
if (dist.x > 32.) { | |
drawSky(); | |
} else { | |
drawScene(eye + dist.x * worldDir, dist.y); | |
} | |
} |