在Qt中使用OpenGL绘制三维点阵
前言:最近在做医学图像处理方面的东西,估计要进行三维重建,初步确定使用Qt+OpenGL作为框架进行开发。第一步,设想是需要显示三维点云,因此先写个能显示几个点的demo,要求能染色,可以调节视角及观察者的位置。
需要的工具及相关包和库:
- VSCode 1.80.2
- CMake 3.26.4
- Qt 5.12.2
- DCMTK 3.6.7(读取dicom文件需要用到,可选)
下载好后上述工具与包之后,需要配置CMake, Qt和DCMTK的环境变量,不然构建项目的时候会找不到路径。
显示三维数据点
Qt+OpenGL显示三维数据点
我们在初始化的时候,需要从一定的距离来观察,因此设置一下摄像机的move, 顶点数据放在_vertices数组中,八个点其实就是立方体的八个顶点,然后给他们两两染上好看的颜色,用于在摄像机移动过程中观察他们的位置变化。
ViewWidget::ViewWidget(QWidget* parent) : QOpenGLWidget(parent) {
startTimer(1000 / 60);
m_camera.move(-9, 0, 3);
m_camera.look(0, 0, 0);
m_camera.update();
installEventFilter(&m_camera);
}
void ViewWidget::initializeGL() {
initializeOpenGLFunctions();
glEnable(GL_DEPTH_TEST);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
glClearColor(0, 0, 0, 1.0);
// 设置立方体的顶点
float _vertices[] = {
// position color
1, 1, 1, 1, 0, 0, // red
1, 1, -1, 1, 0, 0,
-1, 1, -1, 0, 1, 0, // green
-1, 1, 1, 0, 1, 0,
1, -1, 1, 1, 1, 0, // yellow
1, -1, -1, 1, 1, 0,
-1, -1, 1, 0, 0, 1, // blue
-1, -1, -1, 0, 0, 1,
};
// 初始化并绑定vao
m_vao.create();
m_vao.bind();
// 初始化并激活vbo
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(_vertices, 6 * 8 * sizeof(float));
// 如果用m_program->create()会出现访问冲突
m_program = new QOpenGLShaderProgram();
m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/resources/shader/vertex.shader");
m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/resources/shader/fragment.shader");
m_program->link();
// 设置shader里面的传入变量值参数
m_program->bind();
m_program->setAttributeBuffer("vPos", GL_FLOAT, 0 * sizeof(float), 3, 6 * sizeof(float));
m_program->enableAttributeArray("vPos");
m_program->setAttributeBuffer("vColor", GL_FLOAT, 3 * sizeof(float), 3, 6 * sizeof(float));
m_program->enableAttributeArray("vColor");
m_vao.release();
m_vbo.release();
m_program->release();
// 设置物体位置为原来的位置,这里暂时没有用到
m_model.setToIdentity();
}
显示DICOM数据点云
有了这个基础,我们就可以显示文件中的点云了,首先通过DCMTK库把dicom中相关信息读出来,而后再把数据点的坐标乘以对应的spacing(这里x, y, z的spacing有可能完全不一样,需要从一张图片中读取)。
Qt+OpenGL显示dicom图像点云
显示图片数据时需要仔细地调整相机位置,并且转动视角方向,不然可能即使对象已经渲染出来了,但是仍然无法呈现在屏幕上让我们观察。因此在这里我觉得有必要先学一下OpenGL中的摄像机,再做这个点云会更好。
void ViewWidget::initializeGL() {
resize(QSize(800, 600));
initializeOpenGLFunctions();
glEnable(GL_DEPTH_TEST);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
glClearColor(0, 0, 0, 1.0);
ImageInfo imageInfo;
ReadFile::Read_Dicom_Info(imageInfo);
int width = imageInfo.Width, height = imageInfo.Height, slices = imageInfo.Slice_num;
float widthSpacing = imageInfo.Spacing[0], heightSpacing = imageInfo.Spacing[1], slicesSpacing = imageInfo.Spacing[2];
shared_ptr<uint16_t> imageData(new uint16_t[width * height * slices]);
ReadFile::Read_Dicom_File(imageData, slices, width * height);
vector<float> _vertices;
// -12851, 1023,这里最大最小值暂时没有用到
short minValue = 32767, maxValue = -32768;
for(int i = 0; i < width; i++) {
for(int j = 0; j < height; j++) {
for(int k = 0; k < slices; k++) {
short value = (short)imageData.get()[k * width * height + j * width + i];
if(value >= 400) {
_vertices.push_back(i * widthSpacing);
_vertices.push_back(j * heightSpacing);
_vertices.push_back(k * slicesSpacing);
}
if(value < minValue) {
minValue = value;
}
if(value > maxValue) {
maxValue = value;
}
}
}
}
vertexCount = _vertices.size();
m_vao.create();
m_vao.bind();
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(_vertices.data(), vertexCount * sizeof(float));
m_program = new QOpenGLShaderProgram(); // 如果用m_program->create()会出现访问冲突
m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/resources/shader/vertex.shader");
m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/resources/shader/fragment.shader");
m_program->link();
m_program->bind();
m_program->setAttributeBuffer("vPos", GL_FLOAT, 0 * sizeof(float), 3, 3 * sizeof(float));
m_program->enableAttributeArray("vPos");
m_vao.release();
m_vbo.release();
m_program->release();
// 设置物体位置为原来的位置
m_model.setToIdentity();
}
如果以上内容对你有帮助,不妨点个赞再走🌹.
最后附上代码地址:https://gitee.com/inavacuum/show-dicom-points
参考博客:https://blog.csdn.net/github_18974657/article/details/122453424?spm=1001.2014.3001.5502