在 OpenGL 中绘制 2D 纹理 [已关闭]

问题描述 投票:0回答:2

我有一个名为

DrawImage
的绘图函数,但它确实令人困惑,并且仅适用于特定形式的重塑函数,所以我有两个问题:

  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();
}

有人对我说,在

glDisable
glPopMatrix
之间不能打电话给
glMatrixMode
glBegin
glEnd
。问题是——没有它,代码将无法工作。知道如何让它在没有它的情况下工作吗?

  1. 关于
    glutReshapeFunc

    文档说它获取一个指向具有 2 个参数(宽度和高度)的函数的指针。我创建了(到目前为止)一个无效的函数。
    如何编写一个获取宽度和高度的重塑函数,并实际执行它需要执行的重塑操作?
c opengl
2个回答
13
投票
  1. 您的 DrawImage 函数看起来非常好。虽然,是的,你不应该在

    glMatrixMode
    之前调用
    glEnd
    等,所以删除它们。我相信这个问题只是与设置投影矩阵有关,而添加的调用恰好解决了一个本来不应该存在的问题。
    glutReshapeFunc
    用于捕获窗口调整大小事件,因此在需要之前您不必使用它。

  2. SDL 使您可以更好地控制事件和过剩,但需要更长的时间来设置。 GLFW 也是一个不错的选择。我想除非你看到你需要的功能,否则更改并不那么重要。这些是用于创建 GL 上下文并执行一些事件处理的库。土壤可以用于所有这些。

  3. OpenGL 是一个图形 API,提供了用于执行硬件加速 3D 图形的通用接口,而不是 GUI 库。不过有为 OpenGL 编写的 GUI 库。

  4. 是的,我相信很多人都将 OOP 发挥到了极致。我喜欢 C++ 这个术语,因为它是更好的 C,而不是完全重构编码方式。也许继续使用 C,但使用 C++ 编译器。然后,当您看到您喜欢的功能时,请使用它。最终,您可能会发现自己使用了很多,然后对它们存在的原因以及何时使用它们有更好的认识,而不是盲目地遵循编码实践。只是在我看来,这都是非常主观的。

所以,投影矩阵...

要在 2D 屏幕上绘制 3D 内容,您需要将 3D 点“投影”到平面上。我相信你一定见过这样的图片:

enter image description here

这允许您定义任意 3D 坐标系。除了在 2D 中绘制东西之外,很自然地想要直接使用像素坐标。毕竟这就是您监控显示的内容。因此,您想要使用一种旁路投影,它不进行任何透视缩放并在比例和纵横比上匹配像素。

默认投影(或“查看体积”)是正交 -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;
}

6
投票

如果您可以使用 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。我只是在这里创建/销毁它以使代码独立。

© www.soinside.com 2019 - 2024. All rights reserved.