安卓OpenGL ES:如何选择一个2D对象?

4
我一直在Stack Overflow上寻找OpenGL ES中2D选择的入门教程,但大多数都是关于3D的问题。
我正在使用OpenGL ES设计一个运行在Android 4.0.3上的2D基于瓦片的关卡编辑器。在编辑器中,有一个2D的黄色正方形对象位于屏幕中心。我想要实现的是检测用户是否触摸了该对象。
在编辑器中,没有任何瓦片重叠。相反,它们是并排放置的,就像MS Paint中位图图像中的两个相邻像素一样。我的目的是为了能够单独检测到关卡编辑器中每个正方形对象的触摸事件。
该对象是由简单的顶点数组创建的,并使用GL_TRIANGLES绘制2个平面的右三角形。没有任何操作和从文件加载等内容。我唯一知道的是,如果用户触摸了黄色三角形中的任何一个,则两个黄色三角形都将被选中。
有人可以提供一些提示,告诉我应该如何做吗?谢谢。
编辑:以下是draw()函数:
public void draw(GL10 gl) {
    gl.glPushMatrix();
    gl.glTranslatef(-(deltaX - translateX), (deltaY - translateY), 1f);
    gl.glColor4f(1f, 1f, 0f, 1f);
    //TODO: Move ClientState and MatrixStack outside of draw().
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertices);
    gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 6);
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glPopMatrix();
}

编辑2:

我还缺少一些信息。您是否在使用相机?或在模型渲染之前推送其他矩阵?例如,如果您正在使用正交相机,则可以轻松地取消投影屏幕坐标[x_screen,y_screen](y类似于以下内容):

我没有使用相机,但可能正在使用正交投影。再次说明,我不知道,因为我只是使用了一个常见的OpenGL函数。我进行了矩阵推送和弹出,因为我计划将许多具有不同平移矩阵的平铺(方形2D对象)集成在一起。没有两个平铺会具有相同的平移矩阵M。

当涉及到2D时,透视投影与正交投影是否相同?我没有看到这两者之间的区别。

下面是创建表面时的初始设置(扩展GLSurfaceView类,并实现GLSurfaceView.Renderer):

public void onSurfaceChanged(GL10 gl, int width, int height) {
    gl.glViewport(0, 0, width, height);
}

public void onSurfaceCreated(GL10 gl, EGLConfig arg1) {
    reset();
}
public void onDrawFrame(GL10 gl) {
    clearScreen(gl);
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();
    gl.glOrthof(0f, super.getWidth(), 0f, super.getHeight(), 1, -1);
    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity();
    canvas.draw(gl);
}

private void clearScreen(GL10 gl) {
    gl.glClearColor(0.5f, 1f, 1f, 1f);
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
}
3个回答

2
一个基本的方法如下:
  1. 为每个“可触摸”对象定义一个边界框。这可以是一个矩形(x,y,宽度,高度)。
  2. 当您更新世界中的一个瓷砖时,您要更新其边界框(完全以世界坐标为准)。
  3. 当用户触摸屏幕时,您必须将屏幕坐标转换为世界坐标
  4. 检查非投影点是否与任何边界框重叠。
关于前面几项的一些提示。
  • 1 and 2. You should have to keep track of where you are rendering your tiles. Store their position and size. A rectangle is a convenient structure. In your example it could be computed like this. And you have to recompute it when model changes. Lets call it Rectangle r:

    r.x = yourTile.position.x -(deltaX - translateX)  
    r.y = yourTile.position.y -(deltaY - translateY)
    r.width= yourTile.width  //as there is no model scaling
    r.height = yourTile.height//
    
  • 3 - if you are using an orthographic camera, you can easily unproject your screen coordinates [x_screen, y_screen] like this (y is analogous):

    x_model = ((x_screen/GL_viewport_width) -0.5 )*camera.WIDTH + Camera.position.x
    
  • 4 - For each of your Rectangles check if [x_model; y_model] is inside it.

[第二次编辑] 顺便提一下,您正在更新矩阵时,可以考虑使用位置surfaceView.width()/2,surfaceView.height()/2的相机。您将屏幕上的1个像素匹配到世界上的1个单位,因此您不需要反投影任何内容。您可以将这些值替换为我的公式,并获得x_screen = x_model -(由于Java中的Y向下增长,GL中向上增长,因此您需要翻转触摸事件的Y分量)。

最后。如果用户触摸点[x,y],请检查[x,screenHeight-y]*是否命中您的某些矩形,然后您就完成了。进行一些调试,记录触摸点并查看它们是否符合预期。生成您的矩形并查看它们是否与屏幕上看到的相匹配,然后只需检查一个点是否在矩形内即可。

我必须告诉您,您不应将相机设置为屏幕尺寸,因为您的应用程序在不同设备上会有很大的差异。这是一个单独的话题,所以我不会再深入探讨,但请考虑使用与屏幕大小无关的世界单位定义模型。这已经偏离了主题,但我希望您已经对所需了解的内容有了一个好的了解!

*我告诉过您的翻转问题。 PS:坚持使用正交投影(透视投影将更复杂)。


对于#2,我并没有说它们是静态的,但你有点暗示了我写的方式会使对象“静态”。我该怎么做?对于#3,我已经添加了一些代码供您查看。 - tom_mai78101
我已经添加了更多信息,请查看。 - tom_mai78101
现在你应该能够检测到选择了,但我建议你研究一下相机,并将模型与视图/网格分离。这肯定会改善你的代码!想象一下,如果你进行了一些翻译、缩放,你移动用户所看到的内容,放大/缩小,仍然需要检测选择,可能还有碰撞,以免它们重叠...如果你有一个良好组织的代码,只用瓷砖就可以几乎轻松完成! - aacotroneo

1
请允许我对您的问题发表第二个回答。这是完全更高层次/哲学性的。也许是一个愚蠢、无用的答案,但我希望它能帮助一些新手改变他们对“图形模式”的看法。
你不能真正在屏幕上选择一个三角形。那个正方形不是两个三角形。那个正方形只是一堆黄色像素。OpenGL取一些顶点,连接它们,处理它们并在屏幕上着色一些像素。在图形管道的某个阶段,甚至几何信息也会丢失,你只有孤立的像素。这类似于打印机在纸上打印的信件。通常你不会从纸上处理信息(好吧,也许条形码读取器会:D)
如果你需要进一步处理你的绘画作品,你必须使用辅助数据结构对它们进行建模和处理。这就是为什么我建议你创建一个矩形来模拟你的瓷砖。你创建你的想象中的“物体世界”,然后将它们渲染到屏幕上。用户触摸事件不属于同一个世界,所以你必须将屏幕坐标“转换”为你的世界坐标。然后你改变你的世界中的某些东西(也许用户拖动手指,你必须移动一个对象),再告诉OpenGL将你的世界渲染到屏幕上。

你应该在模型上操作,而不是视图上。网格更多的是视图的东西,所以你不应该将它们与模型信息混合在一起,将它们分开是一个好习惯。(请专家纠正我,我只是一个图形爱好者)


0
你有没有看过LibGDX
在使用OpenGL ES时,它可以让生活变得更加轻松。

这不是我要找的解决方案。请再次阅读问题。谢谢。 - tom_mai78101

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接