Android OpenGL - 简单2D转换的困难

时间:2012-01-02 12:23:11

标签: android opengl-es 2d

出于性能原因,我试图将游戏中的图形迁移到OpenGL。

  1. 我需要使用精确的屏幕坐标绘制对象。在240x320屏幕的中心说一个100x100像素的盒子。
  2. 我需要围绕Z轴旋转,保持其大小。
  3. 我需要围绕X轴旋转它,具有透视效果,保持(或接近)它的大小。
  4. 我需要围绕Y轴旋转它,具有透视效果,保持(或接近)它的大小。
  5. Here's a picture.

    到目前为止,我设法完成了前两项任务:

    public void onDrawFrame(GL10 gl) {
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();
        gl.glTranslatef(120, 160, 0); // move rotation point 
        gl.glRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate 
        gl.glTranslatef(-120, -160, 0); // restore rotation point
        mesh.draw(gl); // draws 100x100 px rectangle with the following coordinates: (70, 110, 170, 210)
    }
    
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        gl.glViewport(0, 0, width, height);
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        gl.glOrthof(0f, (float)width, (float)height, 0f, -1f, 1f);
        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();
    }
    

    但是当我试图围绕x或y旋转我的盒子时,我的盒子发生了令人讨厌的事情并且没有透视效果。我尝试使用其他函数而不是glRotate(glFrustum,glPerspective,gluLookAt,应用"倾斜"矩阵),但我无法使它们正常工作。

3 个答案:

答案 0 :(得分:3)

  

出于性能原因,我正试图将游戏中的图形迁移到OpenGL。

     

我需要使用精确的屏幕坐标绘制对象。在240x320屏幕的中心说一个100x100像素的盒子。

对于透视,您还需要一些镜头长度,这决定了FOV。 FOV是观察平面距离与可见范围的比率。在近平面的情况下,它变为{left,right,top,bottom}/near。为简单起见,我们假设水平FOV和对称投影,即

FOV = 2*|left|/near = 2*|right|/near = extent/distance

或者如果你更倾向于角色

FOV = 2*tan(angular FOV / 2)

对于90°FOV,镜头的长度是焦平面宽度的一半。你的焦平面是240x320像素,所以120左右,160到顶部和底部。 OpenGL并没有真正的焦点,但我们可以说近距离和远距离的中间平面是“焦点”。

因此,假设物体的平​​均可见范围约为可见平面极限的数量级,即对于240x360的可见平面,物体的平均尺寸约为200px。因此,有意义的是,近距离到远距离剪切的距离为200,因此关于焦平面的距离为+ - 100。因此,对于90°的FOV,焦平面具有距离

2*tan(90°/2) = extent/distance

2*tan(45°) = 2 = 240/distance
             2*distance = 240
             distance = 120

120,因此近距离和远距离的剪切距离分别为20和220.

最后但并非最不重要的是,近剪裁平面限制必须按near_distance / focal_distance = 20/120

进行缩放

所以

left   = -120 * 20/120 = -20
right  =  120 * 20/120 =  20
bottom = -180 * 20/120 = -30
top    =  180 * 20/120 =  30

所以这给了我们glFrustum参数:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-20, 20, -30, 30, 20, 220);

最后但同样重要的是,我们必须将世界起源转移到“焦点”平面

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0, 0, -120);
  

我需要围绕Z轴旋转它,保持其大小。

进行。

  

我需要围绕X轴旋转它,具有透视效果,保持(或接近)它的大小。   我需要围绕Y轴旋转它,具有透视效果,保持(或接近)它的大小。

透视不保留大小。这就是它的观点。你可以使用很长的镜头,即小视场。

代码更新

作为一般性专业提示:在绘图处理程序中执行所有OpenGL操作。不要在重塑处理程序中设置投影。它很难看,只要你想要一些HUD或其他类型的叠加,你就不得不丢弃它。所以这是如何改变它:

public void onDrawFrame(GL10 gl) {
    // fov, extents are parameters set somewhere else
    // 2*tan(fov/2.) = width/distance =>
    float distance = width/(2.*tan(fov));
    float near = distance - extent/2;
    float far  = distance + extent/2;

    if(near < 1.) {
        near = 1.;
    }
    float  left  =  (-width/2) * near/distance;
    float  right =  ( width/2) * near/distance;
    float bottom = (-height/2) * near/distance;
    float    top = ( height/2) * near/distance;

    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

    gl.glViewport(0, 0, width, height);

    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();
    gl.glFrustum(left, right, bottom, top, near, far);

    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity();

    gl.glTranslatef(0, 0, -focal);

    gl.glTranslatef(120, 160, 0); // move rotation point 
    gl.glRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate 
    gl.glTranslatef(-120, -160, 0); // restore rotation point
    mesh.draw(gl); // draws 100x100 px rectangle with the following coordinates: (70, 110, 170, 210)
}

public void onSurfaceChanged(GL10 gl, int new_width, int new_height) {
    width = new_width;
    height = new_height;
}

答案 1 :(得分:1)

您需要使用透视投影矩阵,然后使用模型视图矩阵来获得正确的位置和缩放。

答案 2 :(得分:0)

您正在使用正交投影(glOrthof()),它会明确禁用透视图。

相反的是glFrustum(),通常由gluPerspective() /包裹,它更易于使用,但需要GLU库。