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自定义DateDiff函数(兼容所有浏览器)
Mar 01 Javascript
使用非html5实现js板连连看游戏示例代码
Sep 22 Javascript
JS教程:window.location使用方法的区别介绍
Oct 04 Javascript
JS如何判断移动端访问设备并解析对应CSS
Nov 27 Javascript
jQuery中delegate与on的用法与区别示例介绍
Dec 20 Javascript
js中将String转换为number以便比较
Jul 08 Javascript
js实现遍历含有input的table实例
Dec 07 Javascript
利用jquery给指定的table动态添加一行、删除一行的方法
Oct 12 Javascript
JavaScript实现反转字符串的方法详解
Apr 27 Javascript
用JS实现简单的登录验证功能
Jul 28 Javascript
详解Webpack + ES6 最新环境搭建与配置
Jun 04 Javascript
vue使用vuex实现首页导航切换不同路由的方法
May 08 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你的验证码安全码?
2007/01/02 PHP
10条PHP编程习惯助你找工作
2008/09/29 PHP
php flush无效,IIS7下php实时输出的方法
2016/08/25 PHP
Windows下php+mysql5.7配置教程
2017/05/16 PHP
Docker搭建自己的PHP开发环境
2018/02/24 PHP
javascript 面向对象编程基础:封装
2009/08/21 Javascript
javascript截取字符串(通过substring实现并支持中英文混合)
2013/06/24 Javascript
jQuery对象的length属性用法实例
2014/12/27 Javascript
JavaScript弹出新窗口后向父窗口输出内容的方法
2015/04/06 Javascript
js右下角弹出提示框示例代码
2016/01/12 Javascript
Jquery 自定义事件实现发布/订阅的简单实例
2016/06/12 Javascript
动态生成的DOM不会触发onclick事件的原因及解决方法
2016/08/06 Javascript
Angularjs CURD 详解及实例代码
2016/09/14 Javascript
js中利用cookie实现记住密码功能
2020/08/20 Javascript
JavaScript & jQuery完美判断图片是否加载完毕
2017/01/08 Javascript
使用vue.js2.0 + ElementUI开发后台管理系统详细教程(一)
2017/01/21 Javascript
jQuery实现鼠标跟随效果
2017/02/20 Javascript
微信小程序iOS下拉白屏晃动问题解决方案
2019/10/12 Javascript
Js逆向实现滑动验证码图片还原的示例代码
2020/03/10 Javascript
[01:00:22]DOTA2-DPC中国联赛定级赛 LBZS vs Magma BO3第三场 1月10日
2021/03/11 DOTA
Django中利用filter与simple_tag为前端自定义函数的实现方法
2017/06/15 Python
对Python中TKinter模块中的Label组件实例详解
2019/06/14 Python
Python爬虫实例——scrapy框架爬取拉勾网招聘信息
2020/07/14 Python
HTML5添加鼠标悬浮音响效果不使用FLASH
2014/04/23 HTML / CSS
基于HTML5+CSS3实现简单的时钟效果
2017/09/11 HTML / CSS
牛津在线药房:Oxford Online Pharmacy
2020/11/16 全球购物
C/C++有关内存的思考题
2015/12/04 面试题
视光学毕业生自荐书范文
2014/02/13 职场文书
ktv筹备计划书
2014/05/03 职场文书
大学生入党积极分子自我评价
2014/09/20 职场文书
领导班子四风查摆对照检查材料思想汇报
2014/10/05 职场文书
晋江市人民政府党组群众路线教育实践活动整改方案
2014/10/25 职场文书
教导主任个人总结
2015/03/03 职场文书
2015年主婚人婚礼致辞
2015/07/28 职场文书
告诉你一个秘密:富人致富的五大优点
2019/07/11 职场文书
win10系统xps文件怎么打开?win10打开xps文件的两种操作方法
2022/07/23 数码科技