1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        three.js 實現(xiàn)火花特效

        共 7830字,需瀏覽 16分鐘

         ·

        2021-07-07 10:59


        前言

        大家好,這里是 CSS兼WebGL 魔法使——alphardex。

        上周末剛在原神里抽到了“火花騎士”可莉,于是就心血來潮,想用three.js來實現(xiàn)一種火系的特效,不是炸彈的爆炸,而是炸彈爆炸后在草上留下的火花效果

        RBvmVJ.jpg

        游戲里的效果相對比較卡通化,而本文的效果將更加逼近現(xiàn)實一點

        讓我們開始吧!

        準備工作

        在開始本項目之前,你首先要了解ray marching這個概念,如果不了解也沒關(guān)系,筆者之前寫過一篇介紹它的入門文章:https://juejin.cn/post/6934461126977519629,或者通過這篇文章:http://jamie-wong.com/2016/07/15/ray-marching-signed-distance-functions/也可以入門,掌握了基礎(chǔ)概念后就可以開始了

        本項目需要用到:

        筆者的three.js模板:點擊右下角的fork即可復制一份

        著色器模塊化:glslify

        著色器npm包:glsl-noise,glsl-sdf-primitives,glsl-sdf-ops

        正文

        場景搭建

        按之前的慣例,搭建一個場景,放一個鋪滿屏幕的平面,設(shè)定一些必要的參數(shù)(火花的速度與顏色)

        class RayMarchingFire extends Base {
          constructor(sel: string, debug: boolean) {
            super(sel, debug);
            this.clock = new THREE.Clock();
            this.cameraPosition = new THREE.Vector3(001);
            this.params = {
              velocity: 2,
            };
            this.colorParams = {
              color1: "#ff801a",
              color2: "#ff5718",
            };
          }
          // 初始化
          init() {
            this.createScene();
            this.createOrthographicCamera();
            this.createRenderer();
            this.createRayMarchingFireMaterial();
            this.createPlane();
            this.createLight();
            this.trackMousePos();
            this.addListeners();
            this.setLoop();
          }
          // 創(chuàng)建材質(zhì)
          createRayMarchingFireMaterial() {
            const rayMarchingFireMaterial = new THREE.ShaderMaterial({
              vertexShader: rayMarchingFireVertexShader,
              fragmentShader: rayMarchingFireFragmentShader,
              side: THREE.DoubleSide,
              uniforms: {
                uTime: {
                  value: 0,
                },
                uMouse: {
                  value: new THREE.Vector2(00),
                },
                uResolution: {
                  value: new THREE.Vector2(window.innerWidth, window.innerHeight),
                },
                uVelocity: {
                  value: 3,
                },
                uColor1: {
                  value: new THREE.Color(this.colorParams.color1),
                },
                uColor2: {
                  value: new THREE.Color(this.colorParams.color2),
                },
              },
            });
            this.rayMarchingFireMaterial = rayMarchingFireMaterial;
            this.shaderMaterial = rayMarchingFireMaterial;
          }
          // 創(chuàng)建平面
          createPlane() {
            const geometry = new THREE.PlaneBufferGeometry(22100100);
            const material = this.rayMarchingFireMaterial;
            this.createMesh({
              geometry,
              material,
            });
          }
          // 動畫
          update() {
            const elapsedTime = this.clock.getElapsedTime();
            const mousePos = this.mousePos;
            if (this.rayMarchingFireMaterial) {
              this.rayMarchingFireMaterial.uniforms.uTime.value = elapsedTime;
              this.rayMarchingFireMaterial.uniforms.uMouse.value = mousePos;
            }
          }
        }
        復制代碼

        接下來開始編寫片元著色器

        創(chuàng)建發(fā)光漸變橢圓

        仔細觀察火花的形狀你會發(fā)現(xiàn)其實它的大致形狀像一個橢圓,而且還是發(fā)光的漸變橢圓,于是我們就要想辦法來創(chuàng)建這種形狀。簡要說下思路:ray marching獲取的值改成光線位置pos和光線移動的進度strength,光線位置的y軸將用于設(shè)定火花的顏色;光線移動的進度strength用于設(shè)定火花的形狀(這里就是橢圓)

        #pragma glslify:centerUv=require(../modules/centerUv)
        #pragma glslify:getRayDirection=require(../modules/getRayDirection)
        #pragma glslify:sdSphere=require(glsl-sdf-primitives/sdSphere)
        #pragma glslify:opU=require(glsl-sdf-ops/union)
        #pragma glslify:cnoise=require(glsl-noise/classic/3d)

        uniform float uTime;
        uniform vec2 uMouse;
        uniform vec2 uResolution;
        uniform float uVelocity;
        uniform vec3 uColor1;
        uniform vec3 uColor2;

        varying vec2 vUv;
        varying vec3 vPosition;

        float fire(vec3 p){
        vec3 p2=p*vec3(1.,.5,1.)+vec3(0.,1.,0.);
        float geo=sdSphere(p2,1.);
        float result=geo;
        return result;
        }

        vec2 sdf(vec3 p){
        float result=opU(abs(fire(p)),-(length(p)-100.));
        float objType=1.;
        return vec2(result,objType);
        }

        vec4 rayMarch(vec3 eye,vec3 ray){
        float depth=0.;
        float strength=0.;
        float eps=.02;
        vec3 pos=eye;
        for(int i=0;i<64;i++){
        pos+=depth*ray;
        float dist=sdf(pos).x;
        depth=dist+eps;
        if(dist>0.){
        strength=float(i)/64.;
        }
        }
        return vec4(pos,strength);
        }

        void main(){
        vec2 p=centerUv(vUv,uResolution);
        p=p*vec2(1.6,-1);

        vec3 ro=vec3(0.,-2.,4.);
        vec3 ta=vec3(0.,-2.5,-1.5);
        float fl=1.25;
        vec3 rd=getRayDirection(p,ro,ta,fl);

        vec3 color=vec3(0.);

        vec4 result=rayMarch(ro,rd);

        float strength=pow(result.w*2.,4.);
        vec3 ellipse=vec3(strength);
        color=ellipse;

        gl_FragColor=vec4(color,1.);
        }
        復制代碼

        centerUv.glsl

        vec2 centerUv(vec2 uv,vec2 resolution){
        uv=2.*uv-1.;
        float aspect=resolution.x/resolution.y;
        uv.x*=aspect;
        return uv;
        }

        #pragma glslify:export(centerUv);
        復制代碼

        getRayDirection.glsl

        #pragma glslify:setCamera=require(./setCamera)

        vec3 getRayDirection(vec2 p,vec3 ro,vec3 ta,float fl){
        mat3 ca=setCamera(ro,ta,0.);
        vec3 rd=ca*normalize(vec3(p,fl));
        return rd;
        }

        #pragma glslify:export(getRayDirection)
        復制代碼

        setCamera.glsl

        mat3 setCamera(in vec3 ro,in vec3 ta,float cr)
        {
        vec3 cw=normalize(ta-ro);
        vec3 cp=vec3(sin(cr),cos(cr),0.);
        vec3 cu=normalize(cross(cw,cp));
        vec3 cv=(cross(cu,cw));
        return mat3(cu,cv,cw);
        }

        #pragma glslify:export(setCamera)
        復制代碼
        R0RM7Q.png

        用噪聲生成火花

        接下來就對這個橢圓應(yīng)用上噪聲(這里選了傳統(tǒng)噪聲,為了更好看的外觀,也可以選擇其他的噪聲)

        float fire(vec3 p){
        vec3 p2=p*vec3(1.,.5,1.)+vec3(0.,1.,0.);
        float geo=sdSphere(p2,1.);
        // float result=geo;
        float displacement=uTime*uVelocity;
        vec3 displacementY=vec3(.0,displacement,.0);
        float noise=(cnoise(p+displacementY))*p.y*.4;
        float result=geo+noise;
        return result;
        }
        復制代碼
        R0fRFH.gif

        莫名感覺像黑魂3里的芙莉德修女的黑焰,盡管這樣也很cool,我們還是給它加上顏色,讓它更像現(xiàn)實中的火花

        給火花加上顏色

        將顏色通過mix函數(shù)混合起來(強度是光線位置的y軸),和之前的顏色相乘即可

        void main(){
        ...

        float fireBody=result.y/64.;
        vec3 mixColor=mix(uColor1,uColor2,fireBody);
        color*=mixColor;

        gl_FragColor=vec4(color,1.);
        }
        復制代碼

        項目地址

        Ray Marching Fire:https://codepen.io/alphardex/pen/OJmPpeJ

        關(guān)于本文

        來源:alphardex

        https://juejin.cn/post/6979744391074316319

        “在看和轉(zhuǎn)發(fā)”就是最大的支持
        瀏覽 59
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产精品免费在线 | 坐公交忘穿内裤被挺进老外文案 | 免费在线视频一区 | 女人被弄到高潮的视频 | 美女被草XX网站 | 国产av电影网站 亚洲成人精品导航 | 日本少妇色图 | 国产美女精品 | 亚洲天堂男人 | 2016超碰|