基于opengl编写一个简易的2d渲染框架-04绘制图片

为了邮箱5 为了邮箱5     2022-08-30     675

关键词:

阅读文章前需要了解的知识,纹理: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

基于opengl编写一个简易的2d渲染框架-09重构渲染器-shader

  Shader只是进行一些简单的封装,主要功能:    1、编译着色程序    2、绑定Uniform数据    3、根据着色程序的顶点属性传递顶点数据到GPU   着色程序的编译GLuintShader::createShaderProgram(constchar*vsname,constchar*p... 查看详情

基于opengl编写一个简易的2d渲染框架-05渲染文本

阅读文章前需要了解的知识:文本渲染 https://learnopengl-cn.github.io/06%20In%20Practice/02%20Text%20Rendering/ 简要步骤:获取要绘制的字符的Unicode码,使用FreeType库获取对应的位图数据,添加到字符表中(后面同样的字符可以再表中... 查看详情

基于opengl编写一个简易的2d渲染框架02——搭建opengl环境

由于没有使用GLFW库,接下来得费一番功夫。阅读这篇文章前请看一下这个网页:https://learnopengl-cn.github.io/01%20Getting%20started/02%20Creating%20a%20window/ 以下,我摘取了一点片段 Windows上的OpenGL库  如果你是Windows平台,opengl32.l... 查看详情

基于opengl编写一个简易的2d渲染框架-13使用例子

 这是重构渲染器的最后一部分了,将会给出一个demo,测试模板测试、裁剪测试、半透明排序等等: 上图是本次demo的效果图,中间的绿色图形展现的是模板测试。 模板测试voidinit(Pass*&p1,Pass*&p2){p1=newPass;p2=newPass;... 查看详情

基于opengl编写一个简易的2d渲染框架-12重构渲染器-blockallocator

 BlockAllocator的内存管理情况可以用下图表示   整体思路是,先分配一大块内存Chunk,然后将Chunk分割成小块Block。由于Block是链表的一个结点,所以可以通过链表的形式把未使用的Block连接起来,并保存到pFreeLists中。当... 查看详情

基于opengl编写一个简易的2d渲染框架-08重构渲染器-整体架构

  事实上,前面编写的渲染器Renderer非常简陋,虽然能够进行一些简单的渲染,但是它并不能满足我们的要求。  当渲染粒子系统时,需要开启混合模式,但渲染其他顶点时却不需要开启混合模式。所以同时渲染粒子系统和... 查看详情

基于opengl编写一个简易的2d渲染框架-11重构渲染器-renderer

 假如要渲染一个纯色矩形在窗口上,应该怎么做?先确定顶点的格式,一个顶点应该包含位置信息vec3以及颜色信息vec4,所以顶点的结构体定义可以这样:structVertex{Vec3position;Vec4color;};然后填充矩形四个顶点是数据信息:Verte... 查看详情

基于opengl编写一个简易的2d渲染框架-07鼠标事件和键盘事件

这次为程序添加鼠标事件和键盘事件   当检测到鼠标事件和键盘事件的信息时,捕获其信息并将信息传送到需要信息的对象处理。为此,需要一个可以分派信息的对象,这个对象能够正确的把信息交到正确的对象。 实... 查看详情

OpenGL 2D像素完美渲染

】OpenGL2D像素完美渲染【英文标题】:OpenGL2Dpixelperfectrendering【发布时间】:2013-12-2115:04:10【问题描述】:我正在尝试渲染2D图像,以便它完全覆盖整个窗口。对于我的测试,我设置了一个窗口,使客户区正好是320x240,纹理也是... 查看详情

iOS 中基于矢量的实时 OSM 渲染器(使用 OpenGL ES)

】iOS中基于矢量的实时OSM渲染器(使用OpenGLES)【英文标题】:Realtimevector-basedOSMrendereriniOS(usingOpenGLES)【发布时间】:2013-04-2415:30:09【问题描述】:我正在寻找一种解决方案,它允许使用OpenStreetMap数据在iOS中渲染基于矢量的2D俯... 查看详情

基于opengles的深度学习框架编写

基于OpenGLES的深度学习框架编写背景与工程定位背景项目组基于深度学习实现了视频风格化和人像抠图的功能,但这是在PC/服务端上跑的,现在需要移植到移动端,因此需要一个移动端的深度学习的计算框架。同类型的库caffe-andr... 查看详情

基于 2D 纹理的体绘制

】基于2D纹理的体绘制【英文标题】:2D-texturebasedvolumerendering【发布时间】:2014-04-1319:24:47【问题描述】:我在网上找到了一个很棒的体积渲染教程:volumerenderingtutorial.示例代码是用Windows编写的,由于我在Mac上工作,因此我尝试... 查看详情

opengl绘制三角形(代码片段)

...对象:ElementBufferObject,EBO或IndexBufferObject,IBO渲染管线在OpenGL中,任何事物都在3D空间中,而屏幕和窗口却是2D像素数组,这导致OpenGL的大部分工作都是关于把3D坐标转变为适应你屏幕的2D像素。3D坐标转为2D坐标的处理过程是由Ope... 查看详情

RGBA 整数值到 OpenGL 纹理

】RGBA整数值到OpenGL纹理【英文标题】:RGBAintegervaluestoOpenGLTexture【发布时间】:2012-04-0216:08:35【问题描述】:我正在用C++编写一个光线投射器,它将其结果输出到常规图像文件。现在,我想将内部RGBA表示(4个整数)渲染到GLUT提... 查看详情

OpenGL 2d矩形没有被渲染

】OpenGL2d矩形没有被渲染【英文标题】:OpenGL2drectanglenotbeingrendered【发布时间】:2016-12-2200:07:43【问题描述】:我正在尝试在屏幕上渲染一个矩形。程序运行时,只显示清晰的颜色,没有矩形显示。代码如下:glClearColor(0.0,0.0,0.0,... 查看详情

OpenGL:如何在 3d 模式下优化多层相互重叠的 2d 渲染?

】OpenGL:如何在3d模式下优化多层相互重叠的2d渲染?【英文标题】:OpenGL:Howtooptimize2drenderingwithmultiplelayersoverlappingeachotherin3dmode?【发布时间】:2011-06-0612:36:31【问题描述】:我知道如何通过简单地首先渲染最近的平面来加速3d渲... 查看详情

opengl工作流程

  在OpenGL中,一切事物都在3D空间中,但我们的屏幕坐标确实2D像素数组,OpenGL大部分工作就是把3D坐标转换成适应屏幕的2D像素。3D坐标转换成2D屏幕坐标的过程是有OpenGL的图形渲染管线管理的。图形渲染管线的工作可以被划分... 查看详情

opengl-渲染流程

参考技术A在OpenGL中,任何事物都处于3D空间中,而屏幕和窗口却都是2D像素数组,这就导致了OpenGL大部分工作都是关于把3D坐标转变为适配你屏幕的2D像素,3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线(指的是一堆原始图... 查看详情