《OpenGL ES 2.0 Programming Guide》第12章 “最简单的Multi-Pass+VBO”示例代码【C语言版】

由于《OpenGL ES 2.0 Programming Guide》原书第12章并没有提供相关的示例,为了加深理解,遂自己实现了一份C语言版本作为练习,希望能够帮助到同样喜欢OpenGL ES 2.0的同学。


在实现的时候遇到的问题——FBO的 Ping Pong技术

文章提到有三种可能的方法

1. Use one FBO with one attachment per texture that is rendered to, and bind a different FBO in each rendering pass using glBindFramebufferEXT().

2. Use one FBO, reattach the render target texture in each pass using glFramebufferTexture2DEXT().

3. Use one FBO and multiple attachment points, switch using glDrawBuffer().


由于ES 2.0的 Frame Buffer 只支持一个Color Attachement,所以不能使用OpenGL 的 Multi-Attachment的方式(速度最快的方法3),然后尝试方法2,通过实时切换FBO 绑定的Color Attachement,但是没有成功(如果有人成功的话,希望指点一下),所以只好用了“最慢”的多FBO的方式(方法1)。


废话不多说,直接上代码(本例使用了2个Pass):


#include <stdlib.h>
#include <stdio.h>

#include "esUtil.h"
#include "utils.h"
#include "userData.h"

#define SIZE 512

GLint InitFbo(ESContext *esContext, GLint width, GLint height,
			  GLuint *pFrameBuffer, GLuint *pTextureId, GLuint *pDepthRenderBuffer)
{
	GLenum status;
	GLint maxRenderbufferSize;

	UserData *userData = (UserData *)esContext->userData;

	glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderbufferSize);

	// check if GL_MAX_RENDERBUFFER_SIZE is >= texWidth and texHeight
	if ((maxRenderbufferSize <= width) || (maxRenderbufferSize <= height))
	{
		// cannot use framebuffer objects as we need to create
		// a depth buffer as a renderbuffer object
		printf("Cannot use framebuffer objects!\n");
		exit(EXIT_FAILURE);
		return FALSE;
	}
	// generate the framebuffer, renderbuffer names
	glGenFramebuffers(1, pFrameBuffer);
	glGenRenderbuffers(1, pDepthRenderBuffer);

	// bind renderbuffer and create a 16-bit depth buffer
	// width and height of renderbuffer = width and height of
	// the texture
	glBindRenderbuffer(GL_RENDERBUFFER, *pDepthRenderBuffer);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);

	// Dst Texture
	glGenTextures(1, pTextureId);
	glBindTexture(GL_TEXTURE_2D, *pTextureId);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
		GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

	// bind the framebuffer
	glBindFramebuffer(GL_FRAMEBUFFER, *pFrameBuffer);
	// ☆ specify texture as color attachment ☆
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *pTextureId, 0);

	// specify depth_renderbufer as depth attachment
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *pDepthRenderBuffer);

	// check for framebuffer complete
	status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	if (status != GL_FRAMEBUFFER_COMPLETE)
	{
		printf("Framebuffer object is not complete!\n");
		exit(EXIT_FAILURE);
		return FALSE;
	}

	return TRUE;
}

GLint InitFboShader(ESContext *esContext, 
					const char* vFboShader, const char* fFboShader, 
					GLuint* pProgramObj, GLint* pPositionLoc, 
					GLint* pTexcoordLoc, GLint* pSamplerLoc)
{
	UserData *userData = (UserData *)esContext->userData;

	const char *vShaderStr = NULL, *fShaderStr = NULL;
	vShaderStr = (const char *)ReadShader(vFboShader);
	fShaderStr = (const char *)ReadShader(fFboShader);

	*pProgramObj = esLoadProgram(vShaderStr, fShaderStr);

	*pPositionLoc = glGetAttribLocation (*pProgramObj, "a_position" );
	*pTexcoordLoc = glGetAttribLocation(*pProgramObj, "a_texCoord");
	*pSamplerLoc = glGetUniformLocation(*pProgramObj, "s_texture");

	return TRUE;
}

GLint InitFboVbo(ESContext *esContext, GLuint* pVboFboIds)
{
	UserData *userData = (UserData *)esContext->userData;

	// VBO Id
	pVboFboIds[0] = 0;
	pVboFboIds[1] = 0;
        // Quad
	const GLfloat vVertices[] =
	{
		-1.f,  1.f, 0.0f,	// Position 0
		0.0f,  0.0f,        // TexCoord 0
		-1.f, -1.f, 0.0f,	// Position 1
		0.0f,  1.0f,        // TexCoord 1
		1.f, -1.f, 0.0f,	// Position 2
		1.0f,  1.0f,        // TexCoord 2
		1.f,  1.f, 0.0f,	// Position 3
		1.0f,  0.0f         // TexCoord 3
	};
	const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };

	const int numIndices = 6;
	const int numVertices = 4;

	// Setup VBO
	glGenBuffers ( 2, pVboFboIds );

	glBindBuffer ( GL_ARRAY_BUFFER, pVboFboIds[0] );
	glBufferData ( GL_ARRAY_BUFFER, (3 + 2) * sizeof(GLfloat) * numVertices,
		vVertices, GL_STATIC_DRAW );

	glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, pVboFboIds[1] );
	glBufferData ( GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(GLushort),
		indices, GL_STATIC_DRAW );

	return TRUE;
}

GLint InitShader(ESContext *esContext)
{
	UserData *userData = (UserData *)esContext->userData;

	const char *vShaderStr = NULL, *fShaderStr = NULL;
	vShaderStr = (const char *)ReadShader("shaders/vertex_shader.glsl");
	fShaderStr = (const char *)ReadShader("shaders/fragment_shader.glsl");

	// Load the shaders and get a linked program object
	userData->programObject = esLoadProgram ( vShaderStr, fShaderStr );

	// Get the attribute locations
	userData->positionLoc = glGetAttribLocation ( userData->programObject, "a_position" );

	// Get the uniform locations
	userData->mvpLoc = glGetUniformLocation( userData->programObject, "u_mvpMatrix" );

	// Get the texture  attribute locations
	userData->texcoordLoc = glGetAttribLocation(userData->programObject, "a_texCoord");
	userData->samplerLoc = glGetUniformLocation(userData->programObject, "s_texture");

	return TRUE;
}

GLint InitVbo(ESContext *esContext)
{
	UserData *userData = (UserData *)esContext->userData;

	// VBO Id
	userData->vboIds[0] = 0;
	userData->vboIds[1] = 0;
	userData->vboIds[2] = 0;

	const int numVertices = 24;
	// Setup VBO
	glGenBuffers ( 3, userData->vboIds );

	glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] );
	glBufferData ( GL_ARRAY_BUFFER, 3 * sizeof(GLfloat) * numVertices,
		userData->vertices, GL_STATIC_DRAW );

	glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[1] );
	glBufferData ( GL_ARRAY_BUFFER, 2 * sizeof(GLfloat) * numVertices,
		userData->texcoords, GL_STATIC_DRAW );

	glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[2] );
	glBufferData ( GL_ELEMENT_ARRAY_BUFFER, userData->numIndices * sizeof(GLuint),
		userData->indices, GL_STATIC_DRAW );

	return TRUE;
}

GLint Init(ESContext *esContext)
{
	UserData *userData = (UserData *)esContext->userData;

	// Load the texture ☆
	userData->textureFBOId = LoadTexture("../beard.tga");

	// Pass 0
	if (!InitFboShader(esContext, 
		"shaders/vertex_fbo_shader.glsl", "shaders/fragment_fbo_shader.glsl",
		&userData->programFBOObject, &userData->positionFBOLoc, 
		&userData->texcoordFBOLoc, &userData->samplerFBOLoc))
	{
		printf("InitFboShader exception ! \n");
		return FALSE;
	}
	// Pass 1
	if (!InitFboShader(esContext, 
		"shaders/vertex_fbo_shader_.glsl", "shaders/fragment_fbo_shader_.glsl",
		&userData->programFBOObject_, &userData->positionFBOLoc_, 
		&userData->texcoordFBOLoc_, &userData->samplerFBOLoc_))
	{
		printf("InitFboShader exception ! \n");
		return FALSE;
	}
	// --------
	if (!InitShader(esContext))
	{
		printf("InitShader exception ! \n");
		return FALSE;
	}
	// --------
	// Pass 0
	InitFbo(esContext, SIZE, SIZE, 
		&userData->frameBuffer, 
		&userData->textureFBOId_,	// dst texture 0
		&userData->depthRenderBuffer);
	InitFboVbo(esContext, userData->vboFBOIds);
	// Pass 1
	InitFbo(esContext, SIZE, SIZE, 
		&userData->frameBuffer_, 
		&userData->textureId,		// dst texture 1
		&userData->depthRenderBuffer_);
	InitFboVbo(esContext, userData->vboFBOIds_);

	// Generate the vertex data
	userData->numIndices = esGenCube( 1.0, &userData->vertices,
		NULL, &userData->texcoords, &userData->indices );
	InitVbo(esContext);

	// Starting rotation angle for the cube
	userData->angle = 45.0f;

	glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f );
	glClearDepthf( 1.0f );
	glEnable(GL_DEPTH_TEST);

	glEnable(GL_CULL_FACE);

	//glEnable(GL_BLEND);
	//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	return TRUE;
}

// Update the mvp matrix
void Update(ESContext *esContext, float deltaTime)
{
	UserData *userData = (UserData *) esContext->userData;
	ESMatrix perspective;
	ESMatrix modelview;
	float    aspect;

	// Compute a rotation angle based on time to rotate the cube
	userData->angle += ( deltaTime * 40.0f );
	if( userData->angle >= 360.0f )
		userData->angle -= 360.0f;

	// Compute the window aspect ratio
	aspect = (GLfloat) esContext->width / (GLfloat) esContext->height;

	// Generate a perspective matrix with a 60 degree FOV
	esMatrixLoadIdentity( &perspective );
	esPerspective( &perspective, 60.0f, aspect, 1.0f, 20.0f );

	// Generate a model view matrix to rotate/translate the cube
	esMatrixLoadIdentity( &modelview );

	// Translate away from the viewer
	esTranslate( &modelview, 0.0, 0.0, -2.0 );

	// Rotate the cube
	esRotate( &modelview, userData->angle, 1.0, 0.0, 1.0 );

	// Compute the final MVP by multiplying the
	// modevleiw and perspective matrices together
	esMatrixMultiply( &userData->mvpMatrix, &modelview, &perspective );

}

void DrawToFbo(ESContext *esContext, 
			   GLuint frameBuffer, GLuint programObj, GLuint texId, GLuint* pVboIds,
			   GLint positionLoc, GLint texcoordLoc, GLint samplerLoc)
{
	UserData *userData = (UserData *)esContext->userData;

	const int numIndices = 6;

	glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);

	// ----------------------------------------------------------
	PrepareForDraw(esContext, programObj);
	// -----------------------------------------------------------
	// Load the vertex position
	glBindBuffer ( GL_ARRAY_BUFFER, pVboIds[0] );
	glVertexAttribPointer ( positionLoc, 3, GL_FLOAT,
		GL_FALSE, (3 + 2) * sizeof(GLfloat), 0 );
	glEnableVertexAttribArray ( positionLoc );
	// -----------------------------------------------------------
	// Load the texture coordinate
	glBindBuffer ( GL_ARRAY_BUFFER, pVboIds[0] );
	glVertexAttribPointer ( texcoordLoc, 2, GL_FLOAT,
		GL_FALSE, (3 + 2) * sizeof(GLfloat), (void *)(3 * sizeof(GLfloat)));
	glEnableVertexAttribArray(texcoordLoc);
	// -----------------------------------------------------------
	// Bind the FBO texture
	// Set the sampler texture unit to 0
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, texId);
	glUniform1i(samplerLoc, 0);

	// -----------------------------------------------------------
	// Draw the quad
	glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, pVboIds[1] );
	glDrawElements ( GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0 );
	// -----------------------------------------------------------
	// Clean
	glBindTexture(GL_TEXTURE_2D, 0);
	glBindBuffer ( GL_ARRAY_BUFFER, 0 );
	glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, 0 );
	glDisableVertexAttribArray ( positionLoc );
	glDisableVertexAttribArray ( texcoordLoc );
}

void Draw(ESContext *esContext)
{
	UserData *userData = (UserData *)esContext->userData;
	// ----------------------------------------------------------
	// Pass 0
	DrawToFbo(esContext, 
		userData->frameBuffer, userData->programFBOObject, userData->textureFBOId, userData->vboFBOIds,
		userData->positionFBOLoc, userData->texcoordFBOLoc, userData->samplerFBOLoc);
	// Pass 1
	DrawToFbo(esContext, 
		userData->frameBuffer_, userData->programFBOObject_, userData->textureFBOId_, userData->vboFBOIds_,
		userData->positionFBOLoc_, userData->texcoordFBOLoc_, userData->samplerFBOLoc_);
	// ----------------------------------------------------------

	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	// ----------------------------------------------------------
	PrepareForDraw(esContext, userData->programObject);
	// ----------------------------------------------------------
	// Load the vertex position
	glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[0] );
	glVertexAttribPointer ( userData->positionLoc, 3, GL_FLOAT,
		GL_FALSE, 3 * sizeof(GLfloat), 0 );
	glEnableVertexAttribArray ( userData->positionLoc );
	// -----------------------------------------------------------
	// Load the texture coordinate
	glBindBuffer ( GL_ARRAY_BUFFER, userData->vboIds[1] );
	glVertexAttribPointer(userData->texcoordLoc, 2, GL_FLOAT,
		GL_FALSE, 2 * sizeof(GLfloat), 0);
	glEnableVertexAttribArray(userData->texcoordLoc);
	// -----------------------------------------------------------
	// Load the MVP matrix
	glUniformMatrix4fv( userData->mvpLoc, 1, GL_FALSE,
		(GLfloat *) &userData->mvpMatrix.m[0][0] );
	// -----------------------------------------------------------
	// Bind the new texture
	// Set the sampler texture unit to 0
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, userData->textureId);

	glUniform1i(userData->samplerLoc, 0);
	// -----------------------------------------------------------
	// Draw the cube
	glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, userData->vboIds[2] );
	glDrawElements ( GL_TRIANGLES, userData->numIndices, GL_UNSIGNED_INT, 0 );
	eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface );
	// -----------------------------------------------------------
	// Clean
	glBindTexture(GL_TEXTURE_2D, 0);
	glBindBuffer ( GL_ARRAY_BUFFER, 0 );
	glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, 0 );
	glDisableVertexAttribArray ( userData->positionLoc );
	glDisableVertexAttribArray ( userData->texcoordLoc );
}

void ShutDown(ESContext *esContext)
{
	UserData *userData = (UserData *)esContext->userData;

	//glDisable(GL_BLEND);

	if ( userData->vertices != NULL )
	{
		free ( userData->vertices );
	}

	if ( userData->texcoords != NULL )
	{
		free ( userData->texcoords );
	}

	if ( userData->indices != NULL )
	{
		free ( userData->indices );
	}

	// Delete program object
	glDeleteProgram (userData->programObject);
	glDeleteProgram(userData->programFBOObject);

	// Delete texture
	glDeleteTextures(1, &userData->textureId);
	glDeleteTextures(1, &userData->textureFBOId);
	glDeleteRenderbuffers(1, &userData->depthRenderBuffer);
	glDeleteFramebuffers(1, &userData->frameBuffer);

	// Delete VBO
	glDeleteBuffers ( 2, userData->vboFBOIds );
	glDeleteBuffers ( 3, userData->vboIds );
}

int main ( int argc, char *argv[] )
{
	ESContext esContext;
	UserData  userData;

	esInitContext ( &esContext );
	esContext.userData = &userData;

	esCreateWindow ( &esContext, "Simple Multi Pass", SIZE, SIZE, ES_WINDOW_RGB | ES_WINDOW_MULTISAMPLE );

	if ( !Init ( &esContext ) )
		return 0;

	esRegisterDrawFunc ( &esContext, Draw );
	esRegisterUpdateFunc ( &esContext, Update );

	esMainLoop ( &esContext );

	ShutDown ( &esContext );
}



效果图:


OpenGL® ES™ is the industry’s leading software interface and graphics library for rendering sophisticated 3D graphics on handheld and embedded devices. The newest version, OpenGL ES 3.0, makes it possible to create stunning visuals for new games and apps, without compromising device performance or battery life. In the OpenGL ES 3.0 Programming Guide, 2nd Edition, the authors cover the entire API and Shading Language. They carefully introduce OpenGL ES 3.0 features such as shadow mapping, instancing, multiple render targets, uniform buffer objects, texture compression, program binaries, and transform feedback. Through detailed, downloadable C-based code examples, you’ll learn how to set up and program every aspect of the graphics pipeline. Step by step, you’ll move from introductory techniques all the way to advanced per-pixel lighting and particle systems. Throughout, you’ll find cutting-edge tips for optimizing performance, maximizing efficiency with both the API and hardware, and fully leveraging OpenGL ES 3.0 in a wide spectrum of applications. All code has been built and tested on iOS 7, Android 4.3, Windows (OpenGL ES 3.0 Emulation), and Ubuntu Linux, and the authors demonstrate how to build OpenGL ES code for each platform. Coverage includes EGL API: communicating with the native windowing system, choosing configurations, and creating rendering contexts and surfaces Shaders: creating and attaching shader objects; compiling shaders; checking for compile errors; creating, linking, and querying program objects; and using source shaders and program binaries OpenGL ES Shading Language: variables, types, constructors, structures, arrays, attributes, uniform blocks, I/O variables, precision qualifiers, and invariance Geometry, vertices, and primitives: inputting geometry into the pipeline, and assembling it into primitives 2D/3D, Cubemap, Array texturing: creation, loading, and rendering; texture wrap modes, filtering, and formats; compressed textures, sampler objects, immutable textures, pixel unpack buffer objects, and mipmapping Fragment shaders: multitexturing, fog, alpha test, and user clip planes Fragment operations: scissor, stencil, and depth tests; multisampling, blending, and dithering Framebuffer objects: rendering to offscreen surfaces for advanced effects Advanced rendering: per-pixel lighting, environment mapping, particle systems, image post-processing, procedural textures, shadow mapping, terrain, and projective texturing Sync objects and fences: synchronizing within host application and GPU execution This edition of the book includes a color insert of the OpenGL ES 3.0 API and OpenGL ES Shading Language 3.0 Reference Cards created by Khronos. The reference cards contain a complete list of all of the functions in OpenGL ES 3.0 along with all of the types, operators, qualifiers, built-ins, and functions in the OpenGL ES Shading Language. Table of Contents Chapter 1. Introduction to OpenGL ES 3.0 Chapter 2. Hello Triangle: An OpenGL ES 3.0 Example Chapter 3. An Introduction to EGL Chapter 4. Shaders and Programs Chapter 5. OpenGL ES Shading Language Chapter 6. Vertex Attributes, Vertex Arrays, and Buffer Objects Chapter 7. Primitive Assembly and Rasterization Chapter 8. Vertex Shaders Chapter 9. Texturing Chapter 10. Fragment Shaders Chapter 11. Fragment Operations Chapter 12. Framebuffer Objects Chapter 13. Sync Objects and Fences Chapter 14. Advanced Programming with OpenGL ES 3.0 Chapter 15. State Queries Chapter 16. OpenGL ES Platforms Appendix A. GL_HALF_FLOAT Appendix B. Built-In Functions Appendix C. ES Framework API Book Details Title: OpenGL ES 3.0 Programming Guide, 2nd Edition Author: Aaftab Munshi, Budirijanto Purnomo, Dan Ginsburg, Dave Shreiner Length: 560 pages Edition: 2 Language: English Publisher: Addison-Wesley Professional Publication Date: 2014-03-10 ISBN-10: 0321933885 ISBN-13: 9780321933881
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShaderJoy

您的打赏是我继续写博客的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值