如何使用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里的效果一样了,而且自己的代码实现可以很方便进一步增加更复杂的处理功能或效果展示。
相关推荐
起司锅仔12 小时前
OpenGL ES 绘制一个三角形(2)
android·安卓·opengl
你看不见我写的blog3 天前
【从0开始自动驾驶】ros2编写自定义消息 msg文件和msg文件嵌套
人工智能·机器学习·自动驾驶·ros·ros2
AliCloudROS3 天前
高效IaC测试利器:AlibabaCloud ROS-Tool-Iact3快速上手
测试工具·云计算·ros
我啥都会3 天前
顶点缓存对象(VBO)与顶点数组对象(VAO)
opengl
刘好念7 天前
[OpenGL]使用OpenGL绘制带纹理三角形
c++·计算机图形学·opengl
战术摸鱼大师12 天前
OpenGL(四) 纹理贴图
贴图·opengl
ansondroider13 天前
Android MediaPlayer + GLSurfaceView 播放视频
android·opengl·mediaplayer·glsurfaceview
AliCloudROS14 天前
ROS CDK魔法书:建立你的游戏王国(Csharp篇)
游戏·c#·云计算·ros
不知道是谁214 天前
百度Apollo打通与ROS的通信,扩展自动驾驶系统生态
机器人·自动驾驶·ros·apollo
—你的鼬先生14 天前
基于树莓派ubuntu20.04的ros-noetic小车
python·嵌入式·ros·树莓派项目