Three.js 是一个 3D 库,该系列是在使用 three.js 中的一些过程和笔记。本文介绍物体的几何形状和材质等,并记录往场景中添加物体的基本过程。

# 物体(Object)

# 概要

上一节我们创建了一个初始化的 3D 世界,这一节我们将会往该世界中添加物体。

最常用的一种物体就是网格(Mesh),网格是由顶点、边、面等组成的物体;其他物体包括线段(Line)、骨骼(Bone)、粒子系统(ParticleSystem,后命名为 Points,其实就是一堆点的集合)等。

在创建物体时,需要传入两个参数,一个是几何形状(Geometry),另一个是材质(Material)。

其中,几何形状决定了物体的顶点位置等信息,材质决定了物体的颜色、纹理等信息。

# 几何形状(Geometry)

几何形状(Geometry)最主要的功能是储存了一个物体的顶点信息,通过存储模型用到的点集和点间关系(哪些点构成一个三角形)来达到描述物体形状的目的。

WebGL 需要程序员指定每个顶点的位置,而在 Three.js 中,可以通过指定一些特征来创建几何形状。

Three 提供了立方体(其实是长方体)、平面(其实是长方形)、球体、圆形、圆柱、圆台等许多基本形状。 也可以通过自己定义每个点的位置来构造形状。 对于比较复杂的形状,我们还可以通过外部的模型文件导入。

# 材质(Material)

材质(Material)是独立于物体顶点信息之外的与渲染效果相关的属性。通过设置材质可以改变物体的颜色、纹理贴图、光照模式等。

  • 基本材质(BasicMaterial)

    • 使用基本材质(BasicMaterial)的物体,渲染后物体的颜色始终为该材质的颜色,而不会由于光照产生明暗、阴影效果。
    • 如果没有指定材质的颜色,则颜色是随机的。
  • Lambert 材质(MeshLambertMaterial)

    • Lambert 材质(MeshLambertMaterial)是符合 Lambert 光照模型的材质
    • Lambert 光照模型的主要特点是只考虑漫反射而不考虑镜面反射的效果,因而对于金属、镜子等需要镜面反射效果的物体就不适应,对于其他大部分物体的漫反射效果都是适用的
  • Phong 材质(MeshPhongMaterial)

    • Phong 材质(MeshPhongMaterial)是符合 Phong 光照模型的材质
    • 和 Lambert 不同的是,Phong 模型考虑了镜面反射的效果,因此对于金属、镜面的表现尤为适合
  • 法向材质(MeshNormalMaterial)

    • 法向材质(MeshNormalMaterial)可以将材质的颜色设置为其法向量的方向,有时候对于调试很有帮助
    • 材质的颜色与照相机与该物体的角度相关
  • 材质的纹理贴图

    • 使用图像作为材质,就需要导入图像作为纹理贴图,并添加到相应的材质中

# 向场景中添加物体

# 添加地板

// 添加地板
function initFloor() {
  var floorGeo = new THREE.PlaneBufferGeometry(12, 8, 1, 1);
  var floorMaterial = new THREE.MeshBasicMaterial({ color: "#aaaaaa" });
  var floor = new THREE.Mesh(floorGeo, floorMaterial);
  floor.position.set(0, 0, -1);
  scene.add(floor);
}
initFloor();

如图: image

# 添加正方体

这里我们简单设计一个生成正方体的函数,当传入路径时则设置为材质的纹理贴图:

// 添加物体
function initCube(imageUrl) {
  var geometry = new THREE.BoxGeometry(1, 1, 1);
  var material;
  if (imageUrl) {
    material = new THREE.MeshLambertMaterial({
      map: THREE.ImageUtils.loadTexture(imageUrl)
    });
  } else {
    material = new THREE.MeshLambertMaterial();
  }
  var cube = new THREE.Mesh(geometry, material);
  scene.add(cube);
  return cube;
}

然后,我们添加几个正方体到场景中:

// 添加物体
var cube1 = initCube("./img/1.jpg");
var cube2 = initCube("./img/2.png");
var cube3 = initCube();
var cube4 = initCube();
cube1.position.set(2, 0, 0);
cube2.position.set(-2, 0, 0);
cube3.position.set(0, -2, 1);
cube4.position.set(1, 1, 3);

如图: image

这里小伙伴们或许觉得奇怪,为什么正方体是黑色的呢,明明都已经添加材质纹理了呀。

这里我们使用的是 Lambert 材质,对光学有些了解的我们会知道,若没有光照射的时候,这些物体是没有反射的光,所以我们是看不到它们的颜色的。

而对于地板,由于我们使用的是基本材质。 前面也说过,使用基本材质的物体,渲染后物体的颜色始终为该材质的颜色,而不会由于光照产生明暗、阴影效果。故这里我们是可以看到地板的颜色的。

# 让物体动起来

上一节《three.js 笔记 1--初始化 3D 世界》 (opens new window)我们也说过,我们在动画渲染的时候可以添加动画处理,这样我们就能看到动画似的效果。

// 让世界动起来
function render() {
  requestAnimationFrame(render);

  // 此处可添加动画处理
  cube1.rotation.x += 0.03;
  cube1.rotation.y += 0.03;

  cube2.rotation.x += 0.02;
  cube3.rotation.y += 0.01;
  cube4.rotation.x -= 0.04;

  renderer.render(scene, camera);
}
render();

这时候我们虽然看不到正方体的颜色,但是我们能看到它们已经动起来了。

# 完整代码

// 设置场景
var scene = new THREE.Scene();

// 创建正交投影照相机
var camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(0, -5, 2);
camera.lookAt(scene.position);

// 定义着色器
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 添加地板
function initFloor() {
  var floorGeo = new THREE.PlaneBufferGeometry(12, 8, 1, 1);
  var floorMaterial = new THREE.MeshBasicMaterial({ color: "#aaaaaa" });
  var floor = new THREE.Mesh(floorGeo, floorMaterial);
  floor.position.set(0, 0, -1);
  scene.add(floor);
}
initFloor();

// 添加物体
function initCube(imageUrl) {
  var geometry = new THREE.BoxGeometry(1, 1, 1);
  var material;
  if (imageUrl) {
    material = new THREE.MeshLambertMaterial({
      map: THREE.ImageUtils.loadTexture(imageUrl)
    });
  } else {
    material = new THREE.MeshLambertMaterial();
  }
  var cube = new THREE.Mesh(geometry, material);
  scene.add(cube);
  return cube;
}
// 添加物体
var cube1 = initCube("./img/1.jpg");
var cube2 = initCube("./img/2.png");
var cube3 = initCube();
var cube4 = initCube();
cube1.position.set(2, 0, 0);
cube2.position.set(-2, 0, 0);
cube3.position.set(0, -2, 1);
cube4.position.set(1, 1, 3);

// 让世界动起来
function render() {
  requestAnimationFrame(render);

  // 此处可添加动画处理
  cube1.rotation.x += 0.03;
  cube1.rotation.y += 0.03;

  cube2.rotation.x += 0.02;
  cube3.rotation.y += 0.01;
  cube4.rotation.x -= 0.04;

  renderer.render(scene, camera);
}
render();

# 参考

# 结束语

这节主要讲了在初始化完毕后的 3D 世界中添加物体,由于 Lambert 材质的物体为漫反射物体,故我们需要在添加光源之后才能看到它们的效果。下一节将介绍光源的添加。
此处查看项目代码 (opens new window)
此处查看页面效果 (opens new window)

部分文章中使用了一些网站的截图,如果涉及侵权,请告诉我删一下谢谢~
温馨提示喵