已经有了很多示例代码,为什么要写
现在百度找到的大多数已有的OpenGL点选代码,使用的是老版本OpenGL的拾取功能,老版本的OpenGL是通过固定管线渲染,有渲染模式和选择模式,但是如果我们使用的现代OpenGL(好像OpenGL3.0以上),则不会有选择模式,需要我们手动去写着色器进行选择。
选择方法
这里使用的方法比较简单,创建一个帧缓冲,对这个帧缓冲添加颜色纹理和深度纹理,根据鼠标点击位置的深度值和颜色纹理判断选择了哪个绘制的物体,这里的颜色纹理并非是物体的本身颜色,我们为每个物体增加一个ID,将ID的值赋值给颜色纹理,来达到点选的目的。
示例代码
我这里使用的是OpenGL4.5版本。
顶点着色器:
#version 450
layout(location = 0) in vec3 aPosition;
uniform int ID = 0;
out VS_OUT_POINTS
{
vec4 vs_color;
}vs_out;
void main()
{
gl_Position = aPosition;
vs_out.vs_color = vec4(ID,ID,ID,ID);
}
片段着色器:
#version 450
out vec4 FragColor;
in VS_OUT_POINTS
{
vec4 vs_color;
}vs_in;
void main()
{
FragColor =vs_in.vs_color;
}
在绘制时,需要将物体的ID传输给着色器:
glUniform1i(glGetUniformLocation(m_shaderID, "ID"), Object->getID());
帧缓冲的创建:
//创建帧缓冲
glGenFramebuffers(1, &m_framebufferPick);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebufferPick);
// 创建纹理附件
glGenTextures(1, &m_textureColorBufferPick);
glBindTexture(GL_TEXTURE_2D, m_textureColorBufferPick);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_glViewport.width(), m_glViewport.height(), 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_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_textureColorBufferPick, 0);
// 创建深度纹理
glGenTextures(1, &m_textureDepthBufferPick);
glBindTexture(GL_TEXTURE_2D, m_textureDepthBufferPick);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, m_glViewport.width(), m_glViewport.height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_textureDepthBufferPick, 0);
// 检查当前帧缓冲是否完整
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!"<<std::endl;
//切换到Qt的默认帧缓冲
glBindFramebuffer(GL_FRAMEBUFFER,0);
点选代码:
int select_index = -1;
glBindFramebuffer(GL_FRAMEBUFFER, m_framebufferPick);
glViewport(0, 0, m_context.glWidth, m_context.glHeight);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST); // enable depth testing (is disabled for rendering screen-space quad)
glClearColor(m_context.backgroundColor.r, m_context.backgroundColor.g, m_context.backgroundColor.b, m_context.backgroundColor.a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, m_textureColorBufferPick);
GLfloat _depth = -1.0f;
//picking
for (int ii = 0; ii < Objects.size(); ++ii)
{
glClearColor(m_context.backgroundColor.r, m_context.backgroundColor.g, m_context.backgroundColor.b, m_context.backgroundColor.a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawObject(Objects[ii], true);
GLfloat depth_;
glReadPixels(x, m_glViewport.height() - 1 - y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth_);
//防止因为渲染顺序导致深度测试无效
if (depth_!=1.0f)
{
if (_depth<0.0f)
{
_depth = depth_;
}
if (depth_<=_depth)
{
GLfloat pixelValue[4];
glReadPixels(x, m_glViewport.height() - 1 - y, 1, 1, GL_RGBA, GL_FLOAT, pixelValue);
if (pixelValue[0] != 0)
{
//最终得到的ObjectID
select_index = pixelValue[0];
}
}
}
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER,0);