返回顶部
首页 > 资讯 > 前端开发 > JavaScript >three.js中的needsUpdate是什么
  • 628
分享到

three.js中的needsUpdate是什么

2024-04-02 19:04:59 628人浏览 八月长安
摘要

本篇文章为大家展示了three.js中的needsUpdate是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。 three.js里

本篇文章为大家展示了three.js中的needsUpdate是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

three.js里的很多对象都有一个needsUpdate属性,文档中很少有写(不过three.js的文档本来就没多少,很多问题还得靠GitHub上的issues),网上各式各样的教程中也不太会写这个,因为对于简单的入门程序而言,是用不到这个属性的。
那么这个属性到底是用来干嘛的,一言以敝之就是告诉renderer这一帧我该更新缓存了,尽管作为一个标志位用途很简单,但是因为要知道为什么要更新缓存,要更新哪些缓存,所以还是有必要好好了解下的。
为什么需要needsUpdate
首先还是来看下为什么需要缓存,缓存的存在一般都是为了减少数据传输的次数,从而减少程序在数据传输上消耗的时间,这里也是,一般一个物体(Mesh)要最后能够成功显示到屏幕前是很不容易的,需要转三次战场
首先是通过程序将所有的顶点数据和纹理数据从本地磁盘读取到内存当中。
然后程序在内存中做了适当的处理之后就要将那些需要绘制到屏幕前的物体的顶点数据和纹理数据传输到显存当中。
最后在每一帧渲染的时候将显存中的顶点数据和纹理数据flush到GPU中进行装配,绘制。
根据那个金字塔式的数据传输模型,第一步显然是最慢的,如果是在webGL这样的环境中通过网络来传输,那就更加慢了,其次是从内存传输到显存的时间,这个后面会做一个简单的数据测试
然后是这三步操作的使用频率,对于小场景来说,第一步是一次性的,就是每次初始化程序的时候就会将一个场景的所有数据都加载到内存中了,对于大场景来说,可能会做一些异步加载,但是目前暂时不在我们考虑的问题当中。 对于第二步的频率,应该是这次要讲的最主要的,首先写个简单的程序测试一下做这一步传输所带来的消耗

代码如下:


var canvas = document.createElement('canvas');
var _gl = canvas.getContext('experimental-WEBgl');
var vertices = [];
for(var i = 0; i < 1000*3; i++){
vertices.push(i * Math.random() );
}
var buffer = _gl.createBuffer();
console.profile('buffer_test');
bindBuffer();
console.profileEnd('buffer_test');
function bindBuffer(){
for(var i = 0; i < 1000; i++){
_gl.bindBuffer(_gl.ARRAY_BUFFER, buffer);
_gl.bufferData(_gl.ARRAY_BUFFER, new Float32Array(vertices), _gl.STATIC_DRAW);
}
}


先简单解释下这个程序,vertices是一个保存顶点的数组,这里是随机生成了1000个顶点,因为每个顶点都有x,y,z三个坐标,所以需要一个3000大小的数组, _gl.createBuffer命令在显存中开辟了一块用来存放顶点数据的缓存,然后使用_gl.bufferData将生成的顶点数据从内存传输一份copy到显存中。 这里假设了一个场景中有1000个1000个顶点的物体,每个顶点是3个32位4个字节的float数据,计算一下就是差不多1000 x 1000 x 12 = 11M的数据,profile一下差不多消耗了15ms的时间,这里可能看看15ms才这么点时间,但是对于一个实时的程序来说,如果要保证30fps的帧率,每一帧所需要的时间要控制在30ms左右,仅仅是做一次数据的传输就花去了一半的时间怎么成,要知道大头应该是GPU中的绘制操作和在CPU中的各种各样的处理啊,应该吝啬整个渲染过程中的每一步操作。
所以应该尽量减少这一步的传输次数,其实可以做到刚加载的时候就把所有的顶点数据和纹理数据从内存一并传输到显存当中,这就是现在three.js做的,第一次就把需要绘制的物体(Geometry)的顶点数据传输到显存中,并且缓存这个buffer到geometry.__webglVertexBuffer,之后每次绘制的时候都会判断Geometry的verticesNeedUpdate属性,如果不需要更新就直接使用现在的缓存,如果看到verticesNeedUpate为true, 就会重新将Geometry中的顶点数据传输到geometry.__webglVertexBuffer中,一般对于静态物体我们是不需要这一步操作的,但是如果遇到顶点会频繁改变的物体,例如用顶点来做粒子的粒子系统,还有使用了骨骼动画的Mesh, 这些物体每一帧都会改变自己的顶点,所以需要每一帧都需要将其verticesNeedUpdate属性设为true来告诉renderer我需要重新传输数据了!
其实在WebGL程序中,更多的会在vertex shader中去改变顶点的位置来完成粒子效果和骨骼动画,尽管如果放在cpu端计算更容易扩展,但是因为javascript的计算能力的限制,更多的还是会把这些计算量大的操作放到gpu端操作。 这种情况下并不需要重新传输一次顶点数据,所以上面那种case在实际程序中其实用到的不多,更多的还是会去更新纹理和材质的缓存。
上面那个case主要描述的是一个传输顶点数据的场景,除了顶点数据,还有一个大头就是纹理,一张1024*1024大小的R8G8B8A8格式的纹理所要占用的内存大小也要高达4M,于是看下面这个例子

代码如下:


var canvas = document.createElement('canvas');
var _gl = canvas.getContext('experimental-webgl');
var texture = _gl.createTexture();
var img = new Image;
img.onload = function(){
console.profile('texture test');
bindTexture();
console.profileEnd('texture test');
}
img.src = 'test_tex.jpg';
function bindTexture(){
_gl.bindTexture(_gl.TEXTURE_2D, texture);
_gl.texImage2D(_gl.TEXTURE_2D, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, img);
}


这里就不需要变态的重复1000次了,一次传输10241024的纹理就已经花了30ms,一张256256的差不多是2ms,所以three.js中对于纹理也是尽量只在最开始的时候传输一次,之后如果texture.needsUpdate属性不手动设为true的话就会一直直接使用已经传输到显存中的纹理。
需要更新哪些缓存
上面通过两个case描述了为什么three.js要加这么一个needsUpdate属性,接下来列举一下几个场景来知道在什么情况下需要手动的更新这些缓存。
纹理的异步加载
这算是一个小坑吧,因为前端的图片是异步加载的,如果在创建好img后直接写texture.needsUpdate=true的话,three.js的renderer中会这一帧中就使用_gl.texImage2D将空的纹理数据传输到显存中,然后就将这个标志位设成false, 之后真正等到图片加载完成的时候确不再更新显存数据了,所以必须要在onload事件中等整张图片加载完成后再写texture.needsUpdate = true
视频纹理
大部分纹理都是像上面那个case直接加载和传输一次图片就行了,但是对于视频纹理来说并不是,因为视频是一个图片流,每一帧要显示的画面都不一样,所以每一帧都需要将needsUpdate设为true来更新显卡中的纹理数据。
使用render buffer
render buffer是比较特殊的对象,一般的程序在整个场景绘制出来后都是直接flush到屏幕了,但是如果多了post processing或这screen based xxx(例如screen based ambient occlusion)的话,就需要将场景先绘制到一个render buffer上,这个buffer其实就是一张纹理,只不过是上一步绘制生成的,而不是从磁盘加载的。three.js中有一个专门的texture对象WebGLRenderTarget来初始化和保存renderbuffer, 这种纹理也需要在每一帧设置一下needsUpdate为true
Material的needsUpdate
材质在three.js中是通过THREE.Material来描述的,其实材质并没有什么数据要传输,但是为什么还要搞一个needsUpdate呢,这里还要说一下shader这个东西,shader直译过来是着色器,提供了在gpu中编程处理顶点和像素的可能性,在绘画中有个shading的术语来表示绘画的明暗法,GPU中的shading也类似,通过程序计算光照的明暗来表现物体的材质,ok, 既然shader是一段跑在GPU上的程序,那么像所有程序一样都需要进行一次编译链接的操作, WebGL中是在运行时对shader程序进行编译的,这当然需要消耗时间,因此也是最好能够一次编译就运行到程序结束。所以three.js中就在material初始化的时候就编译链接了shader程序并且缓存了编译链接后得到的program对象。一般一个material是不需要再去重新编译整个shader了,材质的调整只需要修改shader的unifORM参数就行了。但是如果是替换了整个材质,比如将原来phong的shader替换成了一个lambert的shader,就需要将material.needsUpdate设置成true去重新做一次编译。不过这种情况不多见,更常见的是下面提到的一种情况。
添加和删除灯光
这个应该还是在场景中比较常见了的吧,可能很多刚开始用three.js的人都会掉进这个坑里,在给场景动态添加了一个灯光后发现这个灯光怎么不起作用,不过这是在用three.js内置的shader的情况下,例如phong, lambert,看renderer里的源代码就会发现three.js在内置的shader代码中使用#define来设置场景中灯光的个数,而这个#define的值是在每次更新材质的时候通过字符串拼接shader得到,代码如下

代码如下:


"#define MAX_DIR_LIGHTS " + parameters.maxDirLights,
"#define MAX_POINT_LIGHTS " + parameters.maxPointLights,
"#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights,
"#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights,


确实这种写法能够有效的减少了gpu寄存器的使用,如果只有一盏灯光就可以只声明一个一盏灯光所需要的uniform变量,但是在每次灯光数量改变,特别是添加的时候就需要重新拼接编译链接一次shader,这时候也需要将所有材质的material.needsUpdate设为true;
改变纹理
这里的改变纹理指的并不是更新纹理数据,而是原来材质使用了纹理,后来不使用了,或者原来材质不使用纹理后来又加上去了,如果不手动强制更新材质都会导致最后出来的效果跟自己想的不一样,产生这种问题的原因跟上面添加灯光差不多,也是因为shader中加了一个宏来判断是否使用了纹理,

代码如下:


parameters.map ? "#define USE_MAP" : "",
parameters.envMap ? "#define USE_ENVMAP" : "",
parameters.lightMap ? "#define USE_LIGHTMAP" : "",
parameters.bumpMap ? "#define USE_BUMPMAP" : "",
parameters.normalMap ? "#define USE_NORMALMAP" : "",
parameters.specularMap ? "#define USE_SPECULARMAP" : "",


所以每次map, 或者envMap或者lightMap等改变真值的时候都需要更新材质
其它顶点数据的改变
其实上面纹理的改变还会产生一个问题,主要是在初始化的时候没有纹理,但是后来动态添加上去这种环境下,光是将material.needsUpdate设为true还不够,还需要将geometry.uvsNeedsUpdate设成true, 为什么会有这种问题呢,还是因为three.js对程序的优化,在renderer中第一次初始化geometry, material的时候,如果判断为没有纹理,尽管内存中的数据中有每个顶点uv数据,但 three.js 还是不会将这些数据copy到显存中,初衷应该还是为了节省点宝贵的显存空间,但是在添加纹理后geometry并不会很智能的重新去传输这些uv数据以供纹理使用,必须要我们手动的将设置uvsNeedsUpdate来告知它该更新uv了, 这个问题真是开始的时候坑了我很长时间。
关于几种顶点数据的needUpdate属性可以看这条issue
https://github.com/mrdoob/three.js/wiki/Updates
最后
three.js的优化做的是不错,但是在各种优化下带来的是各种可能踩到的坑,这种情况最好的办法也只能是看源代码了,或者去github上提issues

上述内容就是three.js中的needsUpdate是什么,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注编程网JavaScript频道。

--结束END--

本文标题: three.js中的needsUpdate是什么

本文链接: https://lsjlt.com/news/80720.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • three.js中的needsUpdate是什么
    本篇文章为大家展示了three.js中的needsUpdate是什么,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。 three.js里...
    99+
    2024-04-02
  • Three.js添加阴影和简单后期处理的方法是什么
    本篇内容主要讲解“Three.js添加阴影和简单后期处理的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Three.js添加阴影和简单后期处理的方法是什么”吧!添加阴影在Three.j...
    99+
    2023-07-06
  • WebGL中three.js的示例分析
    小编给大家分享一下WebGL中three.js的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧! 1、WebGL 可以...
    99+
    2024-04-02
  • JS库中Three.js的用法示例
    这篇文章给大家分享的是有关JS库中Three.js的用法示例的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。准备工作在写代码之前,你首先要去下最新的three.js框架包,在你的页...
    99+
    2024-04-02
  • vue中使用three.js报错怎么办
    小编给大家分享一下vue中使用three.js报错怎么办,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前言最近在学习three.js,同时也学习一下vue3,然后...
    99+
    2023-06-29
  • 在微信小程序中怎么使用three.js
    本篇内容主要讲解“在微信小程序中怎么使用three.js”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“在微信小程序中怎么使用three.js”吧!1.首先引入下载材料,最后有完整源码默认你很熟悉...
    99+
    2023-07-05
  • vue3项目中使用three.js的操作步骤
    目录前言一、three.js是什么?二、vue3中下载与安装three.js三、操作步骤1.创建场景2.创建物体3.添加光源4.添加相机5.开始渲染四、myThree.vue源代码五...
    99+
    2023-01-15
    vue3 three.js vue使用three.js
  • javascript中的$是什么
    本篇内容主要讲解“javascript中的$是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“javascript中的$是什么”吧! ...
    99+
    2024-04-02
  • WebGL中three.js怎么实现物体的阴影动画效果
    小编给大家分享一下WebGL中three.js怎么实现物体的阴影动画效果,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!实现物体的...
    99+
    2024-04-02
  • Prometheus中的TSDB是什么,它的作用是什么
    Prometheus中的TSDB是时间序列数据库(Time Series Database)的缩写,它是用来存储和管理时间序列数据的...
    99+
    2024-03-04
    Prometheus
  • Kafka中的ISR是什么它的作用是什么
    Kafka中的ISR指的是In-Sync Replicas,即同步副本。ISR是指与分区领导者保持同步的副本,它们会及时地复制分区的...
    99+
    2024-04-02
  • Webgl&Three.js中物体拾取的示例分析
    小编给大家分享一下Webgl&Three.js中物体拾取的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1、引子...
    99+
    2024-04-02
  • Three.js 中的屏幕空间环境光遮蔽SSAO
    目录简介原理实现总结简介 在计算机图形学中,环境光遮蔽(Ambient Occlusion)是一种可以增强场景深度感、模拟阴影效果的技术。在 Three.js 中,我们可以使用屏幕空...
    99+
    2023-05-17
    Three.js实现SSAO Three.js屏幕空间环境光遮蔽
  • java中的file是什么?
    File是个文件类,可以用其增加、删除、查找某种类型的文件或者文件夹,同时根据其成员变量的特点可以综合利用,避免出现跨系统的时候出现错误,并且查找时最好输入绝对路径,以免出现不存在的文件。使用递归时一定要主要好停止,以免栈内存溢出。(推荐:...
    99+
    2019-05-10
    java
  • java中的锁是什么
    锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源。Java中的锁主要有以下几种概念:同步锁  同一时刻,一个同步锁只能被一个线程访问。以对象为依据,通过synchronized关键字来进行同步,实现对...
    99+
    2018-10-05
    java基础 java
  • java中的gc是什么
    GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃。Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Jav...
    99+
    2014-09-15
    java入门 gc java
  • java中的api是什么
    什么是API?API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件的以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节...
    99+
    2021-07-26
    java入门 java api
  • java中的域是什么
    域也称成员变量,体现了类的状态,而状态的改变由类体的方法实现。域就是类中的成员变量,有时也叫字段,属性都是同一个意思。公有域就是能在其它类中直接访问的域,也就是带有域。例如:class Test { private int a; ...
    99+
    2015-02-15
    java
  • mysql中的innodb是什么
    mysql中的innodb是什么?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。innodb简介InnoDB,是MySQL的数据库引擎之一,...
    99+
    2024-04-02
  • mysql中的myisam是什么
    mysql中的myisam是什么?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。myisam介绍MyISAM是默认存储引擎(Mysql5.1...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作