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图片自动轮换效果实现思路附截图
Apr 30 Javascript
JavaScript字符串对象的concat方法实例(用于连接两个或多个字符串)
Oct 16 Javascript
使表格的标题列可左右拉伸jquery插件封装
Nov 24 Javascript
JS实现的4种数字千位符格式化方法分享
Mar 02 Javascript
js实现的页面矩阵图形变换特效
Jan 26 Javascript
Node.js插件安装图文教程
May 06 Javascript
AngularJS入门教程之路由与多视图详解
Aug 19 Javascript
js实现开启密码大写提示
Dec 21 Javascript
Avalonjs 实现简单购物车功能(实例代码)
Feb 07 Javascript
JavaScript中最常用的10种代码简写技巧总结
Jun 28 Javascript
JS轮播图实现简单代码
Feb 19 Javascript
React组件refs的使用详解
Feb 09 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
php通过ajax实现双击table修改内容
2014/04/28 PHP
php返回相对时间(如:20分钟前,3天前)的方法
2015/04/14 PHP
PHP分页初探 一个最简单的PHP分页代码的简单实现
2016/06/21 PHP
PHP面向对象程序设计内置标准类,普通数据类型转为对象类型示例
2019/06/12 PHP
js 分栏效果实现代码
2009/08/29 Javascript
SlideView 图片滑动(扩展/收缩)展示效果
2010/08/01 Javascript
A标签中通过href和onclick传递的this对象实现思路
2013/04/19 Javascript
jquery清空表单数据示例分享
2014/02/13 Javascript
node.js中的http.response.write方法使用说明
2014/12/14 Javascript
Backbone.js的一些使用技巧
2015/07/01 Javascript
JS实现支持Ajax验证的表单插件
2016/03/24 Javascript
关于验证码在IE中不刷新的快速解决方法
2016/09/23 Javascript
JavaScript数据结构链表知识详解
2016/11/21 Javascript
详解nodejs微信公众号开发——2.自动回复
2017/04/10 NodeJs
iscroll.js滚动加载实例详解
2017/07/18 Javascript
vue ssr 指南详读
2018/06/29 Javascript
Angular中的ng-template及angular 使用ngTemplateOutlet 指令的方法
2018/08/08 Javascript
利用原生的JavaScript实现简单拼图游戏
2018/11/18 Javascript
Nuxt.js开启SSR渲染的教程详解
2018/11/30 Javascript
vue+element项目中过滤输入框特殊字符小结
2019/08/07 Javascript
js实现简单页面全屏
2019/09/17 Javascript
React Native 混合开发多入口加载方式详解
2019/09/23 Javascript
ES5新增数组的实现方法
2020/05/12 Javascript
在NodeJs中使用node-schedule增加定时器任务的方法
2020/06/08 NodeJs
[01:45]DOTA2众星出演!DSPL刀塔次级职业联赛宣传片
2014/11/21 DOTA
python处理大数字的方法
2015/05/27 Python
遗传算法之Python实现代码
2017/10/10 Python
python利用faker库批量生成测试数据
2020/10/15 Python
15款Python编辑器的优缺点,别再问我“选什么编辑器”啦
2020/10/19 Python
主管职责范文
2013/11/09 职场文书
2014银行领导班子四风对照检查材料思想汇报
2014/09/25 职场文书
2015新年寄语大全
2014/12/08 职场文书
集团财务总监岗位职责
2015/04/03 职场文书
幼儿园教学反思范文
2016/03/02 职场文书
oracle连接ODBC sqlserver数据源的详细步骤
2021/07/25 Oracle
windows server2012 R2下安装PaddleOCR服务的的详细步骤
2022/09/23 Servers