欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

WEB端三维可视化(threejs)02

程序员文章站 2022-06-11 10:58:22
...

前言(大部分解释都在代码注释上边)

上一个主要说自己选择的web引擎和前置安装步骤,接着直接开荤,进入正文。
本人的代码大部分都很简单,方便自己看,也方便各位同学们学习,所以不要吐槽。

个人认为:threejs相对来说最方便的是什么?他把webgl那些复杂的点、线、面等等封装成一个个组件,如果不做非常非常精细化的场景,对于展示物体,一些web端三维可视化项目之类的完全够用了。

一、场景scene:

首先,场景scene是什么?想要容纳万物,是不是要先开辟一方空间,而场景scene就相当于这一方空间,有了这一方空间,才能在这方空间里创造万物。

//页面上创建一个div用来盛放threejs的空间,执行渲染renderer之后会有一个canvas填充到这个div里
<div id="threecanvas"></div>
//首先引入threejs的基础库
import * as THREE from 'three'

1.创建场景

var scene
// 初始化场景
function initScene() {
  // 实例化一个场景
  scene = new THREE.Scene()
  // 整个场景的颜色
  scene.background = new THREE.Color( 0x000000 )
}

2.天空盒子

// 天空盒是以正方体6面结构贴图[贴图顺序:右、左、上、下、前、后]
var urls = [
  './static/skyBox/day/px.jpg',
  './static/skyBox/day/nx.jpg',
  './static/skyBox/day/py.jpg',
  './static/skyBox/day/ny.jpg',
  './static/skyBox/day/pz.jpg',
  './static/skyBox/day/nz.jpg'
]
//初始化一个场景
function initScene() {
  // 实例化一个场景
  scene = new THREE.Scene()
  // 整个场景的颜色
  scene.background = new THREE.Color( 0x000000 )
  // 6图天空盒子[贴图顺序:右、左、上、下、前、后]
  scene.background = new THREE.CubeTextureLoader().load(urls)
}

二、相机camera:

相机相当于人的眼睛,用于看到展示在视野内的物体。

var camera
// 初始化相机
function initCamera() {
  //实例化一个相机,内部参数(角度,div的宽/div的高,距离视野中心最近距离,同前最远距离)
  //角度:类似于手机狭角、广角的效果
  //画面比例:div的宽高自己定义,全屏展示的话就按照官网给的window.innerWidth/window.innerHeight即可
  //视野最大(小)远近距离根据自身项目来定
  camera = new THREE.PerspectiveCamera(60,
    document.getElementById('threecanvas').offsetWidth / document.getElementById('threecanvas').offsetHeight, 2, 5000)
  camera.position.set(0, 100, -100)//相机位置
  camera.lookAt(new THREE.Vector3(0, 0, 0))// 相机视野中心
  //相机所展示画面的比例
  camera.aspect = document.getElementById('threecanvas').offsetWidth / document.getElementById('threecanvas').offsetHeight
  //更新相机投影变换矩阵
  camera.updateProjectionMatrix()
  //将相机加入到场景中
  scene.add(camera)
}

三、渲染器renderer:

渲染器,顾名思义,就是把这个三维空间渲染到web页面上,前边我在页面上加了id为threecanvas的div便签,那我可以把场景渲染到threejscanvas里。

var renderer
// 初始化渲染器
function initRenderer() {
  renderer = new THREE.WebGLRenderer({
    alpha: false,// 是否可以设置背景色透明
    antialias: true, // 抗锯齿
    logarithmicDepthBuffer: true, // 图层叠加闪烁问题
    precision: 'lowp', // 着色器精度
    preserveDrawingBuffer: true, // 开启图层缓冲区
    autoClear: true
  })
  renderer.setPixelRatio(window.devicePixelRatio)// 设置分辨率与电脑的分辨率相同
  renderer.setSize(document.getElementById('threecanvas').offsetWidth, document.getElementById('threecanvas').offsetHeight)
  renderer.shadowMap.enabled = true// 渲染器渲染阴影效果
  renderer.shadowMap.type = THREE.PCFSoftShadowMap// 阴影类型
  renderer.outputEncoding = THREE.sRGBEncoding// 色域,鲜明渲染,常规rgb颜色渲染
  // 把这个canvas渲染到指定div里,不指定div则把setSize指定宽高
  document.getElementById('threecanvas').appendChild(renderer.domElement)
}

四、控制器controls:

控制器,就是可以使用鼠标控制三维场景旋转等交互操作,变换视角,以达到三维效果,否则的话,web页面上渲染出来的只能算作平面三维物体,只能看,不能交互,意义不大。

//控制器需要引入控制器插件
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
var controls
// 初始化控制器
function initControls() {
  controls = new OrbitControls(camera, renderer.domElement)
  controls.listenToKeyEvents(window)//监听操作,具体有哪些可以去底层扒一扒
  controls.enableDamping = true// 开启控制阻尼,是否有惯性
  controls.dampingFactor = 0.05 // 阻尼强度,鼠标拖拽旋转灵敏度
  controls.enableZoom = true// 是否可以缩放
  controls.minDistance = 2// 相机距离原点最近距离
  controls.maxDistance = 1000// 相机距离原点最远距离
  controls.enablePan = true // 是否开启右键拖拽
  controls.maxPolarAngle = Math.PI / 2.05 // 最大角度,实际项目中一些模型限制视角到地下
  controls.screenSpacePanning = false //false时只能前后左右平移,不能上下平移,true时哪个方向都可以
  controls.target = new THREE.Vector3(0, 0, 0)// 设置控制器的旋转原点
}
// 更新,实时渲染的时候加进去,有控制交互,就渲染
function update() {
  controls.update()
}

五、光源light:

前边我创建好了场景、相机、渲染器、控制器,还有一个天空盒,这个时候场景中没有光源,什么都看不到,所以需要光源来照亮物体。

//这里我主要使用,环境光和平行光
//环境光是自然环境中全局的光线
//平行光是方向光类似于太阳光,主要用来模拟阴影
var ambient, directional
// 光源
function initLight() {
  // 环境光AmbientLight,影响整个场景的光源
  ambient = new THREE.AmbientLight(0xffffff, 1)
  ambient.name = '环境光'
  scene.add(ambient)

  // 平行光DirectionalLight,模拟太阳光,用于渲染阴影
  directional = new THREE.DirectionalLight(0xffffff, 1.2, 100) // 模拟远处类似太阳的光源
  directional.name = '平行光'
  directional.position.set(directionalx1, directionaly1, directionalz1)
  directional.castShadow = true// 告诉平行光需要开启阴影投射
  directional.shadowDarkness = 1
  directional.shadow.mapSize.width = 512 * 4// 阴影分辨率,默认512
  directional.shadow.mapSize.height = 512 * 4
  // 平行光范围
  directional.shadow.camera.left = -directionalvalue
  directional.shadow.camera.right = directionalvalue
  directional.shadow.camera.top = directionalvalue
  directional.shadow.camera.bottom = -directionalvalue
  directional.shadow.camera.near = 0.5
  directional.shadow.camera.far = 1500
  directional.shadow.bias = 0.0001// 阴影偏移,否则会有阴影锯齿出现,阴影条纹,嗯写过的人应该都遇到过,很恶心的东西,可以用这个bias稍作偏移,具体参数调整起来比较烦
  scene.add(directional)
  // 平行光辅助线,添加辅助线后可以看到平行光笼罩的范围,用于细节调整
  const directionalhelper = new THREE.CameraHelper(directional.shadow.camera)
  directionalhelper1.visible = true
  // 平时不用,注掉这个add
  scene.add(directionalhelper)
}

六、初始化所有组件:

创建一个函数,把所有组件放进去一起加载,这么写呢,主要是方便后期增补或者修改一些内容,这么做比较方便,毕竟商务和产品每次都说这个东西三天或者五天必须给出来,写的太乱了后期不好改。

// 初始化
function init() {
  initScene()// 基础场景
  initCamera()// 相机
  initRenderer()// 渲染器
  initControls()// 控制器
  initLight()// 光源
}
init()//直接执行,或者HTML直接加onload

七、渲染:

// 循环渲染页面场景
function render() {
  update()
  requestAnimationFrame(render)// 执行一个动画.并在动画执行后重新渲染
  // 传统渲染器,如果使用shader着色器,则注掉这个,增加效果合成器,增加通道渲染
  renderer.render(scene, camera)
}

八、完整代码:

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

var scene, camera, controls, renderer, ambient, directional

// 天空盒是以正方体6面结构贴图[贴图顺序:右、左、上、下、前、后]
var urls = [
  './static/skyBox/day/px.jpg',
  './static/skyBox/day/nx.jpg',
  './static/skyBox/day/py.jpg',
  './static/skyBox/day/ny.jpg',
  './static/skyBox/day/pz.jpg',
  './static/skyBox/day/nz.jpg'
]
//初始化一个场景
function initScene() {
  // 实例化一个场景
  scene = new THREE.Scene()
  // 整个场景的颜色
  scene.background = new THREE.Color( 0x000000 )
  // 6图天空盒子[贴图顺序:右、左、上、下、前、后]
  scene.background = new THREE.CubeTextureLoader().load(urls)
}
// 初始化相机
function initCamera() {
  //实例化一个相机,内部参数(角度,div的宽/div的高,距离视野中心最近距离,同前最远距离)
  //角度:类似于手机狭角、广角的效果
  //画面比例:div的宽高自己定义,全屏展示的话就按照官网给的window.innerWidth/window.innerHeight即可
  //视野最大(小)远近距离根据自身项目来定
  camera = new THREE.PerspectiveCamera(60,
    document.getElementById('threecanvas').offsetWidth / document.getElementById('threecanvas').offsetHeight, 2, 5000)
  camera.position.set(0, 100, -100)//相机位置
  camera.lookAt(new THREE.Vector3(0, 0, 0))// 相机视野中心
  //相机所展示画面的比例
  camera.aspect = document.getElementById('threecanvas').offsetWidth / document.getElementById('threecanvas').offsetHeight
  //更新相机投影变换矩阵
  camera.updateProjectionMatrix()
  //将相机加入到场景中
  scene.add(camera)
}
// 初始化渲染器
function initRenderer() {
  renderer = new THREE.WebGLRenderer({
    alpha: false,// 是否可以设置背景色透明
    antialias: true, // 抗锯齿
    logarithmicDepthBuffer: true, // 图层叠加闪烁问题
    precision: 'lowp', // 着色器精度
    preserveDrawingBuffer: true, // 开启图层缓冲区
    autoClear: true
  })
  renderer.setPixelRatio(window.devicePixelRatio)// 设置分辨率与电脑的分辨率相同
  renderer.setSize(document.getElementById('threecanvas').offsetWidth, document.getElementById('threecanvas').offsetHeight)
  renderer.shadowMap.enabled = true// 渲染器渲染阴影效果
  renderer.shadowMap.type = THREE.PCFSoftShadowMap// 阴影类型
  renderer.outputEncoding = THREE.sRGBEncoding// 色域,鲜明渲染,常规rgb颜色渲染
  // 把这个canvas渲染到指定div里,不指定div则把setSize指定宽高
  document.getElementById('threecanvas').appendChild(renderer.domElement)
}
// 初始化控制器
function initControls() {
  controls = new OrbitControls(camera, renderer.domElement)
  controls.listenToKeyEvents(window)//监听操作,具体有哪些可以去底层扒一扒
  controls.enableDamping = true// 开启控制阻尼,是否有惯性
  controls.dampingFactor = 0.05 // 阻尼强度,鼠标拖拽旋转灵敏度
  controls.enableZoom = true// 是否可以缩放
  controls.minDistance = 2// 相机距离原点最近距离
  controls.maxDistance = 1000// 相机距离原点最远距离
  controls.enablePan = true // 是否开启右键拖拽
  controls.maxPolarAngle = Math.PI / 2.05 // 最大角度,实际项目中一些模型限制视角到地下
  controls.screenSpacePanning = false //false时只能前后左右平移,不能上下平移,true时哪个方向都可以
  controls.target = new THREE.Vector3(0, 0, 0)// 设置控制器的旋转原点
}
// 更新,实时渲染的时候加进去,有控制交互,就渲染
function update() {
  controls.update()
}
// 光源
function initLight() {
  // 环境光AmbientLight,影响整个场景的光源
  ambient = new THREE.AmbientLight(0xffffff, 1)
  ambient.name = '环境光'
  scene.add(ambient)

  // 平行光DirectionalLight,模拟太阳光,用于渲染阴影
  directional = new THREE.DirectionalLight(0xffffff, 1.2, 100) // 模拟远处类似太阳的光源
  directional.name = '平行光'
  directional.position.set(directionalx1, directionaly1, directionalz1)
  directional.castShadow = true// 告诉平行光需要开启阴影投射
  directional.shadowDarkness = 1
  directional.shadow.mapSize.width = 512 * 4// 阴影分辨率,默认512
  directional.shadow.mapSize.height = 512 * 4
  // 平行光范围
  directional.shadow.camera.left = -directionalvalue
  directional.shadow.camera.right = directionalvalue
  directional.shadow.camera.top = directionalvalue
  directional.shadow.camera.bottom = -directionalvalue
  directional.shadow.camera.near = 0.5
  directional.shadow.camera.far = 1500
  directional.shadow.bias = 0.0001// 阴影偏移,否则会有阴影锯齿出现,阴影条纹,嗯写过的人应该都遇到过,很恶心的东西,可以用这个bias稍作偏移,具体参数调整起来比较烦
  scene.add(directional)
  // 平行光辅助线,添加辅助线后可以看到平行光笼罩的范围,用于细节调整
  const directionalhelper = new THREE.CameraHelper(directional.shadow.camera)
  directionalhelper1.visible = true
  // 平时不用,注掉这个add
  scene.add(directionalhelper)
}
// 初始化
function init() {
  initScene()// 基础场景
  initCamera()// 相机
  initRenderer()// 渲染器
  initControls()// 控制器
  initLight()// 光源
}
// 循环渲染页面场景
function render() {
  update()
  requestAnimationFrame(render)// 执行一个动画.并在动画执行后重新渲染
  // 传统渲染器,如果使用shader着色器,则注掉这个,增加效果合成器,增加通道渲染
  renderer.render(scene, camera)
}
init()
render()

个人写的vue,分开function函数,原生HTML和vue都能用,包括小程序,可以单独把代码提取成js,使用export{…}导出属性或方法函数,直接在页面import引入即可。
正好今天又修改模型改动代码,重构的时候把代码扣出来,一边整合代码,一边写博客,可能时间有限,写的比较拉。