OpenGL | 纹理贴图

一、加载BMP文件

#include <Windows.h>
#include <fstream>
#include <iostream>
unsigned char* LoadDataFromDisk(const char* path)
{
    unsigned char* content = nullptr;
    FILE * file= fopen(path,"rb");
    if (file)
    {
        int ret = fseek(file, 0, SEEK_END);    //fseek 函数只返回执行的结果是否成功,并不返回文件的读写位置

        int len = ftell(file);                 //ftell 取得当前文件的读写位置

        if (len > 0)
        {
            rewind(file);                      //rewind 函数用于将文件内部的位置指针重新指向一个流(数据流或者文件)的起始位置

            content = new unsigned char[len + 1];

            fread(content, sizeof(unsigned char), len, file);

            content[len] = '\0';
        }

        fclose(file);
    }
    return content;
}

1.笔记:

(1)当创建一个windows桌面应用程序以后,想要看控制台输出,可在“生成后事件”命令行中加入以下设置“editbin /subsystem:console $(OutDir)$(ProjectName).exe”:

(2)当给unsigned char* 赋值后,运行时发现字符串中多了别的“?”等意外字符时,可以使用NotePad++修改编码格式来解决这个问题。

(3)源文件首行添加#define _CRT_SECURE_NO_WARNINGS与在“属性”->“C/C++”->“命令行”中添加/D_CRT_SECURE_NO_WARNINGS都可解决不安全问题。

#define _CRT_SECURE_NO_WARNINGS

二、解码BMP图片

1.运用Notepad++查看BMP文件

当我们用Notepad++打开bmp文件时,可能看到的是类似下方的一堆乱码。别急,用一个插件可以解决这个问题。

  • HexEditor下载

HexEditor是Notepad++的一个插件,支持以16进制形式查看bmp图片数据。根据需要(我用x86版本)在https://download.csdn.net/download/weixin_39766005/87570339下载,下载后可得到一个HexEditor.dll文件。

  • HexEditor安装

  1. 在Notepad++安装目录下的plugins目录下新建一个HexEditor文件夹,并将下载好的HexEditor.dll文件拖入其中。

  1. 打开Notepad++,找到菜单栏下的设置->导入->导入插件,定位到HexEditor.dll文件。重启Notepad++。

  1. 打开一个bmp文件(别急,此时肯定还是乱码),在Notepad++界面中,选择插件 -> 选HexEditor -> View in Hex即可看到类似下图的数据。

2.BMP格式详解

BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序,RGB数据也是倒着念的,原始数据是按B、G、R的顺序排列的BMP格式的文件从头到尾依次是如下信息:bmp文件头(bmp file header)、位图信息头(bitmap information header)、调色板(color palette)、位图数据。接下来详细说明一下bmp文件头与位图信息头。

  • bmp文件头(bmp file header)

位图文件头包含有关于文件类型、文件大小、存放位置等信息,共14字节。在Windows 3.0以上版本的位图文件中用BITMAPFILEHEADER结构来定义:

typedef struct tagBITMAPFILEHEADER {
    UINT bfType;                     
    DWORD bfSize;
    UINT bfReserved1;
    UINT bfReserved2;
    DWORD bfOffBits;
} BITMAPFILEHEADER;
  • bfType:2字节,文件类型,BMP格式的文件这两个字节是0x4D42,10进制就是19778,字符显示就是‘BM’。

  • bfSize:4字节,文件大小。

  • bfReserved1:2字节,保留,必须设置为0。

  • bfReserved2:2字节,保留,必须设置为0。

  • bfOffBits:4字节,从头到位图数据的偏移。

  • 位图信息头(bitmap information)

位图信息头包含有位图文件的大小、压缩类型和颜色格式,共40字节。其结构定义为:

typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;
    LONG biWidth;
    LONG biHeight;
    WORD biPlanes;
    WORD biBitCount;
    DWORD biCompression;
    DWORD biSizeImage;
    LONG biXPelsPerMeter;
    LONG biYPelsPerMeter;
    DWORD biClrUsed;
    DWORD biClrImportant;
} BITMAPINFOHEADER;
  • biSize:4字节,信息头的大小,即40;

  • biWidth:4字节,以像素为单位说明图像的宽度;

  • biHeight:4字节,以像素为单位说明图像的高度,同时如果为正,说明位图倒立(即数据表示从图像的左下角到右上角),如果为负说明正向;

  • biPlanes:2字节,为目标设备说明颜色平面数,总被设置为1;

  • biBitCount:2字节,说明比特数/像素数,值有1、2、4、8、16、24、32;

  • biCompression:4字节,说明图像的压缩类型,最常用的就是0(BI_RGB),表示不压缩;

  • biSizeImages:4字节,说明位图数据的大小,当用BI_RGB格式时,可以设置为0;

  • biXPelsPerMeter:4字节,表示水平分辨率,单位是像素/米,有符号整数;

  • biYPelsPerMeter:4字节,表示垂直分辨率,单位是像素/米,有符号整数;

  • biClrUsed:4字节,说明位图使用的调色板中的颜色索引数,为0说明使用所有;

  • biClrImportant:4字节,说明对图像显示有重要影响的颜色索引数,为0说明都重要;

3.解码BMP图片

unsigned char* DecodeTexture(unsigned char * data, int& width, int& height)
{
    if (0x4D42 == *((unsigned short*)data))
    {
        int pixelDataOffset = *((int*)(data + 10));
        width = *((int*)(data + 18));
        height = *((int*)(data + 22));

        unsigned char* pixelData = data + pixelDataOffset;   //位图数据

        //bgr->rgb
        for (size_t i = 0; i < width*height*3; i+=3)
        {
            unsigned char temp = pixelData[i];
            pixelData[i] = pixelData[i + 2];
            pixelData[i + 2] = temp;
        }
        return pixelData;
    }
    else {
        return nullptr;
    }
}

三、生成纹理

GLuint textureId;
void Init(const char * imagePath)
{
    //加载纹理图片
    unsigned char* data = LoadDataFromDisk(imagePath);

    //解码图片
    int width = 0, height = 0;
    unsigned char* pixelData= DecodeTexture(data, width, height);

    //生成openGl纹理:textureId
    glGenTextures(1, &textureId);
  
    //操作该纹理
    glBindTexture(GL_TEXTURE_2D, textureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

    //将纹理从内存传到显存
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixelData);

    glBindTexture(GL_TEXTURE_2D, 0);

    delete data;
}

四、进行纹理贴图

void Draw()
{
    glClearColor(1, 1, 0, 1.0f); 
    glClear(GL_COLOR_BUFFER_BIT);
 
    Init("C:\\Users\\fengchujun\\Downloads\\272.bmp");
    glEnable(GL_TEXTURE_2D);//开启纹理
    glBindTexture(GL_TEXTURE_2D,textureId);

    glBegin(GL_TRIANGLES);  //注意是逆时针绘制(CCW),可以通过   glFrontFace(GL_CW); 改成顺时针绘制

    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(-0.2f, -0.2f, -1.0f);

    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(0.2f, -0.2f, -1.0f);

    glTexCoord2f(0.5f, 1.0f);
    glVertex3f(0.0f, 0.2f, -1.0f);
    glEnd();
}

五、绘制效果

(在跟随课程学习时,发现有的bmp文件贴图不正确)

猜你喜欢

转载自blog.csdn.net/weixin_39766005/article/details/129481513