ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

【three.js教程】Three.js 运行时更新:矩阵、几何体、材质怎么改才不卡

【three.js教程】Three.js 运行时更新:矩阵、几何体、材质怎么改才不卡 Three.js 运行时更新矩阵、几何体、材质怎么改才不卡原文出处Three.js Manual – How to update Things本文基于官方手册「How to update Things」章节整理用通俗方式带你看懂对象加进场景后矩阵怎么刷新、改几何体顶点要不要重建、材质哪些能随便改、相机改了参数为什么画面没变——把「更新」这件事一次讲清。写在前面写 Three.js 应用大部分时间都在「动态地改东西」让物体动起来、随鼠标调整形状、运行时换贴图、玩家靠近时改相机视野……但这些更新不是「改了属性就完事」不同对象有不同的更新规则。改错了要么画面不刷新要么悄悄掉帧。这一篇按对象类型逐一拆解矩阵、几何体、材质、贴图、相机、InstancedMesh、SkinnedMesh。一、变换矩阵默认自动更新静态对象可手动优化所有对象一旦被加进场景它的矩阵默认就会自动更新constobjectnewTHREE.Object3D();scene.add(object);如果它是另一个已被加入场景的对象的子节点同样会自动更新constobject1newTHREE.Object3D();constobject2newTHREE.Object3D();object1.add(object2);scene.add(object1);// object1 和 object2 的矩阵都会自动更新但如果你确定某个对象永远不会动纯静态可以把自动更新关掉只在需要时手动刷新一次省下每帧的矩阵计算object.matrixAutoUpdatefalse;object.updateMatrix();划重点关掉matrixAutoUpdate后你改了position/rotation/scale不会自动反映到矩阵上必须自己再调一次updateMatrix()。别关了又忘了手动刷。二、BufferGeometry缓冲区不能改大小只能改内容BufferGeometry把顶点位置、索引、法线、颜色、UV 等数据存在typed arrays类型化数组里比老式Geometry更快但用起来更讲究。最关键的一条你不能改变缓冲区的大小。重新分配大小的开销基本等同于新建一个几何体。你只能更新缓冲区里已有的内容。这意味着如果你预感到某个属性比如顶点数会增长就必须预先分配一个够大的缓冲区。当然这也意味着你的几何体有个上限——不存在能无限高效扩张的BufferGeometry。看一个「运行时不断变长的线」的例子。先预分配 500 个顶点的空间但一开始只画前 2 个点靠BufferGeometry.drawRange控制绘制数量constMAX_POINTS500;// 几何体constgeometrynewTHREE.BufferGeometry();// 属性每个点 3 个 floatx, y, zconstpositionsnewFloat32Array(MAX_POINTS*3);geometry.setAttribute(position,newTHREE.BufferAttribute(positions,3));// 绘制范围只画前 2 个点constdrawCount2;geometry.setDrawRange(0,drawCount);// 材质与线constmaterialnewTHREE.LineBasicMaterial({color:0xff0000});constlinenewTHREE.Line(geometry,material);scene.add(line);之后想往线上加点的套路是这样constpositionAttributeline.geometry.getAttribute(position);letx0,y0,z0;for(leti0;ipositionAttribute.count;i){positionAttribute.setXYZ(i,x,y,z);x(Math.random()-0.5)*30;y(Math.random()-0.5)*30;z(Math.random()-0.5)*30;}如果渲染之后想改变绘制点的数量改drawRange即可line.geometry.setDrawRange(0,newValue);如果想渲染之后修改位置数据的值必须打上更新标记否则 GPU 端的缓冲区不会刷新positionAttribute.needsUpdatetrue;// 首次渲染后修改必须设置⚠️ 改完位置数据后通常还要重算包围体否则视锥剔除frustum culling、辅助工具等会算错line.geometry.computeBoundingBox();line.geometry.computeBoundingSphere();官方还提供了一个可拖来改去演示动态线的 fiddlejsfiddle.net/t4m85pLr/1可以照着改。三、Materials哪些能随便改哪些要重建着色器材质的uniform 值颜色、贴图、透明度等可以随便改每帧都会发给着色器跟 GL 状态相关的参数depthTest、blending、polygonOffset等也能随时改。但有这么一批属性一旦材质被渲染过一次就不容易在运行时改了uniforms 的数量和类型以下特性的「有无」texture贴图fog雾vertex colors顶点颜色morphing形变shadow map阴影贴图alpha test透明度测试transparent透明改这些会触发着色器程序重建需要手动打标记material.needsUpdatetrue;性能提醒这会相当慢可能造成掉帧——尤其 Windows 上DirectX 的 shader 编译比 OpenGL 慢。平滑一点的做法是「用假值模拟」比如零强度的灯光、白色贴图、零浓度的雾先用着避免运行时硬切 shader。另外你可以自由更换几何体块chunk用的材质但不能改变物体按面材质被切成几块的方式。如果运行时需要多种材质配置数量少就提前切好比如人的头发/脸/身体/上衣/裤子数量大每个面都可能不同就考虑用 attribute 或贴图来驱动每面的差异。四、Textures改了内容要打 needsUpdate图片、canvas、视频、数据贴图改了之后需要打上更新标记texture.needsUpdatetrue;例外渲染目标Render Target会自己自动更新不用管。五、Cameras改了 fov/aspect 记得重算投影矩阵相机的位置和朝向是自动更新的。但如果你改了下面这些参数fovaspectnearfar就必须重算投影矩阵否则画面不会按新参数变化camera.aspectwindow.innerWidth/window.innerHeight;camera.updateProjectionMatrix();这是个高频坑窗口 resize 时改了camera.aspect却忘了updateProjectionMatrix()结果画面拉伸或不变。Resize 处理里这两行是固定搭档。六、InstancedMesh改实例后要重算包围体InstancedMesh用于便捷地做实例化渲染。视锥剔除、射线拾取等特性依赖最新的包围体包围球/包围盒。由于它的机制特殊类自己带有boundingBox和boundingSphere优先级高于几何体层级的包围体。和你改几何体一样每当底层数据变了比如用setMatrixAt()变换实例就要重算instancedMesh.computeBoundingBox();instancedMesh.computeBoundingSphere();七、SkinnedMesh同理SkinnedMesh在包围体这件事上和InstancedMesh一个道理——类自己有boundingBox/boundingSphere来正确包住动画中的网格。调用computeBoundingBox()/computeBoundingSphere()时会基于**当前的骨骼变换即当前动画状态**来算。总结一张表记住各类对象的更新规则对象更新方式关键注意变换矩阵默认自动matrixAutoUpdate静态对象可关掉手动updateMatrix()BufferGeometry 内容改 typed array 后设needsUpdate缓冲区不能改大小需预分配改位置后重算包围体绘制数量setDrawRange(0, n)配合预分配缓冲使用Material uniform/GL 状态随意改改 texture/fog 等特性需material.needsUpdate true重建 shader慢Texture改后设texture.needsUpdate trueRenderTarget 自动更新Camera fov/aspect/near/far改后updateProjectionMatrix()一起改漏了就画面不对InstancedMesh / SkinnedMesh改数据后computeBoundingBox/Sphere类自带包围体覆盖几何体层级延伸阅读three.js docs – BufferGeometry.setDrawRangethree.js docs – Material.needsUpdatethree.js docs – Texture.needsUpdatethree.js docs – Camera.updateProjectionMatrixthree.js example – WebGL / custom / attributes
返回列表