一、前言
马上2023年的圣诞节要到了,作为一个程序员,没什么可以送给大家的,就给大家画一个圣诞树,作为礼物来送给大家吧。
二、创意名
明月当空飘雪圣诞树
三、效果展示
四、实现步骤
主要是利用three.js和shader来动态实现飘雪粒子。
五、编码实现
CSS:
body { margin: 0;}
JavaScript:
var container;var camera, scene, renderer;var uniforms;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_mouse: { type: "v2", value: new THREE.Vector2() } }; var material = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: document.getElementById('vertexShader').textContent, fragmentShader: document.getElementById('fragmentShader').textContent }); 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.onmousemove = function(e) { uniforms.u_mouse.value.x = e.pageX uniforms.u_mouse.value.y = e.pageY }}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.05; renderer.render(scene, camera);}
Html:
<div id="container"></div><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 vec2 u_mouse;uniform float u_time;vec3 camUp = vec3(0.0, 1.0, 0.0);vec3 camDir= vec3(0.0, 0.0, 1.0);vec3 camPos= vec3(0.0, -0.96, -1.76);vec3 lightPos= vec3(-2.36, 5.75, -7.3);#define TAO 6.283const int MAX_ITER = 100;float PI=3.14159265;//--------------------------------------------------#define time u_timevec3 getNormal(in vec3 p);//ОÑвещение//-------------------------------------------------Ламбертvec3 getlightingLambert(in vec3 pos, in vec3 normal, in vec3 lightDir, in vec3 color){ // const vec3 diffColor = vec3 ( 0.5, 0.0, 0.0 ); vec3 n2 = normalize ( normal); vec3 l2 = normalize ( lightDir-pos ); vec3 diff = color * max ( dot ( n2, l2 ), 0.0 ); return diff;}//-------------------------------------------------по Фонгуvec3 getlightingPhong(in vec3 pos, in vec3 normal, in vec3 lightDir, in vec3 color){ vec3 specColor = vec3(1.0, 0.97, 0.94); float specPower = 36.0; vec3 l = normalize (lightDir-pos); vec3 v = normalize(pos-pos); vec3 n = normalize (normal); vec3 r = reflect ( -l, n ); vec3 diff = color * max ( dot ( n, l ), 0.0 ); vec3 spec = specColor * pow ( max ( dot ( l, r ), 0.0 ), specPower ); return diff + spec;}//------------------------------------------vec2 rot(vec2 p,float r){ vec2 ret; ret.x=p.x*cos(r)-p.y*sin(r); ret.y=p.x*sin(r)+p.y*cos(r); return ret;}//------------------------------------------vec2 rotsim(vec2 p,float s){ vec2 ret=p; ret=rot(p,-PI/(s*2.0)); ret=rot(p,floor(atan(ret.x,ret.y)/PI*s)*(PI/s)); return ret;}//------------------------------------------vec3 sim(vec3 p,float s){ vec3 ret=p; ret=p+s/2.0; ret=fract(ret/s)*s-s/2.0; return ret;}//------------------------------------------------//Примитивы//--------------------------------------------------float trunkCone( vec3 p, float c ){ float q = length(p.xz); return q + p.y * c;}//----------------------------------------------------vec3 background(vec3 rd){ float sky = max(0.0, -dot(rd, vec3(0.0, 1.0, 0.0))); float ground = max(0.0, -dot(rd, vec3(0.0, 6.7, 2.0))); vec3 bFon = pow(ground, 0.5) * vec3(0.4, 0.3, 0.2) + pow(sky, 1.) * vec3(0.4, 0.3, 0.2); return bFon ;}//-------------------------------------------------- vec3 getmaterial( in vec3 p, in float mat){ vec3 pos = p; vec3 color = vec3(1.); vec3 colorObject = vec3(0.5, 0.4, 0.3); vec3 colorObject1; if (mat == 0.) return vec3(0.4662, 0.4565, 0.4488); else if (mat == 1.) return vec3(1.0, 1.0, 1.0); else if (mat == 2.) { float r = pow(colorObject.r, cos(u_time * 0.5)); float g = pow(colorObject.g, cos(u_time * 0.3)); float b = pow(colorObject.b, cos(u_time * 0.7)); colorObject1 = vec3(r, g, b); return colorObject1; } else if (mat == 3.) // Ñтвол return vec3(0.7218, 0.4581, 0.0983); else if (mat == 4.) // иглы return vec3(0.5, 0.6, 0.2); else return vec3(0.3, 0.9,0.5);}//------------------------------------------------//Объекты//----------------------------------------------------vec2 rotate1(vec2 v, float angle) {return cos(angle)*v+sin(angle)*vec2(v.y,-v.x);}vec2 kaleido(vec2 v, float power){return rotate1(v,floor(.5+atan(v.x,-v.y)*power/TAO)*TAO/power);}vec2 kaleido6(vec2 v){return rotate1(v,floor(0.5+atan(v.x,-v.y)*0.95493)*1.0472);}vec2 kaleido12(vec2 v){return rotate1(v,floor(0.5+atan(v.x,-v.y)*1.90986)*0.5236);}mat2 r45=mat2(0.7071,0.7071,-0.7071,0.7071);mat2 r30=mat2(0.866,0.5,-0.5,0.866);mat2 rtrn=mat2(0.9689,-0.2474,0.2474,0.9689);//----------------------------------------------------Ветки ёлкиfloat branch(in vec3 pos, inout float trunk ){ float d = 1.0; for(int i=0;i<2;i++) { vec3 z=pos; float c=floor(z.y*4.); z.yz=rotate1(z.yz,-z.z*0.79*(1.0+c*0.1)); float bm = -z.y - 2.0; z.y=mod(z.y,0.25)-0.05; if(i==1) z.xz=z.xz*rtrn; z.xz=kaleido(z.xz,2.0-c); z.yz=rtrn*z.yz; bm=max(bm,-z.z+c*0.086); trunk=min(trunk,max(max(abs(z.x),abs(z.y)),bm))-0.001-z.z*0.003; float c2=floor(z.z * 16.0); z.z=mod(z.z,0.0625)-0.049; z.xy=rotate1(z.xy,c2*0.25); z.xy=kaleido12(z.xy); z.yz=z.yz*r30; d=min(d,max(max(max(abs(z.x),abs(z.z)),-z.y-0.05+c*0.005),bm)); } return d;}//----------------------------------------------------vec2 tree(in vec3 pos){ float d=1.; float material = 0.; float trunk = trunkCone( pos, 0.025 ); // Ствол d = branch(pos, trunk ); // Ветки if(trunk<d) { d=trunk; material = 3.; } else { material = 4.; } float result = max(0.0,max(d,max(pos.y,-pos.y-2.0))); return vec2(result, material);}//----------------------------------------------------//Starvec2 star(vec3 p){ p.y=p.y - 0.07; p= p * 10.0; float l=length(p); if (l < 2.0) { p.xy=rotsim(p.xy,5.0); p.y=p.y-2.0; p.z=abs(p.z); p.x=abs(p.x); return vec2(dot(p,normalize(vec3(2.0,1,3.0))) / 10.0, 2.); } else return vec2((l-1.9)/4.0, 2.0);}//----------------------------------------------------//Snowfloat makeshowflake(vec3 p){ return length(p)- 0.02;}//----------------------------------------------------float makeShow(vec3 p,float tx,float ty,float tz){ p.y=p.y+time*tx; p.x=p.x+time*ty; p.z=p.z+time*tz; p=sim(p,8.0); return makeshowflake(p);}//----------------------------------------------------vec2 show(vec3 p){ float f=makeShow(p,1.11, 1.03, 1.38); f=min(f,makeShow(p,1.72, 0.74, 1.06)); f=min(f,makeShow(p,1.93, 0.75, 1.35)); return vec2(f,1.0);}//----------------------------------------------------vec4 swag(vec2 pos){ vec3 camSide = cross(camDir, camUp); mat4 cm=mat4( camUp.x, camUp.y, camUp.z, -dot(camUp,camPos), camSide.x, camSide.y, camSide.z, -dot(camSide,camPos), camDir.x, camDir.y, camDir.z, -dot(camDir,camPos), 0.0, 0.0, 0.0, 1.0); vec4 pc=vec4(0,0,0,0); const float maxl=64.0; for(float i=0.0;i<maxl;i++) { vec4 pt=vec4( sin(i*PI*2.0*7.0/maxl) * 0.75 * (1.0-i/maxl), /* Ширина оÑÐ½Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ X */ i/maxl * 2.1, /*Ð’Ñ‹Ñота конуÑа */ cos(i*PI*2.0*7.0/maxl) * 0.75 * (1.0-i/maxl), /* Ширина оÑÐ½Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ Z */ 1.0); pt=pt*cm; vec2 vPos = pos; vPos.y += 1.28; vec2 xy=(pt/(-pt.z )).yx + vPos; float c; c= 0.2/length(xy); pc+=vec4( (sin(i*5.0+time*10.0)*0.5 + 0.5) * c, (cos(i*3.0+time*8.0)*0.5 + 0.5) * c, (sin(i*6.0+time*9.0)*0.5 + 0.5) * c ,0.0); } pc=pc/maxl; pc=smoothstep(0.0,1.0,pc); return pc; }//----------------------------------------------------// вывод объектаvec2 renderFunction(in vec3 pos){ vec2 result; vec3 pos1 = pos; // pos1 = rotationCoord(pos, 3.); vec2 treeMy = tree(pos1); vec2 starMy = star(pos1); vec2 showMy = show(pos1); if(treeMy.x < starMy.x) result = treeMy; else result = starMy; if(result.x > showMy.x) result = showMy; return result;}//-------------------------------------------------vec3 getNormal(in vec3 p){ const float e = 0.0001; return normalize ( vec3 ( renderFunction(p+vec3(e,0.0,0.0)).x - renderFunction(p+vec3(-e,0.0,0.0)).x, renderFunction(p+vec3(0.0,e,0.0)).x - renderFunction(p+vec3(0.0,-e,0.0)).x, renderFunction(p+vec3(0.0,0.0,e)).x - renderFunction(p+vec3(0.0,0.0,-e)).x ) );}//-------------------------------------------------float rndStart(vec2 co){return 0.1+0.9*fract(sin(dot(co,vec2(123.42,117.853)))*412.453);}//-------------------------------------------------vec4 render(in vec3 posOnRay, in vec3 camPos, in vec3 rayDir, out vec2 object){ vec4 color = vec4(0.0); float t = 0.0; vec3 normal; vec3 lightDir = lightPos; vec4 colorMirror = vec4(0.); //--------------Цвет фона vec3 bcol = background(rayDir); for(int i=0; i<MAX_ITER; ++i) { object = renderFunction(posOnRay); // Объект и его цвет//------------------ if(abs(object.x) < 0.004) { normal = normalize(getNormal(posOnRay)); //----------------- vec3 materialColor = getmaterial(posOnRay.xyz, object.y); if(object.y == 2.0) color.rgb = getlightingPhong(posOnRay, normal, lightDir, materialColor); // По Фонгу else color.rgb = getlightingLambert(posOnRay, normal, lightDir, materialColor); return color; } //------------------ t = object.x; posOnRay = posOnRay + t*rayDir; } color.rgb+=bcol*(1.0-clamp(color.w,0.0,1.0)); return vec4(color.rgb, 1.0);}//-------------------------------------------------void main(){ vec2 pos = ( 2.0 * gl_FragCoord.xy - u_resolution.xy ) / u_resolution.y; vec3 camSide = cross(camDir, camUp); vec3 rayDir = normalize(camSide*pos.x + camUp*pos.y + camDir); float t = 0.0, s = 0.1; vec2 object = vec2(1., 1.);//------------------------------ vec3 posOnRay = camPos;//------------------------------ vec4 color = vec4(0.); color= render(posOnRay, camPos, rayDir, object);//------------------------------ vec3 light_color = vec3(0.9, 0.5, 0.1); float c = 0.075/(length(pos - vec2(0.48, 0.66))); //луна vec4 moon = smoothstep(0.95,1.05,c) * vec4(1.0) + vec4(vec3(c) * light_color, 1.0);//-----------------------------vec4 swagMy = swag(pos); // гирлÑнда//----------------------------- gl_FragColor =color + moon + swagMy; }</script>