在 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