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的树控件实现代码(asp.net+json)
Jul 11 Javascript
JavaScript对IE操作的经典代码(推荐)
Mar 10 Javascript
html的DOM中Event对象onabort事件用法实例
Jan 21 Javascript
js简单倒计时实现代码
Apr 30 Javascript
json格式的javascript对象用法分析
Jul 04 Javascript
bootstrap模态框示例代码分享
May 17 Javascript
vue编译打包本地查看index文件的方法
Feb 23 Javascript
JS求Number类型数组中最大元素方法
Apr 08 Javascript
webpack 从指定入口文件中提取公共文件的方法
Nov 13 Javascript
javascript异步编程的六种方式总结
May 17 Javascript
JavaScript数组类型Array相关的属性与方法详解
Sep 08 Javascript
vue3.0自定义指令(drectives)知识点总结
Dec 27 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
PHP中用header图片地址 简单隐藏图片源地址
2008/04/09 PHP
解析centos中Apache、php、mysql 默认安装路径
2013/06/25 PHP
两种php实现图片上传的方法
2016/01/22 PHP
PHP实现长文章分页实例代码(附源码)
2016/02/03 PHP
PHP标准库(PHP SPL)详解
2019/03/16 PHP
用于自动添加Digg This!按钮的JavaScript
2006/12/23 Javascript
nodejs教程之入门
2014/11/21 NodeJs
node.js中的console用法总结
2014/12/15 Javascript
jQuery AjaxUpload 上传图片代码
2016/02/02 Javascript
详细讲解JavaScript中的this绑定
2016/10/10 Javascript
详解JavaScript中return的用法
2017/05/08 Javascript
Angularjs上传文件组件flowjs功能
2017/08/07 Javascript
详解vue几种主动刷新的方法总结
2019/02/19 Javascript
解决layui下拉框监听问题(监听不到值的变化)
2019/09/28 Javascript
[01:14]DOTA2亚洲邀请赛小组赛赛前花絮
2017/03/27 DOTA
[00:10]DOTA2 TI9勇士令状明日上线
2019/05/07 DOTA
详解Django中的权限和组以及消息
2015/07/23 Python
简单的python后台管理程序
2017/04/13 Python
Python3中条件控制、循环与函数的简易教程
2017/11/21 Python
利用python打开摄像头及颜色检测方法
2018/08/03 Python
python实现飞机大战游戏
2020/10/26 Python
Python调用graphviz绘制结构化图形网络示例
2019/11/22 Python
Python Selenium操作Cookie的实例方法
2021/02/28 Python
HTML如何让IMG自动适应DIV容器大小的实现方法
2020/02/25 HTML / CSS
Grow Gorgeous美国官网:只要八天,体验唤醒毛囊后新生的茂密秀发
2018/06/04 全球购物
阿姆斯特丹城市卡:Amsterdam Pass
2019/12/01 全球购物
初中体育教学反思
2014/01/14 职场文书
银行优秀员工事迹材料
2014/05/29 职场文书
2014年教研工作总结
2014/12/06 职场文书
2015年度内部审计工作总结
2015/05/20 职场文书
歌咏比赛主持词
2015/06/29 职场文书
《分数的意义》教学反思
2016/02/20 职场文书
七年级作文之《我和我的祖国》观后感作文
2019/10/18 职场文书
详解PHP服务器如何在有限的资源里最大提升并发能力
2021/05/25 PHP
Python排序算法之插入排序及其优化方案详解
2021/06/11 Python
Nginx+Windows搭建域名访问环境的操作方法
2022/03/17 Servers