以前 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 =  * ;
static const int index_buffer_size = ;
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 = ; 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 core layout(location = ) in vec3 Position;
layout(location = ) in vec2 Texcoord;
layout(location = ) in vec4 Color;
layout(location = ) 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 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 : return texture(Texture0, texcoord);
case : return texture(Texture1, texcoord);
case : return texture(Texture2, texcoord);
case : return texture(Texture3, texcoord);
case : return texture(Texture4, texcoord);
case : return texture(Texture5, texcoord);
case : return texture(Texture6, texcoord);
case : return texture(Texture7, texcoord);
case : return texture(Texture8, texcoord);
case : return texture(Texture9, texcoord);
case : return texture(Texture10, texcoord);
case : return texture(Texture11, texcoord);
case : return texture(Texture12, texcoord);
case : return texture(Texture13, texcoord);
case : return texture(Texture14, texcoord);
case : 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 = ;
GLuint vTextures[nMaxNumberOfTexture];

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

    void Renderer::BindTexture()
{
for ( int i = ; 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

Simple2D-21(重构)渲染部分的更多相关文章

  1. 基于OpenGL编写一个简易的2D渲染框架-10 重构渲染器-Pass

    Pass,渲染通路,一个渲染通路指的是一次像素处理和一次顶点处理,也就是指的是一次绘制.简单来说就是顶点数据在渲染管线中走一遍最后绘制. 渲染粒子系统的粒子时,需要开启 OpenGL 的混合模式,并使 ...

  2. 基于OpenGL编写一个简易的2D渲染框架-09 重构渲染器-Shader

    Shader 只是进行一些简单的封装,主要功能: 1.编译着色程序 2.绑定 Uniform 数据 3.根据着色程序的顶点属性传递顶点数据到 GPU 着色程序的编译 GLuint Shader::cr ...

  3. 基于OpenGL编写一个简易的2D渲染框架-11 重构渲染器-Renderer

    假如要渲染一个纯色矩形在窗口上,应该怎么做? 先确定顶点的格式,一个顶点应该包含位置信息 vec3 以及颜色信息 vec4,所以顶点的结构体定义可以这样: struct Vertex { Vec3 p ...

  4. 基于OpenGL编写一个简易的2D渲染框架-08 重构渲染器-整体架构

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

  5. 基于OpenGL编写一个简易的2D渲染框架-12 重构渲染器-BlockAllocator

    BlockAllocator 的内存管理情况可以用下图表示 整体思路是,先分配一大块内存 Chunk,然后将 Chunk 分割成小块 Block.由于 Block 是链表的一个结点,所以可以通过链表的 ...

  6. Arnold+Shave 渲染毛发

    Arnold是一款基于真实物理光照算法和光线追踪算法的照片级渲染器,参与过多部好莱坞大片的制作,公司官网是:www.solidangle.com,官网上有很多效果图: 这里自己用一个球体测试了一下效果 ...

  7. 基于OpenGL编写一个简易的2D渲染框架-13 使用例子

    这是重构渲染器的最后一部分了,将会给出一个 demo,测试模板测试.裁剪测试.半透明排序等等: 上图是本次 demo 的效果图,中间的绿色图形展现的是模板测试. 模板测试 void init(Pass ...

  8. 学习opengl第一步

    有两个地址一个是学习opengl基础知识的网站, 一个是博客园大牛分享的特别好的文章. 记录一下希望向坚持做俯卧撑一样坚持下去. 学习网站:http://learnopengl-cn.readthed ...

  9. Zend_Framework_1 框架是如何被启动的?

    Zend Framework 1 是一个十年前的老框架了,我接触它也有两年了,现在来写这篇文章,主要原因是最近要写入职培训教程.公司项目基本上都是基于Zend1框架,即使现在要转 Laravel 也肯 ...

  10. Repaints and Reflows 重绘和重排版

    当浏览器下载完所有页面HTML标记,JavaScript,CSS,图片之后,它解析文件并创建两个内部数据 一棵DOM树 表示页面结构 Normal 0 7.8 磅 0 2 false false fa ...

随机推荐

  1. ORA-01791: not a SELECTed expression

    Student表有3个字段:id,name,age 看这两个SQL语句 (1)select name from student order by id; (2)select distinct(name ...

  2. 01.ubuntu14.04安装HI3518EV200 SDK的过程

    转载,侵删 1.海思SDK安装编译 Hi3518EV200_SDK是基于Hi3518EV200_DMEB的软件开发包,包含了在Linux相关应用开发时使用的各种工具及其源代码,是用户开发中最基本的软件 ...

  3. asm数据文件迁移(os–>asm)

    --添加测试表空间 SQL> create tablespace xff datafile '/u01/oradata/xifenfei.dbf' size 10m autoextend on ...

  4. mac下hbase安装

    出处:https://www.jianshu.com/p/510e1d599123 安装到的路径:/usr/local/Cellar/hbase/1.2.6 linux操作: linux命令 作用 . ...

  5. ORA-01919: role 'OLAPI_TRACE_USER' does not exist

    我在用数据泵导入数据的时候报的错 TEST_USER1@ORCL> conn / as sysdbaSYS@ORCL> grant plustrace to TEST_USER1; gra ...

  6. 解决“Replace wireless configuration ”默认被选上的问题

    方法一 1.打开 /home/tingpan/openwrt/barrier_breaker/feeds/luci/modules/admin-full/luasrc/model/cbi/admin_ ...

  7. C#代码规范和质量检查工具

    代码风格检查:StyleCop The StyleCop tool provides warnings that indicate style and consistency rule violati ...

  8. HttpURLConnection与HttpClient浅析---转

    HttpURLConnection与HttpClient浅析 1. GET请求与POST请求 HTTP协议是现在Internet上使用得最多.最重要的协议了,越来越多的Java应用程序需要直接通过HT ...

  9. 夜神模拟器+seleinm抓取手机app(参考资料集合)

    目前准备开始实现这个技术,将看起来还算可靠的参考链接粘贴如下: http://www.cnblogs.com/puresoul/p/4597211.html https://www.cnblogs.c ...

  10. 【精】Linux磁盘I/O性能监控之iostat详解

    [精]Linux磁盘I/O性能监控之iostat详解 Linux命令详解----iostat 使用iostat分析IO性能