GLSL Shader 菲涅尔效果

274 阅读1分钟

GLSL 实现一个菲涅尔效果。效果图如下:

1698724014938.jpg

代码实现

export const Shader = {
  uniforms: {
    b: {
      type: 'f',
      value: 1.0
    }, //bias 颜色最亮的位置
    p: {
      type: 'f',
      value: 1.0
    }, //power决定了透明度变化速度及方向。
    glowColor: {
      type: 'c',
      value: new THREE.Color("#1da9fc")
    },
    sweepColor: {
      value: new THREE.Color('#00FFFF')
    },
    blueColor: {
      value: new THREE.Color('#20285d')
    },
    time: {
      value: 0.0
    }
  },
  vertexShader: `
    attribute vec3 color;

    varying vec3 vNormal;
    varying vec3 vPositionNormal;
    varying vec3 vP;
    varying vec2 vUv;
    varying vec3 vColor;

    void main() 
    {
        vUv = uv;
        vColor = color;
        vP = position;
        vNormal = normalize( normalMatrix * normal );
        vPositionNormal = normalize(( modelViewMatrix * vec4(position, 1.0) ).xyz);
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }
    `,
  fragmentShader: `
    uniform vec3 glowColor;
    uniform vec3 blueColor;
    uniform float b;
    uniform float p;
    uniform vec3 sweepColor;
    uniform float time;

    varying vec3 vNormal;
    varying vec3 vPositionNormal;
    varying vec3 vP;
    varying vec2 vUv;
    varying vec3 vColor;
    
    void main() 
    {

        float a = pow( b - abs(dot(vNormal, vPositionNormal)), p );
        // gl_FragColor = vec4( glowColor, a );
        
        vec3 mixColor = mix(blueColor, glowColor, a);
        gl_FragColor = vec4(mixColor, 1.0);

        // if(abs(time - vP.y) < 1.0) {
        //     gl_FragColor = vec4(sweepColor, 0.8);
        // }
    }
    `
}

const gltfLoa = new GLTFLoader();
gltfLoa.load('xxx模型.gltf', function (object) {
    object.scene.traverse(e => {
        e.material = new THREE.ShaderMaterial({
            uniforms: Shader.uniforms,
            vertexShader: Shader.vertexShader,
            fragmentShader: Shader.fragmentShader,
            side: THREE.DoubleSide,
            // blending: THREE.AdditiveBlending,
            transparent: true
        })
    });

    scene.add(object.scene);
})
        

实现逻辑

先说几个名词:单位向量、法线、点积。不清楚的可以先了解下这个名词。
思路:

  1. 计算当前摄像机位置的单位向量。
  2. 计算当前像素点的法向量的单位向量。
  3. 当前位置向量 和 像素点的法向量 计算点积dot(),可以算出两个向量的夹角。
  4. 我们根据夹角计算当前颜色的透明度。这样就可以实现菲涅尔效果。