目录
1. 概述
2. 疑难点剖析
2.1 SetupRC函数分析
2.2 multibuffer.vs分析
2.3 RenderScene分析
3. 其它
《OpenGL超级宝典(第五版)》如下:
该书第8版的 fbo_drawbuffers工程展示了如下技术点:
说明:请先把OpenGL超级宝典(第五版)第8章中有关上述的知识点,自己研究一遍,扫清基本的概念后再读本博文。
在该函数中有如下代码:
gltMakeTorus(torusBatch, 0.4f, 0.15f, 35, 35);
gltMakeSphere(sphereBatch, 0.1f, 26, 13);
这两句代码,没啥用,可以注释掉,估计编者将前几章例子中的代码直接复制过来改成本例子时,忘记删除这两句了。
该函数通过如下代码加载忍者(受过日本传统打斗和轻功训练的人):
ninja.LoadFromSBM("../../../Src/Models/Ninja/ninja.sbm",
GLT_ATTRIBUTE_VERTEX,
GLT_ATTRIBUTE_NORMAL,
GLT_ATTRIBUTE_TEXTURE0);
转到LoadFromSBM函数:
bool SBObject::LoadFromSBM(const char * filename, int vertexIndex, int normalIndex, int texCoord0Index)
{
FILE * f = NULL;
f = fopen(filename, "rb");
fseek(f, 0, SEEK_END);
size_t filesize = ftell(f);
fseek(f, 0, SEEK_SET);
unsigned char * data = new unsigned char [filesize];
unsigned char * raw_data;
fread(data, filesize, 1, f);
fclose(f);
SBM_HEADER * header = (SBM_HEADER *)data;
raw_data = data + sizeof(SBM_HEADER) + header->num_attribs * sizeof(SBM_ATTRIB_HEADER) + header->num_frames * sizeof(SBM_FRAME_HEADER);
SBM_ATTRIB_HEADER * attrib_header = (SBM_ATTRIB_HEADER *)(data + sizeof(SBM_HEADER));
SBM_FRAME_HEADER * frame_header = (SBM_FRAME_HEADER *)(data + sizeof(SBM_HEADER) + header->num_attribs * sizeof(SBM_ATTRIB_HEADER));
unsigned int total_data_size = 0;
memcpy(&m_header, header, sizeof(SBM_HEADER));
m_attrib = new SBM_ATTRIB_HEADER[header->num_attribs];
memcpy(m_attrib, attrib_header, header->num_attribs * sizeof(SBM_ATTRIB_HEADER));
m_frame = new SBM_FRAME_HEADER[header->num_frames];
memcpy(m_frame, frame_header, header->num_frames * sizeof(SBM_FRAME_HEADER));
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
glGenBuffers(1, &m_attribute_buffer);
glBindBuffer(GL_ARRAY_BUFFER, m_attribute_buffer);
unsigned int i;
for (i = 0; i < header->num_attribs; i++) {
int attribIndex = i;
if(attribIndex == 0)
attribIndex = vertexIndex;
else if(attribIndex == 1)
attribIndex = normalIndex;
else if(attribIndex == 2)
attribIndex = texCoord0Index;
glVertexAttribPointer(attribIndex, m_attrib[i].components, m_attrib[i].type, GL_FALSE, 0, (GLvoid *)total_data_size);
glEnableVertexAttribArray(attribIndex);
total_data_size += m_attrib[i].components * sizeof(GLfloat) * header->num_vertices;
}
glBufferData(GL_ARRAY_BUFFER, total_data_size, raw_data, GL_STATIC_DRAW);
if (header->num_indices) {
glGenBuffers(1, &m_index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer);
unsigned int element_size;
switch (header->index_type) {
case GL_UNSIGNED_SHORT:
element_size = sizeof(GLushort);
break;
default:
element_size = sizeof(GLuint);
break;
}
glBufferData(GL_ELEMENT_ARRAY_BUFFER, header->num_indices * element_size, data + total_data_size, GL_STATIC_DRAW);
}
glBindVertexArray(0);
delete [] data;
return true;
}
该函数前面部分根据ninja.sbm文件的内定格式,解析出相应格式的数据并存放到对应的结构体中用作后续绘图。该函数的后半部是关注的重点,其流程为:
glVertexAttribPointer、glEnableVertexAttribArray、VAO、VBO之间的关系,请参见理解glVertexAttribPointer、glEnableVertexAttribArray、VAO、VBO的关系 博文。
接下来是如下代码:
......................... // 其它代码略
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBufferName);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBufferNames[0]);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, renderBufferNames[1]);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_RENDERBUFFER, renderBufferNames[2]);
// See bind frag location in Chapter 9
processProg = gltLoadShaderPairWithAttributes("multibuffer.vs", "multibuffer_frag_location.fs", 3,
GLT_ATTRIBUTE_VERTEX, "vVertex",
GLT_ATTRIBUTE_NORMAL, "vNormal",
GLT_ATTRIBUTE_TEXTURE0, "texCoord0");
glBindFragDataLocation(processProg, 0, "oStraightColor");
glBindFragDataLocation(processProg, 1, "oGreyscale");
glBindFragDataLocation(processProg, 2, "oLumAdjColor");
......................... // 其它代码略
其中glBindFragDataLocation函数的具体用法参见:glBindFragDataLocation函数说明
该函数定义如下:
void glBindFragDataLocation( GLuint program,
GLuint colorNumber,
const char * name);
该函数功能为:绑定一个用户自定义的varying类型的输出变量到片段着色器的颜色号上。参数 program表示着色器程序的句柄;colorNumber表示绑定到用户自定义的varying类型的输出变量上的颜色号,对于本例来说就是GL_COLOR_ATTACHMENT0、GL_COLOR_ATTACHMENT1、GL_COLOR_ATTACHMENT2这三个枚举值后面的数字0、1、2;name表示片段着色器中用户自定义的varying类型的输出变量的名称,该变量会绑定到GL_COLOR_ATTACHMENTN(N为0到15)。
说白了,上面三句glBindFragDataLocation代码功能就是把片段着色器multibuffer_frag_location.fs中的如下三个输出变量表示的颜色值:
.................. // 其它代码略,具体参见multibuffer_frag_location.fs
out vec4 oStraightColor;
out vec4 oGreyscale;
out vec4 oLumAdjColor;
.................. // 其它代码略,具体参见multibuffer_frag_location.fs
传递给GL_COLOR_ATTACHMENT0、GL_COLOR_ATTACHMENT1、GL_COLOR_ATTACHMENT2连接的渲染缓冲区,而渲染缓冲区是在帧缓冲区中的,帧缓冲区只是一个容器,经过这三句代码后,GL_COLOR_ATTACHMENT0、GL_COLOR_ATTACHMENT1、GL_COLOR_ATTACHMENT2表示的渲染缓冲区也即帧缓冲区的颜色就分别由片段着色器oStraightColor、oGreyscale、oLumAdjColor的值来决定,于是通过改变片段着色器这三个颜色值,就可以相应地改变GL_COLOR_ATTACHMENT0、GL_COLOR_ATTACHMENT1、GL_COLOR_ATTACHMENT2表示的渲染缓冲区也即帧缓冲区颜色。
如下代码:
......................// 其它代码略
// Load the Tan ramp first
glBindBuffer(GL_TEXTURE_BUFFER_ARB, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER_ARB, texBOTexture);
glTexBufferARB(GL_TEXTURE_BUFFER_ARB, GL_R32F, texBO[0]);
glActiveTexture(GL_TEXTURE0);
......................// 其它代码略
这段代码需要注意的是:调用glActiveTexture(GL_TEXTURE1);将纹理单元1 设置为当前活动纹理,接下来设置纹理单元1的纹理数据为texBO[0]缓冲区存放的数据,即LumTan.data文件中的表示的纹理数据。注意,这里用到了多重纹理,GL_TEXTURE0为大理石表示的纹理,而GL_TEXTURE1表示LumTan.data文件中的表示的纹理数据,这样就在大理石纹理上加上了一层类似光强度的纹理,使大理石纹理和光强度纹理糅合在一起,形成视觉上的效果。
multibuffer.vs可以参考本书的第6章的6.14、6.15例子。
该函数有如下代码:
......................... // 其它代码略
if(bUseFBO)
{
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboName);
glDrawBuffers(3, fboBuffs);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Need light position relative to the Camera
M3DVector4f vLightTransformed;
m3dTransformVector4(vLightTransformed, vLightPos, mCamera);
UseProcessProgram(vLightTransformed, vFloorColor, 0);
}
else
{
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDrawBuffers(1, windowBuff);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE,
transformPipeline.GetModelViewProjectionMatrix(), vFloorColor, 0);
}
......................... // 其它代码略
if(bUseFBO)
{
// Direct drawing to the window
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDrawBuffers(1, windowBuff);
glViewport(0, 0, screenWidth, screenHeight);
// Source buffer reads from the framebuffer object
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboName);
// Copy greyscale output to the left half of the screen
glReadBuffer(GL_COLOR_ATTACHMENT1);
glBlitFramebuffer(0, 0, screenWidth/2, screenHeight,
0, 0, screenWidth/2, screenHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST );
// Copy the luminance adjusted color to the right half of the screen
glReadBuffer(GL_COLOR_ATTACHMENT2);
glBlitFramebuffer(screenWidth/2, 0, screenWidth, screenHeight,
screenWidth/2, 0, screenWidth, screenHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST );
// Scale the unaltered image to the upper right of the screen
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBlitFramebuffer(0, 0, screenWidth, screenHeight,
(int)(screenWidth *(0.8)), (int)(screenHeight*(0.8)),
screenWidth, screenHeight,
GL_COLOR_BUFFER_BIT, GL_LINEAR );
glBindTexture(GL_TEXTURE_2D, 0);
}
......................... // 其它代码略
本段代码块说明了如果采用和不采用FBO时,如何绘制,其基本逻辑是:
MoveCamera()句代码没啥用,删除掉,否则链接时会报找不到该函数。估计编者将前几章例子中的代码直接复制过来改成本例子时,忘记删除这两句了。
其它代码很简单,请参见书本中对该例子的分析,不再赘述。