在OpenGL中绘制2D纹理

7

我有一个名为DrawImage的绘图函数,但它非常混乱,并且仅与一种特定形式的reshape函数配合使用,因此我有两个问题:

  1. 如何在OpenGL中绘制纹理?我只想创建一个函数,该函数获取纹理、x、y、宽度、高度和可能的角度并将其绘制和绘制到给定参数。我希望按照GL_QUAD正常方式进行绘制,但我不确定如何做了。人们说我应该使用SDL或SFML来做到这一点,这是推荐的吗?如果是,你能否给我一个简单的加载纹理和绘制纹理的函数?我目前正在使用SOIL来加载纹理。

函数如下:

void DrawImage(char filename, int xx, int yy, int ww, int hh, int angle) 
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, filename);

  glLoadIdentity();
  glTranslatef(xx,yy,0.0);
  glRotatef(angle,0.0,0.0,1.0);
  glTranslatef(-xx,-yy,0.0);

// Draw a textured quad
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex2f(xx,yy);
glTexCoord2f(0, 1); glVertex2f(xx,yy + hh);
glTexCoord2f(1, 1); glVertex2f(xx + ww,yy + hh);
glTexCoord2f(1, 0); glVertex2f(xx + ww,yy);

glDisable(GL_TEXTURE_2D);
glPopMatrix();

glMatrixMode(GL_PROJECTION);
glPopMatrix();

glMatrixMode(GL_MODELVIEW);
glEnd();
}

有人告诉我,在glBeginglEnd之间不能调用glDisableglPopMatrixglMatrixMode。问题是——没有这些调用,代码就无法正常工作。有什么办法可以让它在没有这些调用的情况下正常工作吗?
关于glutReshapeFunc,文档说明它接收一个指向具有2个参数(宽度和高度)的函数的指针。到目前为止,我只创建了一个不带参数的函数,你有什么想法怎样编写一个重塑函数,它可以接收宽度和高度,并实际执行重塑所需的操作。
还有一个小问题:当涉及到OpenGL这样的GUI时,C++比C更好吗?就我所知,只有面向对象编程才是关键,我没有遇到任何OOP可以解决而C不能解决的问题(我是指在OpenGL中)。
不需要回答所有问题——第1个问题对我来说基本上是最重要的 :P

1
有太多问题,其中一些是离题的/基于观点的。请一次只提一个问题。并且始终添加您编写的相关代码,并解释其错误或问题所在。 - user694733
@user694733 抱歉,已完成:P - user3745476
@user3745476,那为什么它被称为文件名呢?:/ - Bartek Banachewicz
@BartekBanachewicz 你说得对,我在我的代码中进行了更改。 - user3745476
2个回答

13
  1. 你的DrawImage函数看起来非常好。虽然,是的,在glEnd之前不应该调用glMatrixMode等函数,所以请将它们移除。我相信问题仅仅与设置投影矩阵有关,而添加的这些调用只是修复了本来不应该存在的问题。在你需要使用glutReshapeFunc捕获窗口调整大小事件之前,你不必使用它。

  2. SDL可以让你更加控制事件和glut,但需要花费更长的时间进行设置。GLFW也是一个很好的替代方案。除非你需要一个特定功能,否则更改它并不是那么重要。这些库用于创建GL上下文并进行一些事件处理。SOIL可用于它们所有。

  3. OpenGL是一个图形API,提供了一个常见的接口来进行硬件加速的3D图形绘制,而不是GUI库。虽然为OpenGL编写了GUI库。

  4. 是的,我相信很多人将OOP推向了极端。我认为C++比C更好,而不是完全重构你的代码方式。也许只需继续使用C,但使用C++编译器。然后,当你看到自己喜欢的功能时,使用它。最终,你可能会发现自己正在使用许多功能,并更好地理解它们存在的原因以及何时使用它们,而不是盲目遵循编码实践。这只是我的个人意见,这一切都非常主观。

所以,投影矩阵...

为了在二维屏幕上绘制三维物体,你需要将三维点"投影"到平面上。我相信你已经看过像这样的图像:

enter image description here

这允许你定义任意的三维坐标系统。除了在二维中绘制物体外,你自然希望直接使用像素坐标。毕竟这就是你的显示器显示的内容。因此,你想要使用一种旁路投影,它不进行任何透视缩放并匹配像素的比例和长宽比。

默认的投影(或“视图体积”)是一个正交的-1到1的立方体。要更改它,

glMatrixMode(GL_PROJECTION); //from now on all glOrtho, glTranslate etc affect projection
glOrtho(0, widthInPixels, 0, heightInPixels, -1, 1);
glMatrixMode(GL_MODELVIEW); //good to leave in edit-modelview mode

可以在任何地方调用此函数,但由于唯一会受到影响的变量是窗口的宽度/高度,因此通常将其放在初始化代码中,或者如果您计划调整窗口大小,则放在重置事件处理程序中,例如:

void reshape(int x, int y) {... do stuff with x/y ...}
...
glutReshapeFunc(reshape); //give glut the callback

这将使屏幕左下角成为原点,并且传递给 glVertex 的值现在可以是像素。

还有一些事情:不要使用 glTranslatef(-xx,-yy,0.0);,可以直接在后面使用 glVertex2f(0,0)。推入/弹出矩阵应该在函数内成对使用,以免调用者需要匹配它。

我将以一个完整的示例结束:

#include <GL/glut.h>
#include <GL/gl.h>
#include <stdio.h>

int main(int argc, char** argv)
{
    //create GL context
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA);
    glutInitWindowSize(800, 600);
    glutCreateWindow("windowname");

    //create test checker image
    unsigned char texDat[64];
    for (int i = 0; i < 64; ++i)
        texDat[i] = ((i + (i / 8)) % 2) * 128 + 127;

    //upload to GPU texture
    GLuint tex;
    glGenTextures(1, &tex);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 8, 8, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, texDat);
    glBindTexture(GL_TEXTURE_2D, 0);

    //match projection to window resolution (could be in reshape callback)
    glMatrixMode(GL_PROJECTION);
    glOrtho(0, 800, 0, 600, -1, 1);
    glMatrixMode(GL_MODELVIEW);

    //clear and draw quad with texture (could be in display callback)
    glClear(GL_COLOR_BUFFER_BIT);
    glBindTexture(GL_TEXTURE_2D, tex);
    glEnable(GL_TEXTURE_2D);
    glBegin(GL_QUADS);
    glTexCoord2i(0, 0); glVertex2i(100, 100);
    glTexCoord2i(0, 1); glVertex2i(100, 500);
    glTexCoord2i(1, 1); glVertex2i(500, 500);
    glTexCoord2i(1, 0); glVertex2i(500, 100);
    glEnd();
    glDisable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);
    glFlush(); //don't need this with GLUT_DOUBLE and glutSwapBuffers

    getchar(); //pause so you can see what just happened
    //System("pause"); //I think this works on windows

    return 0;
}

非常感谢!!!我明白问题不在DrawImage上,而是在我做的一些鼠标x、y坐标检测上 :P - user3745476

7

如果您可以使用OpenGL 3.0或更高版本,则更容易绘制纹理的方法是使用glBlitFramebuffer()。它不支持旋转,但仅将纹理复制到帧缓冲区中的矩形内,包括必要的缩放。

我尚未测试此代码,但它可能如下所示,其中tex是您的纹理ID:

GLuint readFboId = 0;
glGenFramebuffers(1, &readFboId);
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFboId);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                       GL_TEXTURE_2D, tex, 0);
glBlitFramebuffer(0, 0, texWidth, texHeight,
                  0, 0, winWidth, winHeight,
                  GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &readFboId);

如果您想重复绘制纹理,则可以重复使用同一个FBO。我在此处仅创建/销毁它,以使代码自包含。


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