gluUnProject在Android OpenGL ES 1.1中的用法

6

我正在尝试使用gluUnProject将窗口坐标转换为世界坐标。 我不是在模拟器或旧版Android系统(具有OpenGL ES v1.0)中获取工作样本,这不是关于GL函数可用性的问题。 我正在尝试在使用OpenGL ES 1.1的实际设备上进行操作,并且glGet函数返回非零结果。

下面是一个示例:

public Vector3 translateScreenCoordsToWorld(Vector2 screenCoords) {
gl.glLoadIdentity();

final Matrix4x4 modelViewMatrix = new Matrix4x4();
final Matrix4x4 projectMatrix = new Matrix4x4();

int[] viewVectorParams = new int[4];
gl.glGetIntegerv(GL11.GL_VIEWPORT,viewVectorParams,0);
gl.glGetFloatv(GL11.GL_MODELVIEW_MATRIX,modelViewMatrix.buffer,0);
gl.glGetFloatv(GL11.GL_PROJECTION_MATRIX,projectMatrix.buffer,0);

float[] output = new float[4];
GLU.gluUnProject(
    screenCoords.x, screenCoords.y, 0, 
    modelViewMatrix.buffer, 0, 
    projectMatrix.buffer, 0, 
    viewVectorParams, 0, 
    output, 0);             

return new Vector3(output[0],output[1],output[2]);

}

Matrix4x4只是float[]缓冲区的包装器。

通过这个函数,我试图创建一个填充整个屏幕或检测当前投影矩阵的最大世界坐标的平面,但它根本不起作用,因为我不确定我是否正确地使用了这些函数。

例如,当我尝试运行translateScreenCoordsToWorld(new Vector2(480,800))时,它返回非常小的坐标值Vector3(0.27f, 0.42f, -1.0f)。

是否有人可以提供一个在GL_PROJECTION模式下使用gluUnProject和一个定位相机的好示例用法。

已更新 感谢好的链接。但是对我来说仍然不起作用 :( 现在我的函数看起来像:

public Vector3 translateScreenCoordsToWorld(Vector2 screenCoords) {

float winX = screenCoords.x, winY = screenCoords.y, winZ = 0;
winY = (float)currentViewVectorParams[3] - winY;

float[] output = new float[4];
GLU.gluUnProject(
    winX, winY, winZ, 
    currentModelViewMatrix.buffer, 0, 
    currentProjectMatrix.buffer, 0, 
    currentViewVectorParams, 0, 
    output, 0
);              

Vector3 nearPlane = new Vector3(output[0],output[1],output[2]);
winZ = 1.0f;        
GLU.gluUnProject(
    winX, winY, winZ, 
    currentModelViewMatrix.buffer, 0, 
    currentProjectMatrix.buffer, 0, 
    currentViewVectorParams, 0, 
    output, 0
);              
Vector3 farPlane = new Vector3(output[0],output[1],output[2]);

farPlane.sub(nearPlane);
farPlane.div( nearPlane.length());

float dot1, dot2;

Vector3 pointInPlane = new Vector3(), pointPlaneNormal = new Vector3(0,0,-1);
pointInPlane.sub( nearPlane );

dot1 = (pointPlaneNormal.x * pointInPlane.x) + (pointPlaneNormal.y * pointInPlane.y) + (pointPlaneNormal.z * pointInPlane.z);
dot2 = (pointPlaneNormal.x * farPlane.x) + (pointPlaneNormal.y * farPlane.y ) +(pointPlaneNormal.z * farPlane.z);

float t = dot1/dot2;
farPlane.mul(t);

return farPlane.add(nearPlane);
}

这是我配置相机的地方:

public void updateCamera() {
Camera camera = scene.getCamera();
GLU.gluLookAt(gl, 
    camera.position.x, camera.position.y, camera.position.z,
    camera.target.x, camera.target.y, camera.target.z,
    camera.upAxis.x, camera.upAxis.y, camera.upAxis.z
);

    gl.glGetIntegerv(GL11.GL_VIEWPORT,currentViewVectorParams,0);       
    gl.glGetFloatv(GL11.GL_MODELVIEW_MATRIX, currentModelViewMatrix.buffer,0);
    gl.glGetFloatv(GL11.GL_PROJECTION_MATRIX,currentProjectMatrix.buffer,0);
}

该相机配置了以下坐标:

camera.position = { 0, 0, 65 };     
camera.target = { 0, 0, 0 }
camera.upAxis = { 0, 1, 0 }
1个回答

3

通常情况下,使用gluUnProject函数是为了获取屏幕上像素的世界坐标。在调用gluUnProject之前,需要提供除矩阵外的三个信息:屏幕x坐标、屏幕y坐标和对应于该像素的深度缓冲值。通常的处理流程如下:

  1. 绘制一个帧,以便准备好所有深度缓冲信息。
  2. 获取视口/矩阵/屏幕坐标。
  3. 反转屏幕y坐标。
  4. 读取给定像素处的深度值。这实际上是一个归一化的z坐标。
  5. 将x、y和z输入到gluUnProject中。

此时,就可以获得给定像素片段的世界空间坐标。在此处找到更详细的指南。无论如何,我看到你代码中可能会出现两个错误。第一个错误是通常需要反转y屏幕坐标(链接中的教程详细说明了原因),第二个错误是总是将0作为z值传递给gluUnProject。这样做相当于将顶点计算为近平面上的点。这是你想要的吗?

如果我误解了你的问题,请多多包涵。


谢谢!这是一篇不错的NeHe文章。但是OpenGL ES(Android)中没有GL_DEPTH_COMPONENT支持。我无法调用 glReadPixels(winX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ); :( 有什么解决方法吗? - abdolence
3
您说得对,我可能应该记得那个。有两种解决方法,第一种是在近平面和远平面之间创建一条射线并运行手动相交测试(可能会很棘手),这里;第二种是创建一个帧缓冲对象,并从中读取深度缓冲区的值。或者,您可以跳过gluUnProject,尝试实现拾取。看这里 - Darcy Rayner
再提供一下感兴趣的链接。谢谢!我尝试手动实现交集,但是还是不行 :( 请查看更新。 - abdolence

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