Pangolin是基于OpenGL开发的绘图库,提供了基础的绘图功能和许多GUI操作。
工程构建
工程结构
此处,采用Ubuntu20.04+CLion进行开发。新建工程Study,结构如下所示:
.
├── CMakeLists.txt
├── include
│ └── main.h
└── src
├── CMakeLists.txt
└── main.cpp
CMake配置
其中,根目录下CMakeLists.txt
文件内容如下:
# cmake version
cmake_minimum_required(VERSION 3.21)
# project name
project(Study)
# cpp version
set(CMAKE_CXX_STANDARD 14)
# eigen
include_directories("/usr/include/eigen3")
# Sophus
find_package(Sophus REQUIRED)
include_directories(${Sophus_INCLUDE_DIRS})
# pangolin
find_package(Pangolin REQUIRED)
include_directories(${Pangolin_INCLUDE_DIRS})
# incldue
include_directories(include)
# src
add_subdirectory(src)
主要导入Eigen库、Sophas库、Pangolin库,src
目录下CMakeLists.txt
文件内容如下:
# exec
add_executable(Study main.cpp)
# link
target_link_libraries(Study ${Pangolin_LIBRARIES})
主要用于生成可执行二进制文件并链接Pangolin的动态库文件。
头文件
include
目录用于存放头文件main.h
:
#ifndef STUDY_MAIN_H
#define STUDY_MAIN_H
#include <iostream>
#include <cmath>
#include <unistd.h>
// Eigen
#include <Eigen/Core>
#include <Eigen/Dense>
#include <Eigen/Geometry>
// Sophus
#include <sophus/so3.hpp>
#include <sophus/se3.hpp>
// Pangolin
#include <pangolin/pangolin.h>
// namespace
using namespace std;
using namespace Eigen;
#endif //STUDY_MAIN_H
源码
src
目录用于存放源文件main.cpp
:
#include <iostream>
#include "main.h"
int main() {
cout << "Hello World" << endl;
return 0;
}
构建Pangolin相机
在进行绘制具体内容前,首先需要学习如何构建视窗对象、观察相机、交互视图内容
视窗
pangolin::CreateWindowAndBind(std::string window_title, int w = 640, int h = 480, const Params& params = Params())
window_title
:GUI窗口名w
:宽度h
:高度
该函数用于创建一个指定大小、名称的GUI窗口。
开启深度测试
深度测试可以使得Pangolin构建的相机只展示镜头朝向一侧的像素信息,从而避免容易混淆的透视关系。
在3D可视化中应开启深度测试,可用如下语句进行开启:
glEnable(GL_DEPTH_TEST);
如果需要关闭,可使用如下语句:
glDisable(GL_DEPTH_TEST);
相机
应注意,此处构建的相机为用于观测的相机,而非SLAM中的相机传感器。
构建观察相机对象使用如下语句进行:
pangolin::OpenGlRenderState s_cam(const OpenGlMatrix& projection_matrix, const OpenGlMatrix& modelview_matrix)
projection_matrix
:用于构建观察相机的内参系数modelview_matrix
:用于构建观察相机及其视点的初始位置
其中,相机参数projection_matrix
对象构建如下:
OpenGlMatrixSpec ProjectionMatrix(int w, int h, GLprecision fu, GLprecision fv, GLprecision u0, GLprecision v0, GLprecision zNear, GLprecision zFar );
w、h
:相机的视野宽、高fu、fv、u0、v0
相机的内参,对应《视觉SLAM十四讲》中内参矩阵的fx、fy、cx、cy
zNear、zFar
:相机的最近、最远视距
而相机、视点初始坐标 modelview_matrix
对象构建如下:
OpenGlMatrix ModelViewLookAt(GLprecision x, GLprecision y, GLprecision z, GLprecision lx, GLprecision ly, GLprecision lz, AxisDirection up);
x、y、z
:相机的初始坐标lx、ly、lz
:相机视点的初始位置,也即相机光轴朝向up
:相机自身那一轴朝上放置,可选如下参数填写:pangolin::AxisX
:X轴正方向pangolin::AxisY
:Y轴正方向pangolin::AxisZ
:Z轴正方向pangolin::AxisNegX
:X轴负方向pangolin::AxisNegY
:Y轴负方向pangolin::AxisNegZ
:Z轴负方向
交互视图
交互视图(View)用于显示相机观察到的信息内容,首先需要构建相机视图的句柄:
pangolin::Handler3D handler(s_cam);
括号内参数填写相机对象,随后使用如下方式构建交互视图对象:
pangolin::View& d_cam=pangolin::CreateDisplay()
.SetBounds(Attach bottom, Attach top, Attach left, Attach right, double aspect)
.SetHandler(Handler* handler);
SetBounds()
:用于确定视图属性,其内参数如下:bottom、top
:视图在视窗内的上下范围,依次为下、上
,采用相对坐标表示(0:最下侧,1:最上侧)left、right
:视图在视窗内的左右范围,依次为左、右
,采用相对左边表示(0:最左侧,1:最右侧)aspect
:视图的分辨率,也即分辨率- 参数
aspect
取正值,将由前四个参数设置的视图大小来裁剪达到设置的分辨率 - 参数
aspect
取负值,将拉伸图像以充满由前四个参数设置的视图范围
- 参数
SetHandler()
:用于确定视图的相机句柄
清理缓存区
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
用于清空色彩缓冲区和深度缓冲区,刷新显示信息。若不使用清理,视窗将自动保留上一帧信息。
相机激活
d_cam.Activate(const OpenGlRenderState& state )
对交互视图对象进行激活相机
视窗刷新
一般,将构建一个循环用于检测视窗是否被关闭,若未关闭,将进行绘制并循环更迭。
循环条件:视窗未被关闭
!pangolin::ShouldQuit()
绘制一帧完成后,刷新视窗进行下一帧:
pangolin::FinishFrame();
综合
由此,我们就创建了一个完整的Pangolin相机对象:
#include <iostream>
#include "main.h"
int main(int argc,char **argv)
{
// 创建视窗对象 名称:Viewer,大小:640×480
pangolin::CreateWindowAndBind("Viewer",640,480);
// 开启深度测试
glEnable(GL_DEPTH_TEST);
// 构建相机对象
pangolin::OpenGlRenderState s_cam(
// 相机参数
pangolin::ProjectionMatrix(640,480,420,420,320,240,0.2,100),
// 相机、视点初始位置
pangolin::ModelViewLookAt(-2,2,-2,0,0,0,pangolin::AxisY)
);
// 构建交互视图对象
pangolin::Handler3D handler(s_cam);// 句柄
pangolin::View& d_cam=pangolin::CreateDisplay()
.SetBounds(0.0,1,0,1,-640.0f/480.0f)
.SetHandler(&handler);
// 检测视窗是否关闭
while(!pangolin::ShouldQuit())
{
// 清空颜色、深度信息缓存,刷新显示信息
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// 激活相机、交互试图
d_cam.Activate(s_cam);
/*-------- 绘图 --------*/
// 帧循环,推进信息更迭
pangolin::FinishFrame();
}
return 0;
}
效果如下:
此时,点击关闭视窗,程序将自动停止。
绘制基本图形
完成在激活相机后,即可进行进一步的绘制对象。
背景色设置
使用如下语句设置背景颜色,默认为黑色:
glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
red、green、blue
:颜色RGB数值,范围0~1alpha
:透明度
例如,设置红色背景:
绘制立方体
使用如下语句,在原点构建一个立方体对象:
pangolin::glDrawColouredCube();
效果如下:
绘制点
使用如下方式,绘制点:
/*-------- 绘图 --------*/
// 设置点大小
glPointSize(30.0);
// 开始画点
glBegin ( GL_POINTS );
// 设置点颜色,rgb值,float类型
glColor3f(0, 1, 0);
// 设置点1坐标,3维float类型
glVertex3f(0, 0, 0);
// 设置点2颜色、坐标
glColor3f(1, 0, 0);
glVertex3f(0, 1, 0);
// 结束画点
glEnd();
效果如下:
绘制线段
使用如下语句,实现绘制线段:
/*-------- 绘图 --------*/
// 设置大小
glLineWidth(4);
// 开始
glBegin ( GL_LINES );
// 设置颜色
glColor3f(0, 1, 0);
// 设置起点、终点坐标
glVertex3f(0, 0, 0);
glVertex3f(0, 1, 0);
// 结束
glEnd();
效果如下:
绘制折线
使用如下语句,实现绘制折线:
/*-------- 绘图 --------*/
// 设置大小
glLineWidth(2);
// 开始
glBegin ( GL_LINE_STRIP );
// 设置颜色
glColor3f(0, 1, 0);
// 设置折点坐标
glVertex3f(0, 0, 0);
glVertex3f(0, 1, 0);
glVertex3f(1, 1, 0);
// 结束
glEnd();
效果如下:
绘制不规则图形
使用如下语句,实现绘制封闭不规则图像:
/*-------- 绘图 --------*/
// 设置大小
glLineWidth(2);
// 开始
glBegin ( GL_LINE_LOOP );
// 设置颜色
glColor3f(0, 1, 0);
// 设置折点坐标
glVertex3f(0, 0, 0);
glVertex3f(0, 1, 0);
glVertex3f(1, 1, 0);
// 结束
glEnd();
效果如下:
保存图像
对图像进行保持,采用如下语句进行:
pangolin::SaveWindowOnRender(const std::string& filename_hint);
图像将以filename_hint
为名称进行保存在运行目录下,文件后缀名为.png
。