在android opengl es中检测面向相机的立方体的一侧

Posted

技术标签:

【中文标题】在android opengl es中检测面向相机的立方体的一侧【英文标题】:detecting the side of a cube that is facing the camera in android opengl es 【发布时间】:2012-06-30 00:52:11 【问题描述】:

所以我开始创建一个应用程序来在 android 上学习 openGL es。首先,我通过一章解释了如何构建一个立方体并使用系统计时器让它旋转。然后我将每一侧映射到一个图像的不同部分。出于开发目的,每一面都带有一个数字纹理。然后我实现了拖动功能,允许用户根据他们的滑动方式向上/向下或向左/向右旋转立方体。

首先是我的问题的一些背景:

我想跟踪哪一侧朝向相机,因为每个面都在它开始的轴上旋转。例如,给定一个具有如下展开布局的立方体。

  2
  4
3 1 5
  6

其中 1 是面向屏幕的一侧,2 是相反(或背面),4 向上,5 向右,6 向下,3 向左。这意味着 3/5 在 x 轴上, y 轴为 4/6,z 轴为 1/2。

这是我的问题:

如果我只围绕 1 个轴旋转(即我只向左/向右或向上/向下直到我去 360),立方体会正确旋转,但如果我只去 90 180 或 270,那么我应该旋转的轴有切换。发生这种情况是因为上面提到的立方体的每一面都粘在它开始的轴上。

如果您向右旋转一次,使 5 面向,那么从用户角度看,z 轴就是立方体的 x 轴。当您开始向左/向右 90 度然后向上/向下 90 度等时,这会变得更加复杂。

我尝试使用从顶部数字顺时针排列的数字数组来跟踪面部,但取决于您来自哪个数字,周围数字的新方向发生了变化。

例如:

如果每个数字从 1 旋转到屏幕,我会绘制出围绕屏幕的数字

  4         2         4         1         4           
3 1 5     3 4 5     1 5 2     3 6 5     2 3 1     
  6         1         6         2         6      

而 2 是通配符,因为它可以从任何方向得到,所以从 1 开始没有真正的初始数字布局

  6  
3 2 5
  4  

所以我的数组将是

sidesOfFace1[] = 4,5,6,3
sidesOfFace2[] = 6,5,4,3
sidesOfFace3[] = 4,1,6,2
sidesOfFace4[] = 2,5,1,3
sidesOfFace5[] = 4,2,6,1
sidesOfFace6[] = 1,5,2,3

而且 MOVE 可以有值

UP = 1  RIGHT = 2  DOWN = 3  LEFT = 4

然后通过跟踪前一张脸、当前脸和最后一步,我试图找出一个公式来获得一个偏移量,这将帮助我选择下一张脸将被赋予移动到新脸的方向。我大致得出了这个解释:

prevface MOVE currFace
    1 -> UP(1) -> 4 -> RIGHT(2) -> 5

offset = opposite direction of MOVE - (sidesOfFace.indexOf(prevFace)+1)

So first I get
   1) DOWN - sidesOfFace4.indexOf(1)+1 => 3 - 3 = 0

   2) LEFT - sidesOfFace5.indexOf(4)+1 => 4 - 1 = 3

1) 这告诉我 4 周围的边与数组 sideOfFace 的顺序相同,从顶部开始顺时针方向。所以当用户再次滑动时,我可以知道我们要去哪一边。这对于能够设置立方体的正确旋转是必不可少的,因为随着立方体的转动,它们会随着观察者的变化而变化。

2) 这表明有一个偏移量,如果我们查看数组,索引 4 应该是 0,但是立方体已经旋转,使得 UP 侧现在位于索引 3,RIGHT 是 0,DOWN 是 1 , LEFT 为 2。

除了需要知道哪一侧面向屏幕以在我的应用程序中使用其他功能外,我还必须知道,因为根据哪一侧面向屏幕,我必须沿正确的轴旋转立方体。我正在跟踪 xRot 和 yRot,但这些旋转必须根据相机/用户视图发生,而不是立方体轴。

例如我发现:

axis for front face  up/down axis  right/left axis  (as seen by the camera)
    1   +z              +x             +y
    2   -z              -x             -y

    4   +y              +x             +z
    6   -y              -x             -z

    5   +x              +z             +y
    3   -x              -z             -y

这意味着根据面向屏幕的哪一侧,我必须围绕上/下轴进行 xRotations 并围绕右/左轴进行 yRotations。

一位朋友说可能会检查离相机最近的 4 个顶点,但由于我使用的是 glRotate 函数,所以我不确定从哪里可以获得这些信息。但我仍然需要知道正面的哪一侧有哪些数字,这样我才能自动旋转立方体。

如果您真的坐下来阅读所有这些,我真的很感激,如果您能引导我朝着正确的方向前进。也许一个链接,或者更好的是这个问题的已知解决方案将是惊人的。我已经为此苦苦挣扎了几天,我只是想知道这是否已经是一个有解决方案的问题。

谢谢大家,

艾伦

【问题讨论】:

【参考方案1】:

说实话,我没有完全阅读您帖子的最后 3/4,它看起来比实际需要的复杂得多。

但如果您只是想检测立方体的哪一侧最靠近相机,您只需要执行以下操作:

使用未旋转的立方体,为每个方向创建一个矢量:

vec3 left(-1, 0, 0)
vec3 right (1, 0, 0)
vec3 up(0, 1, 0)
etc...

然后获取立方体的当前模型视图矩阵。如果你通过立方体的模型视图变换法线,你将得到眼睛空间中的结果向量。

vec3 leftInEyeSpace = modelView * left;
vec3 upInEyeSpace = modelView * up;
...

这将是每个向量相对于您眼睛的方向。

然后定义一个从立方体中心指向相机的向量:

vec3 cubeToCamera= -normalize((modelView * vec4(0,0,0,1)).xyz);

然后你想用你的 'cubeToCamera' 向量获取每个向量的点积。因为点积随着向量间夹角的增加而减小,所以幅度最大的点积将是最面向相机的点积。

float leftDot = dot(cubeToCamera, leftInEyeSpace)
float rightDot = dot(cubeToCamera, rightInEyeSpace)
...

【讨论】:

我会进一步调查。我最大的问题是我正在尝试使其自动化,以便我可以输入边,并且将有一种简单的方法来按顺序显示每一边。要做到这一点,我必须知道每边转向哪条路,不管它来自哪个号码。这就是为什么描述如此之长,而且似乎令人费解的原因。您的解决方案适用于查找当前面向屏幕的面,但我还需要确定每个方向上的哪些边(与正面相关)。有没有一种可靠的方法可以确定其他向量指向的方向? 有没有一种可靠的方法可以确定其他向量指向的方向? 我认为这应该很容易。您已经有了六个眼睛空间方向向量(在我的示例中为“leftInEyeSpace”等)。因此,只需查看这些向量,您就可以判断每一面的朝向。类似于您可以看到哪一侧朝向相机,您也可以将这些向量与其他方向进行比较。如果变换后,“顶部”向量接近 (1,0,0),那么您知道它指向右侧。 @AlanDeLonga @AlanDeLonga :另外我意识到这与您最初想要的解决方案有点不同,我最初认为这是一个完全 3D 立方体,在所有轴上具有任意旋转,并且带有任意相机位置。如果您使用静态轴对齐相机将立方体锁定到六个方向之一,那么这可能是一个比您需要的更复杂的解决方案。但是,我认为这是在全 3D 中执行此类操作的“正确”方式,无论如何它都应该适合您。【参考方案2】:

有点令人费解,但这样的事情可以吗?我省略了一些方法,但希望您能理解我的总体思路,您应该能够使用布尔变量来确定哪一侧面向用户。

private Boolean right, left, spin;
private Boolean up, down, upsideDown;
private Boolean rightSide, leftSide;
// All false by default.

public void swipe(int swipeDirection)

    // Swipe Direction - 0 = Right, 1 = Left, 2 = Up, 3 = Down
    switch (swipeDirection)
    
    case 0:
        if (upsideDown)
        
            swipeLeft();
        
        else if (rightSide)
        
            swipeDown();
        
        else if (leftSide)
        
            swipeUp();
        
        else
        
            swipeRight();
        
        break;
    case 1:
        if (upsideDown)
        
            swipeRight();
        
        else if (rightSide)
        
            swipeUp();
        
        else if (leftSide)
        
            swipeDown();
        
        else
        
            swipeLeft();
        
        break;
    case 2:
        if (upsideDown)
        
            swipeDown();
        
        else if (rightSide)
        
            swipeRight();
        
        else if (leftSide)
        
            swipeLeft();
        
        else
        
            swipeUp();
        
        break;
    case 3:
        if (upsideDown)
        
            swipeUp();
        
        else if (rightSide)
        
            swipeLeft();
        
        else if (leftSide)
        
            swipeRight();
        
        else
        
            swipeDown();
        
        break;
    


private void swipeRight()

    if (right)
    
        right = false;
        spin = true;
    
    else if (left)
    
        left = false;
    
    else if (spin)
    
        spin = false;
        left = true;
    
    else if (up)
    
        up = false;
        if (rightSide)
        
            rightSide = false;
            upsideDown = true;
        
        else if (leftSide)
        
            leftSide = false;
        
        else
        
            rightSide = true;
        
    
    else if (down)
    
        down = false;
        if (leftSide)
        
            leftSide = false;
            upsideDown = true;
        
        else if (rightSide)
        
            rightSide = false;
        
        else
        
            leftSide = true;
        
    
    else
    
        right = true;
    


private void swipeUp()

    if (down)
    
        down = false;
    
    else if (up)
    
        upsideDown = !upsideDown;
    
    else if (upsideDown)
    
        upsideDown = false;
        up = true;
    

【讨论】:

以上是关于在android opengl es中检测面向相机的立方体的一侧的主要内容,如果未能解决你的问题,请参考以下文章

使用OpenGL的Android相机帧处理

Android音视频(六) 使用OpenGL ES 3.0预览Camera

Android自动裁剪相机捕获的图像

当阴影相机移动时,Opengl es 2.0阴影会闪烁

如何在 Android 中将 OpenCV 旋转和平移矢量与 OpenGL ES 一起使用?

使用 OpenGL 2.0 API 在 GLSurfaceView 上将 Android 相机预览旋转 90 度