ARTICLE DETAIL

资讯详情

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

three.js OutlinePass 详解:为选中 3D 物体渲染高质量轮廓描边

three.js OutlinePass 详解:为选中 3D 物体渲染高质量轮廓描边 three.js OutlinePass 详解为选中 3D 物体渲染高质量轮廓描边【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsOutlinePass 是 three.js 官方提供的一款后处理post-processingPass用于在渲染完成后为选中的一组 3D 物体绘制醒目的轮廓描边/发光效果。它非常适合交互式场景中的物体高亮需求例如鼠标悬停提示、编辑器选中等。本文以仓库内 OutlinePass 官方文档 为主线结合其 源码实现 与 官方交互示例从构造参数、可调属性、方法签名到内部多缓冲渲染管线逐层讲透如何把一个可用的描边效果集成进 EffectComposer。OutlinePass 是什么属于 Pass 体系的一个 addonOutlinePass 继承自后处理基类 Pass该类在 Pass 文档 中被说明为所有后处理 Pass 的抽象基类只在 WebGLRenderer 的后处理流程中生效。作为 addon扩展模块它不会自动打包进 three.js 核心构建产物而必须显式导入import { OutlinePass } from three/addons/postprocessing/OutlinePass.js;仓库内的实际模块位于 examples/jsm/postprocessing/OutlinePass.js同时它作为官方 addon 被集中导出在 examples/jsm/Addons.js其中export * from ./postprocessing/OutlinePass.js;。使用模块化引入后再配合 EffectComposer管理一组有序执行的 Pass 链把OutlinePass加入渲染管线就能在每帧合成后处理结果时自动应用描边。基础用法接入 EffectComposer官方文档给出的最小接入代码如下const resolution new THREE.Vector2( window.innerWidth, window.innerHeight ); const outlinePass new OutlinePass( resolution, scene, camera ); composer.addPass( outlinePass );需要注意两点composer必须已经通过new EffectComposer( renderer )创建且通常先用RenderPass把场景渲染到缓冲参见 RenderPass 文档再用OutputPass完成色彩空间/tone mapping 输出参见 OutputPass 文档。构造时若未传入第四个参数selectedObjects描边集合为空Pass 不会产生任何绘制见下文源码逻辑。构造函数与参数构造函数签名如下见 OutlinePass.jsnew OutlinePass( resolution : Vector2, scene : Scene, camera : Camera, selectedObjects : Array.Object3D )四个参数的语义参数类型说明resolutionVector2效果分辨率。不传时内部回退为(256,256)源码中resolution ! undefined ? new Vector2( resolution.x, resolution.y ) : new Vector2( 256, 256 )。通常应传渲染器当前画布尺寸sceneScene需要进行描边判断的场景cameraCamera当前相机用于把对象投影到深度缓冲、计算轮廓selectedObjectsArray.Object3D需要被描边的对象数组可省略默认[]从源码 constructor 可确认构造期间它会为第 4 个参数做selectedObjects ! undefined ? selectedObjects : []的保护性默认因此即便后补对象也可通过下面的selectedObjects属性随时替换。属性详解从默认值到视觉调优以下属性与官方文档保持一致默认值与源码中逐一对应视觉强度类.edgeStrength : number—— 边缘强度默认3。它直接作为 overlay 合成阶段的乘法因子。.edgeThickness : number—— 边缘厚度默认1。作为第一个可分离模糊的卷积核半径kernelRadius注入着色器。.edgeGlow : number—— 辉光强度默认0。作为第二层边缘纹理的放大系数叠加可实现流光/辉光效果官方文档描述为可用于动画化的 glow/pulse 效果。.downSampleRatio : number—— 降采样比例默认2。官方文档特别强调效果可以远低于 beauty pass 的分辨率进行渲染——这也是该 Pass 高性能的关键所有中间运算都在半分辨率甚至四分之一分辨率缓冲中进行。颜色类.visibleEdgeColor : Color—— 可见边缘颜色默认(1,1,1)白色即物体正面轮廓的颜色。.hiddenEdgeColor : Color—— 隐藏边缘颜色默认(0.1,0.04,0.02)近黑色。当一个物体的边缘被其他未被选中的物体遮挡时这段藏在后面的轮廓会用该颜色描画从而区分可见/被遮挡边缘。.pulsePeriod : number—— 脉冲周期默认0。大于 0 时启用呼吸/闪烁效果详见下文脉冲动画。图案与选择集.usePatternTexture : boolean—— 是否使用图案纹理高亮选中对象默认false。.patternTexture : Texture—— 图案纹理默认null。官方文档明确注明需要同时把usePatternTexture置为true才会生效。示例 webgl_postprocessing_outline.html 中加载纹理后会设置texture.wrapS/texture.wrapT RepeatWrapping以便重复平铺。.selectedObjects : Array.Object3D—— 待描边对象集合默认[]。交互场景中可随鼠标拾取结果实时赋值。.resolution : Vector2—— 效果分辨率默认(256,256)。内部引用类.renderScene : Object—— 构造时保存的场景引用。.renderCamera : Object—— 构造时保存的相机引用。源码中还声明了若干与 Pass 生命周期相关的继承属性详见 Pass.jsenabled默认true、needsSwap默认true但 OutlinePass 在构造尾部覆写为false因为它在结束时直接渲染进readBuffer、clear默认false、renderToScreen默认false链中最后一个 Pass 会被自动渲染到屏幕。脉冲动画的内部实现pulsePeriod 0时源码 render() 会在每帧计算一个统一缩放因子并同时作用于可见/隐藏边缘色const scalar ( 1 0.25 ) / 2 Math.cos( performance.now() * 0.01 / this.pulsePeriod ) * ( 1.0 - 0.25 ) / 2; this.tempPulseColor1.multiplyScalar( scalar ); this.tempPulseColor2.multiplyScalar( scalar );也就是说边缘亮度会在0.251.0倍区间内随Math.cos往复振荡pulsePeriod越小振荡越快。示例中用 GUI 把pulsePeriod限定在0.05范围内调节0表示关闭脉冲。方法签名与行为.dispose()dispose() : void释放本实例占用的全部 GPU 资源。从 源码实现 可看到它逐一 dispose 了 7 个WebGLRenderTargetmask、depth、两个降采样缓冲、两个模糊缓冲、两个边缘缓冲以及 8 个材质/着色器资源depthMaterial、prepareMaskMaterial、edgeDetectionMaterial、两个模糊材质、overlayMaterial、materialCopy和内部全屏四边形_fsQuad。官方文档提醒当 Pass 不再使用时务必调用此方法。它覆写了 Pass#dispose。.setSize( width, height )setSize( width : number, height : number ) : void设置 Pass 尺寸。与普通 Pass 不同OutlinePass 内部有三级分辨率缓冲因此setSize不只是简单 resize见 源码实现全分辨率缓冲renderTargetMaskBuffer、renderTargetDepthBuffer直接取width/height半分辨率缓冲renderTargetMaskDownSampleBuffer、renderTargetBlurBuffer1、renderTargetEdgeBuffer1取width / downSampleRatio并 round四分之一分辨率缓冲renderTargetBlurBuffer2、renderTargetEdgeBuffer2在上述基础上再/2。同时它会把新尺寸写回两个模糊材质的texSizeuniform。窗口 resize 时应随 composer 一起调用它。它覆写了 Pass#setSize。.render( renderer, writeBuffer, readBuffer, deltaTime, maskActive )render( renderer : WebGLRenderer, writeBuffer : WebGLRenderTarget, readBuffer : WebGLRenderTarget, deltaTime : number, maskActive : boolean ) : void执行描边。参数语义renderer为渲染器writeBuffer是本 Pass 的渲染目标readBuffer保存上一个 Pass 的结果供本 Pass 读取deltaTime为帧间隔秒数maskActive表示是否处于模板掩码stencil mask激活状态。它覆写了 Pass#render。该方法的具体执行逻辑详见下文内部管线。内部工作管线多缓冲深度比较算法OutlinePass 的思路并不是对整幅画面做边缘检测而是利用被选中对象 vs 其余场景的深度差异只描出选中物体的轮廓。从 render() 的源码注释可以还原出完整步骤渲染非选中物体的深度先把选中对象临时隐藏_changeVisibilityOfSelectedObjects( false )清空场景background用MeshDepthMaterial配置了side DoubleSide、depthPacking RGBADepthPacking、blending NoBlending作为scene.overrideMaterial把场景渲染进全分辨率renderTargetDepthBuffer。生成选中物体的 mask恢复选中对象可见、隐藏其余对象注意_changeVisibilityOfNonSelectedObjects只处理isMesh/isSpritePoints/Line/Line2一律被强制设为不可见以免影响描边计算——这是源码注释明确声明的限制。随后用prepareMaskMaterial在片元中把当前片元的投影深度与非选中物体的深度纹理做比较texture2DProj( depthTexture, projTexCoord )大于存储深度则判定在后方。比较结果写入全分辨率renderTargetMaskBuffer红色通道记录对象剪影、绿色通道记录前后遮挡可见性并借助textureMatrix0.5缩放 相机投影/逆世界矩阵完成屏幕空间重投影最终该临时状态全部恢复。降采样用CopyShader把 mask 拷贝进半分辨率renderTargetMaskDownSampleBuffer。边缘检测edgeDetectionMaterial在 mask 的 4-邻域上做红通道梯度计算梯度模长即边缘强度再结合绿通道的可见性因子在visibleEdgeColor与hiddenEdgeColor之间二选一结果写入半分辨率renderTargetEdgeBuffer1。两级可分离高斯模糊半分辨率上做 X/Y 两个方向的一维高斯模糊kernelRadius edgeThicknesssigma kernelRadius / 2循环采样次数由编译期宏MAX_RADIUS限定代码中MAX_EDGE_THICKNESS 4、MAX_EDGE_GLOW 4采样步长delta direction * invSize * kernelRadius / MAX_RADIUS模糊结果写入renderTargetBlurBuffer1再回写renderTargetEdgeBuffer1随后把结果再次降半到四分之一分辨率做第二级 X/Y 模糊得到更发散的辉光层renderTargetEdgeBuffer2。叠加合成用overlayMaterial在readBuffer上以AdditiveBlending做透明叠加把两层边缘与 mask、可选 pattern 合成并将readBuffer作为本 Pass 的最终输出目标因此needsSwap false。合成公式对应片段着色器edgeValue edgeTexture1 edgeTexture2 * edgeGlowedgeGlow控制第二层辉光占比finalColor edgeStrength * maskColor.r * edgeValue以 mask 红通道为门控形成贴边外扩的描边当usePatternTexture为真finalColor visibilityFactor * (1.0 - maskColor.r) * (1.0 - patternColor.r)其中 pattern 采样坐标被放大为6.0 * vUv即纹理在屏幕上重复约 6 次。若maskActive为真则在渲染开始前关闭模板测试、结束时恢复同时方法开头会暂存并恢复renderer.autoClear、clearColor 与 clearAlpha。当selectedObjects.length 0时整个复杂流程被跳过仅在renderToScreen时把readBuffer拷到屏幕。一个完整可运行的交互示例仓库官方示例 examples/webgl_postprocessing_outline.html 完整演示了鼠标拾取 → 实时描边 → GUI 调节的典型用法。核心片段整理如下import * as THREE from three; import { EffectComposer } from three/addons/postprocessing/EffectComposer.js; import { RenderPass } from three/addons/postprocessing/RenderPass.js; import { OutlinePass } from three/addons/postprocessing/OutlinePass.js; import { OutputPass } from three/addons/postprocessing/OutputPass.js; // 1. 组装后处理链 composer new EffectComposer( renderer ); composer.addPass( new RenderPass( scene, camera ) ); outlinePass new OutlinePass( new THREE.Vector2( window.innerWidth, window.innerHeight ), scene, camera ); composer.addPass( outlinePass ); composer.addPass( new OutputPass() ); // 负责色彩空间 / tone mapping 输出 // 2. 通过 Raycaster 拾取并切换选中对象 function checkIntersection() { raycaster.setFromCamera( mouse, camera ); const intersects raycaster.intersectObject( scene, true ); if ( intersects.length 0 ) { outlinePass.selectedObjects [ intersects[ 0 ].object ]; } } renderer.domElement.addEventListener( pointermove, onPointerMove ); // 3. GUI 中实时修改描边参数 gui.add( params, edgeStrength, 0.01, 10 ) .onChange( v outlinePass.edgeStrength Number( v ) ); gui.add( params, edgeGlow, 0.0, 1 ) .onChange( v outlinePass.edgeGlow Number( v ) ); gui.add( params, edgeThickness, 1, 4 ) .onChange( v outlinePass.edgeThickness Number( v ) ); gui.add( params, pulsePeriod, 0.0, 5 ) .onChange( v outlinePass.pulsePeriod Number( v ) ); gui.add( params, usePatternTexture ) .onChange( v outlinePass.usePatternTexture v ); gui.addColor( conf, visibleEdgeColor ) .onChange( v outlinePass.visibleEdgeColor.set( v ) ); gui.addColor( conf, hiddenEdgeColor ) .onChange( v outlinePass.hiddenEdgeColor.set( v ) ); // 4. 每帧用 composer 渲染代替 renderer.render renderer.setAnimationLoop( () { controls.update(); composer.render(); } );注意示例中的 GUI 取值范围edgeStrength0.01–10、edgeGlow0–1、edgeThickness1–4、pulsePeriod0–5是演示参数的调节区间而非源码硬性限制实际项目中可按视觉效果自由试探。示例场景加载 OBJ 树模型与随机散布的球体并把鼠标移过的物体实时加入outlinePass.selectedObjects。实战注意事项与性能要点支持的对象类型有限制从 render() 的可见性处理可推断OutlinePass 只对Mesh与Sprite生成描边场景中的Points/Line/Line2即使可见也会在处理期间被强制隐藏以免污染深度比较结果。性能优化核心是降采样downSampleRatio默认2意味着中间 5 个缓冲都在半分辨率或四分之一分辨率运行换来显著的开销下降。若对象轮廓出现明显锯齿或过细可尝试把downSampleRatio降为1以获得全分辨率运算代价是更高的带宽与 fillrate 开销。释放资源场景销毁、切换页面或移除后处理时务必调用outlinePass.dispose()源码会同步释放全部 render target 与材质避免 GPU 内存泄漏。需要正确关闭 stencil/模板状态当链上存在MaskPass等模板用法使maskActive为真时OutlinePass 会在处理期间临时关闭模板测试并在结束后恢复这是保证与其它 Pass 兼容的关键细节。与交互系统配合selectedObjects是一个公开数组直接整体替换即可。注意渲染开始时会临时修改scene.background、scene.overrideMaterial与对象visible方法返回前均已恢复因此不会污染主渲染循环的其它逻辑。小结与延伸阅读OutlinePass 用非选中物体深度 选中物体深度比较的方式精确生成仅属于选中对象的轮廓再通过降采样、边缘检测、两级可分离高斯模糊与加法合成在性能可控的前提下输出带辉光的描边。它的默认参数edgeStrength3、edgeThickness1、downSampleRatio2、白色可见边已能在多数场景中直接获得良好效果剩余属性hiddenEdgeColor、pulsePeriod、usePatternTexture/patternTexture、edgeGlow则为选中反馈提供了丰富的视觉风格空间。进一步深入可以继续阅读仓库内以下资料本文主题文档docs/pages/OutlinePass.html.md基类与生命周期examples/jsm/postprocessing/Pass.js、Pass 文档后处理编排器examples/jsm/postprocessing/EffectComposer.js、EffectComposer 文档完整交互示例examples/webgl_postprocessing_outline.html对应截图见 webgl_postprocessing_outline.jpg【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表