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 相关文章推荐
百度Popup.js弹出框进化版 拖拽小框架发布 兼容IE6/7/8,Firefox,Chrome
Apr 13 Javascript
Javascript引用指针使用介绍
Nov 07 Javascript
Web Inspector:关于在 Sublime Text 中调试Js的介绍
Apr 18 Javascript
JavaScript实现表格排序方法
Jun 14 Javascript
js实现带搜索功能的下拉框实时搜索实时匹配
Nov 05 Javascript
简单的邮箱登陆的提示效果类似于yahoo邮箱
Feb 26 Javascript
基于jquery实现瀑布流布局
Jun 28 Javascript
基于javascript制作经典传统的拼图游戏
Mar 22 Javascript
js获取腾讯视频ID的方法
Oct 03 Javascript
微信小程序 简单教程实例详解
Jan 13 Javascript
React 无状态组件(Stateless Component) 与高阶组件
Aug 14 Javascript
Vue最新防抖方案(必看篇)
Oct 30 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 header()函数使用说明
2008/07/10 PHP
PHP字符串中特殊符号的过滤方法介绍
2014/02/18 PHP
PHP面向对象程序设计方法实例详解
2016/12/24 PHP
php插入mysql数据返回id的方法
2018/05/31 PHP
自写的jQuery异步加载数据添加事件
2014/05/15 Javascript
js实现完美兼容各大浏览器的人民币大小写相互转换
2015/10/29 Javascript
轮播的简单实现方法
2016/07/28 Javascript
jquery popupDialog 使用 加载jsp页面的方法
2016/10/25 Javascript
Angular实现跨域(搜索框的下拉列表)
2017/02/16 Javascript
Angular.js ng-file-upload结合springMVC的使用教程
2017/07/10 Javascript
Node.js实现连接mysql数据库功能示例
2017/09/15 Javascript
JS中Attr的用法详解
2017/10/09 Javascript
详解angular分页插件tm.pagination二次触发问题解决方案
2018/07/20 Javascript
vue init webpack 建vue项目报错的解决方法
2018/09/29 Javascript
js 实现在2d平面上画8的方法
2018/10/10 Javascript
微信小程序云开发使用方法新手初体验
2019/05/16 Javascript
详解小程序开发经验:多页面数据同步
2019/05/18 Javascript
webpack+express实现文件精确缓存的示例代码
2020/06/11 Javascript
Django实现自定义404,500页面教程
2017/03/26 Python
PyCharm设置SSH远程调试的方法
2018/07/17 Python
python manage.py runserver流程解析
2019/11/08 Python
np.dot()函数的用法详解
2020/01/17 Python
Python如何读写字节数据
2020/08/05 Python
详解python命令提示符窗口下如何运行python脚本
2020/09/11 Python
详解HTML5.2版本带来的修改
2020/05/06 HTML / CSS
波兰家具和室内装饰品购物网站:Vivre
2018/04/10 全球购物
Snapfish爱尔兰:在线照片打印和个性化照片礼品
2018/09/17 全球购物
香港彩色隐形眼镜在线商店:Stunninglens(全球免费送货)
2019/05/10 全球购物
拉夫劳伦爱尔兰官方网站:Ralph Lauren爱尔兰
2020/04/10 全球购物
学院书画协会部门职责
2013/11/28 职场文书
2014年维修电工工作总结
2014/11/20 职场文书
2014年财政所工作总结
2014/11/22 职场文书
反邪教警示教育活动总结
2015/05/09 职场文书
2016党员干部反腐倡廉心得体会
2016/01/13 职场文书
浅谈python数据类型及其操作
2021/05/25 Python
Android Flutter实现3D动画效果示例详解
2022/04/07 Java/Android