• 基于OpenGL编写一个简易的2D渲染框架-03 渲染基本几何图形


      阅读文章前需要了解的知识,你好,三角形:https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/

      要渲染出几何图形来,首先需要变换矩阵,那么自然就需要一个数学库了。本来想用 glm 库的,但用不惯这个库,只好编写一个简单的数据库了。这个库暂不需要复杂的功能,就几个向量类和 4x4 的矩阵类,矩阵类有一个重要的函数,用于创建正交矩阵(由于这是一个简单的项目,就不需要 LookAt(视图) 矩阵了)。

        Matrix4 Matrix4::ortho(GLfloat fLeft, GLfloat fRight, GLfloat fBottom, GLfloat fTop, GLfloat fNear, GLfloat fFar)
        {
            Matrix4 mat4 = Matrix4::ZERO;
    
            mat4.m[0][0] = 2 / (fRight - fLeft);
            mat4.m[1][1] = 2 / (fTop - fBottom);
            mat4.m[2][2] = 2 / (fNear - fFar);
            mat4.m[3][3] = 1;
    
            mat4.m[0][3] = -(fRight + fLeft) / (fRight - fLeft);
            mat4.m[1][3] = -(fTop + fBottom) / (fTop - fBottom);
            mat4.m[2][3] = (fNear + fFar) / (fNear - fFar);
    
            return mat4;
        }

    对于这个矩阵的推导感兴趣的话,这里推荐一篇文章:http://blog.csdn.net/popy007/article/details/4126809

    接着需要着色器程序,可以渲染几何图形就行了

    const GLchar *shader_vs = "#version 330 core
    "
    "layout (location = 0) in vec3 position;
    "
    "layout (location = 1) in vec4 color;
    "
    "uniform mat4 projection;
    "
    "out vec4 Vcolor;
    "
    "void main(){
    "
    "gl_Position = vec4(position, 1.0f);
    "
    "Vcolor = color;
    "
    "}";
    
    const GLchar *shader_frag = "#version 330 core
    "
    "out vec4 color;
    "
    "in vec4 Vcolor;
    "
    "void main(){
    "
    "color = Vcolor;
    "
    "}";

    创建着色程序并绑定

        void GraphicsContext::createShaderProgram()
        {
            /* 创建顶点作色器  */
            vertexShader = glCreateShader(GL_VERTEX_SHADER);
            glShaderSource(vertexShader, 1, &shader_vs, NULL);
            glCompileShader(vertexShader);
    
            GLint success;
            GLchar infoLog[512];
            glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
            if ( !success ) {
                glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
                return;
            }
    
            /* 创建片段着色器 */
            fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
            glShaderSource(fragmentShader, 1, &shader_frag, NULL);
            glCompileShader(fragmentShader);
    
            glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
            if ( !success ) {
                glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
                return;
            }
    
            /* 创建着色程序 */
            shaderProgram = glCreateProgram();
            glAttachShader(shaderProgram, vertexShader);
            glAttachShader(shaderProgram, fragmentShader);
            glLinkProgram(shaderProgram);
    
            glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
            if ( !success ) {
                glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
                return;
            }
            glDeleteShader(vertexShader);
            glDeleteShader(fragmentShader);
    
            /* 使用着色程序 */
            glUseProgram(shaderProgram);
        }

    准备阶段已经完成,接下来开始渲染几何图形,新建一个渲染器类 Renderer,渲染顶点数据。此外,还有一个储存顶点数据的类

            class VertexData
            {
            public:
                std::vector<Vec3> positions;
                std::vector<Vec2> texcoords;
                std::vector<Color> colors;
                std::vector<GLuint> indices;
    
                int nPositionCount;
                int nIndexCount;
                bool bHasTexcoord;
    
                RenderType renderType;
    
                void clear()
                {
                    nPositionCount = 0;
                    nIndexCount = 0;
                }
    
                void resize(int positionCount, int indexCount)
                {
                    if ( positions.size() - nPositionCount < positionCount ) {
                        positions.resize(positions.size() + positionCount);
                        colors.resize(colors.size() + positionCount);
    
                        if ( bHasTexcoord ) texcoords.resize(texcoords.size() + positionCount);
                    }
                    if ( indices.size() - nIndexCount < indexCount ) {
                        indices.resize(indices.size() + indexCount);
                    }
                }
    
                void pushData(const Vec3& pos, const Color& color)
                {
                    positions[nPositionCount] = pos;
                    colors[nPositionCount++] = color;
                }
    
                void pushData(const Vec3& pos, const Vec2& texcoord, const Color& color)
                {
                    positions[nPositionCount] = pos;
                    texcoords[nPositionCount] = texcoord;
                    colors[nPositionCount++] = color;
                }
    
                void pushIndex(GLuint index)
                {
                    indices[nIndexCount++] = index;
                }
            };

    主要储存顶点位置,顶点颜色,纹理坐标(现在先不用)和顶点的索引。

    渲染类型

        enum RenderType
        {
            RENDER_TYPE_LINES,
            RENDER_TYPE_TRIANGLES,
            RENDER_TYPE_TEXTURE
        };

    渲染单元的结构

        struct RenderUnit
        {
            Vec3* pPositions;
            int nPositionCount;
    
            Color* pColors;
            bool bSameColor;
    
            GLuint* pIndices;
            int nIndexCount;
    
            RenderType renderType;
        };

    渲染器头文件

    #pragma once
    #include "Common.h"
    #include "Math.h"
    
    #include <vector>
    
    namespace Simple2D
    {
        enum RenderType
        {
            RENDER_TYPE_LINES,
            RENDER_TYPE_TRIANGLES,
            RENDER_TYPE_TEXTURE
        };
    
        struct RenderUnit
        {
            Vec3* pPositions;
            int nPositionCount;
    
            Color* pColors;
            bool bSameColor;
    
            GLuint* pIndices;
            int nIndexCount;
    
            RenderType renderType;
        };
    
        class DLL_export Renderer
        {
            class VertexData
            {
            public:
                std::vector<Vec3> positions;
                std::vector<Vec2> texcoords;
                std::vector<Color> colors;
                std::vector<GLuint> indices;
    
                int nPositionCount;
                int nIndexCount;
                bool bHasTexcoord;
    
                RenderType renderType;
    
                void clear()
                {
                    nPositionCount = 0;
                    nIndexCount = 0;
                }
    
                void resize(int positionCount, int indexCount)
                {
                    if ( positions.size() - nPositionCount < positionCount ) {
                        positions.resize(positions.size() + positionCount);
                        colors.resize(colors.size() + positionCount);
    
                        if ( bHasTexcoord ) texcoords.resize(texcoords.size() + positionCount);
                    }
                    if ( indices.size() - nIndexCount < indexCount ) {
                        indices.resize(indices.size() + indexCount);
                    }
                }
    
                void pushData(const Vec3& pos, const Color& color)
                {
                    positions[nPositionCount] = pos;
                    colors[nPositionCount++] = color;
                }
    
                void pushData(const Vec3& pos, const Vec2& texcoord, const Color& color)
                {
                    positions[nPositionCount] = pos;
                    texcoords[nPositionCount] = texcoord;
                    colors[nPositionCount++] = color;
                }
    
                void pushIndex(GLuint index)
                {
                    indices[nIndexCount++] = index;
                }
            };
    
        public:
            Renderer();
            ~Renderer();
    
            void render();
            void renderVertexData(VertexData& vertexData);
    
            void pushRenderUnit(const RenderUnit& unit);
    
        private:
            void initBuffers();
            Vec3 tranformPosition(Vec3& pos);
    
        private:
            VertexData triangleData;
            VertexData lineData;
    
            GLuint positionBuffer;
            GLuint colorBuffer;
            GLuint indexBuffer;
            GLuint VAO;
    
            Matrix4 mTransformMatrix;
        };
    }

    triangleData,储存绘制三角形图元的顶点数据

    lineData,储存绘制线段的顶点数据

    RenderUnit类,通过 pushRenderUnit 函数, 将顶点数据传到渲染器,然后渲染器将相同渲染类型的 RenderUnit 数据储存到同一个顶点数据缓冲区(VertexData)。

        void Renderer::pushRenderUnit(const RenderUnit& unit)
        {
            VertexData* vertexData = nullptr;
            if ( unit.renderType == RENDER_TYPE_TRIANGLES ) {
                vertexData = &triangleData;
            }
            else if ( unit.renderType == RENDER_TYPE_LINES ) {
                vertexData = &lineData;
            }
    
            /* 填充数据 */
            vertexData->resize(unit.nPositionCount, unit.nIndexCount);
    
            int baseIndex = vertexData->nPositionCount;
            for ( int i = 0; i < unit.nPositionCount; i++ ) {
                if ( unit.bSameColor ) {
                    vertexData->pushData(tranformPosition(unit.pPositions[i]), unit.pColors[0]);
                }
                else {
                    vertexData->pushData(tranformPosition(unit.pPositions[i]), unit.pColors[i]);
                }
            }
            for ( int i = 0; i < unit.nIndexCount; i++ ) {
                vertexData->pushIndex(baseIndex + unit.pIndices[i]);
            }
        }

    在函数中,对顶点位置向量进行了矩阵变换

        Vec3 Renderer::tranformPosition(Vec3& pos)
        {
            return mTransformMatrix * pos;
        }

    这个变换矩阵主要一个正交矩阵和一个变换矩阵组成

            Matrix4 ortho = Matrix4::ortho(0, 800, 600, 0, -1, 1);
            Matrix4 tranform = Matrix4::makeTransform(Vec3(0, 600, 0), Vec3(1, -1, 1));
            mTransformMatrix = ortho * tranform;

    因为坐标原点在左上角,并且 Y 轴朝下为正。所以通过一个变换矩阵 transform 变换到左下角,Y 轴朝上为正。

    最后在函数 renderVertexData 中渲染出来

        void Renderer::renderVertexData(VertexData& vertexData)
        {
            /* 填充顶点数据  */
            glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
            glBufferData(GL_ARRAY_BUFFER, sizeof( Vec3 ) * vertexData.nPositionCount, &vertexData.positions[0], GL_STATIC_DRAW);
    
            glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
            glBufferData(GL_ARRAY_BUFFER, sizeof( Color ) * vertexData.nPositionCount, &vertexData.colors[0], GL_STATIC_DRAW);
            glBindBuffer(GL_ARRAY_BUFFER, 0);
    
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof( GLuint ) * vertexData.nIndexCount, &vertexData.indices[0], GL_STATIC_DRAW);
            glBindVertexArray(0);
    
            glBindVertexArray(VAO);
            switch ( vertexData.renderType ) {
            case RENDER_TYPE_TRIANGLES:
                glDrawElements(GL_TRIANGLES, vertexData.nIndexCount, GL_UNSIGNED_INT, 0);
                break;
            case RENDER_TYPE_LINES:
                glDrawElements(GL_LINES, vertexData.nIndexCount, GL_UNSIGNED_INT, 0);
                break;
            }
            glBindVertexArray(0);
    
            vertexData.clear();
        }

    为了方便绘制几何图形,创建一个画布类 Canvas2D

    #pragma once
    #include "Common.h"
    #include "Math.h"
    
    #include <vector>
    
    namespace Simple2D
    {
        class Renderer;
    
        class DLL_export Canvas2D
        {
        public:
            Canvas2D(Renderer* renderer);
            ~Canvas2D();
    
            void drawLine(int x1, int y1, int z1, int x2, int y2, int z2, Color& color);
    
            void drawCircle(const Vec3& center, int radius, Color& color, int nSlice = 36);
            void fillCircle(const Vec3& center, int radius, int degrees, Color& color);
            void fillCircle(const Vec3& center, int in_radius, int out_radius, int beginAngle, int endAngle, Color& color);
    
            void drawRect(float x, float y, float w, float h, Color color);
            void fillRect(float x, float y, float w, float h, Color color);
    
            void drawTriangles(Vec3* positions, Color* colors, int positionCount, GLuint* indices, int indexCount, bool sameColor = true);
            void drawLines(Vec3* positions, Color* colors, int positionCount, bool sameColor = true);
    
        private:
            void resizeVector(int positionCount, int indexCount);
    
            Renderer* pRenderer;
    
            std::vector<Vec3> vPositions;
            int nPositionCount;
    
            std::vector<GLuint> vIndices;
            int nIndexCount;
        };
    }

    如果要绘制一个矩形,需要准备顶点数据

        void Canvas2D::fillRect(float x, float y, float w, float h, Color color)
        {
            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;
    
            this->drawTriangles(&vPositions[0], &color, 4, &vIndices[0], 6);
        }

    然后在函数 drawTriangle 中将数据传递到 渲染器

        void Canvas2D::drawTriangles(Vec3* positions, Color* colors, int positionCount, GLuint* indices, int indexCount, bool sameColor)
        {
            static RenderUnit unit;
            unit.pPositions = positions;
            unit.nPositionCount = positionCount;
            unit.pIndices = indices;
            unit.nIndexCount = indexCount;
            unit.pColors = colors;
            unit.bSameColor = sameColor;
            unit.renderType = RENDER_TYPE_TRIANGLES;
    
            pRenderer->pushRenderUnit(unit);
        }

    最后在主函数中绘制几个图形

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
        RenderWindow window(DEFAULT_WIN_W, DEFAULT_WIN_H);
        GraphicsContext graphicsContext(&window);
    
        
        Canvas2D canvas(graphicsContext.getRenderer());
    
        MSG msg = { 0 };
        while ( msg.message != WM_QUIT ) {
            if ( PeekMessage(&msg, 0, 0, 0, PM_REMOVE) ) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            else {
                float n = 29;
                for ( int i = 0; i < n; i++ ) {
                    for ( int j = 0; j < n; j++ ) {
                        if ( i % 2 == 0 && j % 2 == 0 ) {
                            canvas.fillRect(
                                10 + i * 20, 10 + j * 20, 19, 19,
                                Color(i / n, i / n, 1, 1));
                        }
                        else {
                            canvas.drawRect(
                                10 + i * 20, 10 + j * 20, 19, 19,
                                Color(i / n, i / n, 1, 1));
                        }
                    }
                }
                
                canvas.drawLine(600, 10, 0, 700, 300, 0, Color(0, 0, 1, 1));
                canvas.drawCircle(Vec3(400, 300, 0), 200, Color(0, 0, 0, 1));
    
                canvas.fillCircle(Vec3(200, 400, 0), 0, 80, 0, 360, Color(1, 0, 0, 1));
                canvas.fillCircle(Vec3(400, 400, 0), 0, 80, 0, 270, Color(0, 1, 0, 1));
                canvas.fillCircle(Vec3(600, 400, 0), 60, 80, 0, 270, Color(0, 0, 1, 1));
    
                graphicsContext.flip();
            }
        }
        return 0;
    }

    运行程序的结果

    源码地址:http://files.cnblogs.com/files/ForEmail5/Simple2D-03.rar

  • 相关阅读:
    浅析嵌入式程序设计中的优化问题
    TCP粘包问题
    使用python 批量 配对t检验 医学 基础研究 数据分析
    Ubuntu误删系统文件修复办法
    飞思卡尔powerpc交叉编译环境的
    ubuntu packege下载网址
    数组对象里面对日期进行排序
    c# 字符串以逗号分割属性加上单引号
    Vue mysql 变量赋值, 获取数组
    Element vue Select 下拉框默认
  • 原文地址:https://www.cnblogs.com/ForEmail5/p/6793239.html
Copyright © 2020-2023  润新知