# 性能监测

ms 表示每帧渲染时间;fps 表示每秒帧数,也就是每秒渲染次数;

# 模型绘制

默认情况下,只有正面的面片才会被绘制,而如果需要双面绘制,需要如下设置:

if (child instanceof THREE.Mesh) {
  child.material.side = THREE.DoubleSide
}

# 阴影

在 threeJS 中,能形成阴影的光源只有 THREE.DirectionalLight 、THREE.SpotLight、THREE.PointLight、THREE.RECTAreaLight,而相对的,能表现阴影效果的材质只有 THREE.LamberMaterial 与 THREE.PhongMaterial。

开启阴影的步骤:

  1. 我们需要在初始化时,告诉渲染器渲染阴影:
renderer.shadowMapEnable = true
  1. 然后,对于光源以及所有要产生阴影的物体调用:
xxx.castShadow = true
  1. 对于接收阴影的物体调用:
xxx.receiveShadow = true

比如场景中一个平面有一个正方体,想要让聚光灯照射在正方体上,产生的阴影投射在平面上,那么就需要对聚光灯和正方体调用 castShadow = true,对于平面调用 receiveShadow = true。

以上就是产生阴影效果的必要步骤了,不过通常还需要设置光源的阴影相关属性,才能正确显示出阴影效果。

  • 对于聚光灯,需要设置 shadowCameraNear、shadowCameraFar、shadowCameraFov 三个值,类比我们在第二章学到的透视投影照相机,只有介于 shadowCameraNear 与 shadowCameraFar 之间的物体将产生阴影,shadowCameraFov 表示张角。
  • 对于平行光,需要设置shadowCameraNear、shadowCameraFar、shadowCameraLeft、shadowCameraRight、shadowCameraTop 以及shadowCameraBottom 六个值,相当于正交投影照相机的六个面。同样,只有在这六个面围成的长方体内的物体才会产生阴影效果。

为了看到阴影照相机的位置,通常可以在调试时开启light.shadowCameraVisible = true。阴影效果已经能正常显示了;

#

threejs 中,可以开启辅助轴,x 轴着色为红色,y 轴着色为绿色,z 轴着色为蓝色;开启代码如下:

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)
const renderer = new THREE.WebGLRenderer()
renderer.setClearColorHex(0xEEEEEE)
renderer.setSize(window.innerWidth, window.innerHeight)

const axes = new THREE.AxisHelper(20)
scene.add(exes)

document.getElementById('webgl-output').appendChild(renderer.domElement)
renderer.render(scene, camera)

# 场景对浏览器的自适应

当浏览器缩小放大时,场景也要跟着进行变化,如下代码:

window.addEventListener('resize', onResize, false)

function onResize() {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()

  renderer.setSize(window.innerWidth, window.innerHeight)
}

# Mesh 相关

# name 属性

我们可以在添加 mesh 对象的时候,设置他的 name 属性;可以方便我们直接通过名字来获取场景中的对象:

THREE.Scene.getObjectByName(name)

# translate

translate() 方法可以改变对象的位置,但是该方式设置的不是物体的绝对位置,而是物体相对于当前位置的平移距离。

# copy

可以通过 position.copy 方法来拷贝一份数据;如下代码:

directionalLight.position.copy(sphereLightMesh.position)

# 设置联合材质

这里需要注意的是,对于 MeshBasicMaterial,要把 transparent 属性设置为 true,并且需要指定一个融合模式。如果不将 transparent 指定为 true,会得到一个纯色的物体;因为 Threejs 不会执行任何操作;如果将 transparent 设置为 true,threejs 会去检查 blending 属性以查看 MeshBasicMaterial 材质如何与背景相互作用;这里所说的背景是 MeshDepthMaterial 材质渲染的结果;

blending 是融合属性,该属性决定物体上的材质如何与背景融合。一般的融合模式是 THREE.NormalBlending,在这种模式下只显示材质的上层;THREE.MultiplyBlending 模式会把前景色和背景色相乘,得到想要的结果。

const cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize)
const cubeMaterial = new THREE.MeshDepthMaterial()
const colorMaterial = new THREE.MeshBasicMaterial({
  color: control.color,
  transparent: true,
  blending: THREE.MultiplyBlending
})

const cube = new THREE.SceneUtils.createMultiMaterialObject(cubeGeometry, [
  colorMaterial,
  cubeMaterial
])

# 在单个几何体上使用多种材质

如下代码:

  const mats = [];
  mats.push(new THREE.MeshBasicMaterial({
    color: 0x009e60
  }));

  mats.push(new THREE.MeshBasicMaterial({
    color: 0x0051ba
  }));

  mats.push(new THREE.MeshBasicMaterial({
    color: 0xffd500
  }));

  mats.push(new THREE.MeshBasicMaterial({
    color: 0xff5800
  }));

  mats.push(new THREE.MeshBasicMaterial({
    color: 0xC41E3A
  }));

  mats.push(new THREE.MeshBasicMaterial({
    color: 0xffffff
  }));

  const cybeG = new THREE.BoxGeometry(3, 3, 3)
  const cubeG = new THREE.Mesh(cybeG, mats)
  scene.add(cubeG)

在 threejs 中,几何体的每一个面都具有一个 materialIndex 属性。该属性指定了该面使用哪一个具体的材质:

cubeGeom.faces.forEach((p, i) => console.log(`face ${i} : ${p.materialIndex}`))

# Scene 相关

# traverse 方法

该方法可以将一个方法作为参数传递;这个传递来的方法将会在每一个子对象上执行。由于 THREE.Scene 对象存储的是对象树,如果子对象本省还有子对象,traverse 方法会在所有的子对象上执行,直到遍历完场景树种所有对象为止;

scene.traverse(obj => {
  if (obj instanceof THREE.Mesh) {
    console.log(obj)
  }
})

# overrideMaterial 属性

使用这个属性可以保证场景中的所有物体都会使用该材质,而不需要在每个 THREE.Mesh 对象上显示地声明他:

const scene = Three.Scene()
scene.overrideMaterial = new THREE.MeshDepthMaterial()

# camera 相关

# lookAt 函数

可以在某个特定的位置设置摄像机。使用该方法还可以让摄像机追随场景中的某个物体。比如追随某个网格对象,代码如下:

camera.lookAt(mesh.position)

# 材质

# 基本属性

  • flatShading:该属性控制物体表面发现的生成方式,从而影响光照效果。属性值为 true 时,在两个相邻但不共面的三角形之间,光照会因为生硬过渡而产生棱角;为 false 时则会产生非常平滑的过渡效果;

比如我们设置材质为:

const meshMatrial = new THREE.MeshBasicMaterial({
  color: 0x7777ff,
  name: 'basic material',
  flatShading: true
})

如果设置为 false 那么不会产生棱角,整个四面体应用的话,会看不到边;设置 true,会看到边;

# geometry

我们获取 geometry 的属性的时候,需要通过他的 parameters 属性去获取,而不是直接 geometry.width 去获取:

const planeGeometry = new THREE.PlaneGeometry(20, 20, 4, 4)
console.log(planeGeometry.parameters.width)

# 选择对象

鼠标选择场景中的对象:

  const projector = new THREE.Projector()

  function onDocumentMouseDown(event) {
      let vector = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
      vector = vector.unproject(camera);

      const raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
      const intersects = raycaster.intersectObjects([sphere, cylinder, cube]);
      
      if (intersects.length > 0) {
        console.log(intersects[0]);
        intersects[0].object.material.transparent = true;
        intersects[0].object.material.opacity = 0.1;
      }
    }

上面的操作实现如下步骤:

  • 首先,基于屏幕上的点击位置会创建一个 THREE.Vector3 向量;
  • 接着,使用 vector.unproject 方法将屏幕上的点击位置转换成 Three.js 场景中的坐标。换句话说,就是将屏幕坐标转换成三维场景中的坐标;
  • 然后,创建 THREE.Raycaster。使用 THREE.Raycaster 可以向场景中发射光线;上面代码是从相机的位置向鼠标的点击位置发射光线;
  • 最后,我们使用 raycaster.intersectObjects 方法来判断指定的对象中哪些被光线照射到了。

选择的对象会有下面几个比较重要的属性:

  • face 和 faceIndex 指的是该网格中被选中的面。
  • distance:从摄像机到被点击对象的距离;
  • point:表明网格上哪个点被点击了;
  • uv:点击位置所对应的 2D 纹理的 uv 值;

# 相机的几种 control

如下图

在这里插入图片描述

更新: 6/22/2021, 3:56:54 AM