帧缓冲区是图形绘制流水线的末端,图形流水线绘制出来的画面被保存在默认的帧缓冲区对象中供显示终端使用。我们也可以创建自己的缓冲区对象,将图形流水线的处理结果保存下来做进一步的使用,比如可以使用帧缓冲区对象进行三维图形的交互编辑操作。
三维图形交互编辑的基本思路是使用同样的数据绘制两次,第一次绘制的时候绑定自己创建的缓冲区对象,并在帧缓冲区中保存最终屏幕中各个像素对应的实际坐标和图元编号,然后通过void glReadPixels(GLint x,GLint y, GLsizei width,GLsizeiheight,GLenum format,GLenum type,GLvoid * data)读取这些信息。第一次渲染获得的信息可以判断当前鼠标点击的是哪一个图元,以及鼠标位置的实际坐标,第二次渲染绑定到默认的缓冲区对象,并利用这些信息对选中的图元进行高亮显示。
假设现在有两个三角形,我们需要将选中的三角形高亮显示,那么我们可以做如下操作:
1. 创建一个缓冲区对象
glGenFramebuffers(1,&fboHandle);
glBindFramebuffer(GL_FRAMEBUFFER, fboHandle);
glActiveTexture(GL_TEXTURE1);
glGenTextures(1,&targetTex);
glBindTexture(GL_TEXTURE_2D, targetTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,800, 800, 0, GL_RGBA,
GL_UNSIGNED_BYTE,NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,targetTex, 0);
GLuint depthBuf;
glGenRenderbuffers(1,&depthBuf);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,
800, 800);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER,depthBuf);
GLenum drawBufs[] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1,drawBufs);
//检测帧缓冲区的状态
if(glCheckFramebufferStatus(GL_FRAMEBUFFER)!= GL_FRAMEBUFFER_COMPLETE)
{
cerr << "Frame buffererror" << endl;
_getch();
//exit(1);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
2. 创建两个着色器程序,第一渲染的时候绑定到帧缓冲区fboHandle,使用的着色器程序如下:
//vertex shader
#version430
layout(location= 0) in vec4 position;
out vec3pixelCoords;
voidmain()
{
pixelCoords = position.xyz;
gl_Position = position;
}
//fragment shader
#version430
layout(location= 0) out vec4 fragColor;
in vec3pixelCoords;
in vec2coords;
voidmain()
{
//第四个分量会被裁剪到0.0~1.0
//gl_PrimitiveID的默认值是0
fragColor = vec4(pixelCoords.xyz,float(gl_PrimitiveID + 1) * 0.1);
}
需要特别注意的是,创建帧缓冲区的时候使用的纹理格式是RGBA,所以fragColor的四个分量范围会被裁剪到[0.0, 1.0]。gl_PrimitiveID是GLSL的内置变量,代表图元的编号,它的默认值是0,而第一个图元的gl_PrimitiveID值也是0,为加以区别,在传递的时候加了1。
3. 第一次渲染后,读取帧缓冲区中的内容
glBindFramebuffer(GL_READ_BUFFER, fboHandle);
glReadBuffer(GL_COLOR_ATTACHMENT0);
float pixels[4];
glReadPixels(x, y, 1, 1, GL_RGBA, GL_FLOAT,(void*)pixels);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_READ_BUFFER, 0);
像素x,y对应的实际坐标和图元被读取到pixels[4]后,对第二次渲染使用的着色器程序变量进行设置
int selectID = pixels[3] *10.0 + 0.5;
GLint loc =glGetUniformLocation(renderProgram, "selectPrimitiveID");
if (loc < 0)
{
cerr << "ERROR : 无法获取参数selectPrimitieID" << endl;
}
4.第二次渲染绑定到默认的缓冲区对象,使用的着色器程序如下:
//vertex shader
#version430
layout(location= 0) in vec4 position;
layout(location= 1) in vec2 texCoord;
out vec2coords;
voidmain()
{
coords = texCoord;
gl_Position = position;
}
//fragment shader
#version430
layout(location= 0) out vec4 fragColor;
uniformsampler2D Tex;
uniformint selectPrimitiveID;
in vec2coords;
voidmain()
{
fragColor = texture2D(Tex, coords);
if(gl_PrimitiveID == (selectPrimitiveID - 1)&& selectPrimitiveID > 0)
{
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
}
绑定到默认的缓冲区后,被选中的三角形将会高亮显示。