在 OpenGL ES 2D Orthographic (Ortho) 模式下绘制问题

Posted

技术标签:

【中文标题】在 OpenGL ES 2D Orthographic (Ortho) 模式下绘制问题【英文标题】:Problems drawing in OpenGL ES 2D Orthographic (Ortho) mode 【发布时间】:2011-05-08 21:21:18 【问题描述】:

几天来,我一直在努力解决这个问题,在搜索 Stack Overflow 和网络之后,我没有找到任何对我有用的示例。我终于得到了看起来很接近的代码,所以也许你们(和女孩?)可以帮助我解决这个问题。

我的第一个问题是我试图通过将屏幕抓取作为纹理来实现运动模糊,然后在下一帧上绘制具有透明度的纹理——或者使用更多帧来实现更多模糊。 (如果有人感兴趣,这是我遵循的指南:http://www.codeproject.com/KB/openGL/MotionBlur.aspx)

我已将屏幕保存为纹理工作正常。我遇到的问题是在屏幕顶部以正交模式绘图。经过多次头撞,我终于得到了一个基本的方形绘图,但是我缺乏对 OpenGL ES 的理解和一个​​易于理解的示例现在阻碍了我。我需要获取我保存的纹理,并将其绘制到我绘制的正方形中。我一直在做的任何事情似乎都不起作用。

另外,我的第二个问题是将更复杂的 3d 模型绘制到正交模式中。我似乎无法绘制任何模型。我正在使用(稍微定制的)min3d 框架(http://code.google.com/p/min3d/),我试图在正交模式下绘制 Object3d,就像我在透视模式下绘制它们一样。据我了解,他们应该画同样的,他们不应该有深度。然而我似乎根本没有看到它们。

这是我正在使用的代码。我尝试了很多不同的东西,这是我得到的最接近的东西(实际上在屏幕上画了一些可以看到的东西)。我仍然不知道如何在正交视图中获得正确的 3d 模型图。我确定我在做一些非常错误的事情,并且可能完全误解了 OpenGL 绘图的一些基本方面。如果我需要发布任何其他代码,请告诉我。

// Gets called once, before all drawing occurs
//
private void reset()

    // Reset TextureManager
    Shared.textureManager().reset();

    // Do OpenGL settings which we are using as defaults, or which we will not be changing on-draw

    // Explicit depth settings
    _gl.glEnable(GL10.GL_DEPTH_TEST);                                   
    _gl.glClearDepthf(1.0f);
    _gl.glDepthFunc(GL10.GL_LESS);                                      
    _gl.glDepthRangef(0,1f);                                            
    _gl.glDepthMask(true);                                              

    // Alpha enabled
    _gl.glEnable(GL10.GL_BLEND);                                        
    _gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);    

    // "Transparency is best implemented using glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 
    // with primitives sorted from farthest to nearest."

    // Texture
    _gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); // (OpenGL default is GL_NEAREST_MIPMAP)
    _gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); // (is OpenGL default)

    // CCW frontfaces only, by default
    _gl.glFrontFace(GL10.GL_CCW);
    _gl.glCullFace(GL10.GL_BACK);
    _gl.glEnable(GL10.GL_CULL_FACE);

    // Disable lights by default
    for (int i = GL10.GL_LIGHT0; i < GL10.GL_LIGHT0 + NUM_GLLIGHTS; i++) 
        _gl.glDisable(i);
    

    //
    // Scene object init only happens here, when we get GL for the first time
    // 


// Called every frame
//
protected void drawScene()
   
    if(_scene.fogEnabled() == true) 
        _gl.glFogf(GL10.GL_FOG_MODE, _scene.fogType().glValue());
        _gl.glFogf(GL10.GL_FOG_START, _scene.fogNear());
        _gl.glFogf(GL10.GL_FOG_END, _scene.fogFar());
        _gl.glFogfv(GL10.GL_FOG_COLOR, _scene.fogColor().toFloatBuffer() );
        _gl.glEnable(GL10.GL_FOG);
     else 
        _gl.glDisable(GL10.GL_FOG);
    

    // Sync all of the object drawing so that updates in the mover
    // thread can be synced if necessary
    synchronized(Renderer.SYNC)
    
        for (int i = 0; i < _scene.children().size(); i++)
        
            Object3d o = _scene.children().get(i);
            if(o.animationEnabled())
            
                ((AnimationObject3d)o).update();
            
            drawObject(o);
        
    

    //
    //
    //
    // Draw the blur

    // Set Up An Ortho View
    _switchToOrtho();

    _drawMotionBlur();

    // Switch back to the previous view
    _switchToPerspective();

    _saveScreenToTexture("blur", 512);


private void _switchToOrtho()

    // Set Up An Ortho View
    _gl.glDisable(GL10.GL_DEPTH_TEST);
    _gl.glMatrixMode(GL10.GL_PROJECTION); // Select Projection
    _gl.glPushMatrix(); // Push The Matrix
    _gl.glLoadIdentity(); // Reset The Matrix
    _gl.glOrthof(0f, 480f, 0f, 800f, -1f, 1f);
    //_gl.glOrthof(0f, 480f, 0f, 800f, -100f, 100f);
    _gl.glMatrixMode(GL10.GL_MODELVIEW); // Select Modelview Matrix
    _gl.glPushMatrix(); // Push The Matrix
    _gl.glLoadIdentity(); // Reset The Matrix


private void _switchToPerspective()

    // Switch back to the previous view
    _gl.glEnable(GL10.GL_DEPTH_TEST);
    _gl.glMatrixMode(GL10.GL_PROJECTION);
    _gl.glPopMatrix();
    _gl.glMatrixMode(GL10.GL_MODELVIEW);
    _gl.glPopMatrix(); // Pop The Matrix


private void _saveScreenToTexture(String $textureId, int $size)

    // Save the screen as a texture
    _gl.glViewport(0, 0, $size, $size);
    _gl.glBindTexture(GL10.GL_TEXTURE_2D, _textureManager.getGlTextureId($textureId));
    _gl.glCopyTexImage2D(GL10.GL_TEXTURE_2D,0,GL10.GL_RGB,0,0,512,512,0);
    _gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
    _gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
    _gl.glViewport(0, 0, 480, 800);


private void _drawMotionBlur()

    // Vertices
    float squareVertices[] = 
        -3f, 0f,     // Bottom Left
        475f, 0f,     // Bottom Right
        475f, 800f, // Top Right
        -3f, 800f  // Top Left
    ;

    ByteBuffer vbb = ByteBuffer.allocateDirect(squareVertices.length * 4);
    vbb.order(ByteOrder.nativeOrder());
    FloatBuffer vertexBuffer = vbb.asFloatBuffer();
    vertexBuffer.put(squareVertices);
    vertexBuffer.position(0);

    //
    //
    // Textures
    FloatBuffer textureBuffer;  // buffer holding the texture coordinates
    float texture[] =          
            // Mapping coordinates for the vertices
            0.0f, 1.0f,     // top left     (V2)
            0.0f, 0.0f,     // bottom left  (V1)
            1.0f, 1.0f,     // top right    (V4)
            1.0f, 0.0f      // bottom right (V3)
    ;
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(squareVertices.length * 4);
    byteBuffer.order(ByteOrder.nativeOrder());
    byteBuffer = ByteBuffer.allocateDirect(texture.length * 4);
    byteBuffer.order(ByteOrder.nativeOrder());
    textureBuffer = byteBuffer.asFloatBuffer();
    textureBuffer.put(texture);
    textureBuffer.position(0);
    //
    //
    //

    _gl.glLineWidth(3.0f);
    _gl.glTranslatef(5.0f, 0.0f, 0.0f);
    _gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertexBuffer);
    _gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

    _gl.glDrawArrays(GL10.GL_LINE_LOOP, 0, 4);
    //_gl.glTranslatef(100.0f, 0.0f, 0.0f);
    //_gl.glDrawArrays(GL10.GL_LINE_LOOP, 0, 4);
    //_gl.glTranslatef(100.0f, 0.0f, 0.0f);
    //_gl.glDrawArrays(GL10.GL_LINE_LOOP, 0, 4);


    _gl.glEnable(GL10.GL_TEXTURE_2D);
    _gl.glEnable(GL10.GL_BLEND);
    _gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    _gl.glLoadIdentity();

    //
    //
    //
    _gl.glBindTexture(GL10.GL_TEXTURE_2D, _textureManager.getGlTextureId("blur"));
    _gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
    _gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    //
    //
    //

    _gl.glDisable(GL10.GL_BLEND);
    _gl.glDisable(GL10.GL_TEXTURE_2D);
    _gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);


编辑:这是一个更简单的例子,它全部在一个函数中,不包括将屏幕保存到纹理的任何内容。这只是绘制一个 3d 场景,切换到 Ortho,绘制一个带有纹理的正方形,然后切换回透视。

// Called every frame
//
protected void drawScene()
   
    // Draw the 3d models in perspective mode
    // This part works (uses min3d) and draws a 3d scene
    //
    for (int i = 0; i < _scene.children().size(); i++)
    
        Object3d o = _scene.children().get(i);
        if(o.animationEnabled())
        
            ((AnimationObject3d)o).update();
        
        drawObject(o);
    

    // Set Up The Ortho View to draw a square with a texture
    // over the 3d scene
    //
    _gl.glDisable(GL10.GL_DEPTH_TEST);
    _gl.glMatrixMode(GL10.GL_PROJECTION); // Select Projection
    _gl.glPushMatrix(); // Push The Matrix
    _gl.glLoadIdentity(); // Reset The Matrix
    _gl.glOrthof(0f, 480f, 0f, 800f, -1f, 1f);
    _gl.glMatrixMode(GL10.GL_MODELVIEW); // Select Modelview Matrix
    _gl.glPushMatrix(); // Push The Matrix
    _gl.glLoadIdentity(); // Reset The Matrix


    // Draw A Square With A Texture
    // (Assume that the texture "blur" is already created properly --
    // it is as I can use it when drawing my 3d scene if I apply it
    // to one of the min3d objects)
    //
    float squareVertices[] = 
        -3f, 0f,     // Bottom Left
        475f, 0f,     // Bottom Right
        475f, 800f, // Top Right
        -3f, 800f  // Top Left
    ;

    ByteBuffer vbb = ByteBuffer.allocateDirect(squareVertices.length * 4);
    vbb.order(ByteOrder.nativeOrder());
    FloatBuffer vertexBuffer = vbb.asFloatBuffer();
    vertexBuffer.put(squareVertices);
    vertexBuffer.position(0);

    FloatBuffer textureBuffer;  // buffer holding the texture coordinates
    float texture[] =          
            // Mapping coordinates for the vertices
            0.0f, 1.0f,     // top left     (V2)
            0.0f, 0.0f,     // bottom left  (V1)
            1.0f, 1.0f,     // top right    (V4)
            1.0f, 0.0f      // bottom right (V3)
    ;
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(squareVertices.length * 4);
    byteBuffer.order(ByteOrder.nativeOrder());
    byteBuffer = ByteBuffer.allocateDirect(texture.length * 4);
    byteBuffer.order(ByteOrder.nativeOrder());
    textureBuffer = byteBuffer.asFloatBuffer();
    textureBuffer.put(texture);
    textureBuffer.position(0);

    _gl.glLineWidth(3.0f);
    _gl.glTranslatef(5.0f, 0.0f, 0.0f);
    _gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertexBuffer);
    _gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

    _gl.glDrawArrays(GL10.GL_LINE_LOOP, 0, 4);

    _gl.glEnable(GL10.GL_TEXTURE_2D);
    _gl.glEnable(GL10.GL_BLEND);
    _gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    _gl.glLoadIdentity();

    _gl.glBindTexture(GL10.GL_TEXTURE_2D, _textureManager.getGlTextureId("blur"));
    _gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
    _gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);

    _gl.glDisable(GL10.GL_BLEND);
    _gl.glDisable(GL10.GL_TEXTURE_2D);
    _gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

    // Switch Back To The Perspective Mode
    //
    _gl.glEnable(GL10.GL_DEPTH_TEST);
    _gl.glMatrixMode(GL10.GL_PROJECTION);
    _gl.glPopMatrix();
    _gl.glMatrixMode(GL10.GL_MODELVIEW);
    _gl.glPopMatrix(); // Pop The Matrix


EDIT2:感谢 Christian 的回答,我删除了第二个 glVertexPointer_gl.glBlendFunc (GL10.GL_ONE, GL10.GL_ONE);(我也从上面的示例代码中删除了它们,以免混淆问题)。我现在有一个纹理渲染,但只在构成正方形的一个三角形中。所以我在屏幕的左侧看到了一个应用了纹理的三角形。为什么它不应用于正方形的两半?我想这是因为我只有一个调用:gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);,所以我实际上只画了一个三角形。

【问题讨论】:

如果您在正交投影方面遇到问题,您是否有更易于阅读的代码示例?所有这些运动模糊相关的方法在这里都非常令人困惑。如果您可以提供更短的代码示例,专注于正交投影及其用法,那么尝试提供帮助会更容易。 添加了更简单的代码版本。 【参考方案1】:

首先,您将混合函数设置为 (GL_ONE, GL_ONE),这只会将模糊纹理添加到帧缓冲区并使整个场景过亮。您可能想要使用 (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA),但是您必须确保您的模糊纹理具有正确的 alpha,通过将纹理环境配置为使用 alpha 的常量值(而不是纹理的)或使用 GL_MODULATE (1,1,1,0.5) 彩色方块。或者使用片段着色器。

其次,您在第二次调用 glVertexPointer 时指定大小为 3,但您的数据是二维向量(第一次调用是正确的)。

glOrtho 不一定是 2D,它只是一个没有透视失真的相机(更远的对象不会变小)。 glOrtho 的参数在视图坐标中指定屏幕平面大小。因此,如果您的场景覆盖了单位立方体中的世界,那么 480x800 的正交就太大了(如果您绘制其他对象而不是透视图,如正方形或 UI 元素,但当您想要绘制相同的 3d天平必须匹配的对象)。另一件事是,在 Ortho 中,近距和远距仍然很重要,掉出的所有东西都被剪掉了。因此,如果您的相机位于 (0,0,0) 并且您沿 -z 以 (0,480,0,800,-1,1) 的 glOrtho 查看,您将只能看到与 (0,0,-1) 相交的那些对象)-(480,800,1)-盒子。

因此请记住,glOrtho 和 glFrustum(或 gluPerspective)都定义了 3d 观看体积。在 Ortho 它是一个盒子,在它的平截头体中,猜一个平截头体(加盖的金字塔)。如果不够清楚,请查阅有关转换和查看的更多介绍性文本。

【讨论】:

感谢您的回复。它实际上已经设置为 (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA),我认为 (GL_ONE, GL_ONE) 已粘贴到我昨晚尝试的其他示例代码中。我会摆脱它。我将正交模式设置为 480x800,因为那是屏幕的大小。正如您所解释的那样,我应该使用更小的坐标并且纹理仍将正确显示?另外,我知道 Ortho 不是 2d,我的意思是我试图在 Ortho 模式(正方形)下显示 2d 的东西。我的第二个任务也是以正交模式显示 3d 对象。 我将尝试按照您的建议更改这些坐标,并删除第二个 glVertexPointer 调用,但我的大问题是获取要渲染的纹理。我可以看到这个框,所以它工作正常,我只是无法显示纹理。 @einsteinx2 对于您的正方形,选择的正交是完美的,通常将正交设置为屏幕尺寸,在渲染 ui-stuff 或类似的东西时,它对您的正常工作不起作用3d 对象(关于你问题的第二部分)。另一种方法是使用 ortho (0,1,0,1) 并绘制一个单位正方形(这样您的屏幕尺寸可以更改并且不会写入正方形坐标),但是您必须调整平移(这不是不再以像素为单位)。请记住。您的方形顶点和平移(用于模糊)必须与正交匹配。 如何应用纹理由纹理坐标指定。它使用每个像素的这些坐标(它们是在三角形上插值的顶点的值)来从纹理中获取颜色。我刚刚看到,您的纹理坐标搞砸了,它们应该是 0,0, 1,0, 1,1, 0,1,以匹配您的顶点,因为在 OpenGL 中,屏幕原点和纹理原点都是在左下角。 我也看到了,你的顶点顺序是错误的。您的三角形条用前三个顶点渲染一个三角形,用最后三个顶点渲染第二个三角形。使用您的顶点,您会在右下角获得三角形,然后在右上角获得一个三角形,该三角形被翻转并因此被背面剔除剔除。在本例中,您只需将原始类型从 GL_TRIANGLE_STRIP 更改为 GL_TRIANGLE_FAN。或者您可以将顶点顺序更改为 0,0, 1,0, 0,1, 1,1 以及 texCoords,但是您的线循环将不起作用,因此三角形扇形是一个很好的解决方案。

以上是关于在 OpenGL ES 2D Orthographic (Ortho) 模式下绘制问题的主要内容,如果未能解决你的问题,请参考以下文章

在 cocos2d 中使用 opengl-es 进行圆形裁剪

Android - 使用 openGL ES 绘制 3D 然后 2D

使用 OpenGL ES 2.0 绘制 2D 图像

在 Android OpenGL ES 中将 2D 纹理保存为 png

Android OpenGL ES:展示一张2d图片

Android OpenGL ES:展示一张2d图片