three.js使用卷积法实现物体描边效果
法线延展法
网上使用法线延展法实现物体描边效果的文章比较多,这里不再描述。
但是这种方法有个缺点:当两个面的法线夹角差别较大时,两个面的描边无法完美连接。如下图所示:

卷积法
这里使用另一种方法卷积法实现物体描边效果,一般机器学习使用该方法比较多。先看效果图:

使用three.js具体的实现方法如下:
- 创建着色器材质,隐藏不需要描边的物体进行渲染,将需要描边的位置渲染成白色,其他位置渲染成黑色。
- 利用片源着色器计算卷积,白色是物体内部,黑色是物体外部,灰色是边框。
- 设置材质透明、不融合,将边框叠加到原图上,可以使用FXAA抗锯齿。
这三步就可以实现了,很简单吧。下面我们将详细介绍实现方法,不想看的可以直接去看完整实现代码:
完整代码: https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/helper/SelectHelper.js
详细的实现过程:
1. 使用three.js正常绘制场景,得到下图,这里不介绍了。

2. 创建着色器材质,隐藏所有不需要描边的物体。将需要描边的物体绘制成白色,其他地方绘制成黑色。
隐藏不需要描边的物体后,将整个场景材质替换。
renderScene.overrideMaterial = this.maskMaterial;
着色器材质:
const maskMaterial = new THREE.ShaderMaterial({
vertexShader: MaskVertex,
fragmentShader: MaskFragment,
depthTest: false
});
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
void main() {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
效果图:

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
});
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
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);
}
效果图:

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;
}
得到最终效果图:

参考资料:
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/#webgl_postprocessing_outline
4. 卷积工作原理:https://www.zhihu.com/question/39022858?sort=created
5. 法线延展法实现物体描边:https://blog.csdn.net/srk19960903/article/details/73863853
three.js使用卷积法实现物体描边效果的更多相关文章
- Cesium源码剖析---Post Processing之物体描边(Silhouette)
Cesium在1.46版本中新增了对整个场景的后期处理(Post Processing)功能,包括模型描边.黑白图.明亮度调整.夜视效果.环境光遮蔽等.对于这么炫酷的功能,我们绝不犹豫,先去翻一翻它的 ...
- Unity Shader实现描边效果
http://gad.qq.com/article/detail/28346 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个对象,会给对象增加一个描边效果.本篇文章和大家介绍下利用S ...
- OpenCV 使用光流法检测物体运动
OpenCV 可以使用光流法检测物体运动,贴上代码以及效果. // opticalflow.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" ...
- js实现欧几里得算法
概念 在数学中,辗转相除法,又称欧几里得算法(英语:Euclidean algorithm),是求最大公约数的算法. 证明 首先假设有两个数a和b,其中a是不小于b的数,记a被b除的余数为r,那么a可 ...
- js中json法创建对象(json里面的:相当于js里面的=)
js中json法创建对象(json里面的:相当于js里面的=) 一.总结 json里面的:相当于js里面的= 4.json创建js对象解决命名冲突:多个人为同一个页面写js的话,命名冲突就有可能发生, ...
- Unity 实现物体破碎效果(转)
感谢网友分享,原文地址(How to Make an Object Shatter Into Smaller Fragments in Unity),中文翻译地址(Unity实现物体破碎效果) In ...
- UE4实现描边效果
描边效果属于常见常用的功能,现VR项目中,也需要射线选中一个物体,使物体高亮. 于是在网上找了部分资料,同时也感谢群里的一位大神的提点,总算将描边的功能实现了,这里也写一个简单的示例步骤. 1.我并不 ...
- TouchPoint.js – 可视化展示 HTML 原型点击效果
TouchPoint.js 是一个用于 HTML 原型展示的 JavaScript 库(作为UX过程的一部分),通过视觉表现用户在屏幕上的点击.TouchPoint 是高度可定制,非常适合屏幕录制,用 ...
- Bounce.js – 快速创建漂亮的 CSS3 动画效果
Bounce.js 是一个用于制作漂亮的 CSS3 关键帧动画的 JavaScript 库,使用其特有的方式生成的动画效果.只需添加一个组件,选择预设,然后你就可以得到一个短网址或者导出为 CSS 代 ...
随机推荐
- ride.py 启动报错
报错问题: C:\Users\iphauser>ride.py Traceback (most recent call last): File , in OnInit self._plugin_ ...
- new Date插入mysql数据库时多了一秒
在使用new Date() 插入数据库时,查询出来比实际多了一秒,mysql 表字段设置为datetime类型,当时间精确到秒时,如果毫秒大于500时,会自动进位. 解决方法为格式化后再插入数据库.
- (五)Unity插件生成
1)新建空的AndroidStudio工程,但是新建过程时最小SDK版本要与unity一致,如下图所示,本次操作均为api16 2)创建Library,如下图所示,新建module,然后选择Andro ...
- Python调试工具
1. 日志 通过日志或者print来打印变量.必要时可以打印locals()和globals() 建议使用logging.debug()来代替print,这样到了正式环境,就可以统一删除这些日志. 2 ...
- ArangoDB安装方法整理
目录 方法一:镜像安装 方法二:离线安装 方法三:在线安装 启动与停止服务 一.镜像安装(推荐方法) 安装docker 安装方法参见docker安装方法整理. 安装arangodb镜像: docker ...
- C#开发命令执行驱动程序 之 控制标志的命令行参数
/// <summary> /// 在cmd窗体内执行如下: /// CtrlOrderDrierApp.exe -f -t /// 返回: /// FOO /// Show Table ...
- vw vh 的概念
视口单位(Viewport units) 什么是视口? 在桌面端,视口指的是在桌面端,指的是浏览器的可视区域:而在移动端,它涉及3个视口:Layout Viewport(布局视口),Visual Vi ...
- C++学习笔记14_C#调用dll
1. 首先C++创建dll项目,各种调第三方库,把这些东西都弄好后,整一个导出接口,例如: extern "C" __declspec(dllexport) int Add(int ...
- [考试反思]1002csp-s模拟测试57:平庸
一天两场,感觉要完. 不粘排行榜,太壮观了. #1:190 #2:180 #4:160 #35:150 #37:140 #39:120 #kx:20呃... 最后一个是考试结束后了. 又是CE盖40分 ...
- NOIP模拟 24
连续爆炸的开端. 从这一场开始我没状态了 T1 star way to heaven 受强降雨boboQQQ影响,我一直认为这是一道和凸包有关的计算几何题 很快就弃了,除了期望没做过带实数的题,所以吓 ...