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 相关文章推荐
jquery 表单取值常用代码
Dec 22 Javascript
jquery form表单提交插件asp.net后台中文解码
Jun 12 Javascript
JavaScript内核之基本概念
Oct 21 Javascript
jQuery登陆判断简单实现代码
Apr 21 Javascript
js在IE与firefox的差异集锦
Nov 11 Javascript
基于css3新属性transform及原生js实现鼠标拖动3d立方体旋转
Jun 12 Javascript
JS绘制微信小程序画布时钟
Dec 24 Javascript
微信小程序之圆形进度条实现思路
Feb 22 Javascript
vue.js实现的幻灯片功能示例
Jan 18 Javascript
JS面向对象编程基础篇(二) 封装操作实例详解
Mar 03 Javascript
利用vue3+ts实现管理后台(增删改查)
Oct 30 Javascript
vue-cli4.0多环境配置变量与模式详解
Dec 30 Vue.js
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
ThinkPHP3.0略缩图不能保存到子目录的解决方法
2012/09/30 PHP
php对二维数组进行相关操作(排序、转换、去空白等)
2015/11/04 PHP
php可变长参数处理函数详解
2017/02/22 PHP
让元素在网页中可拖动示例代码
2013/08/13 Javascript
jquery自定义类似$.ajax()的方法实现代码
2013/08/13 Javascript
js实现鼠标移到链接文字弹出一个提示层的方法
2015/05/11 Javascript
jquery.map()方法的使用详解
2015/07/09 Javascript
最棒的Angular2表格控件
2016/08/10 Javascript
基于JS实现横线提示输入验证码随验证码输入消失(js验证码的实现)
2016/10/27 Javascript
JS调用Android、Ios原生控件
2017/01/06 Javascript
AngularJs实现聊天列表实时刷新功能
2017/06/15 Javascript
JS使用tofixed与round处理数据四舍五入的区别
2017/10/25 Javascript
使用async-validator编写Form组件的方法
2018/01/10 Javascript
jQuery实现带右侧索引功能的通讯录示例【附源码下载】
2018/04/17 jQuery
vue中Element-ui 输入银行账号每四位加一个空格的实现代码
2018/09/14 Javascript
原生javascript自定义input[type=radio]效果示例
2019/08/27 Javascript
Elementui表格组件+sortablejs实现行拖拽排序的示例代码
2019/08/28 Javascript
JavaScript实现省市区三级联动
2020/02/13 Javascript
JS猜数字游戏实例讲解
2020/06/30 Javascript
按日期打印Python的Tornado框架中的日志的方法
2015/05/02 Python
python清除字符串中间空格的实例讲解
2018/05/11 Python
django 中的聚合函数,分组函数,F 查询,Q查询
2019/07/25 Python
Python unittest单元测试框架及断言方法
2020/04/15 Python
PyCharm2019.3永久激活破解详细图文教程,亲测可用(不定期更新)
2020/10/29 Python
python 实现"神经衰弱"翻牌游戏
2020/11/09 Python
使用phonegap操作数据库的实现方法
2017/03/31 HTML / CSS
Lancome兰蔻官方旗舰店:来自法国的世界知名美妆品牌
2018/06/14 全球购物
办公室内勤工作职责
2013/12/11 职场文书
员工拓展培训方案
2014/02/15 职场文书
感情真挚的毕业生求职信
2014/07/19 职场文书
大学生创业计划书
2019/06/24 职场文书
演讲开头怎么书写?
2019/08/06 职场文书
nginx如何将http访问的网站改成https访问
2021/03/31 Servers
Python爬虫之爬取哔哩哔哩热门视频排行榜
2021/04/28 Python
Python写情书? 10行代码展示如何把情书写在她的照片里
2022/04/21 Python
六个好看实用的 HTML + CSS 后台登录入口页面
2022/04/28 HTML / CSS