赞
踩
WebGL(Web Graphics Library,Web图形库),是一个JavaScript API,可在任何兼容的Web浏览器中渲染高性能的交互式3D和2D图形,而无需使用插件 。 WebGL通过引入一个与OpenGL ES 2.0非常一致的API来做到这一点,该API可以在HTML5 元素中使用。 这种一致性使API可以利用用户设备提供的硬件图形加速。通过这些接口,开发者可以直接跟GPU进行通信。
WebGL 程序分为两部分:
着色器程序接收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。
原生创建一个WebGL程序,一般需要4个步骤:
但是对于Treee.js却有所不同,其使用面向对象的方式来构建程序,包含3个基本对象:场景(scene)、 相机(camera)、渲染器(renderer)。
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文件夹下
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参数图解
// 辅助坐标系
const axesHelper = new THREE.AxesHelper(200) //参数200标示坐标系大小,可以根据场景大小去设置
scene.add(axesHelper)
展示效果:
//底坐 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)
展示效果:此时我们可以看到,地面,立方体都没有颜色,是因为没有光源 ,下面添加光源。
// 光源 环境光会均匀的照亮场景中的所有物体 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)
效果展示:
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 轨道控件
const controls = new OrbitControls(camera, renderer.domElement)
controls.addEventListener('change', () => {
renderer.render(scene, camera)
})
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)
})
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) } } })
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) } )
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)
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
})
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。