news 2026/9/3 21:28:50

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【three.js教程】Three.js 运行时更新:矩阵、几何体、材质怎么改才不卡

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

原文出处:Three.js Manual – How to update Things

本文基于官方手册「How to update Things」章节整理,用通俗方式带你看懂:对象加进场景后矩阵怎么刷新、改几何体顶点要不要重建、材质哪些能随便改、相机改了参数为什么画面没变——把「更新」这件事一次讲清。

写在前面

写 Three.js 应用,大部分时间都在「动态地改东西」:让物体动起来、随鼠标调整形状、运行时换贴图、玩家靠近时改相机视野……但这些更新不是「改了属性就完事」,不同对象有不同的更新规则。改错了,要么画面不刷新,要么悄悄掉帧。这一篇按对象类型逐一拆解:矩阵、几何体、材质、贴图、相机、InstancedMesh、SkinnedMesh。

一、变换矩阵:默认自动更新,静态对象可手动优化

所有对象一旦被加进场景,它的矩阵默认就会自动更新

constobject=newTHREE.Object3D();scene.add(object);

如果它是另一个已被加入场景的对象的子节点,同样会自动更新:

constobject1=newTHREE.Object3D();constobject2=newTHREE.Object3D();object1.add(object2);scene.add(object1);// object1 和 object2 的矩阵都会自动更新

但如果你确定某个对象永远不会动(纯静态),可以把自动更新关掉,只在需要时手动刷新一次,省下每帧的矩阵计算:

object.matrixAutoUpdate=false;object.updateMatrix();

划重点:关掉matrixAutoUpdate后,你改了position/rotation/scale不会自动反映到矩阵上,必须自己再调一次updateMatrix()。别关了又忘了手动刷。

二、BufferGeometry:缓冲区不能改大小,只能改内容

BufferGeometry把顶点位置、索引、法线、颜色、UV 等数据存在typed arrays(类型化数组)里,比老式Geometry更快,但用起来更讲究。

最关键的一条:你不能改变缓冲区的大小。重新分配大小的开销,基本等同于新建一个几何体。你只能更新缓冲区里已有的内容。

这意味着:如果你预感到某个属性(比如顶点数)会增长,就必须预先分配一个够大的缓冲区。当然,这也意味着你的几何体有个上限——不存在能无限高效扩张的BufferGeometry

看一个「运行时不断变长的线」的例子。先预分配 500 个顶点的空间,但一开始只画前 2 个点,靠BufferGeometry.drawRange控制绘制数量:

constMAX_POINTS=500;// 几何体constgeometry=newTHREE.BufferGeometry();// 属性:每个点 3 个 float(x, y, z)constpositions=newFloat32Array(MAX_POINTS*3);geometry.setAttribute('position',newTHREE.BufferAttribute(positions,3));// 绘制范围:只画前 2 个点constdrawCount=2;geometry.setDrawRange(0,drawCount);// 材质与线constmaterial=newTHREE.LineBasicMaterial({color:0xff0000});constline=newTHREE.Line(geometry,material);scene.add(line);

之后想往线上加点的套路是这样:

constpositionAttribute=line.geometry.getAttribute('position');letx=0,y=0,z=0;for(leti=0;i<positionAttribute.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.needsUpdate=true;// 首次渲染后修改必须设置

⚠️ 改完位置数据后,通常还要重算包围体,否则视锥剔除(frustum culling)、辅助工具等会算错:

line.geometry.computeBoundingBox();line.geometry.computeBoundingSphere();

官方还提供了一个可拖来改去演示动态线的 fiddle(jsfiddle.net/t4m85pLr/1),可以照着改。

三、Materials:哪些能随便改,哪些要重建着色器

材质的uniform 值(颜色、贴图、透明度等)可以随便改,每帧都会发给着色器;跟 GL 状态相关的参数(depthTestblendingpolygonOffset等)也能随时改。

但有这么一批属性,一旦材质被渲染过一次,就不容易在运行时改了:

  • uniforms 的数量和类型;
  • 以下特性的「有无」:
    • texture(贴图)
    • fog(雾)
    • vertex colors(顶点颜色)
    • morphing(形变)
    • shadow map(阴影贴图)
    • alpha test(透明度测试)
    • transparent(透明)

改这些会触发着色器程序重建,需要手动打标记:

material.needsUpdate=true;

性能提醒:这会相当慢,可能造成掉帧——尤其 Windows 上(DirectX 的 shader 编译比 OpenGL 慢)。平滑一点的做法是「用假值模拟」:比如零强度的灯光、白色贴图、零浓度的雾,先用着,避免运行时硬切 shader。

另外,你可以自由更换几何体块(chunk)用的材质,但不能改变物体按面材质被切成几块的方式。如果运行时需要多种材质配置:数量少就提前切好(比如人的头发/脸/身体/上衣/裤子),数量大(每个面都可能不同)就考虑用 attribute 或贴图来驱动每面的差异。

四、Textures:改了内容要打 needsUpdate

图片、canvas、视频、数据贴图,改了之后需要打上更新标记:

texture.needsUpdate=true;

例外:渲染目标(Render Target)会自己自动更新,不用管。

五、Cameras:改了 fov/aspect 记得重算投影矩阵

相机的位置和朝向是自动更新的。但如果你改了下面这些参数:

  • fov
  • aspect
  • near
  • far

就必须重算投影矩阵,否则画面不会按新参数变化:

camera.aspect=window.innerWidth/window.innerHeight;camera.updateProjectionMatrix();

这是个高频坑:窗口 resize 时改了camera.aspect却忘了updateProjectionMatrix(),结果画面拉伸或不变。Resize 处理里这两行是固定搭档。

六、InstancedMesh:改实例后要重算包围体

InstancedMesh用于便捷地做实例化渲染。视锥剔除、射线拾取等特性依赖最新的包围体(包围球/包围盒)。由于它的机制特殊,类自己带有boundingBoxboundingSphere,优先级高于几何体层级的包围体。

和你改几何体一样:每当底层数据变了(比如用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.setDrawRange
  • three.js docs – Material.needsUpdate
  • three.js docs – Texture.needsUpdate
  • three.js docs – Camera.updateProjectionMatrix
  • three.js example – WebGL / custom / attributes
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 21:27:15

OMSI2国产纯电巴士南宁B23路游玩与录制实战教程

各位车迷朋友、公交模拟爱好者们&#xff0c;大家好&#xff01;之前一直在玩 OMSI2 默认的德国地图和德系车&#xff0c;虽然手感经典&#xff0c;但总少了点“国内公交”的亲切感。直到我接触了国产宇通纯电巴士的玩家自制 MOD&#xff0c;再搭配南宁真实公交线路地图&#x…

作者头像 李华
网站建设 2026/9/3 21:26:39

AI漫画创作工具AIMangaStudio:从Stable Diffusion到完整工作流的源码解析

简介&#xff1a;AIMangaStudio 是一款面向个人漫画创作者与小型工作室的端到端AI漫画制作开源工具&#xff0c;解决无绘画基础用户从文本脚本到成页漫画的全流程创作难题&#xff0c;覆盖剧情生成、角色设定、分镜布局、页间连续性分析及页面输出等核心环节。资源包共42个文件…

作者头像 李华
网站建设 2026/9/3 21:26:37

添加分区、文件系统和持久挂载(三)

创建 GPT 分区GPT 方案同样使用 parted 命令来创建分区&#xff1a;指定要在其上创建分区的磁盘设备。 以 root 用户身份执行 parted 命令&#xff0c;并指定该磁盘设备名称作为参数。[roothost ~]# parted /dev/vdb GNU Parted 3.4 Using /dev/vdb Welcome to GNU Parted! Typ…

作者头像 李华
网站建设 2026/9/3 21:23:45

Delphi项目迁移实战:TMS Component Pack 8.0.9.0 Full Source升级指南

简介&#xff1a;TMS Component Pack 8.0.9.0 完整源码库面向 Delphi 开发人员&#xff0c;覆盖用户界面、数据库、图表、报表与网络通信等常用场景&#xff0c;支持从 Delphi 7 到 DX10 的跨版本开发。压缩包采用 7z 格式封装&#xff0c;整体约 36.71MB&#xff0c;包含 250 …

作者头像 李华
网站建设 2026/9/3 21:23:08

免费使用SquareLine Studio 1.5.2,屏幕控件无限续杯

SquareLine Studio 是一款免费的 LVGL 图形开发工具&#xff0c;支持拖拽式 UI 设计与实时预览&#xff0c;适用于嵌入式设备界面快速开发。本文提供 SquareLine Studio 1.5.2 安装包及无限续杯功能、补丁包获取方式&#xff0c;欢迎联系获取。 免费好用的 LVGL 图形开发工具 S…

作者头像 李华
网站建设 2026/9/3 21:21:50

Go操作Kubernetes API

一、Go操作Kubernetes API —— 实现“动态资源调度与弹性监测” 在您的系统中的作用&#xff1a;当监测到大量投标流量涌入或突发攻击时&#xff0c;自动扩缩容监测Pod&#xff1b;同时实时读取集群元数据用于态势感知。 核心功能项实现&#xff08;Go client-go&#xff09;…

作者头像 李华