文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

DS-SDK封装ThreeJS的三维场景核心库Viewer

2022-11-13 18:28

关注

正文

viewer核心库的封装主要是针对threejs场景进行初始封装,以便多项目复用。具体细节我就不多写了,网上一大堆,但是我发现网上的例子都比较雷同,所以我的这一篇文档会着重从我做过的项目上遇到的一些问题来具体描写,详细请看第七、第八小节,主要是第一我们真实项目中,其实你的渲染页面不是整个页面,而且其中的一小块div,所以你的宽高是div而不是windws的(如下图);第二对于页面大小变化的监视,以我的知识结构来说,还没有很好的解决方案来监视div的大小变化。

基础封装初始化包括以下:

一、ThreeJS-ES6库引入

引入部分除了ThreeJS核心库的东西外,还有标签渲染器,用于后期在场景中添加标签,还有控制器何和帧率显示器。

import {
  Cache,
  WebGLRenderer,
  ACESFilmicToneMapping,
  PCFSoftShadowMap,
  sRGBEncoding,
  PerspectiveCamera,
  Scene,
  Color
} from 'three'
// 二维标签渲染器
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import Stats from 'three/examples/jsm/libs/stats.module.js'

二、初始化渲染器

初始化渲染器部分主要是获取渲染画布的dom,然后初始化场景渲染器,初始化二维标签渲染器,和三维标签渲染器。 这一部分代码我们设置渲染器的大小,这个放到了后面会讲。

_initRenderer () {
    // 获取画布dom
    this.viewerDom = document.getElementById(this.id)
    // 初始化渲染器
    this.renderer = new WebGLRenderer({
      logarithmicDepthBuffer: true,
      antialias: true, // true/false表示是否开启反锯齿
      alpha: true, // true/false 表示是否可以设置背景色透明
      precision: 'highp', // highp/mediump/lowp 表示着色精度选择
      premultipliedAlpha: true, // true/false 表示是否可以设置像素深度(用来度量图像的分辨率)
      preserveDrawingBuffer: true // true/false 表示是否保存绘图缓冲
    })
    this.renderer.domElement.style.zIndex = 1
    // 默认情况下,js的光强数值不真实。为了使得光强更趋于真实值,应该把渲染器的physicallyCorrectLights属性设为true
    this.renderer.physicallyCorrectLights = true
    // 尽管我们的贴图不是HDR,但使用tone mapping可以塑造更真实的效果。
    this.renderer.toneMapping = ACESFilmicToneMapping 
    // 场景中的阴影自动更新
    this.renderer.shadowMap.enabled = true 
    // 设置渲染器开启阴影贴图,并将类型设为PCFSoftShadowMap
    this.renderer.shadowMap.type = PCFSoftShadowMap 
    // 这下我们可以看到更亮的材质,同时这也影响到环境贴图。
    this.renderer.outputEncoding = sRGBEncoding
    // 一个canvas,渲染器在其上绘制输出。
    this.viewerDom.appendChild(this.renderer.domElement)
    // 网页标签
    this.labelRenderer = new CSS2DRenderer()
    this.labelRenderer.domElement.style.zIndex = 2
    this.labelRenderer.domElement.style.position = 'absolute'
    this.labelRenderer.domElement.style.top = '0px'
    this.labelRenderer.domElement.style.left = '0px'
    // 避免HTML标签遮挡三维场景的鼠标事件
    this.labelRenderer.domElement.style.pointerEvents = 'none'
    this.viewerDom.appendChild(this.labelRenderer.domElement)
    // 三维标签
    this.css3DRenderer = new CSS3DRenderer()
    this.css3DRenderer.domElement.style.zIndex = 0
    this.css3DRenderer.domElement.style.position = 'absolute'
    this.css3DRenderer.domElement.style.top = '0px'
    this.css3DRenderer.domElement.style.left = '0px'
    // 避免HTML标签遮挡三维场景的鼠标事件
    this.css3DRenderer.domElement.style.pointerEvents = 'none'
    this.viewerDom.appendChild(this.css3DRenderer.domElement)
  }

三、初始化相机

相机参数里面的aspect(摄像机视锥体长宽比),其实应该是画布dom的长宽比,而不是我们浏览器windows的长宽比,请你仔细品品这句话。

参数 构造器 PerspectiveCamera( fov : Number, aspect : Number, near : Number,> far : Number ) fov — 摄像机视锥体垂直视野角度 aspect — 摄像机视锥体长宽比 near — 摄像机视锥体近端面 far — 摄像机视锥体远端面

  _initCamera () {
    // 渲染相机
    this.camera = new PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 5000)
    this.camera.position.set(50, 0, 50)
    this.camera.lookAt(0, 0, 0)
  }

四、初始化场景

  _initScene () {
    // 渲染场景
    this.scene = new Scene()
    this.scene.background = new Color('rgb(5,24,38)')
  }

五、初始化鼠标控制器

控制器主要是用来控制通过鼠标漫游场景的。

参数 OrbitControls(object:Camera,domElement:HTMLDOMElement) object:(必需)要控制的摄像机。相机不得是其他对象的子对象,除非该对象是场景本身。 domElement:用于事件侦听器的HTML元素。

  _initControl (option) {
    this.controls = new OrbitControls(this.camera, this.renderer.domElement)
    this.controls.enableDamping = false
    this.controls.maxPolarAngle = Math.PI * 0.46// 垂直轨道多远 上限
    this.controls.minPolarAngle = Math.PI * 0.3// 你可以垂直轨道多远,下限
    this.controls.screenSpacePanning = false // 定义平移时如何平移相机的位置 控制不上下移动
  }

六、灯光初始化

没有灯光的话,就会一片漆黑,所以需要添加灯光。

  // 全局光不需要
  const ambientLight = new DS.THREE.AmbientLight(0xffffff, 0.2)
  viewer.scene.add(ambientLight)

七、全局渲染的函数-逐帧获取页面大小

这里是全局渲染的函数,通过requestAnimationFrame函数,可以逐帧去渲染场景。 摄像机视锥体的长宽比,设置渲染器的长宽比,都是这里一直去获取传入的div,而不是window的大小,设置参数(虽然这样写会很耗费性能,但是我也没有找到更好的方法去监听页面的大小变化)。

  function animate () {
      requestAnimationFrame(animate)
      that._undateDom()
      that._readerDom()
      // 全局的公共动画函数,添加函数可同步执行
      that.animateEventList.forEach(event => {
        event.fun && event.content && event.fun(event.content)
      })
    }
    animate()
  // 更新dom大小
  _undateDom () {
    const that = this
    that.controls.update()
    // 摄像机视锥体的长宽比,通常是使用画布的宽/画布的高,所以这里的画布指的是dom,就是一个div,而不是window
    that.camera.aspect = that.viewerDom.clientWidth / that.viewerDom.clientHeight 
    // 更新摄像机投影矩阵。在任何参数被改变以后必须被调用,来使得这些改变生效
    that.camera.updateProjectionMatrix() 
    //设置渲染器的长宽比,所以就不需要在代码里面去写window的页面监听,来改变页面大小【】
    that.renderer.setSize(that.viewerDom.clientWidth, that.viewerDom.clientHeight)
    //that.renderer.setPixelRatio(window.devicePixelRatio) // 设置设备像素比
    that.labelRenderer.setSize(that.viewerDom.clientWidth, that.viewerDom.clientHeight)
    that.css3DRenderer.setSize(that.viewerDom.clientWidth, that.viewerDom.clientHeight)
  }
  // 渲染dom
  _readerDom () {
    this.renderer.render(this.scene, this.camera)
    this.labelRenderer.render(this.scene, this.camera)
    this.css3DRenderer.render(this.css3dScene, this.camera)
  }

八、全局动画函数

这里我做了一个全局所有动画的管理器,页面上所有的需要动画的函数都可以传入运行,包括模型的动画、加载水面的运动、贴图的UV动画。

动画函数数组用来存储所有的动画函数

  //动画函数数组
  this.animateEventList = []

对动画函数的添加

  
  addAnimate (animate) {
    this.animateEventList.push(animate)
  }

这里以状态监视器为例,创建函数,并且添加到全局的动画函数数组里面去

  
  _statsUpdate (statsControls) {
    statsControls.update()
  }
  
  addStats () {
    if (!this.statsControls) this.statsControls = new Stats()
    this.statsControls.dom.style.position = 'absolute'
    this.viewerDom.appendChild(this.statsControls.dom)
    // 添加到动画
    this.statsUpdateObject = {
      fun: this._statsUpdate, // 函数名称,函数在上面
      content: this.statsControls // 绑定传入的参数
    }
    this.addAnimate(this.statsUpdateObject)
  }

对于函数的移除

  
  removeAnimate (animate) {
    this.animateEventList.map((val, i) => {
      if (val === animate) this.animateEventList.splice(i, 1)
    })
  }

以移除状态监视器为例

  
  removeStats () {
    if (this.statsControls) this.viewerDom.removeChild(this.statsControls.dom)
    // 添加到动画
    this.statsUpdateObject = {
      fun: this._statsUpdate,
      content: this.statsControls
    }
    this.removeAnimate(this.statsUpdateObject)
  }

九、销毁页面

  beforeDestroy () {
    this.scene.traverse((child) => {
      if (child.material) {
        child.material.dispose()
      }
      if (child.geometry) {
        child.geometry.dispose()
      }
      child = null
    })
    this.renderer.forceContextLoss()
    this.renderer.dispose()
    this.scene.clear()
  }

以上就是针对核心Viewer库封装的一些记录,也不知道写得行不行,但是里面写得一些也是在工作应用当中遇到的一些真实问题,更多关于ThreeJS核心库Viewer封装DS-SDK的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯