ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

three.js 节点材质中的 ModelNode 解析:从 NodeFrame 动态绑定模型变换与坐标度量

three.js 节点材质中的 ModelNode 解析:从 NodeFrame 动态绑定模型变换与坐标度量 three.js 节点材质中的 ModelNode 解析从 NodeFrame 动态绑定模型变换与坐标度量【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsModelNode 是 three.js 节点材质TSL / Node Material体系中用于访问当前被渲染 3D 对象变换信息的核心访问器节点。与需要显式传入对象引用的 Object3DNode 不同ModelNode 直接从节点帧NodeFrame状态中提取被渲染对象因此天然适配批量渲染、蒙皮、实例化等需要逐对象复用同一材质的场景。读完本文你将理解 ModelNode 的五个 scope 各自代表的坐标系与矩阵语义、update()的刷新机制以及它在着色器中以 uniform 暴露的世界位置、观察空间位置、朝向、缩放与世界矩阵等度量的正确用法。ModelNode 是什么ModelNode 是Object3DNode的特化版本位于 src/nodes/accessors/ModelNode.js其继承链为EventDispatcher → Node → Object3DNode → ModelNode从源码结构看ModelNode 本体非常薄——它只覆盖了两点行为不保存对象引用。构造函数仅接收scope并把对象的引用获取推迟到每一帧的update()阶段从帧状态取对象。在update()中执行this.object3d frame.object;即从当前NodeFrame上取回正在被渲染的对象随后调用父类Object3DNode.update()完成数值刷新。// src/nodes/accessors/ModelNode.js节选 class ModelNode extends Object3DNode { static get type() { return ModelNode; } constructor( scope ) { super( scope ); } update( frame ) { this.object3d frame.object; super.update( frame ); } }这段实现正是ModelNode 与 Object3DNode 的本质差异Object3DNode在构造时或通过 TSL 代理函数绑定一个具体的 Object3D 实例适合固定对象、固定材质的场景而ModelNode绑定的是当前帧正在渲染的对象同一棵节点材质可以被场景中任意数量的 Mesh 共享渲染到谁身上就自动读取谁的矩阵与位置。构造函数与 scope 参数new ModelNode( scope : position | viewPosition | direction | scale | worldMatrix )构造一个新的模型节点scope决定该节点在着色器中暴露的变换度量类型源码中对应五种静态常量见 src/nodes/accessors/Object3DNode.jsscope常量输出类型语义worldMatrixModelNode.WORLD_MATRIXmat4对象的世界矩阵matrixWorld直接以 uniform 引用传入positionModelNode.POSITIONvec3对象世界空间位置由matrixWorld平移分量提取viewPositionModelNode.VIEW_POSITIONvec3对象在观察相机空间的位置即世界位置再乘相机逆矩阵directionModelNode.DIRECTIONvec3对象在世界空间的朝向前向取自getWorldDirection()scaleModelNode.SCALEvec3对象在世界空间的缩放由matrixWorld缩放分量提取在父类Object3DNode中还额外支持RADIUSfloat对象包围球半径因此从生成节点类型的方法看worldMatrix映射为mat4四个向量类 scope 映射为vec3radius映射为floatObject3DNode.js。注意 scope 对对象引用的影响在 TSL 中通常不会直接new ModelNode(...)而是使用预置的不可变单例符号见下文TSL 便捷符号这些符号在import时已经确定了各自 scope。update(frame)帧驱动的对象绑定与度量刷新.update( frame : NodeFrame )覆写了Object3DNode.update其职责可拆成两步绑定对象this.object3d frame.object;即从当前节点帧取回正在渲染的 3D 对象刷新 uniform调用父类update按 scope 把对应的矩阵/向量写入内部UniformNode。NodeFrame是节点系统的每帧上下文在 src/nodes/core/NodeFrame.js 中保存了renderer、scene、object、camera、material以及time、deltaTime、frameId、renderId等状态渲染器在绘制前通过 NodeManager 的getNodeFrame()把当前渲染对象写入nodeFrame.object object随后逐对象调用节点的update()。因为该节点声明了updateType NodeUpdateType.OBJECT见 Object3DNode.jsNodeFrame.updateNode()会在 OBJECT 模式下对每个渲染对象都执行一次updateNodeFrame.js这正是每个物体拿到自己的矩阵的机制保证。父类update()对每个 scope 的底层计算如下Object3DNode.jsWORLD_MATRIX直接把object.matrixWorld赋给 uniform每次更新时 JS 侧只转移矩阵引用开销最小POSITION调用Vector3.setFromMatrixPosition( matrixWorld )提取平移SCALE调用Vector3.setFromMatrixScale( matrixWorld )提取各轴缩放DIRECTION调用Object3D.getWorldDirection()得到世界空间朝向默认取本地Z方向经世界矩阵变换后的结果供lookAt类逻辑使用的语义约定VIEW_POSITION先取世界位置再applyMatrix4( camera.matrixWorldInverse )变换到相机空间等价于把模型摆到相机坐标系下看。TSL 便捷符号以节点声明方式使用 ModelNode为了让着色器代码接近可读的数学表达式ModelNode 在源码文件末尾以nodeImmutable导出了一组预置单例ModelNode.js并且这些符号全部在 src/Three.TSL.js 中对外导出可从three/tsl直接引入TSL 符号底层 scope类型含义modelPositionPOSITIONvec3对象世界空间位置modelViewPositionVIEW_POSITIONvec3对象观察空间位置modelDirectionDIRECTIONvec3对象世界空间方向modelScaleSCALEvec3对象世界空间缩放modelWorldMatrixWORLD_MATRIXmat4对象世界矩阵modelRadiusRADIUSfloat对象包围球半径modelNormalMatrix—mat3法线矩阵Matrix3.getNormalMatrixmodelWorldMatrixInverse—mat4世界矩阵的逆modelViewMatrix—mat4模型视图矩阵mediumpModelViewMatrix—mat4着色器内以 mediump 计算的模型视图矩阵highpModelViewMatrix—mat4CPU 侧以 JS 计算的模型视图矩阵highpModelNormalViewMatrix—mat3CPU 侧计算的模型法线视图矩阵其中modelNormalMatrix、modelWorldMatrixInverse、modelViewMatrix系列并非严格意义上的 ModelNode 实例modelNormalMatrix与modelWorldMatrixInverse是使用uniform().onObjectUpdate()的 Uniform 节点——每当对象更新时分别通过getNormalMatrix( matrixWorld )与copy( matrixWorld ).invert()在 CPU 上计算modelViewMatrix默认返回modelViewMatrix即cameraViewMatrix * modelWorldMatrix在着色器内以 mediump 精度连乘得到但优先读取渲染器上下文中已有的builder.context.modelViewMatrix避免重复计算highpModelViewMatrix则把矩阵乘法放到 JS 侧执行object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld )以 CPU 计算换取着色器内的高精度适合大尺度坐标、深度精度敏感的场景。这些符号在 three.js 内部材质中被大量复用。例如 src/materials/nodes/NodeMaterial.js 用modelViewMatrix.mul( positionLocal ).xyz计算顶点的观察空间位置src/materials/nodes/Line2NodeMaterial.js 用modelViewMatrix与modelWorldMatrixInverse处理线段端点。examples 目录中的 TSL 示例如 CurveModifierGPU.js、Raymarching.js也以这些模型度量参与自定义着色逻辑。实战用法在节点材质中使用模型度量以一个基于MeshBasicNodeMaterial的简单片段为例把对象的世界位置作为颜色分量、并按到相机的观察距离做衰减import * as THREE from three; import { modelPosition, modelViewPosition, modelViewMatrix, positionLocal, vec3 } from three/tsl; const material new THREE.MeshBasicNodeMaterial(); material.colorNode modelPosition.toVec3(); // 世界位置直接参与着色 // 更常见的组合观察空间距离驱动的衰减 material.colorNode vec3( 0.5 ).mul( modelViewPosition.z.negate() );几点约束与建议不要手动new ModelNode()后反复传入对象。既然它从NodeFrame取对象直接复用modelPosition这类单例即可若你的确需要绑定某一个特定对象例如把某个辅助物体的矩阵塞进材质应改用 Object3DNode 系列 的objectPosition( mesh )、objectWorldMatrix( mesh )等带参代理函数更新时机。ModelNode 依赖渲染循环中NodeFrame的状态填充只有通过 renderer 的正常渲染流程才会刷新不要在update()之外、或对象尚未加入场景时假设其 uniform 已同步精度策略。默认modelViewMatrix是着色器内的 mediump 连乘在需要高质量大世界坐标、深度或矩阵精度时可切换为 CPU 计算的highpModelViewMatrix对应源码中的isHighPrecisionModelViewMatrix标志见 ModelNode.js以少量 CPU 开销换取 GPU 端精度实例化与蒙皮。由于绑定的是正在渲染的对象而非固定对象ModelNode 天然支持共享材质的多物体与实例化渲染路径。与其他访问器的关系同类按对象绑定的还有object3DModelMatrix、modelViewMatrix等常规矩阵 uniform它们与 ModelNode 一起构成节点材质中模型变换上下文的完整度量集与 ModelNode 对应的通用节点是 Object3DNode后者是对象即参数构造时确定对象前者是对象即状态帧内动态绑定二者共享同一套 scope 计算逻辑与 uniform 更新机制NodeFrame是二者共同的运行环境负责在每个渲染对象上触发更新可进一步阅读 src/nodes/core/NodeFrame.js 了解 FRAME / RENDER / OBJECT 三种更新粒度的调度差异。小结ModelNode 用极简的实现构造传 scope 帧内取对象把当前渲染对象的变换度量接入 TSL 节点图update()从NodeFrame.object绑定对象后按position / viewPosition / direction / scale / worldMatrix五种 scope 刷新内部 uniform再配合modelPosition、modelWorldMatrix、modelViewMatrix等预置符号让材质编写者可以像写数学表达式一样读取模型的世界/观察空间信息。理解它也就理解了节点材质中逐对象 uniform 刷新这一整套工作流。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表