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学习笔记[1] jQuery中的DOM操作
Dec 03 Javascript
javascript 闭包
Sep 15 Javascript
jquery简单实现鼠标经过导航条改变背景图
Dec 17 Javascript
javascript实现瀑布流自适应遇到的问题及解决方案
Jan 28 Javascript
jQuery实现类似老虎机滚动抽奖效果
Aug 06 Javascript
JavaScript操作class和style样式代码详解
Feb 13 Javascript
JS中如何实现复选框全选功能
Dec 19 Javascript
vue实现点击选中,其他的不选中方法
Sep 05 Javascript
css配合JavaScript实现tab标签切换效果
Oct 11 Javascript
Vue.js 使用v-cloak后仍显示变量的解决方法
Nov 19 Javascript
Node.js 实现简单的无侵入式缓存框架的方法
Jul 21 Javascript
node静态服务器实现静态读取文件或文件夹
Dec 03 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 Laravel 上传图片、文件等类封装
2017/08/16 PHP
PHP使用HTML5 FileApi实现Ajax上传文件功能示例
2019/07/01 PHP
50个优秀经典PHP算法大集合 附源码
2020/08/26 PHP
如何让浏览器支持jquery ajax load 前进、后退功能
2014/06/12 Javascript
JavaScript字符串对象split方法入门实例(用于把字符串分割成数组)
2014/10/16 Javascript
jQuery fancybox在ie浏览器下无法显示关闭按钮的解决办法
2016/02/19 Javascript
jQuery实现字符串全部替换的方法
2016/12/12 Javascript
将鼠标焦点定位到文本框最后(代码分享)
2017/01/11 Javascript
JQuery异步提交表单与文件上传功能示例
2017/01/12 Javascript
jquery实时获取时间的简单实例
2017/01/26 Javascript
理解javascript中的Function.prototype.bind的方法
2017/02/03 Javascript
Vue开发中整合axios的文件整理
2017/04/29 Javascript
js canvas实现画图、滤镜效果
2018/11/27 Javascript
实现vuex与组件data之间的数据同步更新方式
2019/11/12 Javascript
CKEditor扩展插件:自动排版功能autoformat插件实现方法详解
2020/02/06 Javascript
详解vue-cli项目在IE浏览器打开报错解决方法
2020/12/10 Vue.js
[00:39]DOTA2上海特级锦标赛 Liquid战队宣传片
2016/03/04 DOTA
用Python从零实现贝叶斯分类器的机器学习的教程
2015/03/31 Python
python 删除指定时间间隔之前的文件实例
2018/04/24 Python
对Python3使运行暂停的方法详解
2019/02/18 Python
Python生成指定数量的优惠码实操内容
2019/06/18 Python
Scrapy 配置动态代理IP的实现
2020/09/28 Python
Python之字典添加元素的几种方法
2020/09/30 Python
C++和python实现阿姆斯特朗数字查找实例代码
2020/12/07 Python
Notino匈牙利:购买香水和化妆品
2019/04/12 全球购物
英国户外服装品牌:Craghoppers
2019/04/25 全球购物
JD Sports西班牙:英国领先的运动服装公司
2020/01/06 全球购物
财务内勤岗位职责
2014/04/17 职场文书
工作会议方案
2014/05/21 职场文书
产品陈列协议书(标准版)
2014/09/17 职场文书
终止劳动合同协议书
2014/10/05 职场文书
破坏寝室公物检讨书
2014/11/17 职场文书
村官个人总结范文
2015/03/03 职场文书
入党积极分子党支部意见
2015/06/02 职场文书
病假条格式范文
2015/08/17 职场文书
2016新教师培训心得体会范文
2016/01/08 职场文书