three.js使用卷积法实现物体描边效果

摘要:
法线延展法网上使用法线延展法实现物体描边效果的文章比较多,这里不再描述。利用片源着色器计算卷积,白色是物体内部,黑色是物体外部,灰色是边框。color是边框颜色,thickness是边框粗细。注意,要将材质transparent设置为true。
法线延展法

网上使用法线延展法实现物体描边效果的文章比较多,这里不再描述。

但是这种方法有个缺点:当两个面的法线夹角差别较大时,两个面的描边无法完美连接。如下图所示:

three.js使用卷积法实现物体描边效果第1张

卷积法

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

three.js使用卷积法实现物体描边效果第2张three.js使用卷积法实现物体描边效果第3张three.js使用卷积法实现物体描边效果第4张

使用three.js具体的实现方法如下:

  1. 创建着色器材质,隐藏不需要描边的物体进行渲染,将需要描边的位置渲染成白色,其他位置渲染成黑色。
  2. 利用片源着色器计算卷积,白色是物体内部,黑色是物体外部,灰色是边框。
  3. 设置材质透明、不融合,将边框叠加到原图上,可以使用FXAA抗锯齿。

这三步就可以实现了,很简单吧。下面我们将详细介绍实现方法,不想看的可以直接去看完整实现代码:

完整代码:https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/helper/SelectHelper.js

详细的实现过程:

1. 使用three.js正常绘制场景,得到下图,这里不介绍了。

three.js使用卷积法实现物体描边效果第5张

2. 创建着色器材质,隐藏所有不需要描边的物体。将需要描边的物体绘制成白色,其他地方绘制成黑色。

隐藏不需要描边的物体后,将整个场景材质替换。

renderScene.overrideMaterial = this.maskMaterial;

着色器材质:

const maskMaterial = newTHREE.ShaderMaterial({
    vertexShader: MaskVertex,
    fragmentShader: MaskFragment,
    depthTest: false
});
MaskVertex:
voidmain() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0);
}
MaskFragment:
voidmain() {
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}

效果图:

three.js使用卷积法实现物体描边效果第6张

3. 创建着色器材质进行卷积计算,每四个像素颜色求平均值得到一个像素。描边物体内部是白色,外部是黑色,物体边缘处会得到灰色。灰色就是我们所需的边框。

const edgeMaterial = newTHREE.ShaderMaterial({
    vertexShader: EdgeVertex,
    fragmentShader: EdgeFragment,
    uniforms: {
        maskTexture: {
            value: this.maskBuffer.texture
        },
        texSize: {
            value: newTHREE.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;
voidmain() {
    vUv =uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0);
}
EdgeFragment:
uniform sampler2D maskTexture;
uniform vec2 texSize;
uniform vec3 color;
uniform floatthickness;
varying vec2 vUv;
voidmain() {
    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使用卷积法实现物体描边效果第7张

4. 创建着色器材质,将边框叠加到原来的图片上。由于FXAA比较复杂,这里使用简单的叠加方法。

着色器材质:

const copyMaterial = newTHREE.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;
voidmain() {
    vUv =uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0);
}

CopyFragmentShader:

uniform floatopacity;
uniform sampler2D tDiffuse;
varying vec2 vUv;
voidmain() {
    vec4 texel =texture2D( tDiffuse, vUv );
    gl_FragColor = opacity *texel;
}

得到最终效果图:

three.js使用卷积法实现物体描边效果第8张

参考资料:

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使用卷积法实现物体描边效果》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇sql学习笔记--存储过程Unity查找物体的四大主流方法及区别下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

单阶多层检测器: SSD(一)

  对于物体检测任务, 第4章的Faster RCNN算法采用了两阶的检测架构, 即首先利用RPN网络进行感兴趣区域生成, 然后再对该区域进行类别的分类与位置的回归, 这种方法虽然显著提升了精度, 但也限制了检测速度。 YOLO算法利用回归的思想, 使用一阶网络直接完成了物体检测, 速度很快, 但是精度有了明显的下降。   在此背景下, SSD(Singl...

MobileNet系列之MobileNet_v3

​ MobileNet系列之MobileNet_v1 MobileNet系列之MobileNet_v2 导言:     继MobileNet_v1和v2提出后,在2019年,MobileNet_v3在众人的期盼下出来了,MobileNet_v3论文提出了两个模型,MobileNet_v3-Large和MobileNet_v3-small,其主要区别在于层数...

YOLOV5源码解读-export.py网络结构、配置文件

 yolov5默认模型文件格式为:".pt",使用上述可视化工具的时候,需要利用yolov5给的代码(export.py),将模型转为".torchscript.pt"格式,然后就可以完整地可视化网络结构了。 yolo5s四个针对coco数据集的预训练模型下载地址: # Download latest models from     https://gi...

论文阅读笔记十二:Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation(DeepLabv3+)(CVPR2018)

论文链接:https://arxiv.org/abs/1802.02611 tensorflow 官方实现: https: //github.com/tensorflow/models/tree/master/research/deeplab 实验代码:https://github.com/fourmi1995/IronSegExperiment-Dee...

第十三章——卷积神经网络(CNN)

卷积神经网络(Convolutional neural networks,CNNs)来源于对大脑视觉皮层的研究,并于1980s开始应用于图像识别。现如今CNN已经在复杂的视觉任务中取得了巨大成功,比如图像搜索,自动驾驶,语言自动分类等等。同时CNN也应用于了其他领域,比如语音识别和自然语言处理。 13.1视觉皮层机理 David H. Hubel和Tors...

Deep Learning基础--线性解码器、卷积、池化

本文主要是学习下Linear Decoder已经在大图片中经常采用的技术convolution和pooling,分别参考网页http://deeplearning.stanford.edu/wiki/index.php/UFLDL_Tutorial中对应的章节部分。   Linear Decoders:   以三层的稀疏编码神经网络而言,在sparse a...