思路 根据模型的顶点,构建线条 这是我们初始想到的办法,实际上并不合适,性能差,效果也达不到预期。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 export function renderFrameMesh (obj: Mesh ) { const edges = new EdgesGeometry (obj.geometry ); const color = new Color (1 , 0 , 0 ); const lineBaseMaterial = new LineBasicMaterial ({ color, side : FrontSide , linecap : 'round' , linejoin : 'round' , }); const line = new LineSegments (edges, lineBaseMaterial); const objWorldPosition = new Vector3 (); const objWorldQuaternion = new Quaternion (); obj.getWorldPosition (objWorldPosition); obj.getWorldQuaternion (objWorldQuaternion); line.position .x = objWorldPosition.x ; line.position .y = objWorldPosition.y ; line.position .z = objWorldPosition.z ; line.quaternion .x = objWorldQuaternion.x ; line.quaternion .y = objWorldQuaternion.y ; line.quaternion .z = objWorldQuaternion.z ; line.quaternion .w = objWorldQuaternion.w ; return line; }
卷积 three.js使用卷积法实现物体描边效果 - tengge - 博客园
这个方法尝试了下,感觉还是麻烦了一些,然后发现官方居然有描边的Demo,就先放弃这个方案了。
后来发现官方的描边Demo,和这个原理是很类似的,二者的Shader代码都很像。
直接使用官方的OutlinePass 参考官方这个示例:
https://threejs.org/examples/#webgl_postprocessing_outline
这个方法是最简单的,也是我们最终使用的方案。
该方案不用我们自己手动分层处理物体,并且还有呼吸效果。
看了下源码,发现和第二种卷积的方式很类似,也是mask + edge,整体流程如下:
每一个操作都会执行类似这样的流程代码:
1 2 3 4 5 6 7 8 9 this .fsQuad .material = this .edgeDetectionMaterial ;this .edgeDetectionMaterial .uniforms [ 'maskTexture' ].value = this .renderTargetMaskDownSampleBuffer .texture ;this .edgeDetectionMaterial .uniforms [ 'texSize' ].value .set ( this .renderTargetMaskDownSampleBuffer .width , this .renderTargetMaskDownSampleBuffer .height );this .edgeDetectionMaterial .uniforms [ 'visibleEdgeColor' ].value = this .tempPulseColor1 ;this .edgeDetectionMaterial .uniforms [ 'hiddenEdgeColor' ].value = this .tempPulseColor2 ; renderer.setRenderTarget ( this .renderTargetEdgeBuffer1 ); renderer.clear ();this .fsQuad .render ( renderer );
这里应用的材质都是着色器,比如edgeDetectionMaterial、separableBlurMaterial1等。
混合 代码也很有参考性:
1 2 3 4 5 6 7 8 9 this .fsQuad .material = this .overlayMaterial ;this .overlayMaterial .uniforms [ 'maskTexture' ].value = this .renderTargetMaskBuffer .texture ;this .overlayMaterial .uniforms [ 'edgeTexture1' ].value = this .renderTargetEdgeBuffer1 .texture ;this .overlayMaterial .uniforms [ 'edgeTexture2' ].value = this .renderTargetEdgeBuffer2 .texture ;this .overlayMaterial .uniforms [ 'patternTexture' ].value = this .patternTexture ;this .overlayMaterial .uniforms [ 'edgeStrength' ].value = this .edgeStrength ;this .overlayMaterial .uniforms [ 'edgeGlow' ].value = this .edgeGlow ;this .overlayMaterial .uniforms [ 'usePatternTexture' ].value = this .usePatternTexture ;
思考:我们可以将常用特效,封装为Pass类进行复用。这样积累多了,我们的开发效率就上来了。并且今后还可以应用到可视化编辑器中,以类似Unity的组件化 的形式附加到场景中。
如何理解WebGLRenderTarget? 官方定义:
A render target is a buffer where the video card draws pixels for a scene that is being rendered in the background. It is used in different effects, such as applying postprocessing to a rendered image before displaying it on the screen.
再来看看程序中是如何初始化的:
1 2 3 4 5 6 7 8 9 10 let resx = Math .round ( width / this .downSampleRatio );let resy = Math .round ( height / this .downSampleRatio ) ;this .renderTargetBlurBuffer1 = new WebGLRenderTarget ( resx, resy );this .renderTargetBlurBuffer1 .texture .name = 'OutlinePass.blur1' ;this .renderTargetBlurBuffer1 .texture .generateMipmaps = false ;this .renderTargetBlurBuffer2 = new WebGLRenderTarget ( Math .round ( resx / 2 ), Math .round ( resy / 2 ) );this .renderTargetBlurBuffer2 .texture .name = 'OutlinePass.blur2' ;this .renderTargetBlurBuffer2 .texture .generateMipmaps = false ;
所以RenderTarget是一个GPU的帧缓冲,或者直接叫离屏渲染 更好理解一些。我们可以设置其大小。
通过WebGL渲染目标WebGLRenderTarget的纹理属性.texture可以获得WebGL渲染器的渲染结果,该属性返回的结果是一个纹理对象THREE.Texture,可以作为材质Material对象颜色贴图属性map的属性。
参考资料:Three.js WebGLRenderTarget(离屏渲染)_郭隆邦技术博客的博客-CSDN博客_three. webglrendertarget
如何理解卷积? 参考:
https://www.zhihu.com/question/22298352
这就是为什么代码中有一些二分之一、四分之一的缘故。
所谓两个函数的卷积,本质上就是先将一个函数翻转,然后进行滑动叠加。
在图像处理的中,卷积处理的结果,其实就是把每个像素周边的,甚至是整个图像的像素都考虑进来,对当前像素进行某种加权处理。所以说,“积”是全局概念,或者说是一种“混合”,把两个函数在时间或者空间上进行混合。