当前位置:   article > 正文

vue2+three.js做出一个精美的3D地图——3.给地图描上边线,并添加上辉光效果_怎么给3d地图描边

怎么给3d地图描边


前言

代码地址 : https://gitee.com/txcst/3-dmap.git 只想好好做开源。

1.3D地图的第三步,增加描边的线条和辉光

一、如何生成边线?

1.还记得当初我们引入的map.json文件去生成的地图吗?如果忘了,可以去复习第二章,现在我们要做的是创建一个arr,把map.json的数据以省份的格式放进来,如果直接使用地图的数据,线条的起始点和另一个起始点会交叉在一起,非常混乱

//在 generateGeometry方法下新建 linArr数组
 let linArr=[]
  • 1
  • 2

2.每次循环清零linArr,并push进当前省份的数据,把当前linArr的省份数据 发给getLin(),去创建线条,
要调整成一下z轴的高度,是地图z轴高度的下面一点点
linArr.push(5.7)
注意代码里我关于linArr数组的操作

multiPolygon.forEach((polygon) => {
                // linArr.length=0
                //构建几何图形
                //清零
                linArr.length=0
                const shape = new THREE.Shape()
                for (let i = 0; i < polygon.length; i++) {
                    const [x, y] = projection(polygon[i])
                    //如果有NvN的数据就让他跳过,要不然three.js会报错
                    if(!x){
                        continue;
                    }
                    //指定我们的起点
                    if (i === 0) {
                        shape.moveTo(x, -y)
                    }
                    // this. pointArr.push(x)
                    // this. pointArr.push(-y)
                    // this. pointArr.push(2)
                    linArr.push(x)
                    linArr.push(-y)
                    linArr.push(5.7)

                     //后续就开始从起点画线
                     //如果你使用过canvans画线,那你肯定秒懂,他们是一个道理
                    shape.lineTo(x, -y)

                    // lineGeometry.vertices.push(new THREE.Vector3(x, -y, 4))
                }
                  
                //注意这里
                this.getLin(linArr) 


               

                //构建地图的 正面贴图 ,这里使用基础材质,关于材质,大家可以去详细看看api
                material = new THREE.MeshBasicMaterial  ({
                    // color:'#144685',
                    transparent: true,
                    opacity:1,
                    map:texture,
                })
                   //构建地图的 侧面贴图 ,这里使用基础材质
                const material_1 = new THREE.MeshBasicMaterial   ({
                    color: '#558BAB',
                    transparent: true,
                    opacity: 0.45,

                })

                const extrudeSettings = {
                    depth: 2,
                    bevelEnabled: false,
                }
                //这里我们把先前构造的几何图形 通过ExtrudeGeometry 拉伸成几何体
                const geometry = new THREE.ExtrudeGeometry(
                    shape,
                    extrudeSettings
                )

               
                const  extrudeSettings_2={
                    depth: 1,
                    bevelEnabled: false,
                }
                const geometry_2 = new THREE.ExtrudeGeometry(
                    shape,
                    extrudeSettings_2
                )
                const material_2 = new THREE.MeshBasicMaterial   ({
                    color: '#ffffff',
                    transparent: true,
                    opacity: 0,

                })

                //把几何体和  我们两个贴图 合成一个 网格对象 mesh
                const mesh = new THREE.Mesh(geometry, [material, material_1])

                const mesh_2 = new THREE.Mesh(geometry_2, [material_1,material_2])
               
                const mesh_3= mesh_2.clone()
                mesh.castShadow=true
              //把网格对象加入省份的3d对象
                mesh.position.z=4
                mesh_2.position.z=1
                mesh_3.position.z=-2
                province.add(mesh)
                province.add(mesh_2)
                province.add(mesh_3)
               
              

            })
  • 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
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95

3.在生成线条之前,我们需要引入three.meshline来控制线条的宽度

   npm i three.meshline
   //引入
   import { MeshLine, MeshLineMaterial, MeshLineRaycast } from 'three.meshline';
  • 1
  • 2
  • 3

webgl不支持线条的线条的宽度属性,用这个就可以很好实现,还不受版本的限制。 接下来我们看看如何生成线条,我会用大量注释帮助你理解。

getLin(arr){
               //Group对象,你可以把他看做一个数组,用这个主要是为了更方便的去控制每一个省份的线条
                let  groups=new THREE.Group()
                if(arr.length!=0){
                  // 初始化MeshLine
                      const line = new MeshLine()
                  // 传入顶点坐标数据
                     line.setPoints(arr)
                  // 生成线材质
                let material = new MeshLineMaterial({
                    useMap: 0,
                    color: '#0fb9ee',
                    opacity: 1,
                    resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
                    sizeAttenuation: 2,
                    lineWidth: 0.6,
                    transparent: true,
                    wireframe: false,
                    })   
                    //构建线条的网格对象
                    const mesh = new THREE.Mesh(line.geometry,material)
                   //把他放进去分层1  类似于z-index,方便后面实现辉光
                    mesh.layers.enable(1)
                    //加入数组
                    groups.add(mesh)
                    //把group加入地图对象
                    map.add(groups)
                }
            },
  • 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

效果展示:在这里插入图片描述

二、给线条加上辉光效果

1.对于新手来讲,接下来的内容会比较晦涩难懂,我建议你可以结合起来看看three.js的官网案例代码去帮助你理解

three.js案例

2.准备工作,引入包,并开启双层渲染
这些包都是three.js自带的,不要额外引入,去包里找就好了。

import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader';
  • 1
  • 2
  • 3
  • 4
  • 5
     //混合器
    let composer
    const ENTIRE_SCENE = 0,// 全部的,整个的场景
        BLOOM_SCENE = 1; // 光晕场景
    const bloomLayer = new THREE.Layers();// 光晕层次-创建一个图层对象
    bloomLayer.set(BLOOM_SCENE);// 先把光晕层次设置光晕场景的层次1
    const darkMaterial = new THREE.MeshBasicMaterial({ color: "black" });// 跟辉光光晕有关的变量

    const materials = {};// 跟辉光光晕有关的变量
    const params = {
        exposure: 0,// 暴露
        bloomStrength: 0.78,// 光晕强度
        bloomThreshold: 0,// 光晕阈值
        bloomRadius: 0.1,// 光晕半径
    };
    let bloomPass
    let finalComposer
    let bloomComposer
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

主要思路: 请一定要先想明白这里,再继续下去,辉光渲染的主要思路在于 分层渲染,其中的分层 它相当于重新创建了一个渲染的通道,你也可以简单的理解为z-index分层,他们不在一个层次,我们渲染先渲染需要发光的部分 也就是 layers.enable(1)的部分,在渲染另一个 layers.enable(0)的部分,最后再让他们合并。
3.开始构建
顶点和片段着色器
需要用GLSL语言来编写,目前我还不会,这是从官网demo找的代码片段,主要作用就是用于制造光晕

//顶点着色器
<script type="x-shader/x-vertex" id="vertexshader">
      varying vec2 vUv;
      void main() {
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      }
</script>
//片段着色器
<script type="x-shader/x-fragment" id="fragmentshader">
      uniform sampler2D baseTexture;
      uniform sampler2D bloomTexture;
      varying vec2 vUv;
      void main() {
          gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );
      }
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
feng(){

// 通过ShaderPass构造函数把FXAAShader着色器和uniforms构成的对象作为参数,创建一个锯齿通道FXAAShaderPass,然后把锯齿通道插入到composer中。
const effectFXAA = new ShaderPass(FXAAShader);
effectFXAA.uniforms["resolution"].value.set(
    0.6 / window.innerWidth,
    0.6 / window.innerHeight
); // 渲染区域Canvas画布宽高度 
effectFXAA.renderToScreen = true;
// 去掉锯齿---1
const renderScene = new RenderPass(scene, camera);// RenderPass这个通道会在当前场景(scene)和摄像机(camera)的基础上渲染出一个新场景,新建:
// 添加光晕效果---2
bloomPass = new UnrealBloomPass( // UnrealBloomPass通道可实现一个泛光效果。
    new THREE.Vector2(window.innerWidth, window.innerHeight),
    1.5,
    0.4,
    0.85
);
bloomPass.threshold = params.bloomThreshold;
bloomPass.strength = params.bloomStrength;
bloomPass.radius = params.bloomRadius;
// 添加光晕效果---2
// 着色器通道容器--放进容器里
bloomComposer = new  EffectComposer(renderer); // EffectComposer可以理解为着色器通道容器,着色器通道按照先后顺序添加进来并执行
bloomComposer.renderToScreen = false;
bloomComposer.addPass(renderScene);
bloomComposer.addPass(bloomPass); // 添加光晕效果
bloomComposer.addPass(effectFXAA);// 去掉锯齿
// 着色器通道容器--放进容器里
const finalPass = new ShaderPass(
    new THREE.ShaderMaterial({
        uniforms: {
            baseTexture: { value: null },
            bloomTexture: { value: bloomComposer.renderTarget2.texture },
        },
        vertexShader: document.getElementById("vertexshader").textContent,
        fragmentShader: document.getElementById("fragmentshader")
            .textContent,
        defines: {},
    }),
    "baseTexture"
);
finalPass.needsSwap = true;
finalComposer = new EffectComposer(renderer);
finalComposer.addPass(renderScene);
finalComposer.addPass(finalPass);
finalComposer.addPass(effectFXAA);
},
  • 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
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

关键的四个类

  1. ShaderPass:用于添加自定义的GLSL着色器(FXAAShader主要用于制造模糊效果 )

  2. RenderPass:一个新的通道,他会渲染出一个新的场景

  3. UnrealBloomPass:后期处理通道,用于模拟真实世界中的光晕效果,即模拟光线在明亮区域产生的模糊和光晕效果,他有四个参数,主要用来调节光晕
    radius:表示光晕散发的半径。
    strength:表示光晕的强度,值越大,光晕效果越明显。
    threshold:表示产生光晕的光照强度阈值,只有光照强度大于该值的部分才会产生光晕效果。
    blurKernel:表示用于模糊的卷积核,可以根据需要自定义。

  4. EffectComposer:用于实现后期处理效果的一个对象,他可以把先前生成的渲染通道合并起来渲染,每个通道都是一种后期处理的效果,包括颗粒 模糊等

    然后去吧 渲染器的 alpha打开,渲染器输出的图像将具有透明度,黑色将完全透明。

   renderer = new THREE.WebGLRenderer({
          alpha: true,
      })
  • 1
  • 2
  • 3

重写 render方法,渲染手法重点来了

  1. 先把场景里的所有几何体的纹理拿出来,然后给几何体的加上黑色纹理,前面把渲染器的 alpha打开了 渲染的都是透明的
  2. bloomComposer.render(); 渲染通道的集合
  3. 再把纹理还给场景里的几何体,然后删除刚给上的黑色纹理
  4. finalComposer.render(); 在渲染一遍
render() {

scene.traverse((obj) => {

      if (bloomLayer.test(obj.layers) === false) {
          materials[obj.uuid] = obj.material;
          obj.material = darkMaterial;
      }
  });

  bloomComposer.render();
 scene.traverse((obj) => {
      if (materials[obj.uuid]) {
          obj.material = materials[obj.uuid];
          delete materials[obj.uuid];
      }
  });
  finalComposer.render();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

现在已经完工了 你想把哪个几何体设置为辉光效果,就加上 layers.enable(1),让他进入另一个通道渲染
我们回到 getlin创建线条的方法

const mesh = new THREE.Mesh(line.geometry,material)

                    mesh.layers.enable(1)
  • 1
  • 2
  • 3

效果:
在这里插入图片描述

下期预告: 下一期 我们将在地图上添加精灵和别的几何体,然后让它们可以被点击,从而来产生一些交互

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

闽ICP备14008679号