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 相关文章推荐
捕获关闭窗口的脚本
Jan 10 Javascript
取选中的radio的值
Jan 11 Javascript
Jquery Validate 正则表达式实用验证代码大全
Aug 23 Javascript
js获取鼠标点击的位置实现思路及代码
May 09 Javascript
JavaScript获得当前网页来源页面(即上一页)的方法
Apr 03 Javascript
浅谈时钟的生成(js手写简洁代码)
Aug 20 Javascript
vue子组件使用自定义事件向父组件传递数据
May 27 Javascript
jquery ajaxfileupload异步上传插件
Nov 21 jQuery
基于vue v-for 多层循环嵌套获取行数的方法
Sep 26 Javascript
vue2.0 获取从http接口中获取数据,组件开发,路由配置方式
Nov 04 Javascript
深入了解JS之作用域和闭包
Jun 16 Javascript
vue Element-ui表格实现树形结构表格
Jun 07 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
声音就能俘获人心,蕾姆,是哪个漂亮小姐姐配音呢?
2020/03/03 日漫
使用图灵api创建微信聊天机器人
2015/07/23 PHP
详解WordPress中调用评论模板和循环输出评论的PHP函数
2016/01/05 PHP
PHP之多条件混合筛选功能的实现方法
2019/10/09 PHP
$.ajax返回的JSON无法执行success的解决方法
2011/09/09 Javascript
js/html光标定位的实现代码
2013/09/23 Javascript
JS数组的遍历方式for循环与for...in
2014/07/31 Javascript
JQuery Tips相关(1)----关于$.Ready()
2014/08/14 Javascript
JS遍历Json字符串中键值对先转成JSON对象再遍历
2014/08/15 Javascript
js实现背景图片感应鼠标变化的方法
2015/02/28 Javascript
jquery插件orbit.js实现图片折叠轮换特效
2015/04/14 Javascript
JavaScript实现select添加option
2015/07/03 Javascript
基于Jquery easyui 选中特定的tab
2015/11/17 Javascript
JS控制按钮10秒钟后可用的方法
2015/12/22 Javascript
javascript原生ajax写法分享
2016/04/10 Javascript
Vue.js中用v-bind绑定class的注意事项
2016/12/13 Javascript
js+div+css下拉导航菜单完整代码分享
2016/12/28 Javascript
详解webpack+vue-cli项目打包技巧
2017/06/17 Javascript
详解Node.js模板引擎Jade入门
2018/01/19 Javascript
vueScroll实现移动端下拉刷新、上拉加载
2019/03/22 Javascript
Vue+Express实现登录状态权限验证的示例代码
2019/05/05 Javascript
angular异步验证防抖踩坑实录
2019/12/01 Javascript
JS实现长图上下滚动效果
2020/03/19 Javascript
vue打包通过image-webpack-loader插件对图片压缩优化操作
2020/11/12 Javascript
python基础教程项目四之新闻聚合
2018/04/02 Python
详解python 3.6 安装json 模块(simplejson)
2019/04/02 Python
python issubclass 和 isinstance函数
2019/07/25 Python
python GUI库图形界面开发之PyQt5多线程中信号与槽的详细使用方法与实例
2020/03/08 Python
html5的input的required使用中遇到的问题及解决方法
2018/04/24 HTML / CSS
amazeui页面校验功能的实现代码
2020/08/24 HTML / CSS
高中政治教学反思
2014/01/18 职场文书
房务中心文员岗位职责
2014/04/16 职场文书
商铺门前三包责任书
2014/07/25 职场文书
全国优秀教师事迹材料
2014/08/26 职场文书
分家协议书范本
2016/03/22 职场文书
python基于tkinter制作无损音乐下载工具
2021/03/29 Python