OpenGL (3) Load Mapping

Keywords: Attribute Fragment

With the model, you need to map it. The process of loading maps can be divided into two parts: first, the decoding of images, and secondly, the use of UV coordinates to correspond to the model. This paper mainly introduces the loading mapping from two aspects: the basic principle and the third-party library.

Decode

The following two ways are introduced: hard coding implementation and SOIL library.

Hard Coding Implementation

Because different types of images have different offset values, the image type should be determined before loading the image. On the other hand, for DXT compressed images, we need to sample them on the basis of compressed images, instead of restoring them back to uncompressed ones.

static unsigned char* DecodeBMPData(unsigned char* imageData,int&width,int& heigh)
{
    //decode bmp
    int pixelDataOffset =*((int*)(imageData+10));
    width = *((int*)(imageData +18));
    heigh = *((int*)(imageData +22));
    unsigned char* pixelData = (imageData+pixelDataOffset);
    for(int i = 0;i<width*heigh*3;i+=3)
    {
        //bgr->rgb
        unsigned char temp = pixelData[i+2];
        pixelData[i+2] = pixelData[i];
        pixelData[i] = temp;
    }
    return pixelData;
}

const unsigned long  FORMATE_DXT1 = 0x31545844l; //DXT1-> 1 T X D

static unsigned char* DecodeDXT1Data(unsigned char* imageData,int&width,int& height,int& pixelSize)
{
    height = *(unsigned long*)(imageData+sizeof(unsigned long)*3);
    width = *(unsigned long*)(imageData+sizeof(unsigned long)*4);
    pixelSize = *(unsigned long*)(imageData+sizeof(unsigned long)*5);
    unsigned long compressFormate;
    compressFormate = *(unsigned long*)(imageData+sizeof(unsigned long)*21);

    switch (compressFormate)
    {
    case FORMATE_DXT1:
        printf("DXT1\n");
        break;
    default:
        break;
    }
    unsigned char* pixelData = new unsigned char[pixelSize];
    memcpy(pixelData,(imageData+sizeof(unsigned long)*32),pixelSize);

    return pixelData;
}

GLuint CreateTextureFromFile(const char* imagePath)
{
    unsigned char* imageData =(unsigned char*) LoadFileContent(imagePath);

    int width = 0;
    int heigh = 0;
    //decode bmp
    unsigned char* pixelData =nullptr;
    int pixelDataSize = 0;
    GLenum srcForamte = GL_RGB;
    if ((*(unsigned short*)imageData) == 0x4D42)
    {
        pixelData = DecodeBMPData(imageData,width,heigh);
    } 
    else if (memcmp(imageData,"DDS ",4)==0)
    {
        pixelData = DecodeDXT1Data(imageData,width,heigh,pixelDataSize);
        srcForamte = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
    }
    
    if (pixelData == nullptr)
    {
        printf("cannot decode %s \n",imagePath);
        delete imageData;
        return 0;
    }

    GLuint texture;
    glGenTextures(1,&texture);
    glBindTexture(GL_TEXTURE_2D,texture);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    if(srcForamte == GL_RGB){
        glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,width,heigh,0,GL_RGB,GL_UNSIGNED_BYTE,pixelData);
    }
    else if (srcForamte == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
    {
        glCompressedTexImage2D(GL_TEXTURE_2D,0,srcForamte,width,heigh,0,pixelDataSize,pixelData);
    }
    glBindBuffer(GL_TEXTURE_2D,0);
    delete imageData;
    return texture;
}

It can be noted that buffer is generated by glGenTextures, and display parameters and sampling parameters are set by glTexParameteri. Finally, the primitives are generated by glTex Image 2D.

Third Party Library Implementation

Loading maps can also use SOIL library, which is the abbreviation of Simple OpenGL Image Library. It supports most popular image formats and is easy to use. Home page download . If loaded using the SOIL library, the code is encapsulated as follows:

GLuint CreateTextureFromFile(const char* imagePath)
{
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

    int width, height;
    unsigned char* image = SOIL_load_image(imagePath, &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); 

    return texture;
}

Use Map

The location of primitives can be opened by glActiveTexture.

TextureLocation = glGetUniformLocation(s_program,"U_MainTexture");

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,mainTexture);
glUniform1i(TextureLocation,0);

In OpenGL, primitives can be assigned directly without glUniform assignment. It can be transferred from 0 to 16 into the graphics card, respectively. The default 0 corresponds to the location of the first primitive. Of course, it is also possible to assign a location value to the texture sampler by using glUniform 1i, so that multiple loading maps can be set in a fragment shader.

GL_TEXTURE0 is activated by default. Therefore, in the case of a single element, you can only write the following code for rendering.

glBindTexture(GL_TEXTURE_2D,mainTexture);

shader rendering

When writing shader, we need to add texcoord in vs and pass it to fs. For fs, sampler2D is added to receive texture.

//vs
attribute vec3 pos;
attribute vec2 texcoord;
attribute vec3 normal;

uniform mat4 M;
uniform mat4 V;
uniform mat4 P;

varying vec2 V_Texcoord;

void main()
{
    V_Texcoord = texcoord;
    gl_Position=P*V*M*vec4(pos,1.0);
}

//fs
uniform sampler2D U_MainTexture;
varying vec2 V_Texcoord;

void main()
{
   gl_FragColor= texture2D(U_MainTexture,V_Texcoord);
}

summary

With the above code, you can load the map and draw it. It is recommended that it be encapsulated as an interface or class, because this part of the code is very low-level and usually does not change.

Pay attention to my Wechat Public Number and get more quality content

Posted by micklerlop on Fri, 19 Apr 2019 16:45:33 -0700