glTranslatef and mouse clicks - gluUnProject
对OpenGL的长者的吸引力...。我在检测鼠标单击纹理化平面上的相对位置时遇到了很大的问题。
我正在制作一个游戏,在其中绘制单个大正方形并使用生成的大地图纹理对其进行纹理处理。该视图始终是自上而下的,并且您当前只能移动该正方形的X Y和Z坐标。
地图的屏幕截图
OpenGL初始化
1 2 3 4 5 6 7 8 9 | screenRatio = (float)screenW / (float)screenH; System.out.println("init"); glu = new GLU(); GL2 gl2 = drawable.getGL().getGL2(); gl2.glShadeModel( GL2.GL_SMOOTH ); gl2.glHint( GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL2.GL_NICEST ); gl2.glClearColor( 0f, 0f, 0f, 1f ); gl2.glDepthMask(false); gl2.glEnable(GL2.GL_DEPTH_TEST); |
设定相机位置
1 2 3 4 5 6 7 | gl2.glViewport(0, 0, 1024, 768); gl2.glMatrixMode( GL2.GL_PROJECTION ); gl2.glLoadIdentity(); glu.gluPerspective( 45, screenRatio, 1, 100 ); glu.gluLookAt( 0, 0, 3, 0, 0, 0, 0, 1, 0 ); gl2.glMatrixMode(GL2.GL_MODELVIEW); gl2.glLoadIdentity(); |
移动位置开始绘制地图
1 2 3 4 5 6 | // typical camera coord example: // CENTRE: 0.0f, 0.0f, 10f // FULL ZOOM OUT AND TOP LEFT: -25f, 25f, 40f // move position gl2.glTranslatef( -cameraX, -cameraY, -cameraZ ); |
我怀疑glTranslatef z坐标可能是可疑的。当我绘制正方形40f(例如)远离原点时
映射顶点信息
1 2 3 4 5 6 7 | // here are the coordinates/dimensions of my textured square ( my map ) float[] vertexArray = { -25f, 25f, 25f, 25f, 25f, -25f, 25f, -25f, }; |
鼠标点击位置计算
来自Java提示1628-how-to-use-gluunproject-in-jogl.html中的"借用"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | int x = mouse.getX(), y = mouse.getY(); int viewport[] = new int[4]; double mvmatrix[] = new double[16]; double projmatrix[] = new double[16]; int realy = 0; double wcoord[] = new double[4]; gl2.glGetIntegerv(GL2.GL_VIEWPORT, viewport, 0); gl2.glGetDoublev(GL2.GL_MODELVIEW_MATRIX, mvmatrix, 0); gl2.glGetDoublev(GL2.GL_PROJECTION_MATRIX, projmatrix, 0); realy = viewport[3] - (int) y - 1; glu.gluUnProject( (double) x, (double) realy, 0.0, // I have experimented with having this as 1.0 also mvmatrix, 0, projmatrix, 0, viewport, 0, wcoord, 0 ); |
使用近/远位(gluUnProject的第三个参数)进行实验似乎会产生更好的效果,但是似乎没有最佳效果(我发现的最佳值为0.945)
我非常希望mCX,mCY相对于渲染的地图坐标(-25f-25f),无论Z位置如何
1 2 | mCX = (float)wcoord[0]; mCY = (float)wcoord[1]; |
在平移的坐标处绘制一个矩形
1 2 3 4 5 6 7 | gl2.glColor3f(1.f, 0.f, 0.f); gl2.glBegin(GL2.GL_QUADS); gl2.glVertex2f( mCX-0.1f, mCY+0.1f ); gl2.glVertex2f( mCX+0.1f, mCY+0.1f ); gl2.glVertex2f( mCX+0.1f, mCY-0.1f ); gl2.glVertex2f( mCX-0.1f, mCY-0.1f ); gl2.glEnd(); |
目前,坐标相对于x和y平移效果很好,如果我单击屏幕的正中心,则无论我的glTranslatef运动如何,它都会在正确的位置大致绘制一个框。如果单击屏幕中心以外的地方,则会看到指数偏移。
指数偏移量的演示
当我单击屏幕的非常死点的中心时,它将在鼠标点的周围精确地绘制此淡紫色正方形,但是以最小的移动量将产生以下效果:
完全放大,单击中心右侧的几个像素
更新和工作...现在
在为地图生成纹理时,我还生成了一个替代纹理,该纹理将每个"平铺"表示为不同的颜色。在我的最初和当前尝试中,此图块的颜色是其X和Y坐标的函数(地图由100个图块和100个图块向下组成,因此x + y坐标的范围为0-99)
我最终得到的纹理看起来像是从绿色到红色的渐变。鼠标单击时,以下代码将快速渲染此纹理(用户无法感知)并在鼠标下读取rgb值。然后,我们将该rgb值转换为世界坐标和BOOM ...实现地图的相对坐标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | float pX, pY; // render a colourised version of the scene for the purposes of"picking" // https://www.opengl.org/archives/resources/faq/technical/selection.htm public void pick ( GL2 gl2 ) { // DRAW PICKING SCENE gl2.glClearBufferfv(GL2.GL_COLOR, 0, clearColor); gl2.glClearBufferfv(GL2.GL_DEPTH, 0, clearDepth); gl2.glTranslatef( -cameraX, -cameraY, -cameraZ ); // draw my map but use the colour gradient texture for ( Entity e : this.entities ) { e.drawPick( gl2 ); } // not sure what this does #cargo-cult gl2.glFlush(); gl2.glFinish(); gl2.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1); // After rendering ask OpenGL to read the colour of the screen at the given window coordinates! FloatBuffer buffer = FloatBuffer.allocate(4); int realy = 0; int viewport[] = new int[4]; gl2.glGetIntegerv(GL2.GL_VIEWPORT, viewport, 0); realy = viewport[3] - (int) mouse.getY() - 1; gl2.glReadPixels( mouse.getX(), realy, 1, 1, GL2.GL_RGBA, GL2.GL_FLOAT, buffer); float[] pixels = new float[3]; pixels = buffer.array(); // pixels holds rgb values respectively // convert the red + green values back into x + y values pX = (pixels[0] * 255) - 25f; pY = -((pixels[1] * 255) - 25f); // draw the proper texture for ( Entity e : this.entities ) { e.draw( gl2 ); } } |
你差不多了。不过,您将需要在unproject函数中为Z提供一个良好的值。
您要尝试做的是获取光标的位置,然后乘以一个矩阵以在" 3d空间"中给出一个点。您的矩阵可能是4x4或4x3,因此您需要4分量向量。 [x,y,z,w)
绘制地图时,现有点会乘以1个或多个矩阵,包括投影矩阵。 (例如
要执行相反的操作并取消投影,您需要具有有效/良好的Z值。原因是,在NDC中,绘制的所有对象在所有轴上都为-1,1,以获取所有内容(更进一步,将对象压缩了)一点点)。例如,如果您有一个大于100000 zFar的巨大距离,这就是闪烁和怪异的方式,它仍然必须适合-1,1。
做到这一点的最好方法是使用深度缓冲区,通过捕获深度值,它将为您提供NDC中z坐标的良好近似值,您可以将其传递给unproject调用。
0.945是最佳点的原因可能取决于相机距地图的距离,反之亦然。通常情况下,深度缓冲区在距离近平面较近的地方比远处具有更多的细节-它不是线性的。
http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/在页面底部附近具有良好的视觉效果,并且通常是矩阵入门的良好资源:

您会看到由于移至NDC而导致的失真。从透视的角度来看,这是必需的,但是在向后转换时也需要考虑到这一点。
如上所述的颜色选择也可以进行选择,但是仍然需要一些工作。因为只有一个对象,所以必须用不同的颜色渲染图像的每个纹理像素,然后将其输出到单独的颜色缓冲区中,检查缓冲区中的颜色,并以某种方式将其与空间中的点相关联。虽然可能可以完成,但是我想说颜色选择更适合于多个对象。
根据我的阅读,深度缓冲区可能是更适合您的对象,因为它是一个对象,深度缓冲区将为您单击的每个点提供Z坐标。它可能仍在您的遥远飞机上,但仍会为您带来价值。
或者,如@elect所建议,使用正投影。