点击屏蔽,获取点击的图元,并用红色进行渲染。
具体的方法:分两步绘制
- 创建一个framebuffer,并链接一个颜色缓存和深度缓存,渲染时颜色缓存中保存的是像素所对应的物体的索引、物体部件的索引和渲染的图元的索引。物体索引和物体部件索引由程序传入着色器。使用深度缓存可以保证颜色缓存中保存的是距离近平面最近的图元的信息。
//像素信息结构体
struct PixelInfo {
GLuint ObjectID;
GLuint DrawID;
GLuint PrimID;
PixelInfo()
{
ObjectID = 0;
DrawID = 0;
PrimID = 0;
}
};
bool PickingTexture::Init(unsigned int WindowWidth, unsigned int WindowHeight)
{
// Create the FBO
glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
// Create the texture object for the primitive information buffer
glGenTextures(1, &m_pickingTexture);
glBindTexture(GL_TEXTURE_2D, m_pickingTexture);
//注意texutre的格式要与像素信息结构体相匹配
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32UI, WindowWidth, WindowHeight, 0, GL_RGB_INTEGER, GL_UNSIGNED_INT, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pickingTexture, 0);
// Create the texture object for the depth buffer
glGenTextures(1, &m_depthTexture);
glBindTexture(GL_TEXTURE_2D, m_depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthTexture, 0);
glReadBuffer(GL_NONE);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
// Verify that the FBO is correct
GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (Status != GL_FRAMEBUFFER_COMPLETE) {
printf("FB error, status: 0x%x\n", Status);
return false;
}
// Restore the default framebuffer
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return GLCheckError();
}
- 第一步,绘制并保存像素信息。
//顶点着色器
#version 330
layout (location = 0) in vec3 Position;
uniform mat4 gWVP;
void main()
{
gl_Position = gWVP * vec4(Position, 1.0);
}
//片元着色器
#version 330
uniform uint gDrawIndex;
uniform uint gObjectIndex;
//输出像素为uvec3
out uvec3 FragColor;
void main()
{
//颜色缓存清除为(0,0,0), 图元所索加1进行区分
FragColor = uvec3(gObjectIndex, gDrawIndex,gl_PrimitiveID + 1);
}
//程序绘制设置
void PickingPhase()
{
Pipeline p;
p.Scale(0.1f, 0.1f, 0.1f);
p.Rotate(0.0f, 90.0f, 0.0f);
p.SetCamera(m_pGameCamera->GetPos(), m_pGameCamera->GetTarget(), m_pGameCamera->GetUp());
p.SetPerspectiveProj(m_persProjInfo);
m_pickingTexture.EnableWriting();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_pickingEffect.Enable();
//绘制两个物体
for (uint i = 0 ; i < (int)ARRAY_SIZE_IN_ELEMENTS(m_worldPos) ; i++) {
p.WorldPos(m_worldPos[i]);
//设置物体的索引
m_pickingEffect.SetObjectIndex(i);
m_pickingEffect.SetWVP(p.GetWVPTrans());
m_pMesh->Render(&m_pickingEffect);
}
m_pickingTexture.DisableWriting();
}
void Mesh::Render(IRenderCallbacks* pRenderCallbacks)
{
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
GLExitIfError;
for (unsigned int i = 0 ; i < m_Entries.size() ; i++) {
glBindBuffer(GL_ARRAY_BUFFER, m_Entries[i].VB);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)12);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)20);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Entries[i].IB);
GLExitIfError;
const unsigned int MaterialIndex = m_Entries[i].MaterialIndex;
GLExitIfError;
if (MaterialIndex < m_Textures.size() && m_Textures[MaterialIndex]) {
m_Textures[MaterialIndex]->Bind(GL_TEXTURE0);
}
GLExitIfError;
if (pRenderCallbacks) {
//物体的部件索引
pRenderCallbacks->DrawStartCB(i);
}
GLExitIfError;
glDrawElements(GL_TRIANGLES, m_Entries[i].NumIndices, GL_UNSIGNED_INT, 0);
}
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
- 第二步,切换program,绑定默认framebuffer,点击屏幕,获取像素信息,绘制图元,再次绘制物体。
void RenderPhase()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
Pipeline p;
p.Scale(0.1f, 0.1f, 0.1f);
p.Rotate(0.0f, 90.0f, 0.0f);
p.SetCamera(m_pGameCamera->GetPos(), m_pGameCamera->GetTarget(), m_pGameCamera->GetUp());
p.SetPerspectiveProj(m_persProjInfo);
// If the left mouse button is clicked check if it hit a triangle
// and color it red
if (m_leftMouseButton.IsPressed) {
PickingTexture::PixelInfo Pixel = m_pickingTexture.ReadPixel(m_leftMouseButton.x, WINDOW_HEIGHT - m_leftMouseButton.y - 1);
GLExitIfError;
if (Pixel.PrimID != 0) {
m_simpleColorEffect.Enable();
assert(Pixel.ObjectID < ARRAY_SIZE_IN_ELEMENTS(m_worldPos));
p.WorldPos(m_worldPos[Pixel.ObjectID]);
m_simpleColorEffect.SetWVP(p.GetWVPTrans());
// Must compensate for the decrement in the FS!
m_pMesh->Render(Pixel.DrawID, Pixel.PrimID - 1);
}
}
// render the objects as usual
m_lightingEffect.Enable();
m_lightingEffect.SetEyeWorldPos(m_pGameCamera->GetPos());
for (unsigned int i = 0 ; i < ARRAY_SIZE_IN_ELEMENTS(m_worldPos) ; i++) {
p.WorldPos(m_worldPos[i]);
m_lightingEffect.SetWVP(p.GetWVPTrans());
m_lightingEffect.SetWorldMatrix(p.GetWorldTrans());
m_pMesh->Render(NULL);
}
}
//绘制红色图元
void Mesh::Render(unsigned int DrawIndex, unsigned int PrimID)
{
assert(DrawIndex < m_Entries.size());
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, m_Entries[DrawIndex].VB);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)12);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)20);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Entries[DrawIndex].IB);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, (const GLvoid*)(PrimID * 3 * sizeof(GLuint)));
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
效果图: