three.js利用卷积法如何实现物体描边效果


Posted in Javascript onNovember 27, 2019

法线延展法

网上使用法线延展法实现物体描边效果的文章比较多,这里不再描述。

但是这种方法有个缺点:当两个面的法线夹角差别较大时,两个面的描边无法完美连接。如下图所示:

three.js利用卷积法如何实现物体描边效果

卷积法

这里使用另一种方法卷积法实现物体描边效果,一般机器学习使用该方法比较多。先看效果图:

three.js利用卷积法如何实现物体描边效果three.js利用卷积法如何实现物体描边效果three.js利用卷积法如何实现物体描边效果

使用three.js具体的实现方法如下:

  1. 创建着色器材质,隐藏不需要描边的物体进行渲染,将需要描边的位置渲染成白色,其他位置渲染成黑色。
  2. 利用片源着色器计算卷积,白色是物体内部,黑色是物体外部,灰色是边框。
  3. 设置材质透明、不融合,将边框叠加到原图上,可以使用FXAA抗锯齿。

这三步就可以实现了,很简单吧。下面我们将详细介绍实现方法,不想看的可以直接去看完整实现代码:

完整代码:https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/helper/SelectHelper.js

详细的实现过程:

1. 使用three.js正常绘制场景,得到下图,这里不介绍了。

three.js利用卷积法如何实现物体描边效果

2. 创建着色器材质,隐藏所有不需要描边的物体。将需要描边的物体绘制成白色,其他地方绘制成黑色。

隐藏不需要描边的物体后,将整个场景材质替换。

renderScene.overrideMaterial = this.maskMaterial;

着色器材质:

const maskMaterial = new THREE.ShaderMaterial({
 vertexShader: MaskVertex,
 fragmentShader: MaskFragment,
 depthTest: false
});

MaskVertex:

void main() {
 gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

MaskFragment:

void main() {
 gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}

效果图:

three.js利用卷积法如何实现物体描边效果

3. 创建着色器材质进行卷积计算,每四个像素颜色求平均值得到一个像素。描边物体内部是白色,外部是黑色,物体边缘处会得到灰色。灰色就是我们所需的边框。

const edgeMaterial = new THREE.ShaderMaterial({
 vertexShader: EdgeVertex,
 fragmentShader: EdgeFragment,
 uniforms: {
  maskTexture: {
   value: this.maskBuffer.texture
  },
  texSize: {
   value: new THREE.Vector2(width, height)
  },
  color: {
   value: selectedColor
  },
  thickness: {
   type: 'f',
   value: 4
  },
  transparent: true
 },
 depthTest: false
});

其中texSize是计算卷积的canvas宽度和高度,为了让边框更平滑,可以设置为原来canvas的两倍。color是边框颜色,thickness是边框粗细。

注意,要将材质transparent设置为true。

EdgeVertex:

varying vec2 vUv;

void main() {
 vUv = uv;
 gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

EdgeFragment:

uniform sampler2D maskTexture;
uniform vec2 texSize;
uniform vec3 color;
uniform float thickness;

varying vec2 vUv;

void main() {
 vec2 invSize = thickness / texSize;
 vec4 uvOffset = vec4(1.0, 0.0, 0.0, 1.0) * vec4(invSize, invSize);

 vec4 c1 = texture2D( maskTexture, vUv + uvOffset.xy);
 vec4 c2 = texture2D( maskTexture, vUv - uvOffset.xy);
 vec4 c3 = texture2D( maskTexture, vUv + uvOffset.yw);
 vec4 c4 = texture2D( maskTexture, vUv - uvOffset.yw);
 
 float diff1 = (c1.r - c2.r)*0.5;
 float diff2 = (c3.r - c4.r)*0.5;
 
 float d = length(vec2(diff1, diff2));
 gl_FragColor = d > 0.0 ? vec4(color, 1.0) : vec4(0.0, 0.0, 0.0, 0.0);
}

效果图:

three.js利用卷积法如何实现物体描边效果

4. 创建着色器材质,将边框叠加到原来的图片上。由于FXAA比较复杂,这里使用简单的叠加方法。

着色器材质:

const copyMaterial = new THREE.ShaderMaterial({
 vertexShader: CopyVertexShader,
 fragmentShader: CopyFragmentShader,
 uniforms: {
  tDiffuse: {
   value: edgeBuffer.texture
  },
  resolution: {
   value: new THREE.Vector2(1 / width, 1 / height)
  }
 },
 transparent: true,
 depthTest: false
});

注意,transparent要设置为true,否则会把原来的图片覆盖掉。

CopyVertexShader:

varying vec2 vUv;

void main() {
 vUv = uv;
 gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}

CopyFragmentShader:

uniform float opacity;

uniform sampler2D tDiffuse;

varying vec2 vUv;

void main() {
 vec4 texel = texture2D( tDiffuse, vUv );
 gl_FragColor = opacity * texel;
}

得到最终效果图:

three.js利用卷积法如何实现物体描边效果

参考资料:

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://3water.com/article/175213.htm

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
js冒泡、捕获事件及阻止冒泡方法详细总结
May 08 Javascript
基于jQuery的JavaScript模版引擎JsRender使用指南
Dec 29 Javascript
JS更改select内option属性的方法
Oct 14 Javascript
浅谈使用MVC模式进行JavaScript程序开发
Nov 10 Javascript
js中字符串编码函数escape()、encodeURI()、encodeURIComponent()区别详解
Apr 01 Javascript
jQuery实现遍历XML节点和属性的方法示例
Apr 29 jQuery
通过函数作用域和块级作用域看javascript的作用域链
Aug 05 Javascript
vue构建动态表单的方法示例
Sep 22 Javascript
layui实现左侧菜单点击右侧内容区显示
Jul 26 Javascript
基于mpvue的简单弹窗组件mptoast使用详解
Aug 02 Javascript
微信小程序实现上传图片裁剪图片过程解析
Aug 22 Javascript
Postman无法正常返回结果问题解决
Aug 28 Javascript
Angular8引入百度Echarts进行图表分析的实现代码
Nov 27 #Javascript
vue实现浏览器全屏展示功能
Nov 27 #Javascript
js中apply和call的理解与使用方法
Nov 27 #Javascript
vue-cli在 history模式下的配置详解
Nov 26 #Javascript
js实现淘宝首页的banner栏效果
Nov 26 #Javascript
JS实现排行榜文字向上滚动轮播效果
Nov 26 #Javascript
vue实现pdf文档在线预览功能
Nov 26 #Javascript
You might like
教你IIS6的PHP最佳配置方法
2006/09/05 PHP
正则表达式语法
2006/10/09 Javascript
PHP4实际应用经验篇(9)
2006/10/09 PHP
php5 pdo新改动加载注意事项
2008/09/11 PHP
php cookies中删除的一般赋值方法
2011/05/07 PHP
解决File size limit exceeded 错误的方法
2013/06/14 PHP
PHP加密扩展库Mcrypt安装和实例
2013/11/10 PHP
ThinkPHP模板范围判断输出In标签与Range标签用法详解
2014/06/30 PHP
让GoogleCode的SVN下的HTML文件在FireFox下正常显示.
2009/05/25 Javascript
javascript倒计时功能实现代码
2012/06/07 Javascript
iframe子页面获取父页面元素的方法
2013/11/05 Javascript
jquery+ajax请求且带返回值的代码
2015/08/12 Javascript
javascript定义类和类的实现实例详解
2015/12/01 Javascript
在JavaScript中使用JSON数据
2016/02/15 Javascript
详解JavaScript异步编程中jQuery的promise对象的作用
2016/05/03 Javascript
纯js实现倒计时功能
2017/01/06 Javascript
Vue.js bootstrap前端实现分页和排序
2017/03/10 Javascript
Vue + Webpack + Vue-loader学习教程之功能介绍篇
2017/03/14 Javascript
vue-quill-editor实现图片上传功能
2017/08/08 Javascript
Vue 进入/离开动画效果
2017/12/26 Javascript
Vue+Jwt+SpringBoot+Ldap完成登录认证的示例代码
2018/05/21 Javascript
layerui代码控制tab选项卡,添加,关闭的实例
2019/09/04 Javascript
jquery实现商品sku多属性选择功能(商品详情页)
2019/12/20 jQuery
原生JS实现相邻月份日历
2020/10/13 Javascript
TensorFlow实现卷积神经网络CNN
2018/03/09 Python
python+pyqt5实现24点小游戏
2019/01/24 Python
使用Python调取任意数字资产钱包余额功能
2019/08/15 Python
python 两种方法修改文件的创建时间、修改时间、访问时间
2020/09/26 Python
EJB需直接实现它的业务接口或Home接口吗,请简述理由
2016/11/23 面试题
车工岗位职责
2013/11/26 职场文书
白酒市场营销方案
2014/02/25 职场文书
好习惯伴我成长演讲稿
2014/05/21 职场文书
求职信范文怎么写
2015/03/19 职场文书
2015年社区创卫工作总结
2015/04/21 职场文书
2015年治庸问责工作总结
2015/07/27 职场文书
python实现股票历史数据可视化分析案例
2021/06/10 Python