【问题标题】:Three.js: Determining World coordinates of mouse positionThree.js:确定鼠标位置的世界坐标
【发布时间】:2019-02-07 23:58:31
【问题描述】:

我有一个带点的 Three.js 场景,我试图弄清楚我的点的位置和屏幕坐标之间的关系。我以为我可以使用 @WestLangley 提供给 previous question 的函数,但实现这个函数引起了一些混乱。

在下面的场景中,我将左侧和最右侧点的 x 坐标存储在 world.bb.x 中,并在每次鼠标移动时记录光标的世界坐标。但是,当我将鼠标移到最左边和最右边的点时,世界坐标与 world.bb.x 中的最小或最大 x 坐标值不匹配,这是我所期望的。

其他人是否知道我可以在任何给定时间找出光标的世界坐标?非常感谢其他人可以提供的任何帮助!

function World() {
  this.scene = this.getScene();
  this.camera = this.getCamera();
  this.renderer = this.getRenderer();
  this.controls = this.getControls();
  this.color = new THREE.Color();
  this.addPoints();
  this.render();
}

World.prototype.getScene = function() {
  var scene = new THREE.Scene();
  scene.background = new THREE.Color(0xefefef);
  return scene;
}

World.prototype.getCamera = function() {
  var renderSize = getRenderSize(),
      aspectRatio = renderSize.w / renderSize.h,
      camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 100000);
  camera.position.set(0, 1, -10);
  return camera;
}

World.prototype.getRenderer = function() {
  var renderSize = getRenderSize(),
      renderer = new THREE.WebGLRenderer({antialias: true});
  renderer.setPixelRatio(window.devicePixelRatio); // retina displays
  renderer.setSize(renderSize.w, renderSize.h); // set w,h
  find('#gl-target').appendChild(renderer.domElement);
  return renderer;
}

World.prototype.getControls = function() {
  var controls = new THREE.TrackballControls(this.camera, this.renderer.domElement);
  controls.zoomSpeed = 0.4;
  controls.panSpeed = 0.4;
  return controls;
}

World.prototype.render = function() {
  requestAnimationFrame(this.render.bind(this));
  this.renderer.render(this.scene, this.camera);
  this.controls.update();
}

World.prototype.getMouseWorldCoords = function(e) {
  var vector = new THREE.Vector3(),
      camera = world.camera,
      x = (e.clientX / window.innerWidth) * 2 - 1,
      y = (e.clientY / window.innerHeight) * 2 + 1;
  vector.set(x, y, 0.5);
  vector.unproject(camera);
  var direction = vector.sub(camera.position).normalize(),
      distance = - camera.position.z / direction.z,
      scaled = direction.multiplyScalar(distance),
      coords = camera.position.clone().add(scaled);
  return {
    x: coords.x, y: coords.y,
  };
}

World.prototype.addPoints = function() {
  // this geometry builds a blueprint and many copies of the blueprint
  var IBG = THREE.InstancedBufferGeometry,
      BA = THREE.BufferAttribute,
      IBA = THREE.InstancedBufferAttribute,
      Vec3 = THREE.Vector3,
      Arr = Float32Array;

  // add data for each observation; n = num observations
  var geometry = new IBG(),
      n = 10000,
      rootN = n**(1/2),
      // find max min for each dim to center camera
      xMax = Number.NEGATIVE_INFINITY,
      xMin = Number.POSITIVE_INFINITY,
      yMax = Number.NEGATIVE_INFINITY,
      yMin = Number.POSITIVE_INFINITY;

  var translations = new Arr(n * 3),
      colors = new Arr(n * 3),
      uidColors = new Arr(n * 3),
      translationIterator = 0,
      colorIterator = 0,
      uidColorIterator = 0;

  var colorMap = this.getColorMap();

  for (var i=0; i<n; i++) {
    var x = Math.sin(i) * 4,
        y = Math.floor(i / (n/20)) * 0.3,
        color = colorMap[ Math.floor(i / (n/20)) ],
        uidColor = this.color.setHex(i + 1);
    if (x > xMax) xMax = x;
    if (x < xMin) xMin = x;
    if (y > yMax) yMax = y;
    if (y < yMin) yMin = y;
    translations[translationIterator++] = x;
    translations[translationIterator++] = y;
    translations[translationIterator++] = 0;
    colors[colorIterator++] = color.r / 255;
    colors[colorIterator++] = color.g / 255;
    colors[colorIterator++] = color.b / 255;
    uidColors[uidColorIterator++] = uidColor.r;
    uidColors[uidColorIterator++] = uidColor.g;
    uidColors[uidColorIterator++] = uidColor.b;
  }

  // store the min and max coords in each dimension
  this.bb = {
    x: {
      min: xMin,
      max: xMax,
    },
    y: {
      min: yMin,
      max: yMax,
    }
  }

  // center the camera
  this.center = {
    x: (xMax + xMin) / 2,
    y: (yMax + yMin) / 2
  }
  this.camera.position.set(this.center.x, this.center.y, -6);
  this.camera.lookAt(this.center.x, this.center.y, 0);
  this.controls.target = new Vec3(this.center.x, this.center.y, 0);

  // add attributes
  geometry.addAttribute('position', new BA( new Arr([0, 0, 0]), 3));
  geometry.addAttribute('translation', new IBA(translations, 3, 1) );
  geometry.addAttribute('color', new IBA(colors, 3, 1) );
  geometry.addAttribute('uidColor', new IBA(uidColors, 3, 1) );

  var material = new THREE.RawShaderMaterial({
    vertexShader: find('#vertex-shader').textContent,
    fragmentShader: find('#fragment-shader').textContent,
  });

  var mesh = new THREE.Points(geometry, material);
  mesh.frustumCulled = false; // prevent the mesh from being clipped on drag
  this.scene.add(mesh);
}

World.prototype.getColorMap = function() {

  function toHex(c) {
    var hex = c.toString(16);
    return hex.length == 1 ? '0' + hex : hex;
  }

  function rgbToHex(r, g, b) {
    return '#' + toHex(r) + toHex(g) + toHex(b);
  }

  function hexToRgb(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
      r: parseInt(result[1], 16),
      g: parseInt(result[2], 16),
      b: parseInt(result[3], 16),
    } : null;
  }

  var hexes = [
    '#fe4445','#ff583b','#ff6a2f','#ff7a20','#ff8800',
    '#ff9512','#ffa31f','#ffaf2a','#ffbb34',
    '#cfc522','#99cc01',
    '#91c14a','#85b66e','#73ac8f','#57a3ac','#0099cb',
    '#14a0d1','#20a7d8','#2aaedf','#33b5e6'
  ]

  var colorMap = {};
  hexes.forEach(function(c, idx) { colorMap[idx] = hexToRgb(c) })
  return colorMap;
}

/**
* Helpers
**/

function getRenderSize() {
  var elem = find('#gl-target');
  return {
    w: elem.clientWidth,
    h: elem.clientHeight,
  }
}

function find(selector) {
  return document.querySelector(selector);
}

/**
* Main
**/

var world = new World();
world.controls.enabled = false;

find('canvas').addEventListener('mousemove', function(e) {
  find('#bar').style.left = e.clientX + 'px';
  var coords = world.getMouseWorldCoords(e);
  console.log(coords, world.bb.x);
})
html, body {
  width: 100%;
  height: 100%;
  background: #000;
}

body {
  margin: 0;
  overflow: hidden;
}

canvas {
  width: 100%;
  height: 100%;
}

.gl-container {
  position: relative;
}

#gl-target {
  width:700px;
  height:400px
}

#bar {
  width: 1px;
  height: 100%;
  display: inline-block;
  position: absolute;
  left: 30px;
  background: red;
}
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.min.js'></script>
<script src='https://rawgit.com/YaleDHLab/pix-plot/master/assets/js/trackball-controls.js'></script>

<script type='x-shader/x-vertex' id='vertex-shader'>
precision highp float;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

attribute vec3 position;
attribute vec3 translation;

#ifdef PICKING
  attribute vec3 uidColor;
  varying vec3 vUidColor;
#else
  attribute vec3 color;
#endif

varying vec3 vColor;

void main() {
  #ifdef PICKING
    vUidColor = uidColor;
  #else
    vColor = color;
  #endif

  // set point position
  vec3 raw = position + translation;
  vec4 pos = projectionMatrix * modelViewMatrix * vec4(raw, 1.0);
  gl_Position = pos;

  // set point size
  gl_PointSize = 10.0;
}
</script>

<script type='x-shader/x-fragment' id='fragment-shader'>
precision highp float;

#ifdef PICKING
  varying vec3 vUidColor;
#else
  varying vec3 vColor;
#endif

void main() {

  // make point circular
  vec2 coord = gl_PointCoord - vec2(0.5);
  if (length(coord) > 0.5) discard;

  // color the point
  #ifdef PICKING
    gl_FragColor = vec4(vUidColor, 1.0);
  #else
    gl_FragColor = vec4(vColor, 1.0);
  #endif
}
</script>

<div class='gl-container'>
  <div id='bar'></div>
  <div id='gl-target'></div>
</div>

【问题讨论】:

    标签: three.js


    【解决方案1】:

    啊哈,我不需要将事件 x 和 y 坐标除以窗口宽度(仅适用于延伸到整个窗口高度和宽度的画布),我需要将事件 x 和 y 坐标除以画布的宽度和身高!

    function World() {
      this.scene = this.getScene();
      this.camera = this.getCamera();
      this.renderer = this.getRenderer();
      this.color = new THREE.Color();
      this.addPoints();
      this.render();
    }
    
    World.prototype.getScene = function() {
      var scene = new THREE.Scene();
      scene.background = new THREE.Color(0xefefef);
      return scene;
    }
    
    World.prototype.getCamera = function() {
      var renderSize = getRenderSize(),
          aspectRatio = renderSize.w / renderSize.h,
          camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 100000);
      camera.position.set(0, 1, -10);
      return camera;
    }
    
    World.prototype.getRenderer = function() {
      var renderSize = getRenderSize(),
          renderer = new THREE.WebGLRenderer({antialias: true});
      renderer.setPixelRatio(window.devicePixelRatio); // retina displays
      renderer.setSize(renderSize.w, renderSize.h); // set w,h
      find('#gl-target').appendChild(renderer.domElement);
      return renderer;
    }
    
    World.prototype.render = function() {
      requestAnimationFrame(this.render.bind(this));
      this.renderer.render(this.scene, this.camera);
    }
    
    World.prototype.getMouseWorldCoords = function(e) {
      var elem = find('#gl-target'),
          vector = new THREE.Vector3(),
          camera = world.camera,
          x = (e.clientX / elem.clientWidth) * 2 - 1,
          y = (e.clientY / elem.clientHeight) * 2 + 1;
      vector.set(x, y, 0.5);
      vector.unproject(camera);
      var direction = vector.sub(camera.position).normalize(),
          distance = - camera.position.z / direction.z,
          scaled = direction.multiplyScalar(distance),
          coords = camera.position.clone().add(scaled);
      return {
        x: coords.x,
        y: coords.y,
      };
    }
    
    World.prototype.addPoints = function() {
      // this geometry builds a blueprint and many copies of the blueprint
      var IBG = THREE.InstancedBufferGeometry,
          BA = THREE.BufferAttribute,
          IBA = THREE.InstancedBufferAttribute,
          Vec3 = THREE.Vector3,
          Arr = Float32Array;
    
      // add data for each observation; n = num observations
      var geometry = new IBG(),
          n = 10000,
          rootN = n**(1/2),
          // find max min for each dim to center camera
          xMax = Number.NEGATIVE_INFINITY,
          xMin = Number.POSITIVE_INFINITY,
          yMax = Number.NEGATIVE_INFINITY,
          yMin = Number.POSITIVE_INFINITY;
    
      var translations = new Arr(n * 3),
          colors = new Arr(n * 3),
          uidColors = new Arr(n * 3),
          translationIterator = 0,
          colorIterator = 0,
          uidColorIterator = 0;
    
      var colorMap = this.getColorMap();
    
      for (var i=0; i<n; i++) {
        var x = Math.sin(i) * 4,
            y = Math.floor(i / (n/20)) * 0.3,
            color = colorMap[ Math.floor(i / (n/20)) ],
            uidColor = this.color.setHex(i + 1);
        if (x > xMax) xMax = x;
        if (x < xMin) xMin = x;
        if (y > yMax) yMax = y;
        if (y < yMin) yMin = y;
        translations[translationIterator++] = x;
        translations[translationIterator++] = y;
        translations[translationIterator++] = 0;
        colors[colorIterator++] = color.r / 255;
        colors[colorIterator++] = color.g / 255;
        colors[colorIterator++] = color.b / 255;
        uidColors[uidColorIterator++] = uidColor.r;
        uidColors[uidColorIterator++] = uidColor.g;
        uidColors[uidColorIterator++] = uidColor.b;
      }
    
      // store the min and max coords in each dimension
      this.bb = {
        x: {
          min: xMin,
          max: xMax,
        },
        y: {
          min: yMin,
          max: yMax,
        }
      }
    
      // center the camera
      this.center = {
        x: (xMax + xMin) / 2,
        y: (yMax + yMin) / 2
      }
    
      this.camera.position.set(this.center.x, this.center.y, -6);
      this.camera.lookAt(this.center.x, this.center.y, 0);
     
      // add attributes
      geometry.addAttribute('position', new BA( new Arr([0, 0, 0]), 3));
      geometry.addAttribute('translation', new IBA(translations, 3, 1) );
      geometry.addAttribute('color', new IBA(colors, 3, 1) );
      geometry.addAttribute('uidColor', new IBA(uidColors, 3, 1) );
    
      var material = new THREE.RawShaderMaterial({
        vertexShader: find('#vertex-shader').textContent,
        fragmentShader: find('#fragment-shader').textContent,
      });
    
      var mesh = new THREE.Points(geometry, material);
      mesh.frustumCulled = false; // prevent the mesh from being clipped on drag
      this.scene.add(mesh);
    }
    
    World.prototype.getColorMap = function() {
    
      function toHex(c) {
        var hex = c.toString(16);
        return hex.length == 1 ? '0' + hex : hex;
      }
    
      function rgbToHex(r, g, b) {
        return '#' + toHex(r) + toHex(g) + toHex(b);
      }
    
      function hexToRgb(hex) {
        var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return result ? {
          r: parseInt(result[1], 16),
          g: parseInt(result[2], 16),
          b: parseInt(result[3], 16),
        } : null;
      }
    
      var hexes = [
        '#fe4445','#ff583b','#ff6a2f','#ff7a20','#ff8800',
        '#ff9512','#ffa31f','#ffaf2a','#ffbb34',
        '#cfc522','#99cc01',
        '#91c14a','#85b66e','#73ac8f','#57a3ac','#0099cb',
        '#14a0d1','#20a7d8','#2aaedf','#33b5e6'
      ]
    
      var colorMap = {};
      hexes.forEach(function(c, idx) { colorMap[idx] = hexToRgb(c) })
      return colorMap;
    }
    
    /**
    * Helpers
    **/
    
    function getRenderSize() {
      var elem = find('#gl-target');
      return {
        w: elem.clientWidth,
        h: elem.clientHeight,
      }
    }
    
    function find(selector) {
      return document.querySelector(selector);
    }
    
    /**
    * Main
    **/
    
    var world = new World();
    
    find('canvas').addEventListener('mousemove', function(e) {
      find('#bar').style.left = e.clientX + 'px';
      var coords = world.getMouseWorldCoords(e);
      console.log(coords, world.bb.x);
    })
    html, body {
      width: 100%;
      height: 100%;
      background: #000;
    }
    
    body {
      margin: 0;
      overflow: hidden;
    }
    
    canvas {
      width: 100%;
      height: 100%;
    }
    
    .gl-container {
      position: relative;
    }
    
    #gl-target {
      width:700px;
      height:400px
    }
    
    #bar {
      width: 1px;
      height: 100%;
      display: inline-block;
      position: absolute;
      left: 30px;
      background: red;
    }
    <div class='gl-container'>
      <div id='bar'></div>
      <div id='gl-target'></div>
    </div>
    
    <script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/95/three.min.js'></script>
    
    <script type='x-shader/x-vertex' id='vertex-shader'>
    precision highp float;
    
    uniform mat4 modelViewMatrix;
    uniform mat4 projectionMatrix;
    
    attribute vec3 position;
    attribute vec3 translation;
    
    #ifdef PICKING
      attribute vec3 uidColor;
      varying vec3 vUidColor;
    #else
      attribute vec3 color;
    #endif
    
    varying vec3 vColor;
    
    void main() {
      #ifdef PICKING
        vUidColor = uidColor;
      #else
        vColor = color;
      #endif
    
      // set point position
      vec3 raw = position + translation;
      vec4 pos = projectionMatrix * modelViewMatrix * vec4(raw, 1.0);
      gl_Position = pos;
    
      // set point size
      gl_PointSize = 10.0;
    }
    </script>
    
    <script type='x-shader/x-fragment' id='fragment-shader'>
    precision highp float;
    
    #ifdef PICKING
      varying vec3 vUidColor;
    #else
      varying vec3 vColor;
    #endif
    
    void main() {
    
      // make point circular
      vec2 coord = gl_PointCoord - vec2(0.5);
      if (length(coord) > 0.5) discard;
    
      // color the point
      #ifdef PICKING
        gl_FragColor = vec4(vUidColor, 1.0);
      #else
        gl_FragColor = vec4(vColor, 1.0);
      #endif
    }
    </script>

    【讨论】:

      猜你喜欢
      • 2014-11-15
      • 1970-01-01
      • 2013-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-13
      • 2011-03-06
      • 2013-03-21
      相关资源
      最近更新 更多