three.js利用卷积法如何实现物体描边效果
法线延展法
网上使用法线延展法实现物体描边效果的文章比较多,这里不再描述。
但是这种方法有个缺点:当两个面的法线夹角差别较大时,两个面的描边无法完美连接。如下图所示:
卷积法
这里使用另一种方法卷积法实现物体描边效果,一般机器学习使用该方法比较多。先看效果图:
使用three.js具体的实现方法如下:
- 创建着色器材质,隐藏不需要描边的物体进行渲染,将需要描边的位置渲染成白色,其他位置渲染成黑色。
- 利用片源着色器计算卷积,白色是物体内部,黑色是物体外部,灰色是边框。
- 设置材质透明、不融合,将边框叠加到原图上,可以使用FXAA抗锯齿。
这三步就可以实现了,很简单吧。下面我们将详细介绍实现方法,不想看的可以直接去看完整实现代码:
完整代码:https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/helper/SelectHelper.js
详细的实现过程:
1.使用three.js正常绘制场景,得到下图,这里不介绍了。
2.创建着色器材质,隐藏所有不需要描边的物体。将需要描边的物体绘制成白色,其他地方绘制成黑色。
隐藏不需要描边的物体后,将整个场景材质替换。
renderScene.overrideMaterial=this.maskMaterial;
着色器材质:
constmaskMaterial=newTHREE.ShaderMaterial({ vertexShader:MaskVertex, fragmentShader:MaskFragment, depthTest:false });
MaskVertex:
voidmain(){ gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }
MaskFragment:
voidmain(){ gl_FragColor=vec4(1.0,1.0,1.0,1.0); }
效果图:
3.创建着色器材质进行卷积计算,每四个像素颜色求平均值得到一个像素。描边物体内部是白色,外部是黑色,物体边缘处会得到灰色。灰色就是我们所需的边框。
constedgeMaterial=newTHREE.ShaderMaterial({ vertexShader:EdgeVertex, fragmentShader:EdgeFragment, uniforms:{ maskTexture:{ value:this.maskBuffer.texture }, texSize:{ value:newTHREE.Vector2(width,height) }, color:{ value:selectedColor }, thickness:{ type:'f', value:4 }, transparent:true }, depthTest:false });
其中texSize是计算卷积的canvas宽度和高度,为了让边框更平滑,可以设置为原来canvas的两倍。color是边框颜色,thickness是边框粗细。
注意,要将材质transparent设置为true。
EdgeVertex:
varyingvec2vUv; voidmain(){ vUv=uv; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }
EdgeFragment:
uniformsampler2DmaskTexture; uniformvec2texSize; uniformvec3color; uniformfloatthickness; varyingvec2vUv; voidmain(){ vec2invSize=thickness/texSize; vec4uvOffset=vec4(1.0,0.0,0.0,1.0)*vec4(invSize,invSize); vec4c1=texture2D(maskTexture,vUv+uvOffset.xy); vec4c2=texture2D(maskTexture,vUv-uvOffset.xy); vec4c3=texture2D(maskTexture,vUv+uvOffset.yw); vec4c4=texture2D(maskTexture,vUv-uvOffset.yw); floatdiff1=(c1.r-c2.r)*0.5; floatdiff2=(c3.r-c4.r)*0.5; floatd=length(vec2(diff1,diff2)); gl_FragColor=d>0.0?vec4(color,1.0):vec4(0.0,0.0,0.0,0.0); }
效果图:
4.创建着色器材质,将边框叠加到原来的图片上。由于FXAA比较复杂,这里使用简单的叠加方法。
着色器材质:
constcopyMaterial=newTHREE.ShaderMaterial({ vertexShader:CopyVertexShader, fragmentShader:CopyFragmentShader, uniforms:{ tDiffuse:{ value:edgeBuffer.texture }, resolution:{ value:newTHREE.Vector2(1/width,1/height) } }, transparent:true, depthTest:false });
注意,transparent要设置为true,否则会把原来的图片覆盖掉。
CopyVertexShader:
varyingvec2vUv; voidmain(){ vUv=uv; gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0); }
CopyFragmentShader:
uniformfloatopacity; uniformsampler2DtDiffuse; varyingvec2vUv; voidmain(){ vec4texel=texture2D(tDiffuse,vUv); gl_FragColor=opacity*texel; }
得到最终效果图:
参考资料:
1.描边实现完整代码:https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/helper/SelectHelper.js
2.基于three.js的开源三维场景编辑器:https://github.com/tengge1/ShadowEditor
3.three.js后期处理描边:https://threejs.org/examples/
4.卷积工作原理:https://www.zhihu.com/question/39022858?sort=created
5.法线延展法实现物体描边:https://www.nhooo.com/article/175213.htm
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。