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 相关文章推荐
JS 按钮点击触发(兼容IE、火狐)
Aug 07 Javascript
div+css+js实现无缝滚动类似marquee无缝滚动兼容firefox
Aug 29 Javascript
javascript中的nextSibling使用陷(da)阱(keng)
May 05 Javascript
EasyUI闪屏EasyUI页面加载提示(原理+代码+效果图)
Feb 21 Javascript
jQuery实现选项联动轮播效果【附实例】
Apr 19 Javascript
jQuery实现点击查看大图并以弹框的形式居中
Aug 08 Javascript
json定义及jquery操作json的方法
Oct 03 Javascript
微信小程序 常用工具类详解及实例
Feb 15 Javascript
jQuery实现贪吃蛇小游戏(附源码下载)
Mar 04 Javascript
node.js+express+mySQL+ejs+bootstrop实现网站登录注册功能
Jan 12 Javascript
浅谈Webpack 持久化缓存实践
Mar 22 Javascript
如何在JavaScript中使用localStorage详情
Feb 04 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
CI框架学习笔记(一) - 环境安装、基本术语和框架流程
2014/10/26 PHP
PHP获取一年中每个星期的开始和结束日期的方法
2015/02/12 PHP
CI映射(加载)数据到view层的方法
2016/03/28 PHP
PHP中Socket连接及读写数据超时问题分析
2016/07/19 PHP
PHP生成及获取JSON文件的方法
2016/08/23 PHP
laravel框架模型和数据库基础操作实例详解
2020/01/25 PHP
Javascript 类、命名空间、代码组织代码
2011/07/31 Javascript
jQuery中创建实例与原型继承揭秘
2011/12/21 Javascript
一行代码告别document.getElementById
2012/06/01 Javascript
JavaScript修改css样式style动态改变元素样式
2013/12/16 Javascript
javascript页面上使用动态时间具体实现
2014/03/18 Javascript
js操作模态窗口及父子窗口间相互传值示例
2014/06/09 Javascript
举例说明如何为JavaScript的方法参数设置默认值
2015/11/17 Javascript
jquery,js简单实现类似Angular.js双向绑定
2017/01/13 Javascript
AngularJS遍历获取数组元素的方法示例
2017/11/11 Javascript
写一个移动端惯性滑动&回弹Vue导航栏组件 ly-tab
2018/03/06 Javascript
trackingjs+websocket+百度人脸识别API实现人脸签到
2018/11/26 Javascript
js取小数点后两位四种方法
2019/01/18 Javascript
Angular Excel 导入与导出的实现代码
2019/04/17 Javascript
详解微信小程序回到顶部的两种方式
2019/05/09 Javascript
关于vue组件事件属性穿透详解
2019/10/28 Javascript
ES6常用小技巧总结【去重、交换、合并、反转、迭代、计算等】
2019/12/21 Javascript
微信小程序如何加载数据库真实数据的实现
2020/03/04 Javascript
安装多版本Vue-CLI的实现方法
2020/03/24 Javascript
[00:52]DOTA2齐天大圣预告片
2016/08/13 DOTA
Python抓取电影天堂电影信息的代码
2016/04/07 Python
Python 3.8中实现functools.cached_property功能
2019/05/29 Python
python 判断文件还是文件夹的简单实例
2019/06/10 Python
在VS2017中用C#调用python脚本的实现
2019/07/31 Python
Python PyQt5运行程序把输出信息展示到GUI图形界面上
2020/04/27 Python
Cotton On美国网站:澳洲时装连锁品牌
2016/10/25 全球购物
智能电子秤、手表和健康监测仪:Withings(之前为诺基亚健康)
2018/10/30 全球购物
党员领导干部廉洁从政承诺书
2014/03/27 职场文书
2014年管理人员工作总结
2014/12/01 职场文书
总经理聘用协议书
2015/09/21 职场文书
电力培训学习心得体会
2016/01/11 职场文书