Three.js学习笔记-快速入门

1. Three.js 快速入门

想不起来的时候,务必要读代码
参考书籍《Three.js 开发指南》。由于版本问题,部分函数采用最新 Three.js (r100版本)的函数。
大部门代码都以下面的结构进行

<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title>Three.js 入门</title>
  <script src="./lib/three.min.js"></script>
  <script src="./lib/jquery-1.9.0.js"></script>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
  </style>
</head>
<body>
  <div id="WebGL-output"></div>
  <script>
    $(function () {  
      // 这里写 three.js 的代码
    });
  </script>
</body>
</html>

1. 初步渲染并展示三维对象

// 新建场景、相机、渲染器
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
let renderer = new THREE.WebGLRenderer();
renderer.setClearColor('white');
renderer.setSize(window.innerWidth, window.innerHeight);

// 坐标系
let axes = new THREE.AxisHelper(20);
scene.add(axes);

// 平面
var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
var planeMaterail = new THREE.MeshBasicMaterial({color: 0xcccccc});
var plane = new THREE.Mesh(planeGeometry, planeMaterail);
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
scene.add(plane);

// 立方体
var cubeGeometry = new THREE.CubeGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
scene.add(cube);

// 球
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterail = new THREE.MeshBasicMaterial({color: 0x7777ff, wireframe: true});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterail);
sphere.position.x = 20;
sphere.position.y = 4;
sphere.position.z = 2;
scene.add(sphere);

// 照相机
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);

// 将场景构建到 页面 中
$('#WebGL-output').append(renderer.domElement);
renderer.render(scene, camera); // 渲染

目前:

  • 场景中无光源
  • 几个几何体使用的是MeshBasicMaterail材质,对光源无效。
    在这里插入图片描述

2. 添加材质、灯光和阴影

添加光源如下,光源有很多类,这是其中一个:

// 添加灯光
let spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
scene.add(spotLight);

添加可以感应光源的材质,比如MeshLambertMaterail材质。

// 更改了材质,也去掉了 wireframe 属性
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);

在默认情况下,阴影是不开启的,因为阴影比较花费大量的计算资源。
设置投影三步走:

  1. 允许渲染投影映射
  2. 设置哪些几何体接收投影,哪些几何体可以投影
  3. 设置哪个光源可以产生投影
// 1. 允许渲染投影映射
 renderer.shadowMap.enabled = true; // 允许阴影映射
// 2. 设置哪些接收投影,投影
    // 设置平面接收投影
plane.receiveShadow = true;
    // 设置立方体投影
cube.castShadow = true;
// 3. 设置哪个光源可以产生投影
spotLight.castShadow = true;

完整代码如下:

 // 新建场景
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
let renderer = new THREE.WebGLRenderer();
renderer.setClearColor('white');
renderer.shadowMap.enabled = true; // 允许阴影映射
renderer.setSize(window.innerWidth, window.innerHeight);

// 坐标系
let axes = new THREE.AxisHelper(20);
scene.add(axes);

// 添加灯光
let spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
scene.add(spotLight);
// 定义这个光源需要投影
spotLight.castShadow = true;

// 平面
var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
var planeMaterail = new THREE.MeshLambertMaterial({color: 0xcccccc});
var plane = new THREE.Mesh(planeGeometry, planeMaterail);
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
// 设置平面接收投影
plane.receiveShadow = true;
scene.add(plane);

// 立方体
var cubeGeometry = new THREE.CubeGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
// 设置立方体投影
cube.castShadow = true;
scene.add(cube);

// 球
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterail = new THREE.MeshLambertMaterial({color: 0x7777ff});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterail);
sphere.position.x = 20;
sphere.position.y = 4;
sphere.position.z = 2;
// 设置球投影
sphere.castShadow = true;
scene.add(sphere);

// 照相机
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);

$('#WebGL-output').append(renderer.domElement);
renderer.render(scene, camera);

在这里插入图片描述

3. 添加统计图形

  • 引入 stats.js 库文件
  • 添加一个div元素,比如<div id='stats-output'></div>
  • 初始化统计对象
  • 在循环函数中更新

完整代码看下节

4. 添加动画

设置循环函数,引入requestAnimationFrame()方法。

// 循环函数
function render() {
  stats.update();

  cube.rotation.x += 0.02;
  cube.rotation.y += 0.02;
  cube.rotation.z += 0.02;

  step += 0.04;
  sphere.position.x = 20 + (10 * (Math.cos(step)));
  sphere.position.y = 2 + (10 * Math.abs(Math.sin(step)));

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

效果:
在这里插入图片描述

使用 dat.GUI 控制对象

dat.GUI库可以创建一个简单的界面组件,用来修改代码中的变量。
以控制球体弹跳速度和方块的旋转速度为例。

control = {
  立方体旋转速度: 0.02,
  球体弹跳速度: 0.03
}
// 初始化 data-GUI
var gui = new dat.GUI();
gui.add(control, '立方体旋转速度', 0, 0.5);
gui.add(control, '球体弹跳速度', 0, 0.5);

---------------------------
cube.rotation.x += control.立方体旋转速度;
cube.rotation.y += control.立方体旋转速度;
cube.rotation.z += control.立方体旋转速度;

step += control.球体弹跳速度;
sphere.position.x = 20 + (10 * (Math.cos(step)));
sphere.position.y = 2 + (10 * Math.abs(Math.sin(step)));

在这里插入图片描述
完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Three.js 入门</title>
  <script src="./lib/three.min.js"></script>
  <script src="./lib/jquery-1.9.0.js"></script>
  <script src="./lib/stats.js"></script>
  <script src="./lib/dat.gui.js"></script>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
  </style>
</head>
<body>
  <div id="WebGL-output"></div>
  <div id="status-output"></div>
  <script>
    // 变量定义在外部
    let stats,scene,camera,renderer,axes,spotLight,plane,cube,sphere; 
    let step = 0;
    let control; // control 用于dat
    $(function () {  
      initScene();
    });
  
  // 初始化显示对象
  function initStats() {
    var stats = new Stats();
    stats.setMode(0);
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.left = '0px';
    stats.domElement.style.top = '0px';
    $('#status-output').append(stats.domElement);
    return stats;
  }

  // 初始化 场景
  function initScene() {
      // 初始化统计对象
      stats = initStats();
      // 初始化 control
      control = {
        立方体旋转速度: 0.02,
        球体弹跳速度: 0.03
      }
      // 初始化 data-GUI
      var gui = new dat.GUI();
      gui.add(control, '立方体旋转速度', 0, 0.5);
      gui.add(control, '球体弹跳速度', 0, 0.5);

      // 新建场景
      scene = new THREE.Scene();
      camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
      renderer = new THREE.WebGLRenderer();
      renderer.setClearColor('white');
      renderer.shadowMap.enabled = true; // 允许阴影映射
      renderer.setSize(window.innerWidth, window.innerHeight);

      // 坐标系
      axes = new THREE.AxisHelper(20);
      scene.add(axes);

      // 添加灯光
      spotLight = new THREE.SpotLight(0xffffff);
      spotLight.position.set(-40, 60, -10);
      scene.add(spotLight);
      // 定义这个光源需要投影
      spotLight.castShadow = true;
      
      // 平面
      var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
      var planeMaterail = new THREE.MeshLambertMaterial({color: 0xcccccc});
      plane = new THREE.Mesh(planeGeometry, planeMaterail);
      plane.rotation.x = -0.5 * Math.PI;
      plane.position.x = 15;
      plane.position.y = 0;
      plane.position.z = 0;
      // 设置平面接收投影
      plane.receiveShadow = true;
      scene.add(plane);
      
      // 立方体
      var cubeGeometry = new THREE.CubeGeometry(4, 4, 4);
      var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
      cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
      cube.position.x = -4;
      cube.position.y = 3;
      cube.position.z = 0;
      // 设置立方体投影
      cube.castShadow = true;
      scene.add(cube);

      // 球
      var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
      var sphereMaterail = new THREE.MeshLambertMaterial({color: 0x7777ff});
      sphere = new THREE.Mesh(sphereGeometry, sphereMaterail);
      sphere.position.x = 20;
      sphere.position.y = 4;
      sphere.position.z = 2;
      // 设置球投影
      sphere.castShadow = true;
      scene.add(sphere);

      // 照相机
      camera.position.x = -30;
      camera.position.y = 40;
      camera.position.z = 30;
      camera.lookAt(scene.position);

      $('#WebGL-output').append(renderer.domElement);
      renderer.render(scene, camera);

      // 调用循环函数
      render();
  }
  
  // 循环函数
  function render() {
    stats.update();

    cube.rotation.x += control.立方体旋转速度;
    cube.rotation.y += control.立方体旋转速度;
    cube.rotation.z += control.立方体旋转速度;

    step += control.球体弹跳速度;
    sphere.position.x = 20 + (10 * (Math.cos(step)));
    sphere.position.y = 2 + (10 * Math.abs(Math.sin(step)));

    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }
  </script>
  
</body>
</html>

2. 跟场景有关的组件

2.1 场景最常用的几个函数

  • scene.add():在场景中移除物体
  • scene.remove():从场景中移除物体
  • scene.children():获得场景中所有子对象的数组
  • scene.getChildByName():利用name属性,获取场景中某个特定的物体。
  • scene.traverse(func):传入一个函数,每个子对象都会被执行该函数,如下
function render() {  
  // 状态条跟新
  stats.update();
  // 旋转立方体
  scene.traverse(function(e) {
    if(e instanceof THREE.Mesh && e != plane) {
      e.rotation.x += controls.rotationSpeed;
      e.rotation.y += controls.rotationSpeed;
      e.rotation.z += controls.rotationSpeed;
    }
  });
  // 渲染
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}

完整代码如下:

<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title>Three.js 入门</title>
  <script src="./lib/three.min.js"></script>
  <script src="./lib/jquery-1.9.0.js"></script>
  <script src="./lib/dat.gui.js"></script>
  <script src="./lib/stats.js"></script>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
  </style>
</head>
<body>
  <div id="WebGL-output"></div>
  <div id="Stats-output"></div>
  <script>
    window.onload = init;
    // 定义全局变量
    let stats; // 状态条
    let scene; // 场景
    let camera; // 相机
    let renderer; // 渲染器
    let plane; // 创建平面
    let step = 0;
    let controls, gui; // 与动画相关

    function init() {
      stats = initStats();
      // 创建场景
      scene = new THREE.Scene();
      // 创建相机并定位
      camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
      camera.position.x = -30;
      camera.position.y = 40;
      camera.position.z = 30;
      camera.lookAt(scene.position);
      scene.add(camera);
      // 创建渲染器,并设置背景色、大小、可投影
      renderer = new THREE.WebGLRenderer();
      renderer.setClearColor(new THREE.Color('darkcyan'));
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.shadowMap.enabled = true;
      // 创建平面,接受投影
      var planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
      var planeMaterail = new THREE.MeshLambertMaterial({color: 0xffffff});
      plane = new THREE.Mesh(planeGeometry, planeMaterail);
      plane.receiveShadow = true;
      plane.rotation.x = -0.5 * Math.PI;
      plane.position.x = 0;
      plane.position.y = 0;
      plane.position.z = 0;
      scene.add(plane);
      // 创建环境光
      var ambientLight = new THREE.AmbientLight(0x0c0c0c);
      scene.add(ambientLight);
      // 创建聚光灯
      var spotLight = new THREE.SpotLight(0xffffff);
      spotLight.position.set(-40, 60, -10);
      spotLight.castShadow = true;
      scene.add(spotLight);
      // 将场景加入到页面
      renderer.render(scene, camera); // 用相机渲染场景
      document.querySelector('#WebGL-output').appendChild(renderer.domElement);    
      // 初始化 controls 对象
      controls = {
        rotationSpeed: 0.02,
        numberOfObjects: scene.children.length,
        removeCube: function() {
          var allChildren = scene.children;
          var lastObject = allChildren[allChildren.length - 1];
          if(lastObject instanceof THREE.Mesh) {
            scene.remove(lastObject);
            this.numberOfObjects = scene.children.length;
          }
        },
        addCube: function() {
          var cubeSize = Math.ceil(Math.random() * 3);
          var cubeGeo = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
          var cubeMat = new THREE.MeshLambertMaterial({color: Math.random()*0xffffff});
          var cube = new THREE.Mesh(cubeGeo, cubeMat);
          cube.castShadow = true;
          cube.name = 'Cube-' + scene.children.length;
          cube.position.x = -30 + Math.round(Math.random() * planeGeometry.parameters.width);
          cube.position.y = Math.round(Math.random()*5);
          cube.position.z = -20 + Math.round(Math.random() * planeGeometry.parameters.height);
          scene.add(cube);
          this.numberOfObjects = scene.children.length;
        },
        outputObjects: function() {
          console.log(scene.children);
        }
      };
      gui = new dat.GUI();
      gui.add(controls, 'rotationSpeed', 0, 0.5);
      gui.add(controls, 'numberOfObjects').listen();
      gui.add(controls, 'addCube');
      gui.add(controls, 'removeCube');
      gui.add(controls, 'outputObjects');
      render(); // 启动循环
    }
    // 循环函数
    function render() {  
      // 状态条跟新
      stats.update();
      // 旋转立方体
      scene.traverse(function(e) {
        if(e instanceof THREE.Mesh && e != plane) {
          e.rotation.x += controls.rotationSpeed;
          e.rotation.y += controls.rotationSpeed;
          e.rotation.z += controls.rotationSpeed;
        }
      });
      // 渲染
      requestAnimationFrame(render);
      renderer.render(scene, camera);   
    }
    // 初始化 状态条
    function initStats() {
      var stats = new Stats();
      stats.setMode(0); // 0: fps, 1: ms
      // 定位到左上角
      stats.domElement.style.position = 'absolute';
      stats.domElement.style.left = '0px';
      stats.domElement.style.top = '0px';
      document.getElementById("Stats-output").appendChild(stats.domElement);
      return stats;
     }
  </script>
</body>
</html>

效果:
在这里插入图片描述

2.2 给场景加雾化效果

加雾化效果有两种方式:
第一种,线性增长的雾,如下

scene.fog = new THREE.fog(0xffffff, 0.015, 100);

第二种,指数增长的雾,如下

scene.fog = new THREE.FogExp2(0xffffff, 0.015);

第一种效果:
在这里插入图片描述
第二种效果:
在这里插入图片描述

2.3 使用材质覆盖属性

场景可以给所有的子对象添加统一的属性,如下:

scene.overrideMaterial = new THREE.MeshLambertMaterial({color: 'blue'});

在这里插入图片描述

2.4 几何对象的属性和函数

Three.js库提供几个标准的几何体:
在这里插入图片描述
Three.js库中的geometry和其他多数三维库中的一样,基本上是三维空间中的点集,以及一些将这些点连接起来的面。举例,一个方块:

  • 一个方块8个角。每个角定义x, y, z坐标。所以每个方块都是三维空间的8个点,也称为顶点(vertice)。
  • 一个方块有6个面。每个侧面有两个三角形表示。每个侧面称为面(face)。

手动创建几何体可以参考下面的代码片段:

var vertices = [
    new THREE.Vector3(1, 3, 1),
    new THREE.Vector3(1, 3, -1),
    new THREE.Vector3(1, -1, 1),
    new THREE.Vector3(1, -1, -1),
    new THREE.Vector3(-1, 3, -1),
    new THREE.Vector3(-1, 3, 1),
    new THREE.Vector3(-1, -1, -1),
    new THREE.Vector3(-1, -1, 1)
];

var faces = [
    new THREE.Face3(0, 2, 1),
    new THREE.Face3(2, 3, 1),
    new THREE.Face3(4, 6, 5),
    new THREE.Face3(6, 7, 5),
    new THREE.Face3(4, 5, 1),
    new THREE.Face3(5, 0, 1),
    new THREE.Face3(7, 6, 2),
    new THREE.Face3(6, 3, 2),
    new THREE.Face3(5, 7, 0),
    new THREE.Face3(7, 2, 0),
    new THREE.Face3(1, 3, 4),
    new THREE.Face3(3, 6, 4),
];

var geom = new THREE.Geometry();
geom.vertices = vertices;
geom.faces = faces;
geom.computeFaceNormals();

未完待续

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值