OpenGL透明度做了奇怪的事情

时间:2016-01-22 16:17:03

标签: opengl transparency

我正在尝试渲染一个带有alpha通道的纹理。

这就是我用于纹理加载的内容:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);

我在渲染纹理之前启用了GL_BLENDglEnable(GL_BLEND);

我也在代码的开头(初始化)执行了此操作:glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

这是结果(它应该是第一人手的透明纹理): enter image description here

但是当我像这样加载纹理时(没有alpha通道):

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);

结果如下:

enter image description here

有谁知道是什么导致这种情况,还是我必须提供更多代码? 对不起英语不好,提前谢谢。

修改

我的纹理加载代码:

GLuint Texture::loadTexture(const char * imagepath) {

    printf("Reading image %s\n", imagepath);

    // Data read from the header of the BMP file
    unsigned char header[54];
    unsigned int dataPos;
    unsigned int imageSize;
    unsigned int width, height;
    // Actual RGB data
    unsigned char * data;

    // Open the file
    FILE * file = fopen(imagepath, "rb");
    if (!file) { printf("%s could not be opened. \n", imagepath); getchar(); exit(0); }

    // Read the header, i.e. the 54 first bytes

    // If less than 54 bytes are read, problem
    if (fread(header, 1, 54, file) != 54) {
        printf("Not a correct BMP file\n");
        exit(0);
    }
    // A BMP files always begins with "BM"
    if (header[0] != 'B' || header[1] != 'M') {
        printf("Not a correct BMP file\n");
        exit(0);
    }
    // Make sure this is a 24bpp file
    if (*(int*)&(header[0x1E]) != 0) { printf("Not a correct BMP file\n");}
    if (*(int*)&(header[0x1C]) != 24) { printf("Not a correct BMP file\n");}

    // Read the information about the image
    dataPos = *(int*)&(header[0x0A]);
    imageSize = *(int*)&(header[0x22]);
    width = *(int*)&(header[0x12]);
    height = *(int*)&(header[0x16]);

    // Some BMP files are misformatted, guess missing information
    if (imageSize == 0)    imageSize = width*height * 3; // 3 : one byte for each Red, Green and Blue component
    if (dataPos == 0)      dataPos = 54; // The BMP header is done that way

                                         // Create a buffer
    data = new unsigned char[imageSize];

    // Read the actual data from the file into the buffer
    fread(data, 1, imageSize, file);

    // Everything is in memory now, the file wan be closed
    fclose(file);

    // Create one OpenGL texture
    GLuint textureID;
    glGenTextures(1, &textureID);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    glBindTexture(GL_TEXTURE_2D, textureID);
    if (imagepath == "hand.bmp") {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    }else {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);
    }

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

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

    delete[] data;
    return textureID;
}

正如你可以看到它不是我自己编写的代码,我从opengl-tutorial.org获得它

3 个答案:

答案 0 :(得分:3)

我的第一条评论说:

  

重复的偏移模式看起来像数据被视为具有更大的偏移,而实际上它更小(或相反)。

那是在我真正注意到你做了之前。是的,这正是这个。您不能将每像素4个字节的数据视为每像素3个字节的数据。 alpha通道被解释为颜色,这就是为什么它都以这种方式抵消。

如果你想忽略alpha通道,你需要在加载时将其剥离,这样它最终会为OpenGL纹理内存中的每个像素值提供3个字节。 (这就是 @RetoKoradi 的答案,即从RGBA数据创建RGB纹理)。

如果它实际上不应该看起来如此蓝色,也许它实际上不是在BGR布局中?

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
                                                          ^
                                                          \--- change to GL_RGBA as well

我的猜测是,人体皮肤会比它反射的蓝光更红。

答案 1 :(得分:3)

看起来你误解了glTexImage2D()的论据是如何工作的:

  • 第三个参数( internalformat )定义了您希望用于纹理中存储的数据的格式。
  • 第7和第8个参数( format type )将您传递给调用的数据格式定义为最后一个参数。

基于此,如果您作为最后一个参数传递的数据格式是BGRA,并且您想要从中创建RGB纹理,则正确的调用是:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);

请注意,第7个参数现在为GL_BGRA,与输入数据匹配,而第3个参数为GL_RGB,指定您要使用RGB纹理。

答案 2 :(得分:1)

您选择了纹理像素对齐的接缝。要指定正确的值,请尝试使用glPixelStorei来试验GL_UNPACK_ALIGNMENT的值(1,2,4)。

规格:

void glPixelStorei( GLenum pname,  
  GLint param); 

pname 指定要设置的参数的符号名称。一个值会影响像素数据到内存的打包:GL_PACK_ALIGNMENT。另一个影响从内存中解压缩像素数据:GL_UNPACK_ALIGNMENT

param 指定pname设置的值。

glPixelStorei设置影响后续glReadPixels操作的像素存储模式以及纹理模式的解包(请参阅glTexImage2D和g lTexSubImage2D)。

pname是一个符号常量,表示要设置的参数,param是新值。一个存储参数会影响像素数据返回客户端内存的方式:

<强> GL_PACK_ALIGNMENT 指定内存中每个像素行的开始的对齐要求。允许值为1(字节对齐),2(行与偶数字节对齐),4(字对齐)和8(行在双字边界上开始)。

另一个存储参数会影响从客户端内存中读取像素数据的方式:

<强> GL_UNPACK_ALIGNMENT 指定内存中每个像素行的开始的对齐要求。允许值为1(字节对齐),2(行与偶数字节对齐),4(字对齐)和8(行在双字边界上开始)。

下表给出了可以使用glPixelStorei设置的每个存储参数的有效值的类型,初始值和范围。

BMP格式不支持至少最常见的3版本(仅工作GL_BGR模式及其屏蔽修改)的透明度。改为使用PNG,DDS,TIFF,TGA(最简单)。

其次你的总图像数据大小计算公式是错误的

imageSize = width*height * 3; // 3 : one byte for each Red, Green and Blue component

正确的公式是:

imageSize = 4 * ((width * bitsPerPel + 31) / 32) * height;

其中bitsPerPel是每像素的当前图片位数(8,16或24)。

以下是用于加载具有透明度支持的简单TGA文件的函数代码:

// Define targa header.
#pragma pack(1)
typedef struct
    {
    GLbyte  identsize;              // Size of ID field that follows header (0)
    GLbyte  colorMapType;           // 0 = None, 1 = paletted
    GLbyte  imageType;              // 0 = none, 1 = indexed, 2 = rgb, 3 = grey, +8=rle
    unsigned short  colorMapStart;          // First colour map entry
    unsigned short  colorMapLength;         // Number of colors
    unsigned char   colorMapBits;   // bits per palette entry
    unsigned short  xstart;                 // image x origin
    unsigned short  ystart;                 // image y origin
    unsigned short  width;                  // width in pixels
    unsigned short  height;                 // height in pixels
    GLbyte  bits;                   // bits per pixel (8 16, 24, 32)
    GLbyte  descriptor;             // image descriptor
    } TGAHEADER;
#pragma pack(8)

GLbyte *gltLoadTGA(const char *szFileName, GLint *iWidth, GLint *iHeight, GLint *iComponents, GLenum *eFormat)
    {
    FILE *pFile;            // File pointer
    TGAHEADER tgaHeader;        // TGA file header
    unsigned long lImageSize;       // Size in bytes of image
    short sDepth;           // Pixel depth;
    GLbyte  *pBits = NULL;          // Pointer to bits

    // Default/Failed values
    *iWidth = 0;
    *iHeight = 0;
    *eFormat = GL_BGR_EXT;
    *iComponents = GL_RGB8;

    // Attempt to open the fil
    pFile = fopen(szFileName, "rb");
    if(pFile == NULL)
        return NULL;

    // Read in header (binary)
    fread(&tgaHeader, 18/* sizeof(TGAHEADER)*/, 1, pFile);

    // Do byte swap for big vs little endian
#ifdef __APPLE__
    BYTE_SWAP(tgaHeader.colorMapStart);
    BYTE_SWAP(tgaHeader.colorMapLength);
    BYTE_SWAP(tgaHeader.xstart);
    BYTE_SWAP(tgaHeader.ystart);
    BYTE_SWAP(tgaHeader.width);
    BYTE_SWAP(tgaHeader.height);
#endif


    // Get width, height, and depth of texture
    *iWidth = tgaHeader.width;
    *iHeight = tgaHeader.height;
    sDepth = tgaHeader.bits / 8;

    // Put some validity checks here. Very simply, I only understand
    // or care about 8, 24, or 32 bit targa's.
    if(tgaHeader.bits != 8 && tgaHeader.bits != 24 && tgaHeader.bits != 32)
        return NULL;

    // Calculate size of image buffer
    lImageSize = tgaHeader.width * tgaHeader.height * sDepth;

    // Allocate memory and check for success
    pBits = new GLbyte[lImageSize];
    if(pBits == NULL)
        return NULL;

    // Read in the bits
    // Check for read error. This should catch RLE or other 
    // weird formats that I don't want to recognize
    if(fread(pBits, lImageSize, 1, pFile) != 1)
        {
        free(pBits);
        return NULL;
        }

    // Set OpenGL format expected
    switch(sDepth)
        {
        case 3:     // Most likely case
            *eFormat = GL_BGR_EXT;
            *iComponents = GL_RGB8;
            break;
        case 4:
            *eFormat = GL_BGRA_EXT;
            *iComponents = GL_RGBA8;
            break;
        case 1:
            *eFormat = GL_LUMINANCE;
            *iComponents = GL_LUMINANCE8;
            break;
        };


    // Done with File
    fclose(pFile);

    // Return pointer to image data
    return pBits;
    }

iWidthiHeight返回纹理尺寸,eFormat i iCompoments外部和内部图像格式。比实际函数返回值是指向纹理数据的指针。

所以你的功能必须如下:

GLuint Texture::loadTexture(const char * imagepath) {

    printf("Reading image %s\n", imagepath);

    // Data read from the header of the BMP file
    int width, height;
    int component;
    GLenum eFormat;

    // Actual RGB data
    char * data = LoadTGA(imagepath, &width, &height, &component, &eFormat);

    // Create one OpenGL texture
    GLuint textureID;
    glGenTextures(1, &textureID);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    glBindTexture(GL_TEXTURE_2D, textureID);
    if (!strcmp(imagepath,"hand.tga")) { // important because we comparing strings not pointers
        glTexImage2D(GL_TEXTURE_2D, 0, component, width, height, 0, eFormat, GL_UNSIGNED_BYTE, data);
    }else {
        glTexImage2D(GL_TEXTURE_2D, 0, component, width, height, 0, eFormat, GL_UNSIGNED_BYTE, data);
    }

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

    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

    delete[] data;
    return textureID;
}