当前位置:   article > 正文

ThreeJs中场景(scene)、 相机(camera)、渲染器(renderer)等方法类使用_three.scene

three.scene

ThreeJs笔记

简介

WebGL(Web Graphics Library,Web图形库),是一个JavaScript API,可在任何兼容的Web浏览器中渲染高性能的交互式3D和2D图形,而无需使用插件 。 WebGL通过引入一个与OpenGL ES 2.0非常一致的API来做到这一点,该API可以在HTML5 元素中使用。 这种一致性使API可以利用用户设备提供的硬件图形加速。通过这些接口,开发者可以直接跟GPU进行通信。

WebGL 程序分为两部分:

  • 使用 Javascript 编写的运行在CPU的程序
  • 使用 GLSL 编写的运行在GPU的着色器程序

着色器程序接收CPU传过来的数据,并进行一定处理,最终渲染成丰富多彩的应用样式。着色器程序如下:

// 顶点着色器
var vertex_shader_source =
    'void main() {' +
    '   gl_Position = vec4(0.0, 0.0, 0.0, 1.0);' + // 设置顶点坐标
    '   gl_PointSize = 10.0;' + // 设置顶点的大小
    '' +
    '}';
 
// 片元着色器
var fragment_shader_source =
    'void main(){' +
    '   gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);' + //设置顶点颜色
    '}';
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

原生WebGL 能绘制的基本图元只有 3 种,分别是点、线段、三角形,对应了物理世界中的点线面。所有复杂的图形或者立方体,都是先用点组成基本结构,然后用三角形将这些点构成的平面填充起来,最后由多个平面组成几何体。但现实情况是,如果想生成满足各种应用场景的复杂形状,几何结构会非常复杂,代码写起来也会非常复杂。所以我们选择借助一些3d渲染框架来实现应用场景的复杂形状。

例如threeJs(Three.js是国内文档资料最多、使用最广泛的三维引擎)

ThreeJs,是一个基于原生WebGL,轻量级,跨平台的Javascript库,可以在浏览器上结合HTML5的canvas,SVG或者WebGL,创建和展示3D模型和动画。允许我们在不依赖任何浏览器插件的情况下,创建一个GPU加速的3D动画场景,这可能得益于WebGL的出现,因为WebGL的底层实现是基于OpenGL

ThreeJs入门使用

原生创建一个WebGL程序,一般需要4个步骤:

  • 初始化WebGL绘图上下文
  • 初始化着色器程序
  • 建立模型和数据缓存
  • 完成绘制和动画

但是对于Treee.js却有所不同,其使用面向对象的方式来构建程序,包含3个基本对象:场景(scene)、 相机(camera)、渲染器(renderer)。

一、获取three.js

demo版本控制

"node": "v16.18.1",
"three": "0.156.1", 
"vue": "3.3.4",
  • 1
  • 2
  • 3

下载依赖

npm install three
  • 1

项目中引入

import * as THREE from 'three'; //导入全部核心包
import { Scene } from 'three'; //按需导入
// 引入附加组件,附加组件,必须显式导入,例如轨道控制OrbitControls
import { OrbitControls } from ' three/examples/jsm/controls/OrbitControl 
  • 1
  • 2
  • 3
  • 4

注:附加组件以及其他需要单独引入依赖three内置在examples文件夹下

二、创建场景、摄像机、渲染器(画布)

  • 场景(scene) :用于把创建完成的模型 添加到 画布中。
  • 摄像机 (camera): 相当于人眼所在的位置 ;
  • 渲染器(renderer):相当于canvas 画布元素。
  • 创建三个元素之后 ,把画布 插入到html 元素中
const scene = new THREE.Scene();
// 设置场景背景
const textureLoader = new THREE.TextureLoader()
// scene.background = new THREE.Color(0xaaaaaa)
scene.background = textureLoader.load('src/assets/images/t2.png', () => {
  renderer.render(scene, camera)
})

// 创建摄像机
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000); // (角度  , 长宽比  , 最近看到的距离, 最远)
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position); //将相机指向场景

//创建webgl渲染器 (画布)
const renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xeeeeee); //canvas画布颜色 会被scene.background覆盖
renderer.setSize(window.innerWidth, window.innerHeight);  //canvas 画布大小	
	
// 将这个canvas元素插入到 html中
document.getElementById('chartlet-box').appendChild(renderer.domElement);
renderer.render(scene, camera);	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

创建摄像机 PerspectiveCamera参数图解

在这里插入图片描述

三、创建辅助坐标系

  • 更直观的去设置元素的位置
  • 用scene.add 去添加元素到画布
  • 必须在appendChild 插入前
// 辅助坐标系
const axesHelper = new THREE.AxesHelper(200) //参数200标示坐标系大小,可以根据场景大小去设置
scene.add(axesHelper)
  • 1
  • 2
  • 3

展示效果:

在这里插入图片描述

四、平面、立方体、球、线的创建

  • 平面的创建,需要大小(PlaneGeometry)和材质(MeshStandardMaterial) 两个对象;
  • THREE.Mesh 创建一个网格模型对象 几何体是不能被渲染的,只有几何体和材质结合成网格才能被渲染到屏幕上
  • scene.add 添加到画布中
//底坐
const planeGeometry = new THREE.PlaneGeometry(200, 200)
const planeMaterial = new THREE.MeshLambertMaterial({ color: 0xaaaaaa, opacity: 1 })
const plane = new THREE.Mesh(planeGeometry, planeMaterial)
plane.rotation.x = -0.5 * Math.PI
plane.position.y = -25
//告诉底部平面需要接收阴影
plane.receiveShadow = true
scene.add(plane)

// 长方体模型
const geometry = new THREE.BoxGeometry(200, 20, 200)

// 模型材质
const material = new THREE.MeshLambertMaterial({
  color: 'rgb(39, 148, 177)'
})

// 模型材质图片
const texture = textureLoader.load('src/assets/images/t1.png', () => {
  renderer.render(scene, camera)
})
const material1 = new THREE.MeshLambertMaterial({
  map: texture
})
const mesh = new THREE.Mesh(geometry, [material, material, material, material, material1, material])
mesh.castShadow = true
scene.add(mesh)

// 画线
const myLineMaterial = new THREE.LineBasicMaterial({ color: 'red' })
const geometryBuffer = new THREE.BufferGeometry()
const points: any[] = []
points.push(new THREE.Vector3(50, 0, 101))
points.push(new THREE.Vector3(50, -100, 101))
geometryBuffer.setFromPoints(points)
const line = new THREE.Line(geometryBuffer, myLineMaterial)
scene.add(line)

// 画圆
const geometryCircle = new THREE.CircleGeometry(5)
const materialCircle = new THREE.MeshBasicMaterial({ color: 0xffff00 })
const circle = new THREE.Mesh(geometryCircle, materialCircle)
circle.position.x = 50
circle.position.y = -25
circle.position.z = 102
circle.name = 'test'
scene.add(circle)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

展示效果:此时我们可以看到,地面,立方体都没有颜色,是因为没有光源 ,下面添加光源。

在这里插入图片描述

五、光源的创建

  • 创建灯光的类型,和颜色(光源的种类可以在官方文档中去查看)
  • 设置光源的位置
  • 设置光源,投影的长度
  • scene.add添加到画布
// 光源 环境光会均匀的照亮场景中的所有物体
const ambient = new THREE.AmbientLight(0x404040, 16)
scene.add(ambient)

// 平行光可以投射阴影 显示阴影纹路
const directionalLight = new THREE.DirectionalLight()
directionalLight.position.set(200, 200, 200)
directionalLight.shadow.camera.near = 20 //产生阴影的最近距离
directionalLight.shadow.camera.far = 200 //产生阴影的最远距离
directionalLight.shadow.camera.left = -50 //产生阴影距离位置的最左边位置
directionalLight.shadow.camera.right = 50 //最右边
directionalLight.shadow.camera.top = 50 //最上边
directionalLight.shadow.camera.bottom = -50 //最下面
//这两个值决定使用多少像素生成阴影 默认512
directionalLight.shadow.mapSize.height = 1024
directionalLight.shadow.mapSize.width = 1024
//告诉平行光需要开启阴影投射
directionalLight.castShadow = true
scene.add(directionalLight)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

效果展示:

在这里插入图片描述

六、鼠标操控三维场景

  • 鼠标操控三维场景 需要引入OrbitControls规定控制插件来实现鼠标缩放与拖拽画布
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

// 轨道控件
const controls = new OrbitControls(camera, renderer.domElement)
controls.addEventListener('change', () => {
  renderer.render(scene, camera)
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 通过OrbitControls来控制某个模型始终面向相机
controls.addEventListener('change', () => {
  const { x, y, z } = camera.position
  scene.traverse((child) => {
    if (child.name === 'test') {
      child.lookAt(x, y, z)
    }
  })
  renderer.render(scene, camera)
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

七、添加点击事件

  • 画布的生成dom通过addEventListener添加点击事件
  • 结合Raycaster类进行鼠标拾取,计算鼠标或触摸点的位置
  • 根据射线计算与所有对象的交点获取到点击的对象数组
renderer.domElement.addEventListener('click', (event: any) => {
  const raycaster = new THREE.Raycaster() // 光线投射用于进行鼠标拾取
  const mouse = new THREE.Vector2()
  // 计算鼠标或触摸点的位置
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  // 更新射线
  raycaster.setFromCamera(mouse, camera)
  // 计算与所有对象的交点
  const intersects = raycaster.intersectObjects(scene.children, true)
  if (intersects.length > 0) {
    // 处理点击事件
    // intersects[0] 包含了第一个交点
    const clickedObject: any = intersects[0].object
    //通过点击到该模型用名字匹配
    if (clickedObject.name === 'test') {
      console.log('获取的当前模型信息:', clickedObject)
      clickedObject.material.color.set('pink')
      renderer.render(scene, camera)
    }
  }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

ThreeJs其他辅助类介绍

一、加载3D模型文件glb

  • 需要单独引入对应的载入程序
  • 解决报错: THREE.GLTFLoader: No DRACOLoader instance provided
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'

const loader = new GLTFLoader()
/* 报错: THREE.GLTFLoader: No DRACOLoader instance provided */
/* 
解决办法:

在node_modules安装的包中获取three版本对应的draco,路径为node_modules\three\examples\js\libs\draco

将该文件夹复制到public文件夹下并在DRACOLoader.setDecoderPath时候设置该对应路径即可
————————————————
版权声明:本文为CSDN博主「早日退休!」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_48894212/article/details/127241897 
*/
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('/public/draco/') // 设置public下的解码路径,注意最后面的/
loader.setDRACOLoader(dracoLoader)
loader.load(
  '/src/assets/glb/LittlestTokyo.glb',
  function (gltf: any) {
    scene.add(gltf.scene)
  },
  undefined,
  function (error: any) {
    console.error(error)
  }
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

二、创建css3D渲染器

  • ThreeJs提供的CSS3DObject把一个文档dom对象转化成ThreeJs可用的Object3D对象,CSS3DRenderer就是负责渲染这个CSS3DObject的渲染器
  • 创建方式渲染器方式与webgl渲染器一致、需要单独设置渲染器dom的定位样式,才能在webgl的画布上面显示
  • webgl渲染器和css3D渲染器同时存在时,css3D渲染器设置了样式position在顶层显示
  • 在轨道控制以及事件处理方面直接通过3d渲染器去传递下去。如果加载在webgl渲染器上会导致无法操作
const createTag = (obj: any) => {
  const element = document.createElement('div')
  element.className = 'tag'
  element.innerHTML = `<p>名称:${obj.name}</p><p>温度:22°</p><p>湿度:29%</p>`
  // css3d添加点击事件
  element.addEventListener('pointerdown', () => {
    console.log('click')
  })
  const object = new CSS3DObject(element)
  object.visible = true
  //缩放比例
  object.scale.set(0.2, 0.2, 0.2)
  //指定摆放位置
  object.position.copy(obj.position)
  return object
}
const tags: any[] = []
scene.traverse((child) => {
  if (child.isObject3D && child.children.length === 0) {
    //添加标签文字
    const tag = createTag(child)
    tags.push(tag)
    scene.add(tag) //添加到指定的场景里
  }
})

const render3D = new CSS3DRenderer()
//设置渲染器大小
render3D.setSize(width, height)
//需要设置位置---------------------- 重点 ---------------------------
render3D.domElement.style.position = 'absolute'
render3D.domElement.style.top = '0'
//该渲染器也要加上同样的控制器
const controls1 = new OrbitControls(camera, render3D.domElement)
render3D.render(scene, camera)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

scene.tarverse: 该方法接受一个函数作为参数,遍历调用者和它的所有后代,都执行该function

三、关于材质与贴图

// 颜色
const material = new THREE.MeshLambertMaterial({
  color: 'rgb(39, 148, 177)'
})
// 图片
const textureLoader = new THREE.TextureLoader()
const texture = textureLoader.load('src/assets/images/t1.png', () => {
  // 加载完成图片后刷新页面显示材质
  renderer.render(scene, camera)
})
const material1 = new THREE.MeshLambertMaterial({
  map: texture
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号