最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 用 three.js 画个炫彩液晶球

    正文概述 掘金(alphardex)   2021-05-31   649

    前言

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

    本文我们将用three.js来画个炫彩液晶球,以下是最终实现的效果图

    用 three.js 画个炫彩液晶球

    让我们开始吧!

    准备工作

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

    为了将着色器模块化,需要用到glslify

    同时也需要安装如下的npm包:glsl-noiseglsl-constants

    正片

    场景搭建

    创建一个球体即可

    class LiquidCrystal extends Base {
      constructor(sel: string, debug: boolean) {
        super(sel, debug);
        this.clock = new THREE.Clock();
        this.cameraPosition = new THREE.Vector3(0, 0, 25);
        this.params = {
          timeScale: 0.1,
          iriBoost: 8,
        };
      }
      // 初始化
      init() {
        this.createScene();
        this.createPerspectiveCamera();
        this.createRenderer();
        this.createLiquidCrystalMaterial();
        this.createSphere();
        this.trackMousePos();
        this.createOrbitControls();
        this.addListeners();
        this.setLoop();
      }
      // 创建液晶材质
      createLiquidCrystalMaterial() {
        const liquidCrystalMaterial = new THREE.ShaderMaterial({
          vertexShader: liquidCrystalVertexShader,
          fragmentShader: liquidCrystalFragmentShader,
          side: THREE.DoubleSide,
          uniforms: {
            uTime: {
              value: 0,
            },
            uResolution: {
              value: new THREE.Vector2(window.innerWidth, window.innerHeight),
            },
            uMouse: {
              value: new THREE.Vector2(0, 0),
            },
            // 下面几行先注释掉,等写片元着色器时再恢复
            // uIriMap: {
              // value: new ThinFilmFresnelMap(1000, 1.2, 3.2, 64),
            // },
            // uIriBoost: {
              // value: this.params.iriBoost,
            // },
          },
        });
        this.liquidCrystalMaterial = liquidCrystalMaterial;
      }
      // 创建球体
      createSphere() {
        const geometry = new THREE.SphereBufferGeometry(10, 64, 64);
        const material = this.liquidCrystalMaterial;
        this.createMesh({
          geometry,
          material,
        });
      }
      // 动画
      update() {
        const elapsedTime = this.clock.getElapsedTime();
        const time = elapsedTime * this.params.timeScale;
        const mousePos = this.mousePos;
        if (this.liquidCrystalMaterial) {
          this.liquidCrystalMaterial.uniforms.uTime.value = time;
          this.liquidCrystalMaterial.uniforms.uMouse.value = mousePos;
        }
      }
    }
    

    用 three.js 画个炫彩液晶球

    顶点着色器

    用simplex noise实现扭曲效果,这里比较自由,想怎么扭曲就怎么扭曲,只要好看就行

    有个注意点:扭曲位置position后要修正法线normal,不然会显示错误,国外论坛上已经有一个比较好的解法,直接拿来用了

    #pragma glslify:snoise=require(glsl-noise/simplex/3d)
    #pragma glslify:PI=require(glsl-constants/PI)
    #pragma glslify:getWorldNormal=require(../modules/getWorldNormal)
    
    uniform float uTime;
    uniform vec2 uMouse;
    
    varying vec2 vUv;
    varying vec3 vWorldNormal;
    
    vec3 distort(vec3 p){
        vec3 pointDirection=normalize(p);
        vec3 mousePoint=vec3(uMouse,1.);
        vec3 mouseDirection=normalize(mousePoint);
        float mousePointAngle=dot(pointDirection,mouseDirection);
        
        float freq=1.5;
        float t=uTime*100.;
        
        float f=PI*freq;
        float fc=mousePointAngle*f;
        
        vec3 n11=pointDirection*1.5;
        vec3 n12=vec3(uTime)*4.;
        float dist=smoothstep(.4,1.,mousePointAngle);
        float n1a=dist*2.;
        float noise1=snoise(n11+n12)*n1a;
        
        vec3 n21=pointDirection*1.5;
        vec3 n22=vec3(0.,0.,uTime)*2.;
        vec3 n23=vec3(uMouse,0.)*.2;
        float n2a=.8;
        float noise2=snoise(n21+n22+n23)*n2a;
        
        float mouseN1=sin(fc+PI+t);
        float mouseN2=smoothstep(f,f*2.,fc+t);
        float mouseN3=smoothstep(f*2.,f,fc+t);
        float mouseNa=4.;
        float mouseNoise=mouseN1*mouseN2*mouseN3*mouseNa;
        
        float noise=noise1+noise2+mouseNoise;
        vec3 distortion=pointDirection*(noise+length(p));
        return distortion;
    }
    
    #pragma glslify:fixNormal=require(../modules/fixNormal,map=distort)
    
    void main(){
        vec3 pos=position;
        pos=distort(pos);
        vec4 modelPosition=modelMatrix*vec4(pos,1.);
        vec4 viewPosition=viewMatrix*modelPosition;
        vec4 projectedPosition=projectionMatrix*viewPosition;
        gl_Position=projectedPosition;
        
        vec3 distortedNormal=fixNormal(position,pos,normal);
        
        vUv=uv;
        vWorldNormal=getWorldNormal(modelMatrix,distortedNormal).xyz;
    }
    

    修正法线函数 fixNormal.glsl

    #pragma glslify:orthogonal=require(./orthogonal)
    
    vec3 fixNormal(vec3 position,vec3 distortedPosition,vec3 normal){
        vec3 tangent=orthogonal(normal);
        vec3 bitangent=normalize(cross(normal,tangent));
        float offset=.1;
        vec3 neighbour1=position+tangent*offset;
        vec3 neighbour2=position+bitangent*offset;
        vec3 displacedNeighbour1=map(neighbour1);
        vec3 displacedNeighbour2=map(neighbour2);
        vec3 displacedTangent=displacedNeighbour1-distortedPosition;
        vec3 displacedBitangent=displacedNeighbour2-distortedPosition;
        vec3 displacedNormal=normalize(cross(displacedTangent,displacedBitangent));
        return displacedNormal;
    }
    
    #pragma glslify:export(fixNormal)
    

    正交函数 orthogonal.glsl

    vec3 orthogonal(vec3 v){
        return normalize(abs(v.x)>abs(v.z)?vec3(-v.y,v.x,0.)
        :vec3(0.,-v.z,v.y));
    }
    #pragma glslify:export(orthogonal);
    

    获取世界法线函数 getWorldNormal.glsl

    vec4 getWorldNormal(mat4 modelMat,vec3 normal){
        vec4 worldNormal=normalize((modelMat*vec4(normal,0.)));
        return worldNormal;
    }
    
    #pragma glslify:export(getWorldNormal)
    

    用 three.js 画个炫彩液晶球

    片元着色器

    利用pbr生成光照,再加上一个炫彩材质

    炫彩材质直接将ThinFilmFresnelMap.js拉到本地,在LiquidCrystal类里引入即可(也就是把上面的注释删除就行)

    #pragma glslify:snoise=require(glsl-noise/simplex/3d)
    #pragma glslify:invert=require(../modules/invert)
    
    uniform float uTime;
    uniform vec2 uMouse;
    uniform vec2 uResolution;
    uniform sampler2D uIriMap;
    uniform float uIriBoost;
    
    varying vec2 vUv;
    varying vec3 vWorldNormal;
    
    void main(){
        vec2 newUv=vUv;
        
        // pbr
        float noise=snoise(vWorldNormal*5.)*.3;
        vec3 N=normalize(vWorldNormal+vec3(noise));
        vec3 V=normalize(cameraPosition);
        float NdotV=max(dot(N,V),0.);
        float colorStrength=smoothstep(0.,.8,NdotV);
        vec3 color=invert(vec3(colorStrength));
        
        // iri
        vec3 airy=texture2D(uIriMap,vec2(NdotV*.99,0.)).rgb;
        airy*=airy;
        vec3 specularLight=vWorldNormal*airy*uIriBoost;
        
        float mixStrength=smoothstep(.3,.6,NdotV);
        vec3 finalColor=mix(specularLight,color,mixStrength);
        
        gl_FragColor=vec4(finalColor,0.);
    }
    

    反转函数 invert.glsl

    float invert(float n){
        return 1.-n;
    }
    
    vec3 invert(vec3 n){
        return 1.-n;
    }
    
    #pragma glslify:export(invert)
    

    最终效果如下

    用 three.js 画个炫彩液晶球

    项目地址

    Liquid Crystal


    起源地下载网 » 用 three.js 画个炫彩液晶球

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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