glTranslatef和鼠标点击 - gluUnProject

时间:2017-02-09 15:07:37

标签: java opengl mouseevent jogl

请查看问题的底部,了解我已经使用的当前解决方案,感谢Finlaybob,elect,gouessej

呼吁OpenGL的老人......我在检测鼠标点击纹理平面上的相对位置时遇到了很大问题。

我正在制作一个游戏,我正在绘制一个大的正方形,并使用大量生成的地图纹理对其进行纹理处理。视图始终自上而下,您当前只能移动该方块的X Y和Z坐标。

Screenshot of the map

OpenGL init

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);

设置相机位置

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();

移动位置以开始绘制地图

// 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-coord可能是个嫌疑人。因为我正在绘制远离原点的方形40f(例如)

映射顶点信息

// here are the coordinates/dimensions of my textured square ( my map )
float[] vertexArray = {
    -25f,  25f,
     25f,  25f,
     25f, -25f,
     25f, -25f,
};

鼠标点击位置计算

“借来的”来自java-tips 1628-how-to-use-gluunproject-in-jogl.html

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
);

尝试使用near / far位(gluUnProject的第3个参数)似乎产生了更好的效果,但似乎没有最佳位置(我发现的最佳点是0.945)

我非常希望mCX,mCY相对于渲染的地图坐标(-25f - 25f)而不管Z位置

mCX = (float)wcoord[0];
mCY = (float)wcoord[1];

在平移坐标处绘制一个矩形

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&如果我单击屏幕的正中心,它将在正确的位置绘制一个框,而不管我的glTranslatef运动。如果我点击屏幕中心,我会看到一个指数偏移。

演示指数偏移

当我点击屏幕的非常死点时,它会在鼠标点周围绘制这个紫红色的正方形,但运动最小时会产生以下效果:

Fully zoomed in, click a couple of pixels right of centre

更新和工作......现在

在为我的地图生成纹理时,我还生成了一个替代纹理,它将每个“图块”表示为不同的颜色。在我的初始和当前尝试中,此图块的颜色是其X和Y坐标的函数(地图由100个图块组成,100个图块向下,因此x + y坐标范围为0 - 99)

我最终得到的纹理看起来像是从绿色到红色的渐变。下面的代码将在鼠标单击时快速渲染此纹理(用户无法察觉)并读取鼠标下的rgb值。然后我们将rgb值转换为世界坐标和BOOM ...我的地图的相对坐标被实现。

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 );
    }
}

1 个答案:

答案 0 :(得分:0)

你差不多了。但是,在unproject函数中,你需要一个很好的Z值。

您要做的是获取光标的位置并乘以矩阵以在“3d空间”中给出一个点。你的矩阵可能是4x4或4x3,所以你需要一个4分量向量。 (X,Y,Z,W)

绘制地图时,现有点乘以1个或多个矩阵,包括投影矩阵。 (例如-25.0f,25.0f,0.0f,1.0f - 实际上是3d点)。当它与所有矩阵相乘时,GPU基本上返回该顶点的归一化设备坐标(NDC)(所有轴中的-1和1之间)的值。

要做相反的事情并且取消项目,你需要为Z设置一个有效/良好的值。原因是在NDC中所有绘制的内容都在-1,1所有轴上,以便将所有内容都放入(更远的地方)事情被压扁了一点)。如果你有一个巨大的>这就是你如何变得闪烁和怪异。例如,100000 zFar距离,它仍然必须适合-1,1。

执行此操作的最佳方法是使用深度缓冲区,通过捕获深度值,它将为您提供NDC中z坐标的良好近似值,您可以将其传递给unproject调用。

0.945是甜蜜点的原因可能取决于相机与地图的距离,反之亦然。通常情况下,深度缓冲区距离近平面更远的细节远远超过远 - 它不是线性的。

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/在页面底部附近具有良好的视觉效果,并且是一般介绍矩阵的良好资源: link below link below

您可以看到移动到NDC造成的失真。从perspactive视点查看时需要这样做,但是当你向后变换时也需要考虑它。

如上所述的颜色选择对于采摘也是可行的,但仍需要一些工作。因为您有一个对象,所以您必须使用不同的颜色渲染图像的每个纹素,将其输出到单独的颜色缓冲区,检查缓冲区上的颜色,并以某种方式将其与空间中的某个点相关联。它可能会完成,但我会说颜色选择更适合多个对象。

根据我的阅读 - 深度缓冲区可能更适合您,因为它是一个对象,深度缓冲区将为您点击的每个点提供Z坐标。它仍然可以在你的飞机上,但它仍会给你一个价值。

或者,如@elect所建议的那样使用正交投影。