three.js利用卷积法如何实现物体描边效果

 更新时间:2019年11月27日 10:19:07   作者:tengge  
这篇文章主要给大家介绍了关于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正常绘制场景,得到下图,这里不介绍了。

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);
}

效果图:

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);
}

效果图:

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/

4. 卷积工作原理:https://www.zhihu.com/question/39022858?sort=created

5. 法线延展法实现物体描边:https://www.jb51.net/article/175213.htm

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

相关文章

  • JavaScript实现LI列表数据绑定的方法

    JavaScript实现LI列表数据绑定的方法

    这篇文章主要介绍了JavaScript实现LI列表数据绑定的方法,可实现绑定Li列表项对应数值项的功能,涉及javascript鼠标onmousemove、onmouseout及onclick等事件的相关使用技巧,需要的朋友可以参考下
    2015-08-08
  • js异步加载的三种解决方案

    js异步加载的三种解决方案

    默认情况javascript是同步加载的,javascript的加载时阻塞的,后面的元素要等待javascript加载完毕后才能进行再加载,如何解决这个问题呢,接下来将为你详细介绍下异步加载js三种实现方案,感兴趣的你可以参考下哈
    2013-03-03
  • JavaScript日期对象(Date)基本用法示例

    JavaScript日期对象(Date)基本用法示例

    这篇文章主要介绍了JavaScript日期对象(Date)基本用法,结合实例形式较为详细的分析了JavaScript日期对象(Date)获取日期、时间戳、年月日、星期及日期比对等操作技巧,需要的朋友可以参考下
    2017-01-01
  • JavaScript中关于class的调用方法

    JavaScript中关于class的调用方法

    下面小编就为大家带来一片JavaScript中关于class的调用方法。具有很好的参考价值,希望对大家有所帮助
    2017-11-11
  • 小程序实现计算器功能

    小程序实现计算器功能

    这篇文章主要为大家详细介绍了小程序实现计算器功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • JS实现的RGB网页颜色在线取色器完整实例

    JS实现的RGB网页颜色在线取色器完整实例

    这篇文章主要介绍了JS实现的RGB网页颜色在线取色器,结合完整实例形式分析了基于JS运算及鼠标事件响应来操作页面元素实现取色器功能的方法,具有一定参考借鉴价值,需要的朋友可以参考下
    2016-12-12
  • javascript中的注释使用与注意事项小结

    javascript中的注释使用与注意事项小结

    在javascript中有两种注释方式,单行注释与多行注释。
    2011-09-09
  • javascript XML数据显示为HTML一例

    javascript XML数据显示为HTML一例

    通过Javascript把xml转换成html格式输出一列
    2008-12-12
  • 解决bootstrap中modal遇到Esc键无法关闭页面

    解决bootstrap中modal遇到Esc键无法关闭页面

    Bootstrap,来自 Twitter,是目前最受欢迎的前端框架。Bootstrap 是基于 HTML、CSS、JAVASCRIPT 的,它简洁灵活,使得 Web 开发更加快捷。不过在使用的过程中,我们还是会遇到各种小问题,今天我们探讨的就是个人在使用中遇到的一个小BUG的修复。
    2015-03-03
  • js正则表达式的使用详解

    js正则表达式的使用详解

    本篇文章是对js中正则表达式的使用进行了详细的分析介绍,需要的朋友可以参考下
    2013-07-07

最新评论