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);' + //设置顶点颜色

'}';

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

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

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

Three.js官网

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",

下载依赖

npm install three

项目中引入

import * as THREE from 'three'; //导入全部核心包

import { Scene } from 'three'; //按需导入

// 引入附加组件,附加组件,必须显式导入,例如轨道控制OrbitControls

import { OrbitControls } from ' three/examples/jsm/controls/OrbitControl

注:附加组件以及其他需要单独引入依赖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);

创建摄像机 PerspectiveCamera参数图解

三、创建辅助坐标系

更直观的去设置元素的位置用scene.add 去添加元素到画布必须在appendChild 插入前

// 辅助坐标系

const axesHelper = new THREE.AxesHelper(200) //参数200标示坐标系大小,可以根据场景大小去设置

scene.add(axesHelper)

展示效果:

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

平面的创建,需要大小(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)

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

五、光源的创建

创建灯光的类型,和颜色(光源的种类可以在官方文档中去查看)设置光源的位置设置光源,投影的长度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)

效果展示:

六、鼠标操控三维场景

鼠标操控三维场景 需要引入OrbitControls规定控制插件来实现鼠标缩放与拖拽画布

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

// 轨道控件

const controls = new OrbitControls(camera, renderer.domElement)

controls.addEventListener('change', () => {

renderer.render(scene, camera)

})

通过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)

})

七、添加点击事件

画布的生成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)

}

}

})

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)

}

)

二、创建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 = `

名称:${obj.name}

温度:22°

湿度:29%

`

// 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)

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

})

参考链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: