前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >『Three.js』场景 Scene

『Three.js』场景 Scene

作者头像
德育处主任
发布2022-08-30 10:51:00
5.5K0
发布2022-08-30 10:51:00
举报
文章被收录于专栏:前端数据可视化

本文简介

在阅读本文前,我希望你对 Three.js 有一个初步的理解。如果你不清楚 Three.js 是什么,我推荐你先阅读 『Three.js』起飞!

在使用 Three.js 的前,必须先了解 3大组件:摄像机、场景、渲染器。这是 Three.js 的必需品。

本文讲解的是 场景 的用法。

什么是场景?

Three.js 的场景只有1种,用 THREE.Scene 来表示。场景对象自身的属性和方法并不多,学起来非常简单。

场景是用来保存画布上所有元素信息的容器,比如它可以保存 对象、光源、物体 等信息。

创建场景的代码通常如下所示

代码语言:javascript
复制
const scene = new THREE.Scene()
复制代码

在介绍阶段,我先把常用的属性和方法列出来,先过一遍大概知道有什么东东,之后再逐一讲解。

属性

属性名

说明

children

返回一个场景中所有对象的列表,包括摄像机和光源

fog

给场景添加雾化效果,雾化效果的特点是场景中的物体离得越远就会变得越模糊

overrideMaterial

使用该属性可以强制场景中的所有物体使用相同的材质

方法

方法名

说明

add

向场景中添加对象

remove

将对象从场景中移除

traverse

返回场景中的所有物体

getObjectByName

查找特定名字的对象

只看上面的简介应该还是一头雾水的,学 Three.js 最好的方式就是自己敲一遍,然后看效果~

创建场景

只有场景是无法运行的,必须加上摄像机和渲染器才行。

但本文的重点是讲解场景的用法,所以有关摄像机和渲染器的部分可以先不深入理解,这些之后的文章会讲到的,现在只需跟着敲代码就行。

代码语言:javascript
复制
<div id="canvasBox"></div>

<script type="module">
  import {
    Scene, // 导入场景
    PerspectiveCamera, // 导入摄像机
    WebGLRenderer, // 导入渲染器
  } from '../js/Three/Three.js'

  // 场景
  const scene = new Scene()

  // 摄像机
  const camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)

  // 渲染器
  const renderer = new WebGLRenderer()

  // 设置渲染器的大小
  renderer.setSize(window.innerWidth, window.innerHeight)

  // 将场景和摄像机添加到渲染器中并执行渲染
  renderer.render(scene, camera)

  // 将渲染器添加到div中
  document.getElementById('canvasBox').appendChild(renderer.domElement)
</script>
复制代码

此时画面应该是一片黑色。因为画布上只有一个 “空的世界”,还没放物体、光源进去。

方法:添加对象 add

对象包括很多种类,比如物体、光源等等。

因为是刚起步,所以我会放一个最简单的立方体到场景中。

添加对象的方法是 scene.add(object)

01.png
01.png
代码语言:javascript
复制
import {
  Scene,
  PerspectiveCamera,
  WebGLRenderer,
  BoxGeometry, // 几何体
  MeshBasicMaterial, // 网格材质
  Mesh // 网格
} from '../js/Three/Three.js'

// 省略部分代码

// 立方体
let geometry = new BoxGeometry(1, 1, 1)

// 网格基础材质,设置颜色
let material = new MeshBasicMaterial({color: 0x00ff00})

// 把立方几何体与基础材质进行组合后创建一个新的网格对象
let cube = new Mesh(geometry, material)

// 把立方体网格添加到场景中
scene.add(cube)

// 设置摄像机z轴位置
camera.position.z = 5

// 将场景和摄像机添加到渲染器中并执行渲染
renderer.render(scene, camera)
复制代码

我省略了 “创建场景” 的代码。

上面的代码创建了一个立方体,然后通过 scene.add 方法,把立方体添加到场景中。

方法:删除对象 remove

删除对象用的是 scene.remove 方法。

在 “添加对象” 代码的基础上,我用定时器设置 1 秒后删除立方体

代码语言:javascript
复制
setTimeout(() => {
  scene.remove(cube)
  renderer.render(scene, camera)
}, 1000)
复制代码

使用 scene.remove ,里面传入要删除的对象。删除完需要重新渲染一下画布。

执行上面的代码,页面会渲染一个立方体,1秒后会把该立方体删掉。

方法:获取场景中 指定名称的对象 getObjectByName

如果你在创建元素时给元素添加一个 name ,之后就可以使用在场景对象中使用 scene.getObjectByName 方法根据 name 查找元素。

scene.getObjectByName 接收2个参数,第一个参数指定唯一的标识 name ;第二个参数为 true 时,在调用者的所有后代对象上查找。

02.png
02.png
代码语言:javascript
复制
// 几何体
let geometry = new BoxGeometry(1, 1, 1)

// 网格基础材质,设置颜色
let material = new MeshBasicMaterial({color: 0x00ff00})

// 把立方几何体与基础材质进行组合后创建一个新的网格对象
let cube = new Mesh(geometry, material)

// 给 cube 添加一个 name
cube.name = 'hello'

// 通过 name 来查找指定对象
let hello = scene.getObjectByName('hello')
console.log(hello)
复制代码

方法:遍历场景中所有元素 traverse

03.png
03.png
代码语言:javascript
复制
// 省略部分代码

scene.traverse(item => {
  console.log(item)
})
复制代码

traverse() 方法可以遍历当前画布上所有物体。

上面的代码中生成的画面,有2个物体,一个是立方体,一个是场景。

traverse() 方法接收一个参数,这个参数也是一个函数。该函数用于遍历每一个子对象。如果子对象本身还有子对象,该方法将会在所有的子对象上执行,知道遍历完场景树中的所有对象为止。

属性:返回一个场景中所有对象的列表 children

04.png
04.png
代码语言:javascript
复制
// 省略部分代码

console.log(scene.children)
复制代码

scene.children 是一个属性,返回一个场景中所有对象的列表,包括摄像机和光源。

从语义可以看出,children 是返回一个子级的集合,所以是不包含 scene 自身的。

属性:雾化效果 fog

fog 可以给场景添加雾化效果,远处的物体会被淡淡隐藏。

雾化效果的特点是场景中的物体离得越远就会变得越模糊。

雾化效果是 Three.js 的一个方法,调用该方法后,将返回值赋给 scene.fog 即可。

THREE.Fog 接收3个参数,分别是:雾的颜色,最近距离,最远距离

为了演示雾化效果,我需要添加更多的元素。同时添加场景光和聚光灯,这两个东西暂时无需理解,灯光的讲解会放在之后的文章。

05.png
05.png
代码语言:javascript
复制
<div id="canvasBox"></div>

<script type="module">
  import {
    Scene,
    PerspectiveCamera,
    WebGLRenderer,
    BoxGeometry,
    MeshBasicMaterial,
    PlaneGeometry,
    MeshLambertMaterial,
    Mesh,
    AmbientLight, // 环境光
    SpotLight // 聚光灯
  } from '../js/Three/Three.js'

  // 场景
  const scene = new Scene()

  // 相机
  const camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)

  // 渲染器
  const renderer = new WebGLRenderer()

  // 设置渲染器的大小
  renderer.setSize(window.innerWidth, window.innerHeight)

  // 地面
  const planeGeometry = new PlaneGeometry(60, 40, 1, 1)
  const planeMaterial = new MeshLambertMaterial({ color: 0xffffff })
  const plane = new Mesh(planeGeometry, planeMaterial)

  plane.rotation.x = -0.5 * Math.PI // 旋转平面,让它看起来像地面
  // 设置地面位置
  plane.position.x = 2
  plane.position.y = 0
  plane.position.z = -10
  // 将地面添加到场景中
  scene.add(plane)

  // 设置相机位置
  camera.position.x = -70
  camera.position.y = 30
  camera.position.z = 5
  // 将镜头锁定到地面上
  camera.lookAt(scene.position)

  // 环境光
  const ambientLight = new AmbientLight(0x3c3c3c)
  // 将环境光添加到场景中
  scene.add(ambientLight)

  // 聚光灯
  const spotLight = new SpotLight(0xffffff, 1, 150, 120)
  spotLight.position.set(-40, 60, -10)
  // 将聚光灯添加到场景中
  scene.add(spotLight)

  // 立方体列表
  let cubeList = []

  // 循环出20个立方体
  for (let i = 0; i < 20; i++) {
    let cubeSize = Math.ceil((Math.random() * 3)) // 随机生成不同大小的立方体尺寸
    let cubeGeometry = new BoxGeometry(cubeSize, cubeSize, cubeSize)
    let cubeMaterial = new MeshLambertMaterial({
      color: 0xFF0000 // 将立方体设置成红色
    })

    // 生成立方体
    let cube = new Mesh(cubeGeometry, cubeMaterial)

    // 根据地面尺寸,随机设置立方体位置
    cube.position.x = -30 + Math.round((Math.random() * planeGeometry.parameters.width))
    cube.position.y = Math.round((Math.random() * 5))
    cube.position.z = -30 + Math.round((Math.random() * planeGeometry.parameters.height))

    // 将立方体添加到立方体列表中
    cubeList.push(cube)
  }

  // 解构立方体列表,将列表中所有立方体添加到场景中
  scene.add(...cubeList)

  // 将场景和相机添加到渲染器中并执行渲染
  renderer.render(scene, camera)

  // 将渲染器添加到div中
  document.getElementById('canvasBox').appendChild(renderer.domElement)
</script>
复制代码

上面的代码比较多,需要耐性阅读一下注释。

上面的代码主要做了这几件事:

  1. 创建场景
  2. 添加地面
  3. 添加环境光和聚光灯,这样可以展示出更好的立体效果
  4. 将镜头对准地面
  5. 随机生成20个不同大小的立方体,并随机放在地面上

可以看到不管远近的立方体,看上去颜色都是一样的。

Three.js 的场景是提供了雾化效果,只需设置 scene.fog 即可。

06.png
06.png
代码语言:javascript
复制
// 省略部分代码

import {
  // 省略部分导入
  Fog // 雾化
}

// 场景
const scene = new Scene()

scene.fog = new Fog(0xffffff, 10, 100) // 添加雾化效果
复制代码

只需设置 scene.fog 就能产生雾化效果。

公式:scene.fog(雾化颜色, 近值, 远值)

属性:材质覆盖 overrideMaterial

overrideMaterial 属性可以让场景里的所有物体都统一使用同一个材质,即使物体本身设置了自己的材质,也会被覆盖掉。

随机生成20个立方体,并随机设置颜色。

07.png
07.png
代码语言:javascript
复制
<div id="canvasBox"></div>

<script type="module">
  import {
    Scene,
    PerspectiveCamera,
    WebGLRenderer,
    BoxGeometry,
    MeshBasicMaterial,
    PlaneGeometry,
    MeshLambertMaterial,
    Mesh,
    AmbientLight, // 环境光
    SpotLight // 聚光灯
  } from '../js/Three/Three.js'

  // 场景
  const scene = new Scene()

  // 相机
  const camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)

  // 渲染器
  const renderer = new WebGLRenderer()

  // 设置渲染器的大小
  renderer.setSize(window.innerWidth, window.innerHeight)

  // 地面
  const planeGeometry = new PlaneGeometry(60, 40, 1, 1)
  const planeMaterial = new MeshLambertMaterial({ color: 0xffffff })
  const plane = new Mesh(planeGeometry, planeMaterial)

  plane.rotation.x = -0.5 * Math.PI // 旋转平面,让它看起来像地面
  // 设置地面位置
  plane.position.x = 2
  plane.position.y = 0
  plane.position.z = -10
  // 将地面添加到场景中
  scene.add(plane)

  // 设置相机位置
  camera.position.x = -70
  camera.position.y = 30
  camera.position.z = 5
  // 将镜头锁定到地面上
  camera.lookAt(scene.position)

  // 环境光
  const ambientLight = new AmbientLight(0x3c3c3c)
  // 将环境光添加到场景中
  scene.add(ambientLight)

  // 聚光灯
  const spotLight = new SpotLight(0xffffff, 1, 150, 120)
  spotLight.position.set(-40, 60, -10)
  // 将聚光灯添加到场景中
  scene.add(spotLight)

  // 立方体列表
  let cubeList = []

  // 循环出20个立方体
  for (let i = 0; i < 20; i++) {
    let cubeSize = Math.ceil((Math.random() * 3)) // 随机生成不同大小的立方体尺寸
    let cubeGeometry = new BoxGeometry(cubeSize, cubeSize, cubeSize)
    let r = Math.floor(Math.random()  * 256)
    let g = Math.floor(Math.random() * 256)
    let b = Math.floor(Math.random() * 256)
    let color = `#${r.toString(16)}${g.toString(16)}${b.toString(16)}`
    let cubeMaterial = new MeshLambertMaterial({
      color // 设置随机颜色
    })

    // 生成立方体
    let cube = new Mesh(cubeGeometry, cubeMaterial)

    // 根据地面尺寸,随机设置立方体位置
    cube.position.x = -30 + Math.round((Math.random() * planeGeometry.parameters.width))
    cube.position.y = Math.round((Math.random() * 5))
    cube.position.z = -30 + Math.round((Math.random() * planeGeometry.parameters.height))

    // 将立方体添加到立方体列表中
    cubeList.push(cube)
  }

  // 解构立方体列表,将列表中所有立方体添加到场景中
  scene.add(...cubeList)

  // 将场景和相机添加到渲染器中并执行渲染
  renderer.render(scene, camera)

  // 将渲染器添加到div中
  document.getElementById('canvasBox').appendChild(renderer.domElement)
</script>
复制代码

而此时如果我们设置一下 scene.overrideMaterial

08.png
08.png
代码语言:javascript
复制
// 省略部分代码
scene.overrideMaterial = new MeshLambertMaterial({ color: 0xffffff })
复制代码

我把所有正方体都覆盖一层白色材质。最后出来的效果如上图所示。

场景自适应浏览器窗口尺寸

需要使用 window.addEventListener('resize') 监听浏览器窗口变化

代码语言:javascript
复制
// 省略部分three代码(从上面的例子可以随便挑一个在页面上生成点东西)

// 重置窗口尺寸
function onResize() {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
}

// 监听浏览器窗口尺寸变化
window.addEventListener('resize',onResize, false)
复制代码

代码仓库

Three.js Scene 场景

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本文简介
    • 什么是场景?
      • 属性
      • 方法
  • 创建场景
  • 方法:添加对象 add
  • 方法:删除对象 remove
  • 方法:获取场景中 指定名称的对象 getObjectByName
  • 方法:遍历场景中所有元素 traverse
  • 属性:返回一个场景中所有对象的列表 children
  • 属性:雾化效果 fog
  • 属性:材质覆盖 overrideMaterial
  • 场景自适应浏览器窗口尺寸
  • 代码仓库
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档