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 相关文章推荐
Javascript & DHTML 实例编程(教程)(三)初级实例篇1—上传文件控件实例
Jun 02 Javascript
JS制作简单的三级联动
Mar 18 Javascript
JS实现的通用表单验证插件完整实例
Aug 20 Javascript
Javascript实现找不同色块的游戏
Jul 17 Javascript
Angular中sweetalert弹框的基本使用教程
Jul 22 Javascript
如何能分清npm cnpm npx nvm
Jan 17 Javascript
使用watch在微信小程序中实现全局状态共享
Jun 03 Javascript
JS实现图片懒加载(lazyload)过程详解
Apr 02 Javascript
javascript实现滚轮轮播图片
Dec 13 Javascript
Vue实现手机号、验证码登录(60s禁用倒计时)
Dec 19 Vue.js
微信小程序实现录音Record功能
May 09 Javascript
微信小程序中wxs文件的一些妙用分享
Feb 18 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 strncasecmp字符串比较的小技巧
2011/01/04 PHP
基于magic_quotes_gpc与magic_quotes_runtime的区别与使用介绍
2013/04/22 PHP
PHP Global变量定义当前页面的全局变量实现探讨
2013/06/05 PHP
php中json_encode处理gbk与gb2312中文乱码问题的解决方法
2014/07/10 PHP
php对微信支付回调处理的方法
2018/08/23 PHP
Yii框架小部件(Widgets)用法实例详解
2020/05/15 PHP
AJAX 网页保留浏览器前进后退等功能
2011/02/12 Javascript
Jquery 模拟用户点击超链接或者按钮的方法
2013/10/25 Javascript
javaScript实现滚动新闻的方法
2015/07/30 Javascript
jquery常用的12个小功能
2016/07/22 Javascript
JS简单实现表格排序功能示例
2016/12/20 Javascript
js实现漫天星星效果
2017/01/19 Javascript
jQuery实现键盘回车搜索功能
2017/07/25 jQuery
Javascript实现时间倒计时功能
2018/11/17 Javascript
js实现图片3D轮播效果
2019/09/21 Javascript
浅谈vue-router路由切换 组件重用挖下的坑
2019/11/01 Javascript
详解JavaScript 异步编程
2020/07/13 Javascript
Vite和Vue CLI的优劣
2021/01/30 Vue.js
[05:16]《大圣!大圣》——DOTA2新英雄齐天大圣配音李世宏老师专访
2016/12/13 DOTA
python3爬取淘宝信息代码分析
2018/02/10 Python
python模糊图片过滤的方法
2018/12/14 Python
python批量创建指定名称的文件夹
2019/03/21 Python
Python处理session的方法整理
2019/08/29 Python
python中upper是做什么用的
2020/07/20 Python
pycharm配置QtDesigner的超详细方法
2021/01/25 Python
anaconda升级sklearn版本的实现方法
2021/02/22 Python
canvas 实现 github404动态效果的示例代码
2017/11/15 HTML / CSS
墨西哥运动服饰和鞋网上商店:Netshoes墨西哥
2016/07/28 全球购物
古驰英国官网:GUCCI英国
2020/03/07 全球购物
护士毕业生自我鉴定
2014/02/08 职场文书
成立公司计划书
2014/05/07 职场文书
金融专业银行实习证明模板
2014/11/28 职场文书
舞蹈社团活动总结
2015/05/07 职场文书
毕业论文致谢怎么写
2015/05/14 职场文书
MySQL令人大跌眼镜的隐式转换
2021/08/23 MySQL
ubuntu开机后ROS程序自启动问题
2022/12/24 Servers