当前位置:   article > 正文

超详细——手把手教你用threejs实现一个酷炫的模型发光扫描效果(二)_threejs 一个边缘光加流光的炫酷效果

threejs 一个边缘光加流光的炫酷效果

上一篇内容

voidjay,公众号:web前端可视化超详细——手把手教你用threejs实现一个酷炫的模型发光扫描效果(一)

上篇文章搭建了一个基本的3d场景,本篇则主要完成模型效果的开发,包括:模型外边缘线框,扫描线框,通过这两步实现整个项目的核心效果。

模型外边缘线框

这里需要将原模型材质修改为EdgesGeometry,并覆盖到基础模型上,代码如下:

  const renderEffect = function(model) {
      let edgeGroup = new THREE.Group();
      model.traverse((obj) => {
        // 由于汽车由许多mesh组成,因此需要将所有的mesh都转换为EdgesGeometry材质
        if(obj.type === 'Mesh') edgeGroup.add(_renderFrameMesh(obj));
      });
      scene.add(edgeGroup);
      // 重置变换
    function _renderFrameMesh(obj) {
      const edges = new THREE.EdgesGeometry(obj.geometry);
      let color = new THREE.Color(0.1, 0.3, 1);
      var lineBasematerial = new THREE.LineBasicMaterial({
        color: color,
        side: THREE.FrontSide,
        linecap: 'round',
        linejoin: 'round',
      });
      const line = new THREE.LineSegments(edges, lineBasematerial);
      return line;
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

此部分核心方法是_renderFrameMesh方法,我们通过此方法,将模型材质转换为线条材质,覆盖到原模型上,这里具体做了如下几个操作:

  1. 边缘几何体**(EdgesGeometry)**:根据汽车模型构建其边缘几何体,这个方法一般是作为一个辅助对象来查看geometry的边缘。

  2. 基础线条材质(LineBasicMaterial):一种用于绘制线框样式几何体的材质。

  3. 线段(LineSegments):在若干对的顶点之间绘制的一系列的线。

由于汽车由许多mesh组成,因此需要将所有的mesh都转换为EdgesGeometry材质,组合成LineSegments的网格对象,将所有新生成的网格对象放入一个group中,在场景中加入,就可以得到带有边框的汽车模型了。

ShaderMaterial生成扫描特效

接下来我们要实现一个从上到下扫描汽车模型的一个矩形线条效果,这里的实现思路为:生成一个模型的包围盒,通过**着色器材质(ShaderMaterial)**修改包围盒的材质,让其高度根据时间动态显示一部分,其余部分调整为透明,就可以实现上图的效果了,我们一步一步来实现一下吧。

  1. 生成包围盒

生成包围盒我们有两种思路可以实现。

1)通过建模软件添加一个矩形。

2)使用立方缓冲几何体(BoxGeometry)直接在场景中添加外接矩形。

本文使用第一种建模软件Blender添加,后面会说明一下第二种方法的实现思路。

下载并打开Blender,加载模型后如图所示操作添加一个立方体。

将立方体调整到覆盖模型外围的大小,导出为glb格式。

2.修改立方体材质为ShaderMaterial

这里先简单说明一下着色器材质(ShaderMaterial):

shader是一个用GLSL编写的小程序 ,GLSL是用来在OpenGL中编写着色器(顶点着色器vertexShader和片元着色器fragmentShader)程序的语言,在GPU上运行。threejs内置了很多材质,当这些材质无法满足我们的需求的时候,就需要自定义一个ShaderMaterial去实现更多模型效果。顶点着色器和片元着色器是OpenGL的核心,其中顶点着色器控制模型所有顶点位置的绘制,片元着色器则控制所有像素点的颜色,两个着色器相互配合。

GLSL语言与JS没有任何关系,是一门单独的语言,计算机图形学中使用的较多,可直接控制GPU同时对屏幕所有像素进行控制,是threejs进阶必学的一门语言。

我们在renderEffect加入一段修改立方体材质的代码如下:

 var scanConfig = {
    value: 1.0,
    start: 0,
    end: 0,
    during: 3,
  }
  function renderEffect(model) {
      ........
      model.traverse((obj) => {
      // 根据名称判断外接矩形
          if(obj.name == '立方体') {
            let shaderMaterial = new THREE.ShaderMaterial({
                transparent: true,
                side: THREE.DoubleSide,
                uniforms: {
                  height: scanConfig,
                  uFlowColor: {
                    value: new THREE.Vector4(0.0, 1.0, 1.0, 1.0),
                  },
                  uModelColor: {
                    value: new THREE.Vector4(0.0, 0.0, 0.0, 0.0),
                  },
                },
                vertexShader: uperVertext,
                fragmentShader: uperFragment,
              })
              obj.material = shaderMaterial;
              let boundingBox = obj.geometry.boundingBox;
              // 初始化扫描配置,y轴上下需留出一定空间,防止把上下平面扫描出来
                scanConfig.start = boundingBox.min.y+0.1 || 0;
                scanConfig.end = boundingBox.max.y-0.1 || 0;
                scanConfig.value = scanConfig.start;
          }
        .......
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

首先,我们定义一个scanConfig变量用来保存扫描线所有需要使用的数据,包括:value:当前高度,start:起始高度,end:终止高度,during:持续时间,将value传入ShaderMaterial的height变量中。

获取立方体外接矩形boundingBox的坐标,用于初始化scanConfig的参数。还记得我们上面说的**【使用立方缓冲几何体(BoxGeometry)直接在场景中添加外接矩形】**方法吗?借助boundingBox可以获取最小的外接矩形坐标,我们只需要修改boundingBox中x,y,z坐标,将其外扩一定距离,就可以获取到我们想要的包围盒了。

接下来就要完善材质部分,其中顶点着色器(vertexShader)和片元着色器(fragmentShader)代码如下:

const uperVertext = `
varying vec3 vPosition;
void main()
{
  vPosition = position;
  gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1 );
}
`;

const uperFragment = `
varying vec3 vPosition;
  uniform float height;
  uniform vec4 uFlowColor;
  uniform vec4 uModelColor;
void main()
{
  //模型的基础颜色
 vec4 distColor=uModelColor;
// 流动范围当前点z的高度加上流动线的高度
 float topY = vPosition.y +0.02;
if (height > vPosition.y && height < topY) {
 // 颜色渐变 
  distColor = uFlowColor; 
}

 gl_FragColor = distColor;
}`;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

给着色器传入三个变量:扫描光颜色uFlowColor,透明颜色uModelColor,动态height变量。当绘制的片元在height范围内时,修改颜色为扫描光颜色,接下来只需要控制好height变量就可以了。

我们通过calcHeight方法控制每帧高度变化,将其放入render中去逐帧计算当前高度,核心代码如下:

  function calcHeight() {
    let length = scanConfig.end - scanConfig.start;
    // 扫描动态效果实现
    scanConfig.value += length / scanConfig.during / 60;
    if (scanConfig.value >= scanConfig.end) {
      scanConfig.value = scanConfig.start;
    }
  }
  function render() {
    renderer.render(scene, camera);
    calcHeight()
    controls.update()
    requestAnimationFrame(render);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

至此,我们就完成了基本效果的开发,下一章节介绍发光效果的实现,敬请期待哦。


欢迎关注我的公众号获取webgl资料及最新文章,您也可以添加我的微信进行沟通交流:
公众号:web前端可视化
微信:voidjay

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/109503
推荐阅读
  

闽ICP备14008679号