如何在 C++ 中将位图绘制为 OpenGL 纹理?

Posted

技术标签:

【中文标题】如何在 C++ 中将位图绘制为 OpenGL 纹理?【英文标题】:How to draw bitmap as OpenGL texture in C++? 【发布时间】:2011-09-04 09:48:50 【问题描述】:

我有一个位图及其句柄 (Win32 HBITMAP)。有关如何在 OpenGL 四边形上绘制此位图的任何建议(通过缩放和拉动位图的 4 个角以适合四边形的 4 个顶点)?

【问题讨论】:

经常被问到的问题,检查:***.com/questions/3271310/opengl-texturing-a-cube,它实际上询问了如何在立方体的面上应用位图,例如四边形。 @Kheldar 这有点不同,虽然密切相关.. “拉位图的 4 个角”在这里我的意思是使位图不再是矩形,以适应四边形 【参考方案1】:

您需要检索HBITMAP中包含的数据,见http://msdn.microsoft.com/en-us/library/dd144879(v=vs.85).aspx 然后您可以使用glTexImage2DglTexSubImage2D

将DIB数据上传到OpenGL

创建纹理后,您可以像往常一样应用它(启用纹理,给四边形的每个角一个纹理坐标)。

因评论而编辑

这个(未经测试!)代码应该可以解决问题

GLuint load_bitmap_to_texture(
    HDC device_context, 
    HBITMAP bitmap_handle, 
    bool flip_image) /* untested */

    const int BytesPerPixel = sizeof(DWORD);

    SIZE bitmap_size;
    if( !GetBitmapDimensionEx(bitmap_handle, &bitmap_size) )
        return 0;

    ssize_t bitmap_buffer_size = bitmap_size.cx * bitmap_size.cy * BytesPerPixel;

#ifdef USE_DWORD
    DWORD *bitmap_buffer;
#else
    void *bitmap_buffer;
#endif
    bitmap_buffer = malloc(bitmap_buffer_size);

    if( !bitmap_buffer )
        return 0;


    BITMAPINFO bitmap_info;
    memset(&bitmap_info, 0, sizeof(bitmap_info));
    bitmap_info.bmiHeader.biSize = sizeof(bitmap_info.bmiHeader);
    bitmap_info.bmiHeader.biWidth  = bitmap_size.cx;
    bitmap_info.bmiHeader.biHeight = bitmap_size.cy;
    bitmap_info.bmiHeader.biPlanes = 1;
    bitmap_info.bmiHeader.biBitCount = BitsPerPixel;
    bitmap_info.bmiHeader.biCompression = BI_RGB;

    if( flip_image ) /* this tells Windows where to set the origin (top or bottom) */
        bitmap_info.bmiHeader.biHeight *= -1;

    if( !GetDIBits(device_context, 
                   bitmap_handle, 
                   0, bitmap_size.cy, 
                   bitmap_buffer, 
                   &bitmap_info,
                   DIB_RGB_COLORS /* irrelevant, but GetDIBits expects a valid value */ )
     ) 
        free(bitmap_buffer);
        return 0;
    

    GLuint texture_name;
    glGenTextures(1, &texture_name);
    glBindTexture(GL_TEXTURE_2D, texture_name);

    glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
    glPixelStorei(GL_UNPACK_LSB_FIRST,  GL_TRUE);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
    glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
                 bitmap_size.cx, bitmap_size.cy, 0,
                 GL_RGBA,
#ifdef USE_DWORD
                 GL_UNSIGNED_INT_8_8_8_8,
#else
                 GL_UNSIGNED_BYTE, 
#endif
                 bitmap_buffer);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    free(bitmap_buffer);

    return texture_name;

【讨论】:

DIB和BMP有什么区别?他们不一样吗? Windows 提供在 DIBSECTION 上创建 DC。 DIBSECTION 本质上是内存中的 DIB 文件。 DIB 文件是您在文件系统中找到的 .BMP 文件,但这并不重要。重要的是,您可以让 Windows 对进程地址空间中的某些内存进行操作,然后您可以将其传递给其他 API。但是,如果您只想读取 BMP 文件,则通过 HBITMAP 是一种过于复杂的方法。您可以直接加载它。 BMP 文件相当容易阅读。 @Paul:我很惊讶纹理被翻转了,DIB 也起源于 IIRC 的左下角。无论如何,您不需要调整纹理坐标。而是在 BITMAPINFOHEADER 中使用负数:bitmap_info.bmiHeader.biHeight = -bitmap_size.cy;,它告诉 Windows 翻转图像。 @Paul:最初我使用的是 GL_BGRA,因为这是 Windows 用于 DIB 的内部格式。如果通道被翻转,请使用 GL_BGRA。您也可以尝试使用 GL_UNSIGNED_INT_8_8_8_8 或 GL_UNSIGNED_INT_8_8_8_8_REV 而不是 GL_UNSIGNED_BYTE;和一个 DWORD* 而不是 void* 因为这是 Windows 内部使用的类型。 @Paul:我的建议:获取 GLEW,使用 GL/glew.h 而不是 GL/gl.h,不要忘记在上下文创建后调用 glewInit()。

以上是关于如何在 C++ 中将位图绘制为 OpenGL 纹理?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中绘制纹理/位图的区域?

如何在OpenGL中将像素作为纹理绘制到多边形?

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

Opengl es 2.0 在视频上绘制位图叠加

如何纹理 Opengl glut 对象(C++)

尝试在大位图上使用 OpenGL 纹理压缩 - 得到白色方块