读取dds和创建成纹理(来自ogl红宝书)

文章目录

  • 读取dds
  • 创建纹理

读取dds

// Enough mips for 16K x 16K, which is the minumum required for OpenGL 4.x
#define MAX_TEXTURE_MIPS    14

// Each texture image data structure contains an array of MAX_TEXTURE_MIPS,对于16Kx16K的纹理也是足够的
// of these mipmap structures. The structure represents the mipmap data for
// all slices at that level.
struct vglImageMipData
{
    GLsizei width;                              // Width of this mipmap level
    GLsizei height;                             // Height of this mipmap level
    GLsizei depth;                              // Depth pof mipmap level
    GLsizeiptr mipStride;                       // Distance in bytes between mip levels in memory,相邻级别的mipmap 在内存中的距离
    GLvoid* data;                               // Pointer to data
};

struct vglImageData
{
    GLenum target;                              // Texture target (1D, 2D, cubemap, array, etc.)
    GLenum internalFormat;                      // Recommended internal format (GL_RGBA32F, etc).
    GLenum format;                              // Format in memory,内存中的格式
    GLenum type;                                // Type in memory (GL_RED, GL_RGB, etc.),内存中的数据类型
    GLenum swizzle[4];                          // Swizzle for RGBA,RGBA 分量的乱序设置(swizzle)
    GLsizei mipLevels;                          // Number of present mipmap levels
    GLsizei slices;                             // Number of slices (for arrays),(对于纹理数组)切片的数量
    GLsizeiptr sliceStride;                     // Distance in bytes between slices of an array texture,纹理数组中相邻切片之间的距离
    GLsizeiptr totalDataSize;                   // Complete amount of data allocated for texture
    vglImageMipData mip[MAX_TEXTURE_MIPS];      // Actual mipmap data
};
```cpp
struct DDS_HEADER
{
    uint32_t                size;
    uint32_t                flags;
    uint32_t                height;
    uint32_t                width;
    uint32_t                pitch_or_linear_size;
    uint32_t                depth;
    uint32_t                mip_levels;
    uint32_t                reserved[11];
    DDS_PIXELFORMAT         ddspf;
    uint32_t                caps1;
    uint32_t                caps2;
    uint32_t                caps3;
    uint32_t                caps4;
    uint32_t                reserved2;
};

struct DDS_HEADER_DXT10
{
    uint32_t                format;
    uint32_t                dimension;
    uint32_t                misc_flag;
    uint32_t                array_size;
    uint32_t                reserved;
};

struct DDS_FILE_HEADER
{
    uint32_t                magic;
    DDS_HEADER              std_header;
    DDS_HEADER_DXT10        dxt10_header;
};

void*  fread(_Buffer,
        _In_                                             size_t _ElementSize,
        _In_                                             size_t _ElementCount,
        _Inout_                                          FILE*  _Stream
        );

 int fseek(
        _Inout_ FILE* _Stream,
        _In_    long  _Offset,
        _In_    int   _Origin//基准位置
        );
#define SEEK_CUR    1
#define SEEK_END    2
#define SEEK_SET    0
static bool vgl_DDSHeaderToImageDataHeader(const DDS_FILE_HEADER& header, vglImageData* image)
{
    if (header.std_header.ddspf.dwFlags == DDS_DDPF_FOURCC &&
        header.std_header.ddspf.dwFourCC == DDS_FOURCC_DX10)
    {
        if (header.dxt10_header.format < NUM_DDS_FORMATS)
        {
            const DDS_FORMAT_GL_INFO& format = gl_info_table[header.dxt10_header.format];
            image->format = format.format;
            image->type = format.type;
            image->internalFormat = format.internalFormat;
            image->swizzle[0] = format.swizzle_r;
            image->swizzle[1] = format.swizzle_g;
            image->swizzle[2] = format.swizzle_b;
            image->swizzle[3] = format.swizzle_a;
            image->mipLevels = header.std_header.mip_levels;
            return true;
        }
    }
    else if (header.std_header.ddspf.dwFlags == DDS_DDPF_FOURCC)
    {
        image->swizzle[0] = GL_RED;
        image->swizzle[1] = GL_GREEN;
        image->swizzle[2] = GL_BLUE;
        image->swizzle[3] = GL_ALPHA;
        image->mipLevels = header.std_header.mip_levels;

        switch (header.std_header.ddspf.dwFourCC)
        {
            case 116:
                image->format = GL_RGBA;
                image->type = GL_FLOAT;
                image->internalFormat = GL_RGBA32F;
                /*
                image->swizzle[0] = GL_ALPHA;
                image->swizzle[1] = GL_BLUE;
                image->swizzle[2] = GL_GREEN;
                image->swizzle[3] = GL_RED;
                */
                return true;
            default:
                break;
        }
    }
    else
    {
        image->swizzle[0] = GL_RED;
        image->swizzle[1] = GL_GREEN;
        image->swizzle[2] = GL_BLUE;
        image->swizzle[3] = GL_ALPHA;
        image->mipLevels = header.std_header.mip_levels;

        switch (header.std_header.ddspf.dwFlags)
        {
            case DDS_DDPF_RGB:
                image->format = GL_BGR;
                image->type = GL_UNSIGNED_BYTE;
                image->internalFormat = GL_RGB8;
                image->swizzle[3] = GL_ONE;
                return true;
            case (DDS_DDPF_RGB | DDS_DDPF_ALPHA):
            case (DDS_DDPF_RGB | DDS_DDPF_ALPHAPIXELS):
                image->format = GL_BGRA;
                image->type = GL_UNSIGNED_BYTE;
                image->internalFormat = GL_RGBA8;
                return true;
            case DDS_DDPF_ALPHA:
                image->format = GL_RED;
                image->type = GL_UNSIGNED_BYTE;
                image->internalFormat = GL_R8;
                image->swizzle[0] = image->swizzle[1] = image->swizzle[2] = GL_ZERO;
                image->swizzle[3] = GL_RED;
                return true;
            case DDS_DDPF_LUMINANCE:
                image->format = GL_RED;
                image->type = GL_UNSIGNED_BYTE;
                image->internalFormat = GL_R8;
                image->swizzle[0] = image->swizzle[1] = image->swizzle[2] = GL_RED;
                image->swizzle[3] = GL_ONE;
                return true;
            case (DDS_DDPF_LUMINANCE | DDS_DDPF_ALPHA):
                image->format = GL_RG;
                image->type = GL_UNSIGNED_BYTE;
                image->internalFormat = GL_RG8;
                image->swizzle[0] = image->swizzle[1] = image->swizzle[2] = GL_RED;
                image->swizzle[3] = GL_GREEN;
                return true;
            default:
                break;
        }
    }

    image->format = image->type = image->internalFormat = GL_NONE;
    image->swizzle[0] = image->swizzle[1] = image->swizzle[2] = image->swizzle[3] = GL_ZERO;

    return false;
}

static GLenum vgl_GetTargetFromDDSHeader(const DDS_FILE_HEADER& header)
{
    // If the DX10 header is present it's format should be non-zero (unless it's unknown)
    if (header.dxt10_header.format != 0)
    {
        // Check the dimension...
        switch (header.dxt10_header.dimension)
        {
            // Could be a 1D or 1D array texture
            case DDS_RESOURCE_DIMENSION_TEXTURE1D:
                if (header.dxt10_header.array_size > 1)
                {
                    return GL_TEXTURE_1D_ARRAY;
                }
                return GL_TEXTURE_1D;
            // 2D means 2D, 2D array, cubemap or cubemap array
            case DDS_RESOURCE_DIMENSION_TEXTURE2D:
                if (header.dxt10_header.misc_flag & DDS_RESOURCE_MISC_TEXTURECUBE)
                {
                    if (header.dxt10_header.array_size > 1)
                        return GL_TEXTURE_CUBE_MAP_ARRAY;
                    return GL_TEXTURE_CUBE_MAP;
                }
                if (header.dxt10_header.array_size > 1)
                    return GL_TEXTURE_2D_ARRAY;
                return GL_TEXTURE_2D;
            // 3D should always be a volume texture
            case DDS_RESOURCE_DIMENSION_TEXTURE3D:
                return GL_TEXTURE_3D;
        }
        return GL_NONE;
    }

    // No DX10 header. Check volume texture flag
    if (header.std_header.caps2 & DDSCAPS2_VOLUME)
        return GL_TEXTURE_3D;

    // Could be a cubemap
    if (header.std_header.caps2 & DDSCAPS2_CUBEMAP)
    {
        // This shouldn't happen if the DX10 header is present, but what the hey
        if (header.dxt10_header.array_size > 1)
            return GL_TEXTURE_CUBE_MAP_ARRAY;
        else
            return GL_TEXTURE_CUBE_MAP;
    }

    // Alright, if there's no height, guess 1D
    if (header.std_header.height <= 1)
        return GL_TEXTURE_1D;

    // Last ditch, probably 2D
    return GL_TEXTURE_2D;
}

static GLsizei vgl_GetDDSStride(const DDS_FILE_HEADER& header, GLsizei width)
{
    if (header.std_header.ddspf.dwFlags == DDS_DDPF_FOURCC &&
        header.std_header.ddspf.dwFourCC == DDS_FOURCC_DX10)
    {
        if (header.dxt10_header.format < NUM_DDS_FORMATS)
        {
            const DDS_FORMAT_GL_INFO& format = gl_info_table[header.dxt10_header.format];
            return (format.bits_per_texel * width + 7) / 8;
        }
    }
    else
    {
        switch (header.std_header.ddspf.dwFlags)
        {
            case DDS_DDPF_RGB:
                return width * 3;
            case (DDS_DDPF_RGB | DDS_DDPF_ALPHA):
            case (DDS_DDPF_RGB | DDS_DDPF_ALPHAPIXELS):
                return width * 4;
            case DDS_DDPF_ALPHA:
                return width;
            default:
                break;
        }
    }

    return 0;
}

extern "C"
{

void vglLoadDDS(const char* filename, vglImageData* image)
{
    FILE* f;

    memset(image, 0, sizeof(*image));

    f = fopen(filename, "rb");// 以二进制模式打开文件

    if (f == NULL)
        return;

    DDS_FILE_HEADER file_header = { 0, };
	
	// 读取 DDS 文件头
    fread(&file_header, sizeof(file_header.magic) + sizeof(file_header.std_header), 1, f);

	// DDS_MAGIC 应为 0x20534444(即字符 "DDS " 的十六进制表示)
    if (file_header.magic != DDS_MAGIC)
    {
        goto done_close_file;
    }

	//处理 DX10 扩展头
    if (file_header.std_header.ddspf.dwFourCC == DDS_FOURCC_DX10)
    {
        fread(&file_header.dxt10_header, sizeof(file_header.dxt10_header), 1, f);
    }

	//转换 DDS 头信息
    if (!vgl_DDSHeaderToImageDataHeader(file_header, image))
        goto done_close_file;

	//确定 OpenGL 纹理目标类型
    image->target = vgl_GetTargetFromDDSHeader(file_header);

    if (image->target == GL_NONE)
        goto done_close_file;

    size_t current_pos = ftell(f);
    size_t file_size;
    fseek(f, 0, SEEK_END);
    file_size = ftell(f);
    fseek(f, (long)current_pos, SEEK_SET);

    image->totalDataSize = file_size - current_pos;// 排除dds头
    image->mip[0].data = new uint8_t [image->totalDataSize];// 分配内存

    fread(image->mip[0].data, file_size - current_pos, 1, f);//读取

    int level;
    GLubyte * ptr = reinterpret_cast<GLubyte*>(image->mip[0].data);

	// 解析 Mipmap 层级,默认至少1级
    int width = file_header.std_header.width;
    int height = file_header.std_header.height;
    int depth = file_header.std_header.depth;

    image->sliceStride = 0;

    if (image->mipLevels == 0)// 默认至少1级
    {
        image->mipLevels = 1;
    }

    for (level = 0; level < image->mipLevels; ++level)
    {
        image->mip[level].data = ptr;
        image->mip[level].width = width;
        image->mip[level].height = height;
        image->mip[level].depth = depth;
        // 计算当前级别mipmap的像素数量,方便移到下一级别的数据位置,下一级别保存,并不断减半维度
        image->mip[level].mipStride = vgl_GetDDSStride(file_header, width) * height;
        image->sliceStride += image->mip[level].mipStride;
        ptr += image->mip[level].mipStride;// 移动到下一级 mipmap
        width >>= 1;// 宽度减半
        height >>= 1;// 高度减半
        depth >>= 1;// 深度减半(3D纹理)
    }

done_close_file:
    fclose(f);
}

}

创建纹理

GLuint vglLoadTexture(const char* filename,
                      GLuint texture,
                      vglImageData* image)
{
    vglImageData local_image;
    int level;

    if (image == 0)
        image = &local_image;

	// 重新将img数据解析存入内存
    vglLoadImage(filename, image);

    if (texture == 0)
    {
        glGenTextures(1, &texture);
    }

    glBindTexture(image->target, texture);

    GLubyte * ptr = (GLubyte *)image->mip[0].data;

	// 根据不同纹理类型,调用 glTexStorage* 分配存储空间,并逐级上传 Mipmap 数据。
	// mipmap其实是同一张纹理预先生成多个不同分辨率的层级
    switch (image->target)
    {
        case GL_TEXTURE_1D:
            glTexStorage1D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width);
            for (level = 0; level < image->mipLevels; ++level)
            {
                glTexSubImage1D(GL_TEXTURE_1D,
                                level,
                                0,//offset
                                image->mip[level].width,
                                image->format, image->type,
                                image->mip[level].data);
            }
            break;
        case GL_TEXTURE_1D_ARRAY:
            glTexStorage2D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width,
                           image->slices);
            for (level = 0; level < image->mipLevels; ++level)
            {
                glTexSubImage2D(GL_TEXTURE_1D,
                                level,
                                0, 0,
                                image->mip[level].width, image->slices,
                                image->format, image->type,
                                image->mip[level].data);
            }
            break;
        case GL_TEXTURE_2D:
            glTexStorage2D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width,
                           image->mip[0].height);
            for (level = 0; level < image->mipLevels; ++level)
            {
                glTexSubImage2D(GL_TEXTURE_2D,
                                level,
                                0, 0,
                                image->mip[level].width, image->mip[level].height,
                                image->format, image->type,
                                image->mip[level].data);
            }
            break;
        case GL_TEXTURE_CUBE_MAP:
            for (level = 0; level < image->mipLevels; ++level)
            {
                ptr = (GLubyte *)image->mip[level].data;
                for (int face = 0; face < 6; face++)
                {
                    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
                                 level,
                                 image->internalFormat,
                                 image->mip[level].width, image->mip[level].height,
                                 0,
                                 image->format, image->type,
                                 ptr + image->sliceStride * face);
                }
            }
            break;
        case GL_TEXTURE_2D_ARRAY:
            glTexStorage3D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width,
                           image->mip[0].height,
                           image->slices);
            for (level = 0; level < image->mipLevels; ++level)
            {
                glTexSubImage3D(GL_TEXTURE_2D_ARRAY,
                                level,
                                0, 0, 0,
                                image->mip[level].width, image->mip[level].height, image->slices,
                                image->format, image->type,
                                image->mip[level].data);
            }
            break;
        case GL_TEXTURE_CUBE_MAP_ARRAY:
            glTexStorage3D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width,
                           image->mip[0].height,
                           image->slices);
            break;
        case GL_TEXTURE_3D:
            glTexStorage3D(image->target,
                           image->mipLevels,
                           image->internalFormat,
                           image->mip[0].width,
                           image->mip[0].height,
                           image->mip[0].depth);
            for (level = 0; level < image->mipLevels; ++level)
            {
                glTexSubImage3D(GL_TEXTURE_3D,
                                level,
                                0, 0, 0,
                                image->mip[level].width, image->mip[level].height, image->mip[level].depth,
                                image->format, image->type,
                                image->mip[level].data);
            }
            break;
        default:
            break;
    }

	// 设置纹理参数(这里是分量的乱序设置)
    glTexParameteriv(image->target, GL_TEXTURE_SWIZZLE_RGBA, reinterpret_cast<const GLint *>(image->swizzle));

    if (image == &local_image)
    {
        vglUnloadImage(image);
    }

    return texture;
}

你可能感兴趣的:(OPENGL,c++)