zoukankan      html  css  js  c++  java
  • 拾取模型的原理及其在THREE.JS中的代码实现

    1. Three.js中的拾取 

    1.1. 从模型转到屏幕上的过程说开

      由于图形显示的基本单位是三角形,那就先从一个三角形从世界坐标转到屏幕坐标说起,例如三角形abc

     

    乘以模型视图矩阵就进入了视点坐标系,其实就是相机所在的坐标系,如下图:

     

    进入视点坐标系后,再乘以投影矩阵,就会变换到一个立方体内,如下图:

     

    这个时候整个三角形就位于中心位于坐标系原点,边长为2的立方体内,在这个立方体内,三角形要计算光照,要裁剪,然后乘以视口矩阵,最后转到屏幕上。

     

     

    转到屏幕上后,三角形的所有点的Z坐标就是深度坐标,一定在(0, 1)这个区间内,那么哪些点的Z坐标是0呢,在投影坐标系中,一定是投影视景体的前剪切平面上的点,而投影视景体的后剪切平面上的点的Z坐标就是1

    1.2. 思路来了

       根据以上三角形转换到屏幕坐标上的过程可以分析出,鼠标在屏幕上点击的时候,可以得到二维坐标p(x, y),再加上深度坐标的范围(0, 1), 就可以形成两个三位坐标A(x, y, 0), B(x, y, 1),  由于它们的Z轴坐标是01,则转变到投影坐标系的话,一定分别是前剪切平面上的点和后剪切平面上的点,也就是说,在投影坐标系中,A点一定在能看见的所有模型的最前面,B点一定在能看见的所有的模型的最后边,假设视口矩阵的逆矩帧,投影矩阵的逆矩阵,模型视图矩阵的逆矩阵为M, N, P,则 P * N * M * A = A1,  P * N * M * B = B1, 在世界坐标系中,点A1B1就可以形成一个射线,此射线和模型再求交,就能选中模型。如下图是在视点坐标系中的情形。注意,求交可以在视点坐标系或者世界坐标系计算都可以,但一般会在世界坐标坐标系中计算。

     

    1.3. 拾取的优化,射线和AABB包围盒求交

        如果射线和所有的模型求交,显然不是一个好办法,一般情况下会进行一些优化,比如先和模型的包围盒求交,如果和模型的包围盒不相交的话,就放过去,否则就接着往下进行,和模型的所有三角面片求交。

          那么什么是包围盒呢?在计算机图形学与计算几何领域,一组物体的包围体就是将物体组合完全包容起来的一个封闭空间。将复杂物体封装在简单的包围体中,就可以提高几何运算的效率。通常简单的物体比较容易检查相互之间的重叠。其中有一种包围盒叫做AABB, AABB的全称是axis aligned bounding box,就是我们常常提到轴向包围盒,这个盒子的边是平行于x/y/z轴的。 所有的2d3d物体都是由点组成的,所以只要找出这些物体的最大值点和最小值点,那么就可以使用这两个点表示该物体的AABB包围盒了。
           检测碰撞的时候我们只需要检测这些物体的AABB(即他们的最大值点和最小值点)是否相交,就可以判断是否碰撞了。

     

     

    1.4. 射线和三角形相交

         判断射线和包围盒是否求交后,就轮到判断是否和三角形求交了,最先想到的是 首先判断射线是否与三角形所在的平面相交,如果相交,再判断交点是否在三角形内。判断射线是否与平面相交, 判断点是否在三角形内.

    1.5. THREE.JS中求交的代码实现

      three.js中的一个案例,名字叫webgl_interactive_lines.html,可以选中一根线,并显示一个小球。根据以上的思路,代码注释如下:

    //鼠标点击的屏幕坐标转换到视点坐标系

    var vector = new THREE.Vector3( mouse.x, mouse.y, 1 ).unproject( camera );

     //在视点坐标系中形成射线

     raycaster.set( camera.position,vector.sub( camera.position ).normalize() );

     //射线和模型求交,选中一系列直线

    var intersects = raycaster.intersectObjects( parentTransform.children, true);

    if ( intersects.length > 0 ) {

    if ( currentIntersected !== undefined )

     {

     currentIntersected.material.linewidth = 1;

     }

       //第一个直线

    currentIntersected = intersects[ 0 ].object;

    currentIntersected.material.linewidth = 5;

        //把球设为可见,并且位置移到鼠标点击的屏幕位置

    sphereInter.visible = true;

        sphereInter.position.copy( intersects[ 0 ].point );

    }

    欢迎加微信 nuonuodi_1, 交流更多的技术问题

  • 相关阅读:
    ui、li模拟下拉框
    六项精进
    Echarts柱状图添加点击事件
    [UWP]爱恋动漫BT开发小记
    [杂谈]这个四月
    [uwp]自定义图形裁切控件
    [uwp]自定义Behavior之随意拖动
    [uwp]数据绑定再学习
    [mvc]记一次“项目”的历程
    [uwp]ImageSource和byte[]相互转换
  • 原文地址:https://www.cnblogs.com/lizhengjin/p/5914216.html
Copyright © 2011-2022 走看看