2023-03-14---webgl使用宽线绘制行政区划边界

199 阅读1分钟

一、效果

image.png

二、源码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport"
    content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>绘制线</title>
  <style>
    body {
      margin: 0px;
      padding: 0px;
      text-align: center;
    }

    #canvas {
      margin: 0;
      position: absolute;
      width: 100%;
      height: 100%;
      left: 0px;
      top: 0px;
    }
  </style>
  <script src="/lib/webgl-utils.js"></script>
  <script src="/lib/webgl-debug.js"></script>
  <script src="/lib/cuon-utils.js"></script>
  <script src="/lib/cuon-matrix.js"></script>
  <script src="/data/city.js"></script>
</head>

<body>

  <canvas id="canvas"></canvas>

</body>


<script type="module">
  import { SphereGeometry } from "/geometry/SphereGeometry.js"
  import { PointsGeometry } from "/geometry/PointsGeometry.js"
  import { Texture2D } from './texture/Texture2D.js'
  import { Program } from './core/Program.js'
  import { toRadian, Cartesian3 } from './core/Math.js'
  import { Material } from './material/Material.js'
  import { Control } from './core/Control.js'
  import { Object3D } from './core/Object3D.js'
  import { Scene } from './core/Scene.js'
  import { Camera } from './core/Camera.js'
  import { Renderer } from './core/Renderer.js'
  import { MeshLineGeometry } from './geometry/MeshLineGeometry.js'
  import { earthVS, earthFS } from './material/shaders/Earth.js'
  import { shanxiJSON } from './data/shanxi.js'

  //顶点着色器
  var VSHADER_SOURCE = /*glsl*/`
        attribute vec3 a_PrevPosition;
        attribute vec3 a_NextPosition;
        attribute vec3 a_Position;
        attribute float a_Sides;
        attribute vec2 a_Uv;
        uniform mat4 u_MvpMatrix; 
        uniform vec2 u_Resolution;
        uniform mat4 u_ModelMatrix;
        uniform mat4 u_ViewMatrix;
        uniform mat4 u_ProjectMatrix;
        // uniform float u_Test;
       
        varying vec2 v_Uv;

        
        vec2 transToNDC(vec4 clipPosition,float aspect){
          
          vec2 ndcPosition=clipPosition.xy/clipPosition.w;
          ndcPosition.x*=aspect;
          return ndcPosition;
        }


        vec2 screenToClip(vec2 screenPosition){
          return vec2(screenPosition.x/u_Resolution.x,screenPosition.x/u_Resolution.x);
        }

        void main(){ 
          float width=5.0;
            mat4 mvp_Matrix=u_ProjectMatrix*u_ViewMatrix*u_ModelMatrix;

            vec4 clip_Position=mvp_Matrix*vec4(a_Position,1.0);
            vec4 clip_PrevPosition=mvp_Matrix*vec4(a_PrevPosition,1.0);
            vec4 clip_NextPosition=mvp_Matrix*vec4(a_NextPosition,1.0);


            float aspect=u_Resolution.x/u_Resolution.y;
            // float test=u_Test;
            //转换到NDC空间
            vec2 ndcPosition=transToNDC(clip_Position,aspect);
            vec2 ndcPrevPosition=transToNDC(clip_PrevPosition,aspect);
            vec2 ndcNextPosition=transToNDC(clip_NextPosition,aspect);

            vec2 dir=vec2(0.0);

            float theckness=width;
        
            //计算方向
            if(ndcPosition==ndcPrevPosition){
              dir=normalize(ndcNextPosition-ndcPosition);
            }else if(ndcPosition==ndcNextPosition){
              dir=normalize(ndcPosition-ndcPrevPosition);
            }else{
                vec2 dir1= normalize(ndcPosition-ndcPrevPosition);
                vec2 dir2= normalize(ndcNextPosition-ndcPosition);
                vec2 tanget=normalize(dir1+dir2);
                vec2 dir1Normal=vec2(-dir1.y,dir1.x);
                vec2 normal=vec2(-tanget.y,tanget.x);
                dir=tanget;
                float cosTheta=dot(dir1Normal,normal);
               
                theckness=min(width/abs(cosTheta),width*5.0);
            }
         
           float sizeAttenuation=0.0;
            vec4 normal=vec4(-dir.y,dir.x,0.0,1.0);
            normal.xy*=theckness*0.5;
            // normal*=u_ProjectMatrix;
          
            
              normal.x/=aspect;
            
            if( sizeAttenuation == 0. ) {
             
                normal.xy *= clip_Position.w;
                normal.x*=aspect;
                normal.xy /= vec2(u_Resolution.x,u_Resolution.y);
            }
            
            clip_Position.xy+=normal.xy*a_Sides;
            v_Uv=a_Uv;
            gl_Position=clip_Position;
            // gl_Position=clip_Position+clipOffset;

        }`;

  //片元着色器
  var FSHADER_SOURCE = /*glsl*/`
        #ifdef GL_ES
        precision mediump float;
        #endif 
        uniform vec3 u_Color;      
        varying vec2 v_Uv;
        void main(){
            gl_FragColor=vec4(u_Color,1.0);
       
        }`;



  //声明js需要的相关变量
  var canvas = document.getElementById("canvas");
  console.log(canvas.clientWidth)
  canvas.width = canvas.clientWidth;
  canvas.height = canvas.clientHeight;
  window.gl = getWebGLContext(canvas);


  // //设置透视投影矩阵
  // var projMatrix = new Matrix4();
  // projMatrix.setPerspective(30, canvas.width / canvas.height, 0.1, 10);

  const scene = new Scene()
  const camera = new Camera(30, canvas.width / canvas.height, 0.1, 10)
  // var projMatrix = camera.projectMatrix
  //设置视角矩阵的相关信息(视点,视线,上方向)
  var viewMatrix = camera.viewMatrix
  const renderer = new Renderer()

  main()

  async function main() {

    if (!gl) {
      console.log("你的浏览器不支持WebGL");
      return;
    }



    const earthProgram = new Program(earthVS, earthFS)
    //绘制球形
    const sphereGeometry = new SphereGeometry(gl, 1, 180, 90)
    earthProgram.addUniform('test')
    console.log('earthProgram,', earthProgram)
    //绘制纹理
    const texture = Texture2D.initTexture('./image/earth.jpg', 0)
    const sphereMaterial = new Material({
      program: earthProgram,
      uniforms: {
        texture: texture,
        normalTexture: null,
        test: 1.0
      },
    })

    const earth = new Object3D(sphereGeometry, sphereMaterial)
    scene.add(earth)



    // const position = [
    //   { x: 1.0, y: 0, z: 0.1 },
    //   { x: 0, y: 1, z: 0.3 },
    //   { x: -1, y: -1, z: 0.2 },
    //   { x: 0, y: -1, z: 0.4 },
    // ]


    const program = new Program(VSHADER_SOURCE, FSHADER_SOURCE)
    program.addAttribute('prevPosition')
    program.addAttribute('nextPosition')
    program.addAttribute('sides')
    program.addUniform('resolution')
    program.addUniform('color')
    // program.addUniform('modelMatrix')
    // program.addUniform('viewMatrix')
    // program.addUniform('projMatrix')
    console.log(program)

    const material = new Material({
      program,
      uniforms: {
        resolution: new Vector2([canvas.clientWidth, canvas.clientHeight]),
        color: new Vector3([1, 0, 0])
      }
    })



    console.log(shanxiJSON)
    const features = shanxiJSON.features
    features.forEach(feature => {
      const coordinates = feature.geometry.coordinates
      //单独画
      coordinates.forEach(coordinate => {
        console.log(coordinate[0])
        const worldPosition = []
        //循环转为世界坐标
        coordinate[0].forEach(coord => {
          worldPosition.push(Cartesian3.fromDegrees(coord[0], coord[1], 0))
        })
        const geometry = new MeshLineGeometry(worldPosition)
        const object3D = new Object3D(geometry, material)
        scene.add(object3D)
      })
    })





    const control = new Control(viewMatrix, canvas)
    var tick = function () {
      //设置底色
      gl.clearColor(0.0, 0.0, 0.0, 1.0);
      control.update()

      renderer.render(scene, camera)
      requestAnimationFrame(tick)
    }
    tick()


    // Control.registerMouseEvent(canvas)
  }




</script>

</html>