最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • three.js 实现火花特效

    正文概述 掘金(alphardex)   2021-07-01   791

    本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

    前言

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

    上周末刚在原神里抽到了“火花骑士”可莉,于是就心血来潮,想用three.js来实现一种火系的特效,不是炸弹的爆炸,而是炸弹爆炸后在草上留下的火花效果

    three.js 实现火花特效

    游戏里的效果相对比较卡通化,而本文的效果将更加逼近现实一点

    three.js 实现火花特效

    让我们开始吧!

    准备工作

    在开始本项目之前,你首先要了解ray marching这个概念,如果不了解也没关系,笔者之前写过一篇介绍它的入门文章,或者通过这篇文章也可以入门,掌握了基础概念后就可以开始了

    本项目需要用到:

    笔者的three.js模板:点击右下角的fork即可复制一份

    着色器模块化:glslify

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

    正文

    场景搭建

    按之前的惯例,搭建一个场景,放一个铺满屏幕的平面,设定一些必要的参数(火花的速度与颜色)

    class RayMarchingFire extends Base {
      constructor(sel: string, debug: boolean) {
        super(sel, debug);
        this.clock = new THREE.Clock();
        this.cameraPosition = new THREE.Vector3(0, 0, 1);
        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();
      }
      // 创建材质
      createRayMarchingFireMaterial() {
        const rayMarchingFireMaterial = new THREE.ShaderMaterial({
          vertexShader: rayMarchingFireVertexShader,
          fragmentShader: rayMarchingFireFragmentShader,
          side: THREE.DoubleSide,
          uniforms: {
            uTime: {
              value: 0,
            },
            uMouse: {
              value: new THREE.Vector2(0, 0),
            },
            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;
      }
      // 创建平面
      createPlane() {
        const geometry = new THREE.PlaneBufferGeometry(2, 2, 100, 100);
        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;
        }
      }
    }
    

    接下来开始编写片元着色器

    创建发光渐变椭圆

    仔细观察火花的形状你会发现其实它的大致形状像一个椭圆,而且还是发光的渐变椭圆,于是我们就要想办法来创建这种形状。 简要说下思路:ray marching获取的值改成光线位置pos和光线移动的进度strength,光线位置的y轴将用于设定火花的颜色;光线移动的进度strength用于设定火花的形状(这里就是椭圆)

    #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)
    

    three.js 实现火花特效

    用噪声生成火花

    接下来就对这个椭圆应用上噪声(这里选了传统噪声,为了更好看的外观,也可以选择其他的噪声)

    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;
    }
    

    three.js 实现火花特效

    莫名感觉像黑魂3里的芙莉德修女的黑焰,尽管这样也很cool,我们还是给它加上颜色,让它更像现实中的火花

    给火花加上颜色

    将颜色通过mix函数混合起来(强度是光线位置的y轴),和之前的颜色相乘即可

    void main(){
        ...
        
        float fireBody=result.y/64.;
        vec3 mixColor=mix(uColor1,uColor2,fireBody);
        color*=mixColor;
        
        gl_FragColor=vec4(color,1.);
    }
    

    three.js 实现火花特效

    项目地址

    Ray Marching Fire


    起源地下载网 » three.js 实现火花特效

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元