十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

three.js AnaglyphPassNode 深度解析:基于物理正确离轴立体投影的红青色差立体渲染(TSL/WebGPU 版)

three.js AnaglyphPassNode 深度解析:基于物理正确离轴立体投影的红青色差立体渲染(TSL/WebGPU 版) three.js AnaglyphPassNode 深度解析基于物理正确离轴立体投影的红青色差立体渲染TSL/WebGPU 版【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js本文围绕 three.js 官方 API 文档 AnaglyphPassNode 展开讲解这个 TSL 渲染 Pass 节点如何把左右眼视图合成为红青色差Anaglyph立体图像。文档将完整覆盖其构造函数、eyeSep/planeDistance/algorithm/colorMode等全部属性与默认值并结合仓库源码 examples/jsm/tsl/display/AnaglyphPassNode.js 剖析其离轴投影的数学实现与色差矩阵体系最后给出官方示例中的完整可运行用法。读完后你将掌握如何在 WebGPU 渲染管线中接入 Anaglyph 效果、如何通过frameCorners()实现零视差的虚拟屏幕平面以及 7 种合成算法 × 3 种色卡的矩阵是如何定义与切换的。1. 定位与继承体系AnaglyphPassNode是一个渲染 Pass 节点用物理正确的离轴off-axis立体投影生成红青色差立体效果。其官方描述为A render pass node that creates anaglyph effect using physically-correct off-axis stereo projection. This implementation uses CameraUtils.frameCorners() to align stereo camera frustums to a virtual screen plane, providing accurate depth perception with zero parallax at the plane distance.即它借助 CameraUtils.frameCorners() 把立体相机的视锥体对齐到一个虚拟屏幕平面在该平面的距离处视差为零从而提供准确的深度感知。继承链来自文档首行EventDispatcher → Node → TempNode → PassNode → StereoCompositePassNode → AnaglyphPassNode从源码结构看AnaglyphPassNode继承自 StereoCompositePassNode后者是一个抽象的立体复合 Pass与普通StereoPassNode输出左右两路视图不同它把左右眼图像合并为单张图像——这正是色差合成或视差屏障Parallax Barrier类效果所必需的能力。AnaglyphPassNode在这个通用立体渲染框架之上只负责两件事重写updateStereoCamera()用frameCorners代替默认的StereoCamera.update()实现离轴投影重写setup()提供 TSLThree Shading Language片段着色器用一对 3×3 颜色矩阵把左右眼颜色混合为最终像素。2. 导入方式AnaglyphPassNode属于 addonexamples/jsm下的扩展模块必须显式导入参考官方手册 Installation#Addons。模块导出三样东西AnaglyphPassNode类默认导出AnaglyphAlgorithm算法枚举AnaglyphColorMode色卡枚举anaglyphPassTSL 函数语法糖等价于new AnaglyphPassNode(scene, camera)。import { anaglyphPass, AnaglyphAlgorithm, AnaglyphColorMode } from three/addons/tsl/display/AnaglyphPassNode.js;注意该模块内部从three/webgpu与three/tsl导入依赖见 源文件首行因此它运行在 WebGPU 渲染器WebGPURendererRenderPipeline体系下而非经典WebGLRenderer EffectComposer 体系。3. 构造函数与属性速查3.1 构造函数new AnaglyphPassNode( scene : Scene, camera : Camera )scene要渲染的场景camera用于渲染场景的相机内部会基于它派生左右眼相机与虚拟屏幕。3.2 属性一览属性类型默认值说明.algorithmstringgetter/setterdubois当前色差合成算法取值见第 5 节的AnaglyphAlgorithm枚举setter 变更时自动重建左右颜色矩阵.colorModestringgetter/setterredCyan当前色卡模式取值见AnaglyphColorMode枚举同样在变更时重建矩阵.eyeSepnumber0.064瞳距eye separation世界单位。典型人类瞳距为 0.064 米64mm.planeDistancenumber0.5观察点到虚拟屏幕平面的距离零视差面。该距离处的物体正好落在屏幕表面更近的物体出现在屏幕前方负视差更远的物体在屏幕后方正视差。屏幕尺寸会由相机 FOV 与宽高比在该距离处推导确保立体视图与相机视场一致.isAnaglyphPassNodebooleanreadonlytrue类型测试标志从 构造函数源码 可以确认几个实现细节_algorithm与_colorMode是私有状态通过 getter/setter 暴露setter 中做了值变化才更新的短路判断if ( this._algorithm ! value )变更时调用_updateMatrices()左右眼颜色矩阵各自是一个 TSLuniform( new Matrix3() )节点_colorMatrixLeft/_colorMatrixRight构造函数末尾立即_updateMatrices()初始化为默认的 dubois redCyan 组合_updateMatrices()的查表逻辑为ANAGLYPH_MATRICES[ algorithm ][ colorMode ]再分别fromArray写入左右两个Matrix3L432-L439。4. 立体渲染管线父类如何工作理解AnaglyphPassNode之前先看父类 StereoCompositePassNode 的每帧流程这解释了子类为什么只需提供相机更新 合成着色器两个钩子。父类持有this.stereo new StereoCamera()——内部立体相机两个半精度浮点渲染目标_renderTargetL/_renderTargetRHalfFloatType保证 HDR 中间结果以及对应的 TSL 纹理节点_mapLeft/_mapRight一个QuadMesh用于把合成结果全屏绘制。每帧在updateBefore( frame )中执行L133-L169保存并重置渲染器状态RendererUtils.resetRendererState读取当前pixelRatio调用this.updateStereoCamera( renderer.coordinateSystem )——这正是子类重写离轴投影的切入点按渲染器当前尺寸同步两个渲染目标尺寸分别把场景用stereo.cameraL/stereo.cameraR渲染进左右两个渲染目标切回 Pass 的输出目标把QuadMesh的材质设为子类的_material并渲染——完成左右图 → 单图的合成恢复渲染器状态。dispose()会释放两个渲染目标与合成材质Pass 不再使用时应调用。5. 核心原理一离轴立体投影updateStereoCamera文档标注AnaglyphPassNodeOverridesStereoCompositePassNode#updateStereoCamera。父类的默认实现只是把coordinateSystem同步给双眼后调用this.stereo.update( this.camera )即传统 StereoCamera 的等距平行投影而AnaglyphPassNode的 重写版本L447-L502 实现了物理正确的对屏to-screen几何updateStereoCamera( coordinateSystem ) { const { stereo, camera } this; stereo.cameraL.coordinateSystem coordinateSystem; stereo.cameraR.coordinateSystem coordinateSystem; // 从相机世界矩阵取出局部坐标轴 camera.matrixWorld.extractBasis( _right, _up, _forward ); _right.normalize(); _up.normalize(); _forward.normalize(); // 1) 双眼位置沿相机右轴各偏移半个瞳距 const halfSep this.eyeSep / 2; _eyeL.copy( camera.position ).addScaledVector( _right, -halfSep ); _eyeR.copy( camera.position ).addScaledVector( _right, halfSep ); // 2) 屏幕中心相机前方 planeDistance 处 _screenCenter.copy( camera.position ).addScaledVector( _forward, -this.planeDistance ); // 3) 屏幕尺寸由 FOV 与宽高比在 planeDistance 处推导 const halfHeight this.planeDistance * Math.tan( DEG2RAD * camera.fov / 2 ); const halfWidth halfHeight * camera.aspect; // 4) 由中心点 ± 半宽/半高得到三个角点右下、左下、左上 // ... // 5) 每个眼睛相机near/far 继承主相机然后用 frameCorners 精确瞄准屏幕角点 frameCorners( stereo.cameraL, _screenBottomLeft, _screenBottomRight, _screenTopLeft, true ); stereo.cameraL.matrixWorld.compose( ... ); stereo.cameraL.matrixWorldInverse.copy( stereo.cameraL.matrixWorld ).invert(); // cameraR 同理 }关键步骤拆解眼位双眼沿相机世界系右轴对称分布间距即eyeSep与真实人眼几何一致虚拟屏幕中心在相机正前方planeDistance处半高 planeDistance · tan(fov/2)半宽 半高 ×aspect。这一步保证了立体视图覆盖范围与原相机视场完全一致也解释了文档中screen dimensions are derived from the cameras FOV and aspect ratio at this distance的说法frameCorners把每个眼睛相机的投影矩阵和朝向直接对准屏幕的三个角点。由于眼睛并不在屏幕中心正后方左右眼看到的是旋转且投影中心偏移的画面——这正是off-axis的含义也是它优于传统平行投影会导致梯形失真的地方。frameCorners() 实现 的核心是由屏幕角点构造屏幕的右轴_vr、上轴_vu、法线_vn计算眼点到屏幕各边缘的投影距离l / r / b / t然后用四元数把相机的 Y 轴对齐_vu、Z 轴对齐_vn使焦平面与屏幕共面直接手写非对称off-axis透视投影矩阵其水平/垂直中心由(rl)/(r-l)、(tb)/(t-b)项表达投影中心的偏移若estimateViewFrustum true本 Pass 传入的就是true会对camera.fov写一个保守估计值防止视锥剔除frustum culling把超出原始视场裁剪盒的离轴画面裁掉。另外注意frameCorners文档注释明确提醒它直接覆写投影矩阵忽略 fov/aspect 等标准参数调用后不要调用updateProjectionMatrix()——AnaglyphPassNode中确实没有调用这与实现是吻合的。零视差的直观含义位于planeDistance处的物体在左右眼画面中投影到同一屏幕点戴上红青眼镜后恰好贴在屏幕表面planeDistance因此就是立体效果中的屏幕深度旋钮。6. 核心原理二7 算法 × 3 色卡的颜色矩阵体系合成本质是一次逐像素的颜色加权混合。setup()中生成的 TSL 片段L510-L533const colorL this._mapLeft.sample( uvNode ); const colorR this._mapRight.sample( uvNode ); const color clamp( this._colorMatrixLeft.mul( colorL.rgb ) .add( this._colorMatrixRight.mul( colorR.rgb ) ) ); return vec4( color.rgb, max( colorL.a, colorR.a ) );即output clamp( M_left × L.rgb M_right × R.rgb )透明度取左右眼中较大者。M_left、M_right两个 3×3 矩阵完全由(algorithm, colorMode)决定全部集中在源文件的ANAGLYPH_MATRICES常量表中L114-L270。6.1 枚举取值// 算法.algorithm 的合法取值 const AnaglyphAlgorithm { TRUE: true, GREY: grey, COLOUR: colour, HALF_COLOUR: halfColour, DUBOIS: dubois, OPTIMISED: optimised, COMPROMISE: compromise }; // 色卡.colorMode 的合法取值 const AnaglyphColorMode { RED_CYAN: redCyan, MAGENTA_CYAN: magentaCyan, MAGENTA_GREEN: magentaGreen };6.2 各算法的设计取向矩阵表注释标明了每个算法对应的眼镜通道模型Paper: Left..., Right...与取舍算法左眼通道右眼通道特点TRUE纯 R亮度Lum进青通道经典真色差左眼红色、右眼青灰色右眼无色立体感清晰但色彩损失明显GREY亮度进 R亮度进 G/B纯灰度基无色彩、重影ghosting最小COLOUR纯 R纯 G、B全彩色但视网膜竞争retinal rivalry最严重易疲劳HALF_COLOUR亮度进 R全彩 G、B折中牺牲左眼色彩换取右眼全彩DUBOIS最小二乘拟合矩阵含负系数同左针对特定红青眼镜镜片透射率的最优化矩阵默认算法源文件注释注明其矩阵源自 Dubois 的经典研究资料OPTIMISED0.7G0.3B 进 R全彩 G、B改进色彩、降低视网膜竞争COMPROMISE0.439R0.447G0.148B 进 R加权 RGB 进 G/B源自 Ahtik 的 Compromise Anaglyph 研究论文矩阵 [8]色彩与立体效果的最佳平衡之一亮度系数采用 ITU-R BT.601 标准LUMINANCE { R: 0.299, G: 0.587, B: 0.114 }。6.3 三种色卡的含义redCyan红/青最通用的红青眼镜左红右青magentaCyan洋红/青矩阵中额外引入约 50% 的 B 通道共享如b: [ 0, 0, 0.5 ]或半亮度项[ 0.15, 0.29, 0.06 ]减轻纯红青的眼镜色差magentaGreen洋红/绿左眼保留部分 B、右眼只走 G 通道适配洋红/绿眼镜。矩阵的存储值得注意createMatrixPair()把输出通道 → 输入通道贡献的行主序规格转换成Matrix3.fromArray要求的列主序 9 元数组L60-L94其中LUM简写直接展开为[0.299, 0.587, 0.114]。因此你不需要自己计算矩阵——只要设置.algorithm与.colorModesetter 会自动查表更新两个 uniform 矩阵无需手动标记更新。7. 完整用法官方示例对照官方示例 examples/webgpu_display_stereo.html 同时演示了stereoPass、anaglyphPass、parallaxBarrierPass三种立体效果的切换其中 Anaglyph 相关部分可直接提炼为如下可运行模板import * as THREE from three/webgpu; import { anaglyphPass, AnaglyphAlgorithm, AnaglyphColorMode } from three/addons/tsl/display/AnaglyphPassNode.js; const camera new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 ); camera.position.z 3; const scene new THREE.Scene(); scene.add( /* ... 你的网格 ... */ ); const renderer new THREE.WebGPURenderer(); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); const renderPipeline new THREE.RenderPipeline( renderer ); // 创建 Anaglyph Pass const anaglyph anaglyphPass( scene, camera ); // 关键参数 anaglyph.eyeSep 0.064; // 瞳距世界单位典型人眼 0.064 anaglyph.planeDistance 3; // 零视差平面距离放在你希望贴屏幕的深度处 anaglyph.algorithm AnaglyphAlgorithm.DUBOIS; // 默认即为 dubois anaglyph.colorMode AnaglyphColorMode.RED_CYAN; // 默认即为 redCyan renderPipeline.outputNode anaglyph; // 把整个管线的输出交给该 Pass renderer.setAnimationLoop( () renderPipeline.render() ); window.addEventListener( resize, () { camera.aspect window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } );官方示例中可交互的参数范围也值得参考GUI 定义见 示例源码 L149-L173eyeSep滑杆范围0.001 ~ 0.15步长0.001planeDistance滑杆范围0.5 ~ 10步长0.1algorithm/colorMode下拉框枚举上表全部 7 种算法与 3 种色卡。使用建议planeDistance对齐场景焦点把零视差平面设在你希望观众聚焦的深度例如场景中心物体所在距离。示例中相机在z3、物体散布在±5的范围内故取3。eyeSep与场景尺度匹配默认值0.064假设世界单位是米若你的场景以米以外的单位建模需相应换算否则立体效果会过强或过弱。切换效果时直接改renderPipeline.outputNode并置renderPipeline.needsUpdate true即可见示例update()函数。注意运行环境该模块依赖three/webgpu与three/tsl构建入口需使用WebGPURendererRenderPipeline在经典 WebGL 路径下对应的老实现是 examples/jsm/effects/AnaglyphEffect.js基于 EffectComposer二者同名枚举思路相似但本 Pass 版本额外提供了离轴投影与更多算法矩阵。8. 相关文档与源码索引内容路径本 API 文档docs/pages/AnaglyphPassNode.html.md核心实现examples/jsm/tsl/display/AnaglyphPassNode.js父类立体复合 Pass 框架examples/jsm/tsl/display/StereoCompositePassNode.js离轴投影工具函数examples/jsm/utils/CameraUtils.js可交互官方示例examples/webgpu_display_stereo.htmlWebGL 版对应实现examples/jsm/effects/AnaglyphEffect.jsTSL 总览文档docs/pages/TSL.html.md小结AnaglyphPassNode的价值在于两点工程化封装——用frameCorners()把瞳距 零视差平面直接翻译成离轴投影矩阵让立体几何在任意相机姿态下都保持正确用查表式的algorithm × colorMode矩阵对把学术界的多种色差合成策略变成两个属性赋值即可完成切换的操作。理解其父类StereoCompositePassNode的双渲染目标 全屏合成的管线骨架后你也能以同样模式扩展出其他左右图合并类效果。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表