基於OpenGL編寫一個簡易的2D渲染框架-04 繪制圖片


閱讀文章前需要了解的知識,紋理:https://learnopengl-cn.github.io/01%20Getting%20started/06%20Textures/

 

  過程簡述:利用 FreeImage 庫加載圖像數據,再創建 OpenGL 紋理,通過 Canvas2D 畫布繪制,最后又 Renderer 渲染器渲染

 

  本來想用 soil 庫加載圖像數據的,雖然方便,但是加載有些格式的圖像文件時會出現一些問題。最后,改用 FreeImage 庫來加載圖像了。

 

添加 FreeImage 庫到工程

  解壓 FreeImage.rar 文件后得到三個文件

將其分別拷貝到 debug文件夾、External 文件夾、Lib 文件夾中,再鏈接上 lib 庫。

 

繪制圖片

  創建一個紋理結構,儲存紋理索引、大小以及紋理坐標

    struct DLL_export Texture
    {
        Rect size;
        Vec2 texcoords[4];

        GLuint texture;
    };

  創建一個紋理管理器類 TextureManager,用於創建和管理紋理。

  利用 FreeImage 庫加載紋理

    Texture* TexrureManager::createTexture(const char* filename)
    {
        GLuint texture = -1;
        std::string fullName = PathHelper::fullPath(filename);

        unsigned char* image_data = nullptr;
        FIBITMAP* bmp = nullptr;

        /* 初始化 FreeImage */
        FreeImage_Initialise(TRUE);

        /* 獲取圖像文件類型 */
        FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
        fif = FreeImage_GetFileType(fullName.c_str());

        if ( fif == FIF_UNKNOWN ) {
            fif = FreeImage_GetFIFFromFilename(fullName.c_str());
        }
        /* 加載所支持圖像類型的圖像 */
        if ( (fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif) ) {
            bmp = FreeImage_Load(fif, fullName.c_str(), JPEG_DEFAULT);
        }
        if ( !bmp ) return nullptr;

        int w = FreeImage_GetWidth(bmp);
        int h = FreeImage_GetHeight(bmp);
        int pixel_count = w * h;

        int byte_per_pixel = FreeImage_GetLine(bmp) / w;
        image_data = ( unsigned char* ) malloc(sizeof( unsigned char ) * pixel_count * 4);

        unsigned char* bits = FreeImage_GetBits(bmp);

        int current_pixel = 0;
        if ( byte_per_pixel == 4 ) {
            for ( int i = 0; i < pixel_count; i++ ) {
                image_data[i * 4 + 2] = bits[current_pixel++];
                image_data[i * 4 + 1] = bits[current_pixel++];
                image_data[i * 4 + 0] = bits[current_pixel++];
                image_data[i * 4 + 3] = bits[current_pixel++];
            }
        }
        else {
            for ( int i = 0; i < pixel_count; i++ ) {
                image_data[i * 4 + 2] = bits[current_pixel++];
                image_data[i * 4 + 1] = bits[current_pixel++];
                image_data[i * 4 + 0] = bits[current_pixel++];
                image_data[i * 4 + 3] = 255;
            }
        }

        if ( bmp ) FreeImage_Unload(bmp);
        FreeImage_DeInitialise();

        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);

        /* 設置紋理選項 */
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
        glBindTexture(GL_TEXTURE_2D, 0);

        free(image_data);

        Texture* tex = new Texture();
        tex->texture = texture;
        tex->size.set(0, 0, w, h);
        tex->texcoords[0].set(0, 0);
        tex->texcoords[1].set(0, 1);
        tex->texcoords[2].set(1, 1);
        tex->texcoords[3].set(1, 0);

        return tex;
    }

  在函數中,使用 FreeImage 庫加載紋理數據,然后創建 OpenGL 2D紋理,將創建的紋理保存到 Texture 結構中,並設置了紋理坐標。

 

在 Canvas2D 中繪制紋理

    void Canvas2D::drawTexture(int x, int y, Texture* texture, Color& color)
    {
        int w = texture->size.w;
        int h = texture->size.h;

        this->resizeVector(4, 6);
        vPositions[0].set(x + 0, y + 0, 0);
        vPositions[1].set(x + 0, y + h, 0);
        vPositions[2].set(x + w, y + h, 0);
        vPositions[3].set(x + w, y + 0, 0);

        vIndices[0] = 0;
        vIndices[1] = 2;
        vIndices[2] = 1;
        vIndices[3] = 0;
        vIndices[4] = 3;
        vIndices[5] = 2;

        static RenderUnit unit;
        unit.pPositions = &vPositions[0];
        unit.nPositionCount = 4;
        unit.pTexcoords = texture->texcoords;
        unit.pIndices = &vIndices[0];
        unit.nIndexCount = 6;
        unit.color = color;
        unit.texture = texture;
        unit.renderType = RENDER_TYPE_TEXTURE;

        pRenderer->pushRenderUnit(unit);
    }

  函數很簡單,設置了頂點數據並填充了 RenderUnit,再傳到 渲染器中渲染。與繪制幾何圖形相比,多了紋理坐標,並把渲染類型設置為 渲染紋理。

 

渲染器 Renderer 渲染紋理

  添加成員

std::map<Texture*, VertexData*> textureDatas;

  每張紋理都有其相應的頂點數據,這樣可以把多張相同紋理的頂點數據放到一個緩沖區中渲染,保證了渲染多張相同紋理時只使用使用一個 DrawCall(調用 函數 glDrawElements 進行繪制的次數),提高渲染效率。

  在 pushRenderUnit 函數中

        else if ( unit.renderType == RENDER_TYPE_TEXTURE ) {
            auto it = textureDatas.find(unit.texture);
            if ( it == textureDatas.end() ) {
                vertexData = new VertexData();
                vertexData->bHasTexcoord = true;
                vertexData->renderType = RENDER_TYPE_TEXTURE;
                textureDatas.insert(std::make_pair(unit.texture, vertexData));
            }
            else {
                vertexData = it->second;
            }
        }

  

索引出紋理對應的 VertexData,然后填充數據。最后的渲染函數中添加填充紋理坐標代碼

        /* 設置紋理 */
        if ( vertexData->bHasTexcoord ) {
            glBindBuffer(GL_ARRAY_BUFFER, texcoordBuffer);
            glBufferData(GL_ARRAY_BUFFER, sizeof( Vec2 ) * vertexData->nPositionCount, &vertexData->texcoords[0], GL_DYNAMIC_DRAW);

            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, texrure);
            glUniform1i(glGetUniformLocation(shaderProgram, "defaulteTexture"), 0);
        }

 

  為了能夠渲染紋理需要更改着色程序

  頂點着色器

#version 330 core

layout(location = 0) in vec3 Position;
layout(location = 1) in vec2 Texcoord;
layout(location = 2) in vec4 Color;

out vec2 texcoord;
out vec4 color;

uniform int bRenderTexture;

void main()
{
    gl_Position = vec4(Position, 1.0f);
    color = Color;

    if( bRenderTexture != 0 ){
        texcoord = Texcoord;
    }
}

  片段着色器

#version 330 core

out vec4 Color;

in vec2 texcoord;
in vec4 color;

uniform sampler2D defaultTexture;
uniform int bRenderTexture;

void main()
{
    if( bRenderTexture != 0 ){
        Color = texture(defaultTexture, texcoord) * color * color.w;
    }
    else{
        Color = color;
    }
}

  為了開啟 Alpha 效果,設置OpenGL 的混合狀態

        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  

  在主函數中添加繪制圖像的代碼

    Texture* texture = TexrureManager::instance()->getTexture("image.png");
    Texture* texture1 = TexrureManager::instance()->getTexture("image.jpg");
            canvas.drawTexture(180, 0, texture1, Color(1, 1, 1, 1));
            canvas.drawTexture(120, 100, texture, Color(1, 1, 1, 0.8));

  程序的運行結果

  這里繪制了 png 和 jpg 格式的圖像

 

源碼下載:http://pan.baidu.com/s/1skOmP21


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM