1. 程式人生 > >OpenGL程式設計指南8-Transform Feedback例子理解

OpenGL程式設計指南8-Transform Feedback例子理解

transform feedback 是,OpenGL管線中,的,頂點處理階段結束之後,圖元裝配和光柵化之前的一個步驟。transform feedback,可以重新捕獲即將裝配為圖元(點、線段、三角形)的頂點,然後將它們的部分或全部屬性傳遞到快取物件中。

transform feedback主要API

  • void glGenTransformFeedbacks(GLsizei n, GLuint* ids)
  • void glBindTransformFeedback(GLenum target, GLuint id)
  • void glDeleteTransformFeedbacks(GLsizei n, GLuint* ids)
  • GLboolean glIsTransformFeedback(GLenum id)

  • void glBindBufferBase(GLenum target, GLuint index, GLuint buffer)

  • void glBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size)

  • void glTransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar** varyings, GLenum bufferMode)

  • void glBeginTransformFeedback(GLenum primitiveMode)
  • void glEndTransformFeedback()
  • void glPauseTransformFeedback()
  • void glResumeTransformFeedback()

紅寶書(OpenGL程式設計指南8)中,transform feedback示例程式碼中的一些細節理解:

  • 並沒有直接建立feedback物件,而是使用了系統預設的feedback
  • 繪製粒子時,使用兩個VBO,一個用於當前繪製的資料,一個用於feedback,捕獲更新後的三角形頂點和速度資訊,用於下一幀的繪製資料
  • 建立tbo,用於儲存三角形頂點資料,此處的tbo被同時繫結到兩個物件中,1. tbo被繫結到feedback中,用於捕獲模型繪製的三角形資料,2. tbo被繫結到紋理物件,用於例子系統中獲取三角形資料
// ch05-tranformFeedback.cpp

#include <vapp.h>
#include <vutils.h>
#include <vmath.h>
#include "vbm.h"
#include <loadShaders.h>
#include <stdio.h>

using namespace vmath;

const int point_count = 5000;
static unsigned int seed = 0x13371337;

static inline float random_float()
{
	float res;
	unsigned int tmp;

	seed *= 16807;

	tmp = seed ^ (seed >> 4) ^ (seed << 15);

	*((unsigned int *)&res) = (tmp >> 9) | 0x3F800000;

	return (res - 1.0f);
}

static vmath::vec3 random_vector(float minmag = 0.0f, float maxmag = 1.0f)
{
	vmath::vec3 randomvec(random_float() * 2.0f - 1.0f, random_float() * 2.0f - 1.0f, random_float() * 2.0f - 1.0f);
	randomvec = normalize(randomvec);
	randomvec *= (random_float() * (maxmag - minmag) + minmag);

	return randomvec;
}


BEGIN_APP_DECLARATION(TransformFeedback)

// override functions from base class 
virtual void Initialize(const char* title);
virtual void Display(bool auto_redraw);
virtual void Finalize();
virtual void Reshape(int width, int height);

private:

	float  m_aspect;
	GLuint m_baseProg;   // 用於繪製物體
	GLuint m_updateProg; // 用於繪製更新的粒子
	GLuint m_VAO[2];     // 用於關聯粒子系統的VBO
	GLuint m_VBO[2];     // 儲存粒子座標、速度資訊
	//GLuint m_xfb; // transformFeedback 物件
	GLuint m_feedback_VAO;  // 用於關聯 給feedback使用胡tbo
	GLuint m_geometry_tbo;
	GLuint m_geometry_tex;
	
	 
	GLint m_base_model_matrix_loc;
    GLint m_base_projection_matrix_loc;
	
	GLint m_update_model_matrix_loc;
    GLint m_update_projection_matrix_loc;
	
	GLint m_time_step_loc;
	GLint m_triangle_count_loc;
	
	VBObject 	m_object;
	
END_APP_DECLARATION()

DEFINE_APP(TransformFeedback ,"ch05-transformFeedback")

void TransformFeedback::Initialize(const char* title)
{
	glClearColor(1.0f,1.0f,1.0f,1.0f);
	base::Initialize(title);
	 
	 // init shader
	static ShaderInfo base_shader_info[] = 
	{
		{GL_VERTEX_SHADER,"Media/Shaders/5/base.vs.glsl"},
		{GL_FRAGMENT_SHADER,"Media/Shaders/5/base.fs.glsl"},
		{GL_NONE,NULL}
	};
	
	static ShaderInfo update_shader_info[] = 
	{
		{GL_VERTEX_SHADER,"Media/Shaders/5/update.vs.glsl"},
		{GL_FRAGMENT_SHADER,"Media/Shaders/5/update.fs.glsl"},
		{GL_NONE,NULL}
	};
	
	m_updateProg = LoadShaders(update_shader_info);
	glUseProgram(m_updateProg);

 		
	m_update_model_matrix_loc = glGetUniformLocation(m_updateProg,"model_matrix");
	m_update_projection_matrix_loc = glGetUniformLocation(m_updateProg,"projection_matrix");

	m_triangle_count_loc = glGetUniformLocation(m_updateProg,"triangle_count");
	m_time_step_loc = glGetUniformLocation(m_updateProg,"time_step");

	static const char* varyings[] = 
	{
		"position_out", "velocity_out"
	};
	/*glTransformFeedbackVaryings(GLuint program,
									GLsizei count,
									const GLchar *const* varyings,
									GLenum bufferMode)
	*/
	// 使用交錯模式的快取
	glTransformFeedbackVaryings(m_updateProg,2,varyings,GL_INTERLEAVED_ATTRIBS);
	glLinkProgram(m_updateProg); // 重新link著色器
	//glUseProgram(m_updateProg); 
	
	m_baseProg = LoadShaders(base_shader_info);
	glUseProgram(m_baseProg);

	m_base_model_matrix_loc = glGetUniformLocation(m_baseProg,"model_matrix");
	m_base_projection_matrix_loc = glGetUniformLocation(m_baseProg,"projection_matrix");
	
	static const char * varyings2[] =
	{
		"world_space_position"
	};
	
	glTransformFeedbackVaryings(m_baseProg,1,varyings2,GL_INTERLEAVED_ATTRIBS);
	glLinkProgram(m_baseProg);
	//glUseProgram(m_baseProg);
	
	glGenVertexArrays(2,m_VAO);
	glGenBuffers(2,m_VBO);
	
	//此處沒有呼叫glGenTransformFeedbacks和glBindTransformFeedback函式,因為系統存在一個預設的feedback,可以直接使用	
	for(int i = 0 ; i < 2; ++i)
	{
		glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER,m_VBO[i]);
		/*
		glBindBuffer(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
		*/
		glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER,
					point_count * (sizeof(vec4) + sizeof(vec3)),
					NULL,
					GL_DYNAMIC_COPY);
		
		// 初始化第一個buffer
		if(i == 0)
		{
			struct buffer_t
			{
				vec4 position;
				vec3 velocity;
			} *buffer = (buffer_t*)glMapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER,GL_WRITE_ONLY);
			
			for(int j = 0; j < point_count; ++j)
			{
				buffer[j].velocity = random_vector();
				buffer[j].position = vec4(buffer[j].velocity + vec3(-0.5f, 40.0f, 0.0f), 1.0f);
				buffer[j].velocity = vec3(buffer[j].velocity[0], buffer[j].velocity[1] * 0.3f, buffer[j].velocity[2] * 0.3f);
			}
			
			glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
		}
		
		glBindVertexArray(m_VAO[i]);
		glBindBuffer(GL_ARRAY_BUFFER,m_VBO[i]);
		/*
		glVertexAttribPointer(GLuint index, 
		GLint size, 
		GLenum type,
		GLboolean normalized,
		GLsizei stride,
		const void* pointer);
		*/
		glVertexAttribPointer(0,4,GL_FLOAT,GL_FALSE,
		sizeof(vec4) + sizeof(vec3),0);
		glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,
		sizeof(vec4) + sizeof(vec3),(GLvoid*)sizeof(vec4));
		glEnableVertexAttribArray(0);
		glEnableVertexAttribArray(1);
		
		glBindVertexArray(0);
		glDisableVertexAttribArray(0);
		glDisableVertexAttribArray(1);
	}
	
	// 建立tbo,用於儲存三角形頂點資料,此處的tbo被同時繫結到兩個物件中,
	// 1. tbo被繫結到feedback中,用於捕獲模型繪製的三角形資料
	// 2. tbo被繫結到紋理物件,用於例子系統中獲取三角形資料
	glGenBuffers(1,&m_geometry_tbo);
	glGenTextures(1,&m_geometry_tex);
	glBindBuffer(GL_TEXTURE_BUFFER,m_geometry_tbo);
	glBufferData(GL_TEXTURE_BUFFER,1024 * 1024 * sizeof(vec4),NULL,GL_DYNAMIC_COPY);
	glBindTexture(GL_TEXTURE_BUFFER,m_geometry_tex);
	/*
	glTexBuffer(GLenum target, GLenum internalFormat, GLuint buffer);
	*/
	// 將快取區關聯到紋理物件上
	glTexBuffer(GL_TEXTURE_BUFFER,GL_RGBA32F,m_geometry_tbo);
	
	//  
	glGenVertexArrays(1, &m_feedback_VAO);
	glBindVertexArray(m_feedback_VAO);
	glBindBuffer(GL_ARRAY_BUFFER,m_geometry_tbo);
	glVertexAttribPointer(0,4,GL_FLOAT,GL_FALSE,0,NULL);
	glEnableVertexAttribArray(0);
	glBindVertexArray(0);
	glDisableVertexAttribArray(0);
	
	// init object
	std::string path = Utils::instance()->getMediaPath() + "Media\\armadillo_low.vbm";
    m_object.LoadFromVBM(path.c_str(), 0, 1, 2);
	//m_object.BindVertexArray();
	
   // glBindVertexArray(0);
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}

void TransformFeedback::Reshape(int width, int height)
{
	glViewport(0,0,width,height);
	m_aspect = float(height)/float(width);
}

void TransformFeedback::Display(bool auto_redraw)
{
	static int frame_count = 0;
	float t = float(GetTickCount() & 0x3FFFF) / float(0x3FFFF);
	static float q = 0.0f;
	static const vmath::vec3 X(1.0f, 0.0f, 0.0f);
	static const vmath::vec3 Y(0.0f, 1.0f, 0.0f);
	static const vmath::vec3 Z(0.0f, 0.0f, 1.0f);
	
	vmath::mat4 projection_matrix(vmath::frustum(-1.0f, 1.0f, -m_aspect, m_aspect, 1.0f, 5000.0f) * vmath::translate(0.0f, 0.0f, -100.0f)); // project and view matrix 
	vmath::mat4 model_matrix(vmath::scale(0.3f) *
		vmath::rotate(t * 360.0f, 0.0f, 1.0f, 0.0f) *
		vmath::rotate(t * 360.0f * 3.0f, 0.0f, 0.0f, 1.0f));
		
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	
	// Setup
    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
	
	
	// Active instancing program
	glUseProgram(m_baseProg);
	
	glUniformMatrix4fv(m_base_model_matrix_loc, 1, GL_FALSE, model_matrix);
	glUniformMatrix4fv(m_base_projection_matrix_loc,1,GL_FALSE, projection_matrix);
	
	glBindVertexArray(m_feedback_VAO);
	glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER,0,m_geometry_tbo); 
	
	// 捕獲三角形頂點資料
	glBeginTransformFeedback(GL_TRIANGLES);
	m_object.Render();
	glEndTransformFeedback();
	
	glUseProgram(m_updateProg);
	model_matrix = vmath::mat4::identity();
	glUniformMatrix4fv(m_update_model_matrix_loc,1,GL_FALSE,model_matrix);
	glUniformMatrix4fv(m_update_projection_matrix_loc,1,GL_FALSE,projection_matrix);
	glUniform1i(m_triangle_count_loc,m_object.GetVertexCount()/3);
	
	// 時間控制
	if( t > q)
	{
		glUniform1f(m_time_step_loc,(t-q)*2000.0f);
		// printf("time:%f\n",(t-q)*2000.0f);
	}
	
	q = t;
	
	// 每隔一幀,交換VBO,使用兩個VBO,一個用於當前繪製的資料,一個用於feedback,捕獲更新後的三角形頂點和速度資訊
	if((frame_count & 1) != 0)
	{
		glBindVertexArray(m_VAO[1]);  // 啟用用於繪製的VBO,即m_VBO[1];
		glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER,0,m_VBO[0]); // 使用m_VBO[0]捕獲粒子系統的當前位置和速度
		// printf("frame:%d\n",frame_count);
	}
	else
	{
		glBindVertexArray(m_VAO[0]);// 啟用用於繪製的VBO,即m_VBO[0];
		glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER,0,m_VBO[1]);
	}
	
	glBeginTransformFeedback(GL_POINTS);
	glDrawArrays(GL_POINTS,0,min(point_count,(frame_count >> 3)));
	glEndTransformFeedback();
	
	glBindVertexArray(0);
	glUseProgram(0);
	
	frame_count++;
	
	base::Display();
}
void TransformFeedback::Finalize()
{
	glUseProgram(0);
	glDeleteBuffers(2, m_VBO);
	glDeleteBuffers(1,&m_geometry_tbo);
    glDeleteVertexArrays(2, m_VAO);
	glDeleteVertexArrays(1,&m_feedback_VAO);
}