一、前言

马上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>