2024年3月

Threejs实现Points对象实时指向指定坐标

全景图里常常会往场景里添加箭头,希望箭头可以指向某个3d坐标
箭头指向目标点

matrial关键代码:

function createMaterial(size = 24) {
  const vertexShader = `
    attribute vec3 a_direction;
    uniform float u_size;
    uniform float u_aspect;
    varying float v_rotation;

    void main(){
      gl_PointSize = u_size;
      // 箭头的位置坐标
      vec4 mvPosition = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      // 箭头指向的位置坐标
      vec4 targetPos = projectionMatrix * modelViewMatrix * vec4(a_direction, 1.0);

      vec2 dir = targetPos.xy/targetPos.w -mvPosition.xy/mvPosition.w;
      float theta = atan(dir.y / u_aspect, dir.x);

      v_rotation =  theta; //  mod( theta + PI, 2.0 * PI );
      gl_Position = mvPosition;
    }
  `;
  const fragmentShader = `
    uniform vec3 u_color;
    uniform float u_opacity;
    uniform sampler2D u_map;
    varying float v_rotation;

    void main(){

      vec2 uv = gl_PointCoord - vec2(0.5);

      float xn = uv.x * cos(v_rotation) - uv.y * sin(v_rotation);
      float yn = uv.x * sin(v_rotation) + uv.y * cos(v_rotation);

      vec2 new_uv = vec2(xn +0.5, yn+0.5);

      if(new_uv.x < 0.0 || new_uv.x > 1.0 || new_uv.y < 0.0 || new_uv.y > 1.0){
        discard;
      }

      float mapColor = texture2D(u_map, new_uv).r;

      gl_FragColor = vec4(u_color, u_opacity * mapColor);
    }
  `;
  const texture = createCanvasTexture();
  const usize = size * window.devicePixelRatio;
  const material = new ShaderMaterial({
    uniforms: {
      u_size: { value: usize },
      u_color: { value: new Color(0xff0000) },
      u_opacity: { value: 1 },
      u_map: { value: texture },
      u_aspect: { value: aspect },
    },
    defines: {
      PI: Math.PI,
    },
    vertexShader,
    fragmentShader,
    transparent: true,
  });
  return material;
}

需要注意的是shader里面获取到的向量还需要除以canvas真实宽高比,否则计算出来的角度会不对.

附上github源码及demo

github源码地址
示例demo