three.js自定义材质 切线空间及阴影

three.js自定义材质 切线空间及阴影


前言

法线贴图中的法线向量定义在切线空间中,在切线空间中,法线永远指着正z方向。切线空间是位于三角形表面之上的空间:法线相对于单个三角形的本地参考框架。它就像法线贴图向量的本地空间;它们都被定义为指向正z方向,无论最终变换到什么方向。使用一个特定的矩阵我们就能将本地/切线空间中的法线向量转成世界或视图空间下,使它们转向到最终的贴图表面的方向。

摘自法线贴图 - LearnOpenGL CN

一、three.js获取切线空间

新版three.js 的BufferGeometryUtils.js 删除了computeTangents方法,以computeMikkTSpaceTangents 替代

 computeMikkTSpaceTangents 参数为( BuffGeoMetry , MikkTSpace , negateSign )

  • negateSign -- 是否对每个切线的符号分量 (.w) 取反。某些格式的法线贴图约定需要,包括 glTF。

返回切线数据(vec4)

mikktspace 为 three/examples/jsm/libs/mikktspace.module.js (旧版没有,可以npm引入)

使用computeMikkTSpaceTangents前需要等MikkTSpace.ready完成

二、使用步骤

1.初始化initMikkTSpace

代码如下:

import { computeMikkTSpaceTangents } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
import { wasm,isReady,ready,generateTangents } from 'three/examples/jsm/libs/mikktspace.module.js'


async initMikkTSpace(cb){
      await ready
      cb()
    }


const geo01 = new THREE.SphereGeometry(6, 64, 32)
      // const geo01 = new THREE.BoxGeometry( 6, 6, 6 );
      this.initMikkTSpace(cb=>{
        let MikkTSpace = {
          wasm:wasm,
          isReady:isReady,
          generateTangents:generateTangents
        }
        computeMikkTSpaceTangents(geo01,MikkTSpace)
})

2.编辑材质

getShadowMask()返回的是阴影

主要步骤:1.attribute vec4 tangent;是computeMikkTSpaceTangents直接添加到geo的切线数据

2.

vec3 nowPoint = vec3((modelViewMatrix * vec4( position, 1.0 )).xyz);

lightToPos = myLight - nowPoint;

获取光与点的向量

vNormal = normalize(normalMatrix * normal);//获取法线

          vec3 vTangent = normalize( normalMatrix * tangent.xyz );//切线

          vec3 vBinormal = normalize(cross( vNormal, vTangent ) * tangent.w);//副切线

          tbn = mat3(vTangent, vBinormal, vNormal);//切线空间

代码如下:

initShader(tangent,myLight){
      let textureLoader = new THREE.TextureLoader();
      let vertexShader = `
      varying vec3 vNormal;
      varying vec2 vUv;
      attribute vec4 tangent;
      varying vec4 vtangent;
      varying mat3 tbn;

      uniform vec3 myLight;
      varying vec3 lightToPos;
      ${THREE.ShaderChunk[ "common" ]}
      ${THREE.ShaderChunk[ "bsdfs" ]}
      ${THREE.ShaderChunk[ "shadowmap_pars_vertex" ]}
      void main()
      {
          ${THREE.ShaderChunk['beginnormal_vertex']}
          ${THREE.ShaderChunk['defaultnormal_vertex']}
          ${THREE.ShaderChunk[ "begin_vertex" ]}
          ${THREE.ShaderChunk[ "project_vertex" ]}
          ${THREE.ShaderChunk[ "worldpos_vertex" ]}
          ${THREE.ShaderChunk[ "shadowmap_vertex" ]}

          vec3 nowPoint = vec3((modelViewMatrix * vec4( position, 1.0 )).xyz);
          lightToPos = myLight - nowPoint;
          vNormal = normalize(normalMatrix * normal);
          vec3 vTangent = normalize( normalMatrix * tangent.xyz );
          vec3 vBinormal = normalize(cross( vNormal, vTangent ) * tangent.w);
          tbn = mat3(vTangent, vBinormal, vNormal);

          vUv = uv;
          vtangent = tangent;
          gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      }`
      let fragmentShader = `
      // uniform vec3 light;
      varying vec3 vNormal;
      varying vec3 lightToPos;
      varying vec2 vUv;
      varying vec4 vtangent;
      uniform sampler2D u_texture;
      uniform sampler2D u_textureNormal;
      varying mat3 tbn;
      ${THREE.ShaderChunk[ "common" ]}
      ${THREE.ShaderChunk[ "packing" ]}
      ${THREE.ShaderChunk[ "bsdfs" ]}
      ${THREE.ShaderChunk[ "lights_pars_begin" ]}
      ${THREE.ShaderChunk[ "shadowmap_pars_fragment" ]}
      ${THREE.ShaderChunk[ "shadowmask_pars_fragment" ]}

      //获取纹理颜色
      vec3 mygetPixelColor(sampler2D mytexture) {
				return texture2D(mytexture, vUv).rgb;
			}

      void main(){
          vec3 normalColor = mygetPixelColor(u_textureNormal);
          normalColor = normalColor * 2.0 - 1.0;
          vec3 anynormalColor = normalize(tbn * normalColor);

          //处理光照
          float diff = max( dot(anynormalColor , normalize(lightToPos)) , 0.0 );
          vec3 diffuse = diff * vec3(1,1,1);//diff * lightColor
          vec3 textureColor = mygetPixelColor(u_texture);
          vec3 addDiffuse = textureColor + diffuse;

          vec3 shadowColor = vec3(0,0,0);
          vec3 addShadow = mix( shadowColor , addDiffuse ,getShadowMask());

         	gl_FragColor = vec4(addShadow,1.0);
      }`
    
      //着色器材质
      let sm = new THREE.ShaderMaterial({
          uniforms: THREE.UniformsUtils.merge( [
            THREE.UniformsLib[ "lights" ],
            {
              opacity:  { type: 'f', value: 1.0 },
              // tangent:  { value: tangent },
              myLight:  { value: myLight.position},
              u_texture:{value:textureLoader.load(require('../../assets/brickwall.jpg'))},
              u_textureNormal:{value:textureLoader.load(require('../../assets/brickwall_normal.jpg'))},
            }
          ] ),
          vertexShader: vertexShader,
          fragmentShader: fragmentShader,
          side: THREE.FrontSide,
          lights: true
      });
      return sm
    },

3.阴影处理

renderer.shadowMap.enabled = true;

...

let light = new THREE.DirectionalLight(0xffffff);
      light.position.set(42,60,0);
      //告诉平行光需要开启阴影投射
      light.castShadow = true;
      light.shadow.mapSize.width = 1024; // default 512
      light.shadow.mapSize.height = 1024; // default 512
      //阴影相机范围
      light.shadow.camera.near = 0.5; // default 0.5
      light.shadow.camera.far = 100; // default 500
      light.shadow.camera.left = -30
      light.shadow.camera.right = 30
      light.shadow.camera.top = 30
      light.shadow.camera.bottom = -30
      scene.add(light);

效果

 

 材质图片

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值