将3d对象中心坐标转换为2d可见视口坐标

时间:2013-05-15 10:44:06

标签: iphone opengl-es

我在Iphone OpenGL中加载了一个wavefront对象。

enter image description here

它可以围绕x / y轴旋转,平移,放大/缩小。

我的任务是 - 当点击对象时,在屏幕上突出显示它的2d中心坐标,例如:(想象一下+位于可见对象的中心。) enter image description here

加载OpenGL对象时,我存储它:

  1. 世界中的对象中心位置,
  2. x,y,z位置偏移,
  3. x,y,z rotation,
  4. 缩放比例。
  5. 当用户点击屏幕时,我可以区分哪个对象被点击。但是 - 因为用户可以点击对象上的任何位置 - 点击点不是中心。

    当用户触摸某个物体时,我希望能够找到相应的物体可见近似中心坐标。

    我该怎么做?

    我能找到的google中的大多数代码都意味着 - 将3d坐标转换为2d但没有旋转。

    代码中的一些变量:

    Vertex3D centerPosition;  
    Vertex3D currentPosition;
    Rotation3D currentRotation;
    
    //centerPosition.x,  centerPosition.y, centerPosition.z
    //currentPosition.x,  currentPosition.y, currentPosition.z
    //currentRotation.x,  currentRotation.y, currentRotation.z
    

    提前谢谢你。

    (找出我点击的对象 - 以不同颜色重新着色每个对象,因此我知道用户点击了什么颜色。)

    对象drawSelf函数:

    // Save the current transformation by pushing it on the stack
    glPushMatrix();
    
    // Load the identity matrix to restore to origin
    glLoadIdentity();
    
    // Translate to the current position
    glTranslatef(currentPosition.x, currentPosition.y, currentPosition.z);
    
    // Rotate to the current rotation
    glRotatef(currentRotation.x, 1.0, 0.0, 0.0);
    glRotatef(currentRotation.y, 0.0, 1.0, 0.0);
    glRotatef(currentRotation.z, 0.0, 0.0, 1.0);
    
    
    // Enable and load the vertex array
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, vertices);
    glNormalPointer(GL_FLOAT, 0, vertexNormals);
    // Loop through each group
    
    if (textureCoords != NULL)
    {
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glTexCoordPointer(valuesPerCoord, GL_FLOAT, 0, textureCoords);
    }
    for (OpenGLWaveFrontGroup *group in groups)
    {
        if (textureCoords != NULL && group.material.texture != nil)
            [group.material.texture bind];
        // Set color and materials based on group's material
        Color3D ambient = group.material.ambient;
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (const GLfloat *)&ambient);
    
        Color3D diffuse = group.material.diffuse;
        glColor4f(diffuse.red, diffuse.green, diffuse.blue, diffuse.alpha);
        glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,  (const GLfloat *)&diffuse);
    
        Color3D specular = group.material.specular;
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (const GLfloat *)&specular);
    
        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, group.material.shininess);
    
        glDrawElements(GL_TRIANGLES, 3*group.numberOfFaces, GL_UNSIGNED_SHORT, &(group.faces[0]));
    }
    if (textureCoords != NULL)
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    // Restore the current transformation by popping it off
    glPopMatrix();
    

1 个答案:

答案 0 :(得分:1)

好吧,正如我所说,你需要将相同的变换应用到对象中心,这些变换由图形管道应用于对象的顶点;只有这一次,图形管道不会帮助你 - 你必须自己做。它涉及一些矩阵计算,所以我建议得到一个像the OpenGL Maths library这样的好的数学库,它具有功能名称等与OpenGL极其相似的优点。

步骤1:将中心形状对象坐标转换为模型视图坐标

在您的代码中,您可以像下面这样设置4x4模型视图矩阵:

// Load the identity matrix to restore to origin
glLoadIdentity();

// Translate to the current position
glTranslatef(currentPosition.x, currentPosition.y, currentPosition.z);

// Rotate to the current rotation
glRotatef(currentRotation.x, 1.0, 0.0, 0.0);
glRotatef(currentRotation.y, 0.0, 1.0, 0.0);
glRotatef(currentRotation.z, 0.0, 0.0, 1.0);

你需要将该矩阵与对象中心相乘,而OpenGL对此没有帮助,因为它本身不是数学库。如果你使用glm,有一些函数,如rotate(),translate()等功能类似于glRotatef()& glTranslatef(),您可以使用它们来构建模型视图矩阵。此外,由于矩阵是4x4,你必须将1.f作为第四个组件附加到对象中心(称为“w-component”),否则你不能将它与4x4矩阵相乘。

或者,您可以直接从OpenGl查询模型视图矩阵的当前值:

GLfloat matrix[16]; 
glGetFloatv (GL_MODELVIEW_MATRIX, matrix);

但是你必须为乘法编写自己的代码...

第2步:从modelview坐标转到剪辑坐标

从您发布的内容来看,我无法判断您是否更改了投影矩阵(某处是否存在glMatrixMode(GL_PROJECTION)?) - 如果您从未触摸投影矩阵,则可以省略此步骤;否则你现在需要将变换后的物体中心与投影矩阵相乘。

第3步:透视划分

将对象中心的所有4个分量除以第4个 - 然后扔掉第4个分量,只保留xyz。 如果省略了第2步,则也可以省略除法。

第4步:将对象中心坐标映射到窗口坐标

现在,对象中心在标准化设备坐标中定义,x& y分量在[-1.f,1.f]范围内。最后一步是将它们映射到视口,即映射到像素位置。无论如何,z分量并不重要,所以让我们忽略z并调用x& y分别是obj_x和obj_y。

应使用glViewport( viewport_x, viewport_y, width, height )在代码中的某处设置视口尺寸。从函数参数中,您可以像这样计算中心的像素位置:

pixel_x = width/2 * obj_x + viewport_x + width/2;
pixel_y = height/2 * obj_y + viewport_y + height/2; 

基本上就是这样。