• OpenGL学习日记-2015.3.13——多实例渲染


        实例化(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/ 应该能够轻易下到。感谢作者们的辛勤劳动
    #include "instancing_1.h"
    #include "vutils.h"
    #include "vmath.h"
    #include "vbm_ch3_intancing1.h"
    
    namespace instancing1
    {
    	float shade_aspect = 800 / 600;
    	GLuint color_buffer;
    	GLuint model_matrix_buffer;
    	GLuint render_prog;
    	GLuint model_matrix_loc;
    	GLuint view_matrix_loc;
    	GLuint projection_matrix_loc;
    
    	VBObject object;
    	#define INSTANCE_COUNT 100
    
    	GLenum gl_err = 0;
    	static const GLubyte* errorStr = NULL;
    };
    
    
    using namespace instancing1;
    
    void instancing1Init()
    {
    	int n;
    	
    	//创建着色器程序
    	render_prog = glCreateProgram();
    
    	//着色器字符串
    	static const char render_vs[] =
    		"#version 410
    "
    		"
    "
    		"// 'position' and 'normal' are regular vertex attributes
    "
    		"layout (location = 0) in vec4 position;
    "
    		"layout (location = 1) in vec3 normal;
    "
    		"
    "
    		"// Color is a per-instance attribute
    "
    		"layout (location = 2) in vec4 color;
    "
    		"
    "
    		"// model_matrix will be used as a per-instance transformation
    "
    		"// matrix. Note that a mat4 consumes 4 consecutive locations, so
    "
    		"// this will actually sit in locations, 3, 4, 5, and 6.
    "
    		"layout (location = 3) in mat4 model_matrix;
    "
    		"
    "
    		"// The view matrix and the projection matrix are constant across a draw
    "
    		"uniform mat4 view_matrix;
    "
    		"uniform mat4 projection_matrix;
    "
    		"
    "
    		"// The output of the vertex shader (matched to the fragment shader)
    "
    		"out VERTEX
    "
    		"{
    "
    		"    vec3    normal;
    "
    		"    vec4    color;
    "
    		"} vertex;
    "
    		"
    "
    		"// Ok, go!
    "
    		"void main(void)
    "
    		"{
    "
    		"    // Construct a model-view matrix from the uniform view matrix
    "
    		"    // and the per-instance model matrix.
    "
    		"    mat4 model_view_matrix = view_matrix * model_matrix;
    "
    		"
    "
    		"    // Transform position by the model-view matrix, then by the
    "
    		"    // projection matrix.
    "
    		"    gl_Position = projection_matrix * (model_view_matrix * position);
    "
    		"    // Transform the normal by the upper-left-3x3-submatrix of the
    "
    		"    // model-view matrix
    "
    		"    vertex.normal = mat3(model_view_matrix) * normal;
    "
    		"    // Pass the per-instance color through to the fragment shader.
    "
    		"    vertex.color = color;
    "
    		"}
    ";
    
    	static const char render_fs[] =
    		"#version 410
    "
    		"
    "
    		"layout (location = 0) out vec4 color;
    "
    		"
    "
    		"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);
    "
    		"}
    ";
    
    	//shader创建,编译,装载
    	vglAttachShaderSource( render_prog, GL_VERTEX_SHADER, render_vs );
    	vglAttachShaderSource( render_prog, GL_FRAGMENT_SHADER, render_fs );
    
    	//连接着色器成为可用程序
    	glLinkProgram(render_prog);
    	//激活着色器
    	glUseProgram(render_prog);
    
    	//获取视图矩阵位置
    	view_matrix_loc = glGetUniformLocation(render_prog, "view_matrix");
    	//获取投影矩阵位置
    	projection_matrix_loc = glGetUniformLocation(render_prog, "projection_matrix");
    
    	//载入vbm模型文件
    	//@pram 1路径、2顶点location 3法线location、4纹理location(颜色)
    	object.LoadFromVBM("../8edlib/media/armadillo_low.vbm", 0, 1, 2);
    
    	//绑定顶点数组
    	object.BindVertexArray();
    
    	//获取顶点着色器in变量location
    	int position_loc = glGetAttribLocation( render_prog, "position" );
    	int normal_loc = glGetAttribLocation( render_prog, "normal" );
    	int color_loc = glGetAttribLocation( render_prog, "color" );
    	int matrix_loc = glGetAttribLocation( render_prog, "model_matrix");
    
    	//每一个实例的颜色数组
    	vmath::vec4 colors[INSTANCE_COUNT];
    	for (n = 0; n < INSTANCE_COUNT; n++ )
    	{
    		float a = float(n) / 4.0f;
    		float b = float(n) / 5.0f;
    		float c = float(n) / 6.0f;
    
    		colors[n][0] = 0.5f + 0.25f * (sinf(a + 1.0f) + 1.0f );
    		colors[n][1] = 0.5f + 0.25f * (sinf(b + 2.0f) + 1.0f );
    		colors[n][2] = 0.5f + 0.25f * (sinf(c + 3.0f) + 1.0f );
    		colors[n][3] = 1.0f;
    	}
    
    	//缓存对象
    	glGenBuffers(1, &color_buffer);
    	glBindBuffer(GL_ARRAY_BUFFER, color_buffer);//3个工作。
    	glBufferData( GL_ARRAY_BUFFER, sizeof(colors), colors, GL_DYNAMIC_DRAW );//动态改变用于绘制的数据
    
    	glVertexAttribPointer( color_loc, 4, GL_FLOAT, GL_FALSE, 0, NULL );
    	glEnableVertexAttribArray( color_loc );
    
    	//!!!启用顶点属性多实例属性,每一个实例读取一次颜色值
    	glVertexAttribDivisor( color_loc, 1);
    
    	//模型矩阵数据
    	glGenBuffers(1, &model_matrix_buffer );
    	glBindBuffer( GL_ARRAY_BUFFER, model_matrix_buffer );
    	//NULL保留内存
    	glBufferData( GL_ARRAY_BUFFER, INSTANCE_COUNT * sizeof(vmath::mat4), NULL, GL_DYNAMIC_DRAW );
    	for (int i = 0; i < 4; i++ )
    	{
    		glVertexAttribPointer(matrix_loc + i, 4, GL_FLOAT, GL_FALSE, sizeof(vmath::mat4), (void *)(sizeof(vmath::vec4)* i));
    		glEnableVertexAttribArray( matrix_loc + i );
    		glVertexAttribDivisor( matrix_loc + i, 1 );
    	}
    
    	//解除vao绑定,至于为什么要这句呢,我的理解是:这是个好习惯。用完之后吧OpenGL还原默认状态。
    	glBindVertexArray(0);
    }
    static inline int min( int a, int b )
    {
    	return a < b ? a : b;
    }
    
    void instancing1Display()
    {
    	float t = float(GetTickCount() & 0x3FFF) / float(0x3FFF);
    	int n;
    
    	//清屏
    	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    
    	//
    	glEnable( GL_CULL_FACE );
    	glEnable( GL_DEPTH_TEST );
    	glDepthFunc( GL_LEQUAL );
    
    	//绑定vbo,改动数据
    	glBindBuffer( GL_ARRAY_BUFFER, model_matrix_buffer );
    
    	//获取当前vbo的OpenGL指针
    	vmath::mat4* matrices = (vmath::mat4*)glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY );
    	for (n = 0; n < INSTANCE_COUNT; n++ )
    	{
    		float a = 50.0f * float(n) / 4.0f;
    		float b = 50.0f * float(n) / 5.0f;
    		float c = 50.0f * float(n) / 6.0f;
    
    		matrices[n] = vmath::rotate(a + t * 360.0f, 1.0f, 0.0f, 0.0f) *
    					  vmath::rotate(a + t * 360.0f, 0.0f, 1.0f, 0.0f) *
    					  vmath::rotate(a + t * 360.0f, 0.0f, 0.0f, 1.0f) *
    					  vmath::translate(10.0f + a, 40.0f + b, 50.0f + c);//平移,再旋转
    	}
    
    	//数据操作完成,解除映射!必须
    	glUnmapBuffer( GL_ARRAY_BUFFER );
    
    	//确认激活须要的着色器程序
    	glUseProgram( render_prog );
    
    	//生成视图矩阵和投影矩阵
    	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));
    	vmath::mat4 projection_matrix(vmath::frustum(-1.0f, 1.0f, -shade_aspect, shade_aspect, 1.0f, 5000.0f));
    
    	//设置视图矩阵和投影矩阵
    	glUniformMatrix4fv( view_matrix_loc, 1, GL_FALSE, view_matrix );//这次你就没写错參数,能不能不坑爹,之前我都以为自己智商出问题了。
    	glUniformMatrix4fv( projection_matrix_loc, 1, GL_FALSE, projection_matrix );
    
    	//渲染多个实例
    	object.Render( 0, INSTANCE_COUNT ); //能不能不坑爹。

    。后面的vbm竟然不兼容前面的vbm..... GLenum error = glGetError();// const GLubyte* errorStr = gluErrorString(error); //这个几个意思。。

    。lookat仅仅是生成了一个矩阵而已。。。 //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) ); //摄像机?????????/ } void instancing1Update(float) { }



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

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

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

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

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

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

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

  • 相关阅读:
    【转】Math.Atan2 方法
    【转】Excel快捷键大全
    Tricks(四十七)—— 布尔矩阵(0-1矩阵)取反
    Tricks(四十七)—— 布尔矩阵(0-1矩阵)取反
    tensorflow 函数接口的理解
    tensorflow 函数接口的理解
    TensorFlow 学习(十一)—— 正则(regularizer)
    TensorFlow 学习(十一)—— 正则(regularizer)
    TensorFlow 需注意的细节问题
    TensorFlow 需注意的细节问题
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5098114.html
Copyright © 2020-2023  润新知