实例化(instancing)或者多实例渲染(instancd rendering)是一种连续运行多条同样渲染命令的方法。而且每一个命令的所产生的渲染结果都会有轻微的差异。

是一种很有效的。有用少量api调用来渲染大量几何体的方法。OpenGL提供多种机制,同意着色器对不同渲染实例赋予不同的顶点属性。

几个简单的多实例渲染命令:

    1、void glDrawArraysInstanced( GLenum mode, GLint first, GLsizei count, GLsizei primCount )
            该函数是glDrawArrays()的多实例版本号,參数全然等价。仅仅是多了个primCount,该參数用于设置渲染实例个数。
    2、void glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, void* indices, GLsizei primcount )
            该函数是glDrawElements()的多实例版本号。相同仅仅是多了个primCount參数而已,相同是用于设置渲染实例个数。
    3、void glDrawElementsInstancedBaseVertex( GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instanceCount, GLuint baseVertex )
            该函数是glDrawElementsBaseVertex()的多实例版本号。instanceCount表示渲染的实例数目。

多实例渲染顶点属性控制:

    1、void glVertexAttribDivisor( GLenum index, GLuint divisor )
            index相应于着色器中输入变量的location。divisor:表示顶点属性的更新频率,每隔多少个实例将又一次设置实例的该属性,比如设置为1。那么每一个实例的属性都不一样,设置为2则每两个实例同样,3则每三个实例改变属性。

而该属性的属性数组大小将为(instance/divisor),instance为之前设置的渲染实例数(primCount),如果在多实例渲染中改变实例的颜色,设divisor为2。instance为100。颜色数组至少为100/2
= 50组rgba数据,才干保证每一个实例都有自己的颜色值,不然将是黑漆漆的。

最后假设divisor设置为0,将代表是非实例化,渲染的结果是,全部实例都是黑漆漆的,可能这个黑漆漆的结果也不是必定的,我猜想的是这时候着色器的输入变量vec4 color为默认的(0.0,0.0。0.0,1.0)并没有设置它的值,所以是黑色的。


顶点着色器分析:

#version 410

    //输入变量position。顶点坐标
  layout (location = 0) in vec4 position;
//normal顶点法线计算
  layout (location = 1) in vec3 normal;
//顶点颜色
  layout (location = 2) in vec4 color;

//特别注意。这里设置的mat4类型的输入变量,location为3,可是一个mat4类型会占领连续的4个位置
//因此model_matrix占领了3,4,5,6四个索引位置。

  layout (location = 3) in mat4 model_matrix;

    //在程序渲染过程是常量的视图矩阵,和投影矩阵(just这个程序是常量)
  uniform mat4 view_matrix;
  uniform mat4 projection_matrix;
  
//输入变量,一个简单的结构体,法线。和颜色。
  out VERTEX
  {
      vec3    normal;
      vec4    color;
  } vertex;

  void main(void)
  {
        //计算模型视图矩阵
      mat4 model_view_matrix = view_matrix * model_matrix;
    
        //计算顶点坐标
      gl_Position = projection_matrix * (model_view_matrix * position);
    
    //计算法线和颜色,并输出
      vertex.normal = mat3(model_view_matrix) * normal;
      vertex.color = color;
  }

片元着色器分析:

#version 410

//片元着色器的输出
  layout (location = 0) out vec4 color;

//和顶点着色器差点儿一样的片元着色器输入。in/out必须是匹配的。

  in VERTEX
  {
      vec3    normal;
      vec4    color;
  } vertex;

  void main(void)
  {
    //结合法线计算终于的颜色
      color = vertex.color * (0.1 + abs(vertex.normal.z)) + vec4(0.8, 0.9, 0.7, 1.0) * pow(abs(vertex.normal.z), 40.0);
  }

应用程序代码:

代码并没有太多的分析。主要是用了前面几个函数,进行多实例渲染的设置,代码在关键的地方都有或多或少的凝视。在这里把尽可能把代码贴全。代码中用到了vbm格式的模型数据,大概是书者自定义的一种格式。在源码中vbm管理类历经变迁,新版本号还不能读旧版本号的数据。

在这里顺便提供第八版源码的下载地址:http://www.opengl-redbook.com/ 应该能够轻易下到。感谢作者们的辛勤劳动
  1. #include "instancing_1.h"
  2. #include "vutils.h"
  3. #include "vmath.h"
  4. #include "vbm_ch3_intancing1.h"
  5.  
  6. namespace instancing1
  7. {
  8. float shade_aspect = 800 / 600;
  9. GLuint color_buffer;
  10. GLuint model_matrix_buffer;
  11. GLuint render_prog;
  12. GLuint model_matrix_loc;
  13. GLuint view_matrix_loc;
  14. GLuint projection_matrix_loc;
  15.  
  16. VBObject object;
  17. #define INSTANCE_COUNT 100
  18.  
  19. GLenum gl_err = 0;
  20. static const GLubyte* errorStr = NULL;
  21. };
  22.  
  23. using namespace instancing1;
  24.  
  25. void instancing1Init()
  26. {
  27. int n;
  28.  
  29. //创建着色器程序
  30. render_prog = glCreateProgram();
  31.  
  32. //着色器字符串
  33. static const char render_vs[] =
  34. "#version 410\n"
  35. "\n"
  36. "// 'position' and 'normal' are regular vertex attributes\n"
  37. "layout (location = 0) in vec4 position;\n"
  38. "layout (location = 1) in vec3 normal;\n"
  39. "\n"
  40. "// Color is a per-instance attribute\n"
  41. "layout (location = 2) in vec4 color;\n"
  42. "\n"
  43. "// model_matrix will be used as a per-instance transformation\n"
  44. "// matrix. Note that a mat4 consumes 4 consecutive locations, so\n"
  45. "// this will actually sit in locations, 3, 4, 5, and 6.\n"
  46. "layout (location = 3) in mat4 model_matrix;\n"
  47. "\n"
  48. "// The view matrix and the projection matrix are constant across a draw\n"
  49. "uniform mat4 view_matrix;\n"
  50. "uniform mat4 projection_matrix;\n"
  51. "\n"
  52. "// The output of the vertex shader (matched to the fragment shader)\n"
  53. "out VERTEX\n"
  54. "{\n"
  55. " vec3 normal;\n"
  56. " vec4 color;\n"
  57. "} vertex;\n"
  58. "\n"
  59. "// Ok, go!\n"
  60. "void main(void)\n"
  61. "{\n"
  62. " // Construct a model-view matrix from the uniform view matrix\n"
  63. " // and the per-instance model matrix.\n"
  64. " mat4 model_view_matrix = view_matrix * model_matrix;\n"
  65. "\n"
  66. " // Transform position by the model-view matrix, then by the\n"
  67. " // projection matrix.\n"
  68. " gl_Position = projection_matrix * (model_view_matrix * position);\n"
  69. " // Transform the normal by the upper-left-3x3-submatrix of the\n"
  70. " // model-view matrix\n"
  71. " vertex.normal = mat3(model_view_matrix) * normal;\n"
  72. " // Pass the per-instance color through to the fragment shader.\n"
  73. " vertex.color = color;\n"
  74. "}\n";
  75.  
  76. static const char render_fs[] =
  77. "#version 410\n"
  78. "\n"
  79. "layout (location = 0) out vec4 color;\n"
  80. "\n"
  81. "in VERTEX\n"
  82. "{\n"
  83. " vec3 normal;\n"
  84. " vec4 color;\n"
  85. "} vertex;\n"
  86. "\n"
  87. "void main(void)\n"
  88. "{\n"
  89. " color = vertex.color * (0.1 + abs(vertex.normal.z)) + vec4(0.8, 0.9, 0.7, 1.0) * pow(abs(vertex.normal.z), 40.0);\n"
  90. "}\n";
  91.  
  92. //shader创建,编译,装载
  93. vglAttachShaderSource( render_prog, GL_VERTEX_SHADER, render_vs );
  94. vglAttachShaderSource( render_prog, GL_FRAGMENT_SHADER, render_fs );
  95.  
  96. //连接着色器成为可用程序
  97. glLinkProgram(render_prog);
  98. //激活着色器
  99. glUseProgram(render_prog);
  100.  
  101. //获取视图矩阵位置
  102. view_matrix_loc = glGetUniformLocation(render_prog, "view_matrix");
  103. //获取投影矩阵位置
  104. projection_matrix_loc = glGetUniformLocation(render_prog, "projection_matrix");
  105.  
  106. //载入vbm模型文件
  107. //@pram 1路径、2顶点location 3法线location、4纹理location(颜色)
  108. object.LoadFromVBM("../8edlib/media/armadillo_low.vbm", 0, 1, 2);
  109.  
  110. //绑定顶点数组
  111. object.BindVertexArray();
  112.  
  113. //获取顶点着色器in变量location
  114. int position_loc = glGetAttribLocation( render_prog, "position" );
  115. int normal_loc = glGetAttribLocation( render_prog, "normal" );
  116. int color_loc = glGetAttribLocation( render_prog, "color" );
  117. int matrix_loc = glGetAttribLocation( render_prog, "model_matrix");
  118.  
  119. //每一个实例的颜色数组
  120. vmath::vec4 colors[INSTANCE_COUNT];
  121. for (n = 0; n < INSTANCE_COUNT; n++ )
  122. {
  123. float a = float(n) / 4.0f;
  124. float b = float(n) / 5.0f;
  125. float c = float(n) / 6.0f;
  126.  
  127. colors[n][0] = 0.5f + 0.25f * (sinf(a + 1.0f) + 1.0f );
  128. colors[n][1] = 0.5f + 0.25f * (sinf(b + 2.0f) + 1.0f );
  129. colors[n][2] = 0.5f + 0.25f * (sinf(c + 3.0f) + 1.0f );
  130. colors[n][3] = 1.0f;
  131. }
  132.  
  133. //缓存对象
  134. glGenBuffers(1, &color_buffer);
  135. glBindBuffer(GL_ARRAY_BUFFER, color_buffer);//3个工作。
  136. glBufferData( GL_ARRAY_BUFFER, sizeof(colors), colors, GL_DYNAMIC_DRAW );//动态改变用于绘制的数据
  137.  
  138. glVertexAttribPointer( color_loc, 4, GL_FLOAT, GL_FALSE, 0, NULL );
  139. glEnableVertexAttribArray( color_loc );
  140.  
  141. //!!!启用顶点属性多实例属性,每一个实例读取一次颜色值
  142. glVertexAttribDivisor( color_loc, 1);
  143.  
  144. //模型矩阵数据
  145. glGenBuffers(1, &model_matrix_buffer );
  146. glBindBuffer( GL_ARRAY_BUFFER, model_matrix_buffer );
  147. //NULL保留内存
  148. glBufferData( GL_ARRAY_BUFFER, INSTANCE_COUNT * sizeof(vmath::mat4), NULL, GL_DYNAMIC_DRAW );
  149. for (int i = 0; i < 4; i++ )
  150. {
  151. glVertexAttribPointer(matrix_loc + i, 4, GL_FLOAT, GL_FALSE, sizeof(vmath::mat4), (void *)(sizeof(vmath::vec4)* i));
  152. glEnableVertexAttribArray( matrix_loc + i );
  153. glVertexAttribDivisor( matrix_loc + i, 1 );
  154. }
  155.  
  156. //解除vao绑定,至于为什么要这句呢,我的理解是:这是个好习惯。用完之后吧OpenGL还原默认状态。
  157. glBindVertexArray(0);
  158. }
  159. static inline int min( int a, int b )
  160. {
  161. return a < b ? a : b;
  162. }
  163.  
  164. void instancing1Display()
  165. {
  166. float t = float(GetTickCount() & 0x3FFF) / float(0x3FFF);
  167. int n;
  168.  
  169. //清屏
  170. glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  171.  
  172. //
  173. glEnable( GL_CULL_FACE );
  174. glEnable( GL_DEPTH_TEST );
  175. glDepthFunc( GL_LEQUAL );
  176.  
  177. //绑定vbo,改动数据
  178. glBindBuffer( GL_ARRAY_BUFFER, model_matrix_buffer );
  179.  
  180. //获取当前vbo的OpenGL指针
  181. vmath::mat4* matrices = (vmath::mat4*)glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY );
  182. for (n = 0; n < INSTANCE_COUNT; n++ )
  183. {
  184. float a = 50.0f * float(n) / 4.0f;
  185. float b = 50.0f * float(n) / 5.0f;
  186. float c = 50.0f * float(n) / 6.0f;
  187.  
  188. matrices[n] = vmath::rotate(a + t * 360.0f, 1.0f, 0.0f, 0.0f) *
  189. vmath::rotate(a + t * 360.0f, 0.0f, 1.0f, 0.0f) *
  190. vmath::rotate(a + t * 360.0f, 0.0f, 0.0f, 1.0f) *
  191. vmath::translate(10.0f + a, 40.0f + b, 50.0f + c);//平移,再旋转
  192. }
  193.  
  194. //数据操作完成,解除映射!必须
  195. glUnmapBuffer( GL_ARRAY_BUFFER );
  196.  
  197. //确认激活须要的着色器程序
  198. glUseProgram( render_prog );
  199.  
  200. //生成视图矩阵和投影矩阵
  201. vmath::mat4 view_matrix(vmath::translate(0.0f, 0.0f, -1500.0f) * vmath::rotate(t * 360.0f * 2.0f, 0.0f, 1.0f, 0.0f));
  202. vmath::mat4 projection_matrix(vmath::frustum(-1.0f, 1.0f, -shade_aspect, shade_aspect, 1.0f, 5000.0f));
  203.  
  204. //设置视图矩阵和投影矩阵
  205. glUniformMatrix4fv( view_matrix_loc, 1, GL_FALSE, view_matrix );//这次你就没写错參数,能不能不坑爹,之前我都以为自己智商出问题了。
  206. glUniformMatrix4fv( projection_matrix_loc, 1, GL_FALSE, projection_matrix );
  207.  
  208. //渲染多个实例
  209. object.Render( 0, INSTANCE_COUNT ); //能不能不坑爹。
  210.  
  211. 。后面的vbm竟然不兼容前面的vbm.....
  212. GLenum error = glGetError();//
  213. const GLubyte* errorStr = gluErrorString(error);
  214. //这个几个意思。。
  215.  
  216. lookat仅仅是生成了一个矩阵而已。。。
  217. //vmath::lookat( vmath::vec3(0.0f, 0.0f, 0.0f), vmath::vec3(1.0f, 0.0f, 0.0f), vmath::vec3(0.0f, 1.0f, 0.0f) ); //摄像机?????????/
  218. }
  219.  
  220. void instancing1Update(float)
  221. {
  222.  
  223. }

还有一个多实例渲染实例,纹理打包,着色器内置变量gl_InstanceID使用:

实例计数器gl_InstanceID:
当前实例的索引值能够再顶点着色器中通过内置变量gl_InstanceID变量获得。

该变量被声明为一个整数,初始为0,每一个实例被渲染之后,他会加1.他总是存在于顶点着色器中,即使当前没有启用多实例特性,此时他的值保持0.gl_InstanceID能够作为uniform数组的索引使用,也能够作为纹理查找的參数,或者作为某个分析函数的输入,等等。

新实例分析:
新演示样例实现了上一个实例的同样画面。仅仅是程序的实现方式不一样。

在这里用到了纹理缓存对象,对于我又是一个未使用过的特性,在接受新东西的时候总不会认为乏味。

...................尽管不认为乏味,但还是会困。明天还得上班QAQ,To Be Continue~~~~~

看了代码之后,发现这个演示样例并没有什么好总结。或者是记录的东西,唯一就是让我对于应用程序和着色器程序之间的数据传递的灵活性有了进一步的了解。演示样例使用了两个tbo对象,以纹理缓存作为媒介,把颜色值和模型变换矩阵,传递到模型着色器中使用。最不明确的地方还是在于对于glsl着色器内置函数和sampler的陌生。之后具体学习sampler之后应该疑惑自然就能够解决。

OpenGL学习日记-2015.3.13——多实例渲染的更多相关文章

  1. OpenGL学习之路(三)

    1 引子 这些天公司一次次的软件发布节点忙的博主不可开交,另外还有其它的一些事也占用了很多时间.现在坐在电脑前,在很安静的环境下,与大家分享自己的OpenGL学习笔记和理解心得,感到格外舒服.这让我回 ...

  2. Linux学习日记-使用EF6 Code First(四)

    一.在linux上使用EF 开发环境 VS2013+mono 3.10.0 +EF 6.1.0 先检测一下EF是不是6的 如果不是  请参阅 Linux学习日记-EF6的安装升级(三) 由于我的数据库 ...

  3. python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例

    python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...

  4. OpenGL学习进程(12)第九课:矩阵乘法实现3D变换

    本节是OpenGL学习的第九个课时,下面将详细介绍OpenGL的多种3D变换和如何操作矩阵堆栈.     (1)3D变换: OpenGL中绘制3D世界的空间变换包括:模型变换.视图变换.投影变换和视口 ...

  5. OpenGL学习进程(11)第八课:颜色绘制的详解

        本节是OpenGL学习的第八个课时,下面将详细介绍OpenGL的颜色模式,颜色混合以及抗锯齿.     (1)颜色模式: OpenGL支持两种颜色模式:一种是RGBA,一种是颜色索引模式. R ...

  6. OpenGL学习笔记:拾取与选择

    转自:OpenGL学习笔记:拾取与选择 在开发OpenGL程序时,一个重要的问题就是互动,假设一个场景里面有很多元素,当用鼠标点击不同元素时,期待作出不同的反应,那么在OpenGL里面,是怎么知道我当 ...

  7. OpenGL学习之路(一)

    1 引子 虽然是计算机科班出身,但从小对几何方面的东西就不太感冒,空间想象能力也较差,所以从本科到研究生,基本没接触过<计算机图形学>.为什么说基本没学过呢?因为好奇(尤其是惊叹于三维游戏 ...

  8. OpenGL学习之路(四)

    1 引子 上次读书笔记主要是学习了应用三维坐标变换矩阵对二维的图形进行变换,并附带介绍了GLSL语言的编译.链接相关的知识,之后介绍了GLSL中变量的修饰符,着重介绍了uniform修饰符,来向着色器 ...

  9. android学习日记05--Activity间的跳转Intent实现

    Activity间的跳转 Android中的Activity就是Android应用与用户的接口,所以了解Activity间的跳转还是必要的.在 Android 中,不同的 Activity 实例可能运 ...

随机推荐

  1. hotmail邮箱pop3server设置方法

    hotmail邮箱 的POP3/SMTP功能仅仅向Hotmail Plus的用户开放,普通用户想要使用这一功能的话,得进行一些特别的设置.如今这一功能总算面向全部的用户开放了,虽然微软官方还没宣布这一 ...

  2. sha1加密java代码

    sha1 加密 java代码 public static String getSha1(String str){ if(str==null||str.length()==0){ return null ...

  3. U14Linux的帐号与用户组

    1.在/etc/group和/etc/gshadow中查找mousegroup: grep mousegroup /etc/group /etc/gshadow (grep的使用) 2.其实Linux ...

  4. jQuery Fancybox插件说明

    这里有jquery影像回放路径插件称为Fancybox,项目主页地址:http://fancybox.net/ Fancybox的特点例如以下: 1.能够支持图片.html文本.flash动画.ifr ...

  5. 在html中禁用自己主动完毕

    输入框输入内容时总是显示历史输入历史记录,现禁用的方法是加入一个属性: <input type="text name="txt_xm" autocomplete=& ...

  6. LeetCode: Unique Binary Search Trees [095]

    [题目] Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For ...

  7. Unable to start MySQL service. Another MySQL daemon is already running with the same UNIX socket

    Unable to start MySQL service. Another MySQL daemon is already running with the same UNIX socket 特征 ...

  8. SQL SERVER IN参数化处理

    方法一. CREATE TABLE [dbo].[Users] ( Id INTEGER IDENTITY(1, 1) PRIMARY KEY , Name NVARCHAR(50) NOT NULL ...

  9. registerWithTouchDispatcher()函数的使用

    registerWithTouchDispatcher()函数的使用 registerWithTouchDispatcher()函数主要用于注册Touch事件. 当我们使用this->setTo ...

  10. 基于VLC的视频播放器

    原文:基于VLC的视频播放器 最近在研究视频播放的功能,之前是使用VideoView.在网上看了一下,感觉不是很好,支持的格式比较少,现在网络视频的格式各种各样,感觉用VideoView播放起来局限性 ...