我正在尝试在OpenGL中实现对象选择,但是我遇到了一些问题。
从图中可以看出,选择在第一个视图中效果很好,但是当您旋转相机时,对象选择不正确。
我以这种方式实现了选择。单击鼠标,将场景绘制在帧缓冲区中,每个对象以不同的颜色(也与背景颜色)描绘,然后检查单击的像素是否具有某些对象的颜色。
这些是相关的代码段。
struct CubeData
{
QMatrix4x4 model;
bool selected;
};
QMap<QString, CubeData> cubes;
初始化GL
void initializeGL()
{
/* ... */
auto FindColor = [=] ()
{
QVector<GLubyte> color;
bool flag = true;
while (flag)
{
color = RandomColor();
flag = false;
if (color == RgbFromColorToByte(background))
{
flag = true;
continue;
}
if (cubes.contains(RgbFromByteToString(color)))
{
flag = true;
continue;
}
}
return color;
};
// cubes
float offset = 3.0f;
float x = -1.0f;
while (cubes.size() < 3)
{
CubeData cube;
cube.model.translate(x++ * offset, 0, 0);
cube.selected = false;
auto color = FindColor();
cubes[RgbFromByteToString(color)] = cube;
}
}
paintGL
void paintGL()
{
/* ... */
// enable depth test
glEnable(GL_DEPTH_TEST);
// clear buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// draw cubes
for (auto cube : cubes)
{
DrawCube(cube);
}
update();
}
mousePressEvent
void mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
makeCurrent();
glBindFramebuffer(GL_FRAMEBUFFER, addFBO(FBOIndex::TEST));
{
// enable depth test
glEnable(GL_DEPTH_TEST);
// clear buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// draw test cubes
DrawTest();
// test pixel
QVector<GLubyte> pixel;
pixel.resize(3);
glReadPixels(lastX, lastY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &(pixel[0]));
for (auto key : cubes.keys())
{
cubes[key].selected = false;
}
QString key = RgbFromByteToString(pixel);
if (cubes.contains(key))
{
cubes[key].selected = true;
}
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
doneCurrent();
}
}
DrawCube
void DrawCube(CubeData cube)
{
GLuint program = addProgram(ProgramIndex::CUBE);
glUseProgram(program);
glUniformMatrix4fv(glGetUniformLocation(program, "model"), 1, GL_FALSE, cube.model.constData());
glUniformMatrix4fv(glGetUniformLocation(program, "view"), 1, GL_FALSE, view.constData());
glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, projection.constData());
glUniform1i(glGetUniformLocation(program, "cube.selected"), cube.selected);
glDrawArrays(GL_POINTS, 0, 1);
}
DrawTest
void DrawTest()
{
GLuint program = addProgram(ProgramIndex::TEST);
glUseProgram(program);
glUniformMatrix4fv(glGetUniformLocation(program, "view"), 1, GL_FALSE, view.constData());
glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, projection.constData());
for (auto key : cubes.keys())
{
glUniformMatrix4fv(glGetUniformLocation(program, "model"), 1, GL_FALSE, cubes[key].model.constData());
glUniform3fv(glGetUniformLocation(program, "cube.TestColor"), 1, RgbFromStringToFloat(key).constData());
glDrawArrays(GL_POINTS, 0, 1);
}
}
多维数据集几何着色器
#version 450 core
struct Cube
{
bool selected;
};
layout (points) in;
layout (triangle_strip, max_vertices = 14) out;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform Cube cube;
out FragData
{
smooth vec3 color;
} frag;
void main()
{
vec3 offset[14];
offset[0x0] = vec3(-1.0f, +1.0f, +1.0f);
offset[0x1] = vec3(+1.0f, +1.0f, +1.0f);
offset[0x2] = vec3(-1.0f, -1.0f, +1.0f);
offset[0x3] = vec3(+1.0f, -1.0f, +1.0f);
offset[0x4] = vec3(+1.0f, -1.0f, -1.0f);
offset[0x5] = vec3(+1.0f, +1.0f, +1.0f);
offset[0x6] = vec3(+1.0f, +1.0f, -1.0f);
offset[0x7] = vec3(-1.0f, +1.0f, +1.0f);
offset[0x8] = vec3(-1.0f, +1.0f, -1.0f);
offset[0x9] = vec3(-1.0f, -1.0f, +1.0f);
offset[0xA] = vec3(-1.0f, -1.0f, -1.0f);
offset[0xB] = vec3(+1.0f, -1.0f, -1.0f);
offset[0xC] = vec3(-1.0f, +1.0f, -1.0f);
offset[0xD] = vec3(+1.0f, +1.0f, -1.0f);
mat4 MVP = projection * view * model;
vec3 albedo = cube.selected ? vec3(1.0f, 0.0f, 0.0f) : vec3(1.0f);
// grayscale weights
vec3 weight = vec3(0.2126f, 0.7152f, 0.0722f);
// shade power
float power = 0.5f;
for (int i = 0; i < 14; i++)
{
gl_Position = MVP * vec4(offset[i], 1.0f);
float shade = dot((normalize(offset[i]) + 1.0f) * 0.5f, weight);
frag.color = albedo * (shade + (1.0f - shade) * power);
EmitVertex();
}
EndPrimitive();
}
测试几何着色器
#version 450 core
struct Cube
{
vec3 TestColor;
};
layout (points) in;
layout (triangle_strip, max_vertices = 14) out;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform Cube cube;
out FragData
{
flat vec3 color;
} frag;
void main()
{
vec3 offset[14];
offset[0x0] = vec3(-1.0f, +1.0f, +1.0f);
offset[0x1] = vec3(+1.0f, +1.0f, +1.0f);
offset[0x2] = vec3(-1.0f, -1.0f, +1.0f);
offset[0x3] = vec3(+1.0f, -1.0f, +1.0f);
offset[0x4] = vec3(+1.0f, -1.0f, -1.0f);
offset[0x5] = vec3(+1.0f, +1.0f, +1.0f);
offset[0x6] = vec3(+1.0f, +1.0f, -1.0f);
offset[0x7] = vec3(-1.0f, +1.0f, +1.0f);
offset[0x8] = vec3(-1.0f, +1.0f, -1.0f);
offset[0x9] = vec3(-1.0f, -1.0f, +1.0f);
offset[0xA] = vec3(-1.0f, -1.0f, -1.0f);
offset[0xB] = vec3(+1.0f, -1.0f, -1.0f);
offset[0xC] = vec3(-1.0f, +1.0f, -1.0f);
offset[0xD] = vec3(+1.0f, +1.0f, -1.0f);
mat4 MVP = projection * view * model;
for (int i = 0; i < 14; i++)
{
gl_Position = MVP * vec4(offset[i], 1.0f);
frag.color = cube.TestColor;
EmitVertex();
}
EndPrimitive();
}
我还尝试通过以PPM格式保存其内容来查看整个测试帧缓冲区,但这就是我所得到的。
这是我生成PPM图像的方式。
mousePressEvent
void mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
makeCurrent();
glBindFramebuffer(GL_FRAMEBUFFER, addFBO(FBOIndex::TEST));
{
/* ... */
int w = geometry().width();
int h = geometry().height();
QVector<GLubyte> frame;
frame.resize(w * h * 3);
glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, &(frame[0]));
QString name = "test.ppm";
QFile file(name);
file.open(QIODevice::WriteOnly);
{
QTextStream stream(&file);
stream << "P3" << endl;
stream << w << " " << h << endl;
stream << "255" << endl;
int i = 0;
QVector<GLubyte> pixel;
while (!(pixel = frame.mid(i++ * 3, 3)).isEmpty())
{
stream << pixel[0] << " " << pixel[1] << " " << pixel[2] << endl;
}
}
file.close();
}
glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
doneCurrent();
}
}
感谢您的任何建议。
答案 0 :(得分:-1)
我发现了我犯的(愚蠢的)错误。
QMouseEvent::pos()返回的像素坐标从左上角开始,而glReadPixels接受的像素坐标从左下角开始。
因此,以这种方式在glReadPixels
中将通话更改为mousePressEvent
glReadPixels(lastX, geometry().height() - lastY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &(pixel[0]));
解决了问题。