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\n"
		"\n"
		"// 'position' and 'normal' are regular vertex attributes\n"
		"layout (location = 0) in vec4 position;\n"
		"layout (location = 1) in vec3 normal;\n"
		"\n"
		"// Color is a per-instance attribute\n"
		"layout (location = 2) in vec4 color;\n"
		"\n"
		"// model_matrix will be used as a per-instance transformation\n"
		"// matrix. Note that a mat4 consumes 4 consecutive locations, so\n"
		"// this will actually sit in locations, 3, 4, 5, and 6.\n"
		"layout (location = 3) in mat4 model_matrix;\n"
		"\n"
		"// The view matrix and the projection matrix are constant across a draw\n"
		"uniform mat4 view_matrix;\n"
		"uniform mat4 projection_matrix;\n"
		"\n"
		"// The output of the vertex shader (matched to the fragment shader)\n"
		"out VERTEX\n"
		"{\n"
		"    vec3    normal;\n"
		"    vec4    color;\n"
		"} vertex;\n"
		"\n"
		"// Ok, go!\n"
		"void main(void)\n"
		"{\n"
		"    // Construct a model-view matrix from the uniform view matrix\n"
		"    // and the per-instance model matrix.\n"
		"    mat4 model_view_matrix = view_matrix * model_matrix;\n"
		"\n"
		"    // Transform position by the model-view matrix, then by the\n"
		"    // projection matrix.\n"
		"    gl_Position = projection_matrix * (model_view_matrix * position);\n"
		"    // Transform the normal by the upper-left-3x3-submatrix of the\n"
		"    // model-view matrix\n"
		"    vertex.normal = mat3(model_view_matrix) * normal;\n"
		"    // Pass the per-instance color through to the fragment shader.\n"
		"    vertex.color = color;\n"
		"}\n";

	static const char render_fs[] =
		"#version 410\n"
		"\n"
		"layout (location = 0) out vec4 color;\n"
		"\n"
		"in VERTEX\n"
		"{\n"
		"    vec3    normal;\n"
		"    vec4    color;\n"
		"} vertex;\n"
		"\n"
		"void main(void)\n"
		"{\n"
		"    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"
		"}\n";

	//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~~~~~

时间: 2024-10-27 02:37:32

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

OpenGL学习日记-2015.3.5——Hello glsl(着色器)

过年前忍不住买了本新版的OpenGL编程指南,主要的目的还是为了系统的学习着色器编程,另外就是接触新版的OpenGL技术和思想.看了几页,就过年了QAQ.回来后也是各种不在状态,不想上班,不想工作,不想写代码...昨天终于强迫自己继续看书,也找回了些状态. 书本基础知识的全面性和权威性就不用说了,不过这个源代码就....这第一个例子照着代码来抄结果...我想应该是原来的代码一个参数错了,折腾了半天,代码分析是详说.主要是分析代码,有什么说什么,并没有全面的说明着色器的基本内容,想着在着色器的基础

OpenGL学习日记-2014.12.21--光照

o(╯□╰)o深患中度拖延症,也是从开始写这篇笔记到结束居然用了一个月...虽然中间是发生了不少事,不过明明就有无数机会可以完成,就是拖着没写代码,各种借口...面对如此拖延症该如何是好QAQ 正文: 突然觉得这些日记写着写着就没什么意思...只是简单梳理一下书中的内容,没经过很多的思考,可不写心里更虚,怕自己几天就把看的书忘了.对于很多概念,都由于没有好好去写代码验证,而理解流于表面.对于光照这章也是下决心细细琢磨一番(现在才下的决心o(╯□╰)o),毕竟这很重要. 一.光照和颜色密切相关,光

OpenGL学习笔记-2015.3.24——transform feedback缓存&amp;粒子系统示例分析

transform feedback是OpenGL中比较酷炫的特性之一,他让客户端应用程序可以获取到渲染流水线上的顶点数据.基于这一特性实现了基于z-pass场景决策渲染技术,当然在此并没有去了解何为z-pass场景决策渲染技术,总之是一个可以有效减少渲染数据的输送.这里只是通过一个简单的例子系统,去了解transform feedback对象的使用方法. 正文: 1.transform feedback: transform feedback是OpenGL渲染管线中,顶点处理阶段结束之后,图元

OpenGL 学习笔记-2015.4.18——立方体纹理映射-天空盒子-环境映射

立方体映射(cube-map)纹理是一种特殊类型的纹理,用于环境映射,使用一组图像并把他们作为立方体的面.立方体映射的6个面用正方形并且大小相同的6个子纹理表示.要从立方体纹理中采样的时候,使用的纹理坐标是3维,并且被看做来自原点的方向!方向指向用来读取纹理的立方体映射表面的位置.在这里例子中,有关于环境映射的代码,主要思想是通过观察向量和表面的法向量反射来确定采样的纹理坐标. 通过将一个新的纹理名绑定到GL_TEXTURE_CUBE_MAP纹理目标,然后调用glTexStorage2D()以G

OpenGL学习日记-2014.1.21--混合

混合可以实现很多效果,笔记单纯的记录混合的原理,和混合公式,混合函数的使用.最后分析书中的两个混合代码例子,两个例子结合起来说明了绘图顺序对混合最终效果的影响,及如何去规避这样的问题.在颜色中之前一直忽略的第四个分量alpha终于派上用场.在启用混合情况下,alpha常常用于把被处理片断的颜色值与已经存在帧缓冲区的像素颜色值进行组合.混合操作是在场景进行了光栅化并转换为像素之后,但是在最终的像素绘制到帧缓冲区之前进行. 一.源(新片断)因子和目标(旧片断)因子 在混合过程中,新旧片断的组合分两步

Java 学习笔记(2015.7.13~17)

Java 学习笔记(2015.7.13~17) Java this关键字 表示本类中的属性,调用本类中的方法 class Person {        private String name;         private int age;         public Person(String name, int age) {         this.name = name;//调用本类中的属性         this.age = age;//同上} //get&set方法:    

学习日记-----各种问题

用.net做B/S结构的系统,您是用几层结构来开发,每一层之间的关系以及为什么要这样分层? 答: 从下至上分别为:数据访问层.业务逻辑层(又或成为领域层).表示层 数据访问层:有时候也称为是持久层,其功能主要是负责数据库的访问 业务逻辑层:是整个系统的核心,它与这个系统的业务(领域)有关 表示层:是系统的UI部分,负责使用者与整个系统的交互.  优点:  分工明确,条理清晰,易于调试,而且具有可扩展性. 缺点:  增加成本. 分层式结构究竟其优势何在? 1.开发人员可以只关注整个结构中的其中某一

【转】android学习日记01--综述

转自:http://www.cnblogs.com/aiguozhe/p/3541941.html 一.总体框架 先上一张google提供官方的Android框架图: Android系统架构由5部分组成,分别是:Linux Kernel.Android Runtime.Libraries.Application Framework.Applications(E文不好就不翻译了,其实这也是简单的计算机E文啦)下面分别讲述每部分的主要作用: 1.Linux Kernel Android基于Linux

OpenGl学习进程(7)第五课:点、边和图形(二)边

本节是OpenGL学习的第五个课时,下面介绍OpenGL边的相关知识: (1)边的概念: 数学上的直线没有宽度,但OpenGL的直线则是有宽度的.同时,OpenGL的直线必须是有限长度,而不是像数学概念那样是无限的.可以认为,OpenGL的“直线”概念与数学上的“线段”接近,它可以由两个端点来确定.     (2)如何绘制边: 1)OpenGL支持绘制三种类型的边: GL_LINES :指定两个顶点,在它们之间绘制一条直线.如果为GL_LINES指定了奇数个顶点,那么最后一个顶点会被忽略. GL