如何单击THREE.js中的对象

Sta*_*ers 7 three.js

我正在努力完成这本书,我想我做得很好,但是我已经找到了一些我并没有真正得到的东西.

以下是如何在单击的3D空间中登录控制台和对象的方法:

renderer.domElement.addEventListener('mousedown', function(event) {
    var vector = new THREE.Vector3(
        renderer.devicePixelRatio * (event.pageX - this.offsetLeft) / this.width * 2 - 1,
        -renderer.devicePixelRatio * (event.pageY - this.offsetTop) / this.height * 2 + 1,
        0
    );

    projector.unprojectVector(vector, camera);

    var raycaster = new THREE.Raycaster(
        camera.position,
        vector.sub(camera.position).normalize()
    );

    var intersects = raycaster.intersectObjects(OBJECTS);
    if (intersects.length) {
        console.log(intersects[0]);
    }
}, false);
Run Code Online (Sandbox Code Playgroud)

这是本书对此代码如何工作的解释:

前面的代码在渲染器的画布上侦听mousedown事件.

得到它,我们通过使用找到渲染器正在使用的domElement renderer.domElement.然后我们用它绑定一个事件监听器addEventListner,并指定我们想要监听一个mousedown.单击鼠标时,我们启动匿名函数并将event变量传递给函数.

然后,它创建一个新的Vector3实例,屏幕上的鼠标坐标相对于画布中心的百分比为画布宽度的百分比.

什么?我得到了我们如何创建一个新实例new THREE.Vector3,我得到的三个参数Vector3是它的x, y and z坐标,但这就是我的理解完全彻底崩溃的地方.

首先,我在这里做一个假设,但是为了绘制一个矢量,你必须在空间中需要两个点来投射?如果你只给它一套坐标,它怎么知道投射的方向?我的猜测是你实际用它Raycaster来绘制"矢量"......

现在我们传递的论点Vector3......我得到的z是0.因为我们只对我们点击屏幕的地方感兴趣.我们可以向上或向下,向左或向右单击,但不能进入或退出屏幕,因此我们将其设置为零.现在让我们来解决x:

renderer.devicePixelRatio * (event.pageX - this.offsetLeft) / this.width * 2 - 1,
Run Code Online (Sandbox Code Playgroud)

我们得到了设备的PixelRatio,按照我们沿x轴点击的位置,除以渲染器的domElement宽度,将其除以2并取走一个.

如果你没有得到什么,你需要说出你得到的东西,这样人们才能最好地帮助你.所以当我说:我觉得这样的傻瓜时:

  • 我不明白为什么我们甚至需要像素比率我不明白为什么我们按照我们点击x的位置
  • 我不明白为什么我们把它除以宽度
  • 完全不明白为什么我们需要时间乘以2并带走1.时间乘2,带走1.那真的可能是大象的时代,带走花生,它会有同样的意义.

我变得y更少了:

-renderer.devicePixelRatio * (event.pageY - this.offsetTop) / this.height * 2 + 1,
Run Code Online (Sandbox Code Playgroud)

为什么我们现在随机使用-devicePixelRatio?为什么现在决定add一个而不是minus一个?

然后,该矢量相对于相机未投影(从2D到3D空间).

什么?

一旦我们在3D空间中有一个代表鼠标位置的点,我们就使用Raycaster为它绘制一条线.它收到的两个参数是起点和结束点的方向.

好的,我明白了,这就是我上面提到的.我们如何需要两点来绘制"矢量".在三次谈话中,一个向量似乎被称为"raycaster".

但是,我们作为论点传递给它的两点并没有多大意义.如果我们传入相机position和矢量position并从这两点中绘制投影,我会得到它,实际上我们使用的camera.position是第一点,但是

vector.sub(camera.position).normalize()
Run Code Online (Sandbox Code Playgroud)

我们为什么要减去camera.position?我们为什么要正常化?为什么这本无用的书不想解释什么?

我们通过减去鼠标和摄像机位置来获得方向,然后对结果进行标准化,该结果将每个维度除以向量的长度以对其进行缩放,以使任何维度的值都不大于1.

什么?我不是懒惰,过去没有一句话by在这里有意义.

最后,我们使用ray通过intersectObjects方法检查哪些对象位于给定方向(即鼠标下).OBJECTS是要检查的对象数组(通常是网格); 请务必为您的代码更改它.返回鼠标后面的对象数组并按距离排序,因此第一个结果是单击的对象.intersects数组中的每个对象都有一个object,point,face和distance属性.这些属性的值分别是单击的对象(通常是网格),表示空间中单击位置的Vector3实例,单击位置处的Face3实例以及从相机到单击点的距离.

我明白了.我们抓住矢量通过的所有对象,将它们按距离顺序放入数组并记录第一个,即最近的一个:

console.log(intersects[0]);
Run Code Online (Sandbox Code Playgroud)

老实说,你认为我应该放弃三个吗?我的意思是,我肯定已经掌握了它,我理解它的所有编程方面,创建新实例,使用数据对象,如数组,使用匿名函数和传入变量,但每当我点击数学我似乎痛苦地停下来.

或者这真的很难吗?你觉得这很棘手吗?只是这本书并不觉得有必要详细解释,也没有其他 答案,好像这些东西对大多数人来说都是正常的.我觉得自己像个白痴.我应该放弃吗?我想制作3D游戏.我真的非常想要,但我被创造一个整个世界的诗意所吸引.不是数学.如果我说我没有发现数学困难,我会撒谎.

Eva*_*man 13

我理解你的麻烦,我来帮忙.您似乎有一个主要问题:对矢量执行哪些操作以准备点击检测?

让我们回顾一下原始声明vector:

var vector = new THREE.Vector3(
    renderer.devicePixelRatio * (event.pageX - this.offsetLeft) / this.width * 2 - 1,
    -renderer.devicePixelRatio * (event.pageY - this.offsetTop) / this.height * 2 + 1,
    0
);
Run Code Online (Sandbox Code Playgroud)
  • renderer.devicePixelRatio 涉及虚拟站点像素/真实设备像素的比率
  • event.pageX并且.pageY是mouseX,mouseY
  • this上下文renderer.domElement,所以.width, .height, .offsetLeft/Right涉及到的
  • 1 似乎是计算的校正"神奇"数字(为了尽可能在视觉上精确)

我们不关心z值,THREE会为我们处理.X和Y是我们最关心的问题.让我们推导出来:

  1. 我们首先找到鼠标到画布边缘的距离: event.pageX - this.offsetLeft
  2. 我们除以它this.width来得到mouseX占屏幕宽度的百分比
  3. 我们乘以renderer.devicePixelRatio从设备像素转换为站点像素
  4. 我不确定为什么我们会乘以2,但它可能与用户有视网膜显示器的假设有关(如果这是错误的话,有人可以随意纠正我).
  5. 1 再次,魔术可以解决可能只是偏移误差的问题
  6. 因为y,我们将整个表达式乘以-1补偿倒置坐标系(0是顶部,this.height是底部)

因此,您可以获得向量的以下参数:

  renderer.devicePixelRatio * (event.pageX - this.offsetLeft) / this.width * 2 - 1,
  -renderer.devicePixelRatio * (event.pageY - this.offsetTop) / this.height * 2 + 1,
  0
Run Code Online (Sandbox Code Playgroud)

现在,对于下一位,几个术语:

  • 归一化矢量意味着将其简化为小于1的x,y和z分量.为此,您只需将向量的x,y和z分量除以向量的大小即可.它似乎毫无用处,但它很重要,因为它在鼠标向量的方向上创建了一个单位向量(幅度= 1)!
  • Raycaster投射穿过帆布产生的三维景观的载体.它的构造函数是THREE.Raycaster( origin, direction )

考虑到这些条款,我可以解释为什么我们这样做:vector.sub(camera.position).normalize().首先,我们得到描述从鼠标位置矢量到相机位置矢量的距离的矢量vector.sub(camera.position).然后,我们将其标准化以使其成为direction矢量(同样,幅度= 1).这样,我们就可以在鼠标位置的方向上将摄像机从摄像机投射到3D空间!此操作允许我们通过比较对象位置和光线矢量来找出鼠标下的任何对象.

我希望这有帮助.如果您还有其他问题,请随时发表评论,我会尽快回答.

哦,不要让数学劝阻你.THREE.js本质上是一门数学繁重的语言,因为你在3D空间中操纵物体,但经验将帮助你克服这些理解障碍.我会继续学习并回答Stack Overflow的问题.培养数学能力可能需要一些时间,但如果你不尝试,你就不会学习!