zoukankan      html  css  js  c++  java
  • Simple2D-21(重构)渲染部分

      以前 Simple2D 的渲染方法是先设置 Pass,然后添加顶点数据,相同 Pass 的顶点数据会合并在一起。当设置新的 Pass 时,将旧的 Pass 和对应的顶点数据添加到渲染数组中。最后在帧结束时遍历渲染数组,根据 Pass 设置 OpenGL 状态,绘制对应的顶点数据。

      这次改为更加简单的方法,类似状态机。设置混合状态(Blend)、着色程序(Shader Program)。渲染部分由 Graphics ContextRendererShader Program 组成:

      Graphics Context:图形上下文,设置渲染的混合状态(Blend)和使用的着色程序(Shader Program)。所有顶点数据的渲染都会使用 Graphics Context 当前使用的 Blend 和 Shader Program,直到 Graphics Context 设置新的 Blend 或 Shader Program。当 Blend 或 Shader Program 改变时会将上一个状态的顶点数据立即进行渲染,这是和以前 Simple2D 渲染方法的区别。

      这次去掉了裁剪测试、深度测试、模板测试,这些是非必须使用的功能。保留了混合是因为要渲染透明纹理。

      Renderer:渲染器,对顶点数据进行合并和管理。需要渲染顶点数据时,会将数据传递给 Graphics Context,Graphics Context 然后根据当前设置的 Blend 和 Program 绘制数据。

      Shader Program:着色程序,使用着色器可以实现炫酷的效果。这次重构的 Simple2D 可以使用标准的 Program 和自定义的 Program 渲染顶点数据,通过自定义 Program 实现标准 Program 不能实现的效果。

      因此 Simple2D 则可以去掉 Pass 类和 BlockAllocator 类,以前使用 Simple2D 渲染顶点数据时要为顶点分配空间,渲染后又要释放空间,中间的步骤十分麻烦。为什么要使用如此麻烦的方法,纯粹是我脑袋瓦特了。

      实现


      Renderer

      Renderer 内部有两个一定大小缓冲区,用于存储顶点数据和索引数据:

            static const int vertex_buffer_size = 1024 * 1024;
            static const int index_buffer_size = 40000;
            char vVertexBuffer[vertex_buffer_size];        /* 用于合并顶点的缓冲区 */
            uint32 vIndexBuffer[index_buffer_size];        /* 用于合并索引的缓冲区 */

      渲染一个正方形,需要 4 个顶点和 6 个索引。通过 AppendRenderData( ) 函数将顶点数据和索引数据传递给 Renderer,然后 Renderer 将顶点数据和索引数据拷贝到缓冲区中,当缓冲区的空间不足时,会调用 Flush( ) 函数将渲染数据提交给 Graphics Context 进行渲染。AppendRenderData( ) 是一个模板函数,通过模板的特性可以知道顶点结构的大小,从而进行拷贝操作:

         template<class Type>
            void AppendRenderData(Type* vertex_data, int vertex_count, uint32* index_data, int index_count, PrimType type)
            {
                int total_vertex_count = vertex_buffer_size / sizeof(Type);
                if ( total_vertex_count - nVertexCount < vertex_count || index_buffer_size - nIndexCount < index_count ) {
                    this->Flush();
                }
    
                for ( int i = 0; i < index_count; i++ ) {
                    vIndexBuffer[nIndexCount + i] = nVertexCount + index_data[i];
                }
    
                char* data_header = vVertexBuffer + nVertexCount * sizeof(Type);
                memcpy(data_header, ( char* ) vertex_data, vertex_count * sizeof(Type));
    
                nVertexCount += vertex_count;
                nIndexCount += index_count;
                primType = type;
            }

      当然也可以通过函数参数传递顶点结构的大小,但这样太麻烦了。

      如果要渲染纹理,同时希望减少 drawcall。因为当时局限于一个着色程序绑定一张纹理的想法,所以以前 Simple2D 通过合并相同纹理的顶点数据以达到一张纹理一个 drawcall,可以减小切换纹理而带来的开销。但是着色程序是可以绑定多张纹理的,可以在顶点数据中添加一个索引的数据,指定使用哪一个绑定的纹理,这样可以达到多张纹理一个 drawcall 了。下面是 Simple2D 定义的纹理渲染标准着色程序:

        const char* Sprite_Vertex = R"(
            #version 330 core
    
            layout(location = 0) in vec3 Position;
            layout(location = 1) in vec2 Texcoord;
            layout(location = 2) in vec4 Color;
            layout(location = 3) in float Texindex;
    
            uniform mat4x4 MVPMatrix;
    
            out vec2 texcoord;
            out vec4 color;
            flat out int texindex;
    
            void main()
            {
                gl_Position =  MVPMatrix * vec4(Position, 1.0f);
                color = Color;
                texcoord = Texcoord;
                texindex = int(Texindex);
            }
        )";
        const char* Sprite_Fragment = R"(
            #version 330 core
    
            in vec2 texcoord;
            in vec4 color;
            flat in int texindex;
    
            uniform sampler2D Texture0;
            uniform sampler2D Texture1;
            uniform sampler2D Texture2;
            uniform sampler2D Texture3;
            uniform sampler2D Texture4;
            uniform sampler2D Texture5;
            uniform sampler2D Texture6;
            uniform sampler2D Texture7;
            uniform sampler2D Texture8;
            uniform sampler2D Texture9;
            uniform sampler2D Texture10;
            uniform sampler2D Texture11;
            uniform sampler2D Texture12;
            uniform sampler2D Texture13;
            uniform sampler2D Texture14;
            uniform sampler2D Texture15;
    
            vec4 SampleTexture(int index)
            {
                switch( index )
                {
                case 0: return texture(Texture0, texcoord);
                case 1: return texture(Texture1, texcoord);
                case 2: return texture(Texture2, texcoord);
                case 3: return texture(Texture3, texcoord);
                case 4: return texture(Texture4, texcoord);
                case 5: return texture(Texture5, texcoord);
                case 6: return texture(Texture6, texcoord);
                case 7: return texture(Texture7, texcoord);
                case 8: return texture(Texture8, texcoord);
                case 9: return texture(Texture9, texcoord);
                case 10: return texture(Texture10, texcoord);
                case 11: return texture(Texture11, texcoord);
                case 12: return texture(Texture12, texcoord);
                case 13: return texture(Texture13, texcoord);
                case 14: return texture(Texture14, texcoord);
                case 15: return texture(Texture15, texcoord);
                default: return vec4(1.0, 1.0, 1.0, 1.0);
                }
            }
    
            void main()
            {
                gl_FragColor = SampleTexture(texindex) * color;
            }
        )";

      这个着色程序一次可以绑定 16 张纹理,这意味着你可以一个 drawcall 渲染 16 张纹理。所以在 Renderer 中设置一个纹理数组:

            int nCurrentTextureCount;
            static const int nMaxNumberOfTexture = 16;
            GLuint vTextures[nMaxNumberOfTexture];

      储存渲染纹理,在渲染前绑定纹理到着色程序即可:

        void Renderer::BindTexture()
        {
            for ( int i = 0; i < nCurrentTextureCount; i++ ) {
                glActiveTexture(GL_TEXTURE0 + i);
                glBindTexture(GL_TEXTURE_2D, vTextures[i]);
            }
        }

      其中纹理存储在数组中的位置就是纹理在着色程序中的纹理索引。

      Shader Program

      Simple2D 内置有两个标准着色程序  Standard Program,分别用于渲染纹理和几何图形。如果有特别需要的话,可以自定义着色程序,为此需要处理 Uniform 数据,所以定义一个类 ProgramEffect 来管理 Uniform 数据。

      Graphics Context

      Graphics Context 用于设置 Blend 和 Shader Program 以及渲染顶点数据,实现较为简单。

      源码下载:Simple2D-20.rar

  • 相关阅读:
    requireJS搭建
    html启动本地.exe文件
    自定义input[type="checkbox"]的样式
    使用rem单位时css sprites的坑
    visibility API
    css动画
    去除ios端输入框的弹出
    *java类的生命周期
    处理高并发,防止库存超卖
    java注解的使用
  • 原文地址:https://www.cnblogs.com/ForEmail5/p/7461175.html
Copyright © 2011-2022 走看看