如何使用OpenGL画出ROS rviz那样的点云可视化效果

【请尊重原创!转载和引用文章内容务必注明出处!未经许可上传到某文库或其他收费阅读/下载网站赚钱的必追究责任!】

ROS rviz可以将点云以多种形式渲染出来比较漂亮,尤其是根据intensity渲染点云不同的色彩和亮度的功能比较好, 比PCL和mayavi等提供的可视化API画的点云效果好看多了,缺点是你想在既有rviz工具里对点云效果增加功能处理或渲染效果的话不得不去读rviz很多的代码后去做修改,如果一知半解就会改出问题来,如果自己实现用OpenGL画点云的代码,想怎么改都行,但是很难找得到有资料介绍rviz里点云的效果(主要是比较好看的rainbow)到底是怎么画出来的,于是花了些空余时间先琢磨用集成到Qt里的OpenGL简单的用一种颜色画出点云(这时看到的是类似PCL或mayavi画点云的效果,比较单一呆板,无法表现出和intensity有关的颜色和亮度的不同),然后读rviz的源码找到了画rainbow效果的实现代码移植到点云的点着色程序中,解决掉在移植过程中出现的不符合GLSL语言语法规则的错误一些错误后(例如GLSL对数据类型转换有较C++更强的要求),画出来的效果和rviz一样了。

这里列出一些主要的关键代码,下面是点云数据准备和渲染的主要程序:

复制代码
class opengl_widget : public QOpenGLWidget, QOpenGLFunctions_4_5_Core
{
	Q_OBJECT

    public:
	   opengl_widget(QWidget* parent);
	   ~opengl_widget();

    protected:
	   void initializeGL();
	   void paintGL();
	   void resizeGL(int width, int height);
    
    private:
       QOpenGLShaderProgram glsp_point;
       unsigned int pointVBO;
       unsigned int pointVAO;
       unsigned int vertexCount;
       unsigned int pointCount;
       std::vector<float> pointData;
       float max_intensity;
       float min_intensity;

    ...

...
   unsigned int opengl_widget::preparePoints(std::vector<float>& pointData)
   {
      glGenVertexArrays(1, &pointVAO);
      glBindVertexArray(pointVAO);
      glGenBuffers(1, &pointVBO);
      glBindBuffer(GL_ARRAY_BUFFER, pointVBO);
      glBufferData(GL_ARRAY_BUFFER, pointData.size() * sizeof(float), &pointData[0], GL_STATIC_DRAW);
      glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
      glEnableVertexAttribArray(0);
      glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(3 * sizeof(float)));
      glEnableVertexAttribArray(1);
      glBindBuffer(GL_ARRAY_BUFFER, 0);
      glBindVertexArray(0);
      return pointData.size() / 4;
   }
   opengl_widget::opengl_widget(QWidget* parent)
    : QOpenGLWidget(parent)
   {
      ...
      pointData = read_bag(bag_path, max_intensity, min_intensity);
   }
   void opengl_widget::initializeGL()
   {
      initializeOpenGLFunctions();
      ...
      glsp_point.addShaderFromSourceFile(QOpenGLShader::Fragment, "glsp_point.fs");
      glsp_point.addShaderFromSourceFile(QOpenGLShader::Vertex, "glsp_point.vs");
      glsp_point.link();
      pointCount = preparePoints(pointData);
   }
   void opengl_widget::paintGL()
   {
      ...
      glsp_point.bind();
      ...
      glsp_point.setUniformValue("max_intensity", max_intensity);
      glsp_point.setUniformValue("min_intensity", min_intensity);
      glBindVertexArray(pointVAO);
      glPointSize(2.0f);
      glDrawArrays(GL_POINTS, 0, pointCount);
      ...
   }

下面分别是点云vertex和fragment着色程序,是画出rainbow效果的关键:

复制代码
#version 450 core

layout (location = 0) in vec3 pos;
layout (location = 1) in float intensity;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform float max_intensity;
uniform float min_intensity;

out vec3 vtxColor;

vec3 getRainbowColor(float value)
{
  // this is HSV color palette with hue values going only from 0.0 to 0.833333.
  vec3 color = vec3(0.0f,0.0f,0.0f);
  value = min(value, 1.0f);
  value = max(value, 0.0f);

  float h = value * 5.0f + 1.0f;
  int i = int(floor(h));
  float f = h - i;
  if (!bool(i & 1))
    f = 1 - f; // if i is even
  float n = 1.0f - f;

  if (i <= 1)
    color[0] = n, color[1] = 0, color[2] = 1;
  else if (i == 2)
    color[0] = 0, color[1] = n, color[2] = 1;
  else if (i == 3)
    color[0] = 0, color[1] = 1, color[2] = n;
  else if (i == 4)
    color[0] = n, color[1] = 1, color[2] = 0;
  else if (i >= 5)
    color[0] = 1, color[1] = n, color[2] = 0;
  return color;
}

void main()
{
    gl_Postion = projection * view * model * vec4(pos, 1.0);
    float diff_intensity = max_intensity - min_intensity;
    float normalized_intensity = (intensity - min_intensity) / diff_intensity;
    float value = 1.0 - normalized_intensity;  // use rainbow
    vtxColor = getRainbowColor(value);
}

上面的根据点云的intensity值计算rainbow色彩的计算算法是从rviz的代码修改来的,作归一化用的max_intensity和min_intensity是当前帧点云的最大/最小强度值,是通过uniform变量传入的。每个点云对应的vertex的xyz和intensity值的传入格式是在preparePoints()里定义的。

复制代码
#version 450 core

out vec4 fragColor;

in vec3 vtxColor;

void main()
{
    fragColor = vec4(vtxColor, 1.0f);
}

画出来的效果是这样:

效果和rviz里一样了,在增加些针对鼠标事件触发的对点云图的缩放和翻转之类的控制,实现

复制代码
opengl_widget::mouseMoveEvent()、opengl_widget::mousePressEvent()、opengl_widget::mouseReleaseEvent()、opengl_widget::wheelEvent()等事件函数,得到的最终效果就和rviz里的效果一样了,而且自己的代码实现可以很方便进一步增加更复杂的处理功能或效果展示。
相关推荐
:mnong16 小时前
Web 三维可视化技术对比:ParaView Web vs 本项目 wasm + Three.js
前端·webgl·opengl·opencl·科学可视化
扶尔魔ocy2 天前
【qt openGL入门笔记】OSG实现显示飞机模型飞行的点迹(二)
qt·opengl·osg
扶尔魔ocy2 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行(三)
opengl·osg
扶尔魔ocy3 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行
qt·opengl·osg
扶尔魔ocy7 天前
【qt openGL入门笔记】FPS相机
qt·opengl
扶尔魔ocy7 天前
qt openGL入门笔记】FPS相机和轨道相机
qt·opengl
扶尔魔ocy9 天前
【qt openGL入门笔记】基础光照
qt·opengl
扶尔魔ocy9 天前
【qt openGL入门笔记】坐标系统和摄像机
qt·opengl
茉莉玫瑰花茶12 天前
OpenGL [ 纹理 ]
开发语言·c++·opengl
wdfk_prog14 天前
Wi-Fi Direct教程 01:在 Ubuntu 搭建双 mac80211_hwsim + wpa_supplicant 2.12
linux·运维·服务器·ubuntu·ros·wifi-direct