OpenGL开发

来自BeeLab Wiki
跳转至: 导航搜索

实例化OpenGL绘图简述

高效实例化OpenGL开发绘图在科研上的应用还是比较广泛,这部分的开发我们在这里做个简单示例说明,如果您有特殊应用请联系我们做更多应用。

为实现显卡并发高效绘图,例如应用于千万量级原子体系绘图,需要使用glDrawElementsInstanced或glDrawArraysInstanced API,主要涉及GLSL,VBO,VAO等的正确使用。

注意事项:

  • shaders应该创建于特定的program id下
  • VBO,VAO的显存使用和program没有关系,但顶点属性的定义(vertex attribe location)必须和shader中的location一致
  • 可以多个VAO共享一个VBO
  • 要实现更高效绘图,还需要进行culling、遮挡等处理

例子

GLSL语言在不同厂家显卡有些不同,例如在Intel显卡上存在if/else解析不完整,会造成属性丢失,因此尽量少用if/else,在编译shaders代码里可以用glGetAttribLocation,glGetUniformLocation进行验证。

Vertex shader

#version 330 
layout (location = 0) in vec3 vertexpos;
layout (location = 1) in vec3 vertexnor;
layout (location = 2) in vec4 objoffset;
layout (location = 3) in vec3 objcolor;
out vec3 in_col;
uniform mat4 modelview;
uniform mat4 projection;
uniform vec3 light_pos;	
uniform vec3 light_col;
void main() {
  vec3 mnor=normalize(modelview*vec4(vertexnor,0.0)).xyz;	
  vec4 mpos=modelview*vec4(vertexpos.x*objoffset.w+objoffset.x,vertexpos.y*objoffset.w+objoffset.y,vertexpos.z*objoffset.w+objoffset.z, 1.0);	
  gl_Position = projection*mpos;  
  float li=dot(mnor,light_pos);  
  if (li<0.0){
  	//li=0.0;
  	in_col=vec3(0,0,0);
  	return;
  } 
  in_col=objcolor*(light_col*li);
}

fragment shader

#version 330 
out vec4 FragColor;
in vec3 in_col;
 
void main()
{    
    FragColor=vec4(in_col,1.0);    
    //col = vec4(vec_col.x*vec_nor.x,vec_col.y*vec_nor.y,vec_col.z*vec_nor.z,1.0);
}

编译shaders

void MyGL_VAO::CreateShaders(wxString fnvs, wxString fnfs)
{	
	wxString srcVShaderCode;
	{
		wxString sfn=fnvs;//MySysUtil::FindAppFile("mgishader.vs");
		wxArrayString lines;
		bee_gettextfilecontent(&lines,sfn,false,false);
		srcVShaderCode=bee_strings2string(lines,"\n");
	}
	const char *psrcV=srcVShaderCode.c_str().AsChar();
	GLuint vertex = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertex, 1, &psrcV, NULL);
	glCompileShader(vertex);
	GLint success;
	glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
	if (!success) {
		char infoLog[512];
		glGetShaderInfoLog(vertex, 512, NULL, infoLog);
		wxString serr=infoLog;
		beeDebug("** Failed to compile vshader: %ls\n",WSTR2CSTR(serr));
	}
	else{
		beeLog("## vectex shader compiled\n");
	}
 
	//fragment shader
	wxString srcFShaderCode;
	{
		wxString sfn=fnfs;//MySysUtil::FindAppFile("mgishader.fs");
		wxArrayString lines;
		bee_gettextfilecontent(&lines,sfn,false,false);
		srcFShaderCode=bee_strings2string(lines,"\n");
	}
	const char *psrcF=srcFShaderCode.c_str().AsChar();
	GLuint fragment = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragment, 1, &psrcF, NULL);
	glCompileShader(fragment);
	glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
	if (!success) {
		char infoLog[512];
		glGetShaderInfoLog(fragment, 512, NULL, infoLog);
		wxString serr=infoLog;
		beeDebug("** Failed to compile fshader: %ls\n",WSTR2CSTR(serr));
	}
	else{
		beeLog("## fragment shader compiled\n");
	}
 
	//attach shader and link
	glAttachShader(m_id_program, vertex);
	glAttachShader(m_id_program, fragment);
	glLinkProgram(m_id_program);
	glGetProgramiv(m_id_program, GL_LINK_STATUS, &success);
	if (!success) {
		char infoLog[512];
		glGetProgramInfoLog(m_id_program, 512, NULL, infoLog);
		wxString serr=infoLog;
		beeDebug("** failed to attach shader: %ls\n",WSTR2CSTR(serr));
	}
	else{
		beeLog("## shader attached\n");
	}
 
	m_iloc_attr_vertex=glGetAttribLocation(m_id_program, "vertexpos");
	m_iloc_attr_normal=glGetAttribLocation(m_id_program,"vertexnor");
	m_iloc_attr_translation_scale=glGetAttribLocation(m_id_program,"objoffset");
	m_iloc_attr_color=glGetAttribLocation(m_id_program,"objcolor");
 
	beeDebug("# vertex  location: %d\n",m_iloc_attr_vertex);
	beeDebug("# normal  location: %d\n", m_iloc_attr_normal);
	beeDebug("# offset  location: %d\n",m_iloc_attr_translation_scale);
	beeDebug("# color   location: %d\n",m_iloc_attr_color);
 
 
	m_iloc_uniform_projection=glGetUniformLocation(m_id_program,"projection");
	m_iloc_uniform_modelview=glGetUniformLocation(m_id_program,"modelview");
 
	beeDebug("# projection loc: %d\n",m_iloc_uniform_projection);
	beeDebug("# modelview  loc: %d\n",m_iloc_uniform_modelview);
 
	m_iloc_uniform_light_pos=glGetUniformLocation(m_id_program,"light_pos");
	m_iloc_uniform_light_col=glGetUniformLocation(m_id_program,"light_col");
	m_iloc_uniform_light_amb_col=glGetUniformLocation(m_id_program,"light_amb_col");
 
	beeDebug("# light         pos loc: %d\n",m_iloc_uniform_light_pos);
	beeDebug("# light       color loc: %d\n",m_iloc_uniform_light_col);
	beeDebug("# light amber color loc: %d\n",m_iloc_uniform_light_amb_col);
 
	//release 
	glDeleteShader(vertex);
	glDeleteShader(fragment);	
	glReleaseShaderCompiler();
}

VBO

如果使用glDrawElementsInstanced,VBO里就得有一个GL_ELEMENT_ARRAY_BUFFER,也就是EBO,三角形顶点序号序列,对于3D绘图需要在vertex shader里有一个vertex normal属性,用于光照计算。
void MyGL_VBO::Create(vector <GLfloat> &vertices, vector <GLfloat> &normals, vector <GLushort> &indices)
{
	//vertexes create vbo ;
	glGenBuffers(1, &m_ibuffer_vertex);
	glBindBuffer(GL_ARRAY_BUFFER, m_ibuffer_vertex);
	glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(GLfloat), &vertices[0], GL_STATIC_DRAW);
 
	//create ibo, triangles indexes;
	glGenBuffers(1, &m_ibuffer_indeces);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibuffer_indeces);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(GLushort),&indices[0], GL_STATIC_DRAW);
	m_nIndecies=indices.size();
 
	//vertexes normal
	glGenBuffers(1,&m_ibuffer_normal);
	glBindBuffer(GL_ARRAY_BUFFER, m_ibuffer_normal);
	glBufferData(GL_ARRAY_BUFFER, normals.size()*sizeof(GLfloat),&normals[0],GL_STATIC_DRAW);	
 
	glBindBuffer(GL_ARRAY_BUFFER,0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
}

VAO

VAO可以理解为VBO的包装,附加每个实例的属性,例如这里一个球体有N顶点,每个原子表示为一个球,每个原子只有一位置、大小、颜色、纹理,通过glVertexAttribDivisor来指定每个球体的属性位置

配置Instance属性的顺序为:

  1. glBindBuffer 绑定显存地址
  2. glEnableVertexAttribArray 启用某个属性
  3. glVertexAttribPointer 指定属性地址,最后两个参数是内存步长和偏移
  4. glVertexAttribDivisor 拆分
例如一个原子包含位置3个float,大小1个float,(这里代码的位置和大小合并为一个4个float的属性),颜色三个float,所有原子都独立的属性数据,也即每个原子7个float,所有原子的属性是序列排列的,那么这里的glVertexAttribPointer 最后两个参数是7*sizeof(float),位置的偏移量为0,颜色的偏移量为4*sizeof(float),
void MyGL_VAO::Create(MyGL_VBO *pVBO, int ninstance)
{
	m_pVBO=pVBO;
 
	//gen vao 
	glGenVertexArrays(1, &m_id_vao);
	glBindVertexArray(m_id_vao);
 
	//create ibo, triangles indexes;
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pVBO->m_ibuffer_indeces);
 
	glBindBuffer(GL_ARRAY_BUFFER, m_pVBO->m_ibuffer_vertex);
	glEnableVertexAttribArray(m_iloc_attr_vertex);
	glVertexAttribPointer(m_iloc_attr_vertex, 3, GL_FLOAT, GL_FALSE, 0, 0);
 
	//vertexes normal
	//glGenBuffers(1,&m_sphere_nbuffer);
	glBindBuffer(GL_ARRAY_BUFFER, m_pVBO->m_ibuffer_normal);
	glEnableVertexAttribArray(m_iloc_attr_normal);
	glVertexAttribPointer(m_iloc_attr_normal, 3, GL_FLOAT, GL_FALSE, 0, 0);
 
	//instance color, i.e one color for each atom
 
	AllocInstancesMemory(ninstance);
 
	//must unbind vao first
	glBindVertexArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);
	glDisableVertexAttribArray(2);
	glDisableVertexAttribArray(3);
 
	m_nInstances=ninstance;
}
void MyGL_VAO::AllocInstancesMemory(int ninstance)
{
	if(ninstance>m_nInstances_allocated){
		if(m_id_buffer_instance_data!=0){
			//beeDebug("## call delete m_id_buffer_instance_data\n");
			glDeleteBuffers(1,&m_id_buffer_instance_data);
			m_id_buffer_instance_data=0;
		}
 
		glGenBuffers(1, &m_id_buffer_instance_data);
		glBindBuffer(GL_ARRAY_BUFFER, m_id_buffer_instance_data);
		glBufferData(GL_ARRAY_BUFFER,ninstance*(4+3)*sizeof(GLfloat),NULL,GL_STATIC_DRAW);
 
		glEnableVertexAttribArray(m_iloc_attr_translation_scale);
		glVertexAttribPointer(m_iloc_attr_translation_scale, 4, GL_FLOAT, GL_FALSE,7*sizeof(GLfloat), 0);
		glVertexAttribDivisor(m_iloc_attr_translation_scale, 1);
 
		glEnableVertexAttribArray(m_iloc_attr_color);
		glVertexAttribPointer(m_iloc_attr_color, 3, GL_FLOAT, GL_FALSE, 7*sizeof(GLfloat), (void *)(4*sizeof(GLfloat)));
		glVertexAttribDivisor(m_iloc_attr_color, 1);
 
 
		m_nInstances_allocated=ninstance;
	}
	m_nInstances=ninstance;
}

使用VAO

void MyGL_VAO::Draw(float *fLightPosition, float *fLightColor)
{
	if(m_pVBO==NULL || m_id_program==0 || m_nInstances==0 || m_id_vao==0) return;
 
	glUseProgram(m_id_program);
	glBindVertexArray(m_id_vao);
 
	glUniform3fv(m_iloc_uniform_light_pos,1,fLightPosition);
	glUniform3fv(m_iloc_uniform_light_col,1,fLightColor);
	glUniform3fv(m_iloc_uniform_light_amb_col,1,fLightColor);
	GLfloat m[16];
	glGetFloatv(GL_MODELVIEW_MATRIX,m);
	glUniformMatrix4fv(m_iloc_uniform_modelview,1,GL_FALSE,m);
	glGetFloatv(GL_PROJECTION_MATRIX,m);
	glUniformMatrix4fv(m_iloc_uniform_projection,1,GL_FALSE,m);
 
	glDrawElementsInstanced(GL_TRIANGLES,m_pVBO->m_nIndecies,GL_UNSIGNED_SHORT,NULL,m_nInstances);
 
	glBindVertexArray(0);
	glUseProgram(0);
}