常见的3D绘图的程序库有很多,MATLAB、Python的Matplotlib、OpenGL。在Linux上常用的一个3D绘图库是Pangolin,它是基于OpenGL完成的,它不但支持OpenGL的基本操作,还提供了一些GUI的功能。对于在SLAM的学习中,它是必不可少的3D显示工具。
(1) 安装依赖库
sudo apt install libgl1-mesa-dev
sudo apt install libglew-dev
sudo apt install cmake
#建议安装的库
sudo apt install libpython2.7-dev
sudo apt install pkg-config
sudo apt install libegl1-mesa-dev libwayland-dev libxkbcommon-dev wayland-protocols
另外还有一些可选的库,包括视频输入、CUDA相关,根据自己的需求选择安装。
(2)安装源文件
git clone https://github.com/stevenlovegrove/Pangolin.git
cd Pangolin
mkdir build
cd build
cmake ..
cmake --build .
以下使用的所有代码,编译时对应的CMakeLists.txt文件如下:
cmake_minimum_required(VERSION 3.16)
project(generate_test)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE "release")
find_package(Pangolin REQUIRED)
include_directories(${Pangolin_INCLUDE_DIRS})
add_executable(generate_test main.cpp)
target_link_libraries(
${Pangolin_LIBRARIES}
)
Pangolin常用代码形式如下,为了避免注释都写在代码中看起来很乱,我先贴出代码,然后逐行解释。
#include
int main()
{
pangolin::CreateWindowAndBind("Main",640,480);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
pangolin::OpenGlRenderState s_cam(
pangolin::ProjectionMatrix(640,480,420,420,320,320,0.2,100),
pangolin::ModelViewLookAt(2,0,2, 0,0,0, pangolin::AxisY)
);
pangolin::Handler3D handler(s_cam);
pangolin::View& d_cam = pangolin::CreateDisplay()
.SetBounds(0.0, 1.0, 0.0, 1.0, -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;
}
下面逐行介绍代码的意思:
(1) pangolin::CreateWindowAndBind("Main",640,480);
创建一个名叫"Main"的GUI窗口用于显示,窗口的大小是640x480像素。
(2)glEnable(GL_DEPTH_TEST);
启动深度测试,开启这个功能之后,窗口中只会绘制面朝相机的那一面像素。一般如果你使用的3D可视化,就要打开这个功能。
(3)glEnable(GL_BLEND);
打开颜色混合,把某一像素位置原来的颜色和将要画上去的颜色,通过某种方式混在一起,从而实现特殊的效果。这个有点儿类似你透过红色玻璃看绿色物体的效果。
(4)glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
使用glEnable(GL_BLEND);
之后,后面紧跟着这行代码,表示两种颜色以怎么样的方式进行混合。我默认情况下就是使用这种方法,目前使用上没有碰到问题。如果你感兴趣更进一步的使用,可以参考这个博客
(5)
pangolin::OpenGlRenderState s_cam(
pangolin::ProjectionMatrix(640,480,420,420,320,320,0.2,100),
pangolin::ModelViewLookAt(2,0,2, 0,0,0, pangolin::AxisY)
);
该行代码表示创建一个相机的观察试图,相当于是模拟一个真实的相机去观测虚拟的三维世界。既然是模拟相机观测,那就得有相机的一些配置参数:
(6)pangolin::Handler3D handler(s_cam);
创建相机视图句柄,需要使用它来显示前面设置的相机所“拍摄”到的内容
(7)
pangolin::View& d_cam = pangolin::CreateDisplay()
.SetBounds(0.0, 1.0, 0.0, 1.0, -640.0f/480.0f)
.SetHandler(&handler);
进行显示设置。SetBounds函数前四个参数依次表示视图在视窗中的范围(下、上、左、右),最后一个参数是显示的长宽比。(0.0, 1.0, 0.0, 1.0)第一个参数0.0表示显示的拍摄窗口的下边在整个GUI中最下面,第二个参数1.0表示上边在GUI的最上面,以此类推。如果在中间就用0.5表示。
(9)pangolin::ShouldQuit()
检测你是否关闭OpenGL窗口
(10)glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
清空颜色和深度缓存。这样每次都会刷新显示,不至于前后帧的颜信息相互干扰。
(11)d_cam.Activate(s_cam);
激活显示并设置状态矩阵。
(12)pangolin::FinishFrame();
执行后期渲染,事件处理和帧交换,相当于前面设置了那么多现在可以进行最终的显示了。
以上就是一个完整的Pangolin用法,其中你要显示的东西就可以写在代码中我标注的//需要绘制的东西写在这里
。
是不是很简单的
下面我要向你实际介绍一些用法,包括怎么绘制点、直线、轨迹等等,甚至还包括多线程显示、按钮操作等等。
#include
int main() {
pangolin::CreateWindowAndBind("Main",640,480);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
pangolin::OpenGlRenderState s_cam(
pangolin::ProjectionMatrix(640,480,420,420,320,320,0.2,100),
pangolin::ModelViewLookAt(2,0,2, 0,0,0, pangolin::AxisY)
);
pangolin::Handler3D handler(s_cam);
pangolin::View& d_cam = pangolin::CreateDisplay()
.SetBounds(0.0, 1.0, 0.0, 1.0, -640.0f/480.0f)
.SetHandler(&handler);
while( !pangolin::ShouldQuit() )
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
d_cam.Activate(s_cam);
//绘制点
glBegin( GL_POINTS );//点设置的开始
glColor3f(1.0,1.0,1.0);
glVertex3f(0.0f,0.0f,0.0f);
glVertex3f(1,0,0);
glVertex3f(0,2,0);
glEnd();//点设置的结束
//绘制直线
glBegin(GL_LINES);
glLineWidth(2.0);
glColor3f(1.0, 1.0, 1.0);
glVertex3f(0,0,0);
glVertex3f(1,1,1);
glVertex3f(0,0,0);
glVertex3f(0,1,1);
glVertex3f(0,0,0);
glVertex3f(1,0,1);
glEnd();
//绘制三角形
glBegin(GL_TRIANGLES);
glVertex3f(0,1,1);
glVertex3f(0,0,0);
glVertex3f(1,0,1);
glEnd();
pangolin::FinishFrame();
}
return 0;
}
可设置的形状还有
总之你绘制的图形必须,以及图像的相关设置必须放在glBegin()
和glEnd()
之间。这之间还有一些可用的配置如下:
#include
void function(){
std::cout << "Hello pangolin" << std::endl;
}
int main() {
pangolin::CreateWindowAndBind("Main",640,480);
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
pangolin::OpenGlRenderState s_cam(
pangolin::ProjectionMatrix(640,480,420,420,320,320,0.2,100),
pangolin::ModelViewLookAt(2,0,2, 0,0,0, pangolin::AxisY)
);
pangolin::Handler3D handler(s_cam);
pangolin::View& d_cam = pangolin::CreateDisplay()
.SetBounds(0.0, 1.0, 0.0, 1.0, -640.0f/480.0f)
.SetHandler(&handler);
pangolin::CreatePanel("ui").SetBounds(0.0, 1.0, 0.0, pangolin::Attach::Pix(180));//创建
pangolin::Var<bool> a_button("ui.A_Button", false, false);//设置一个按钮,默认值为false,最后一个false表示按钮形式
pangolin::Var<double> a_double("ui.A_Double", 3, 0, 5);//设置一个double的、可拖动变换值的玩意(不知道咋形容)!
pangolin::Var<int> a_int("ui.A_Int", 2, 0, 5);//设置一个int的、可拖动变换值的玩意
pangolin::Var<std::function<void(void)>> reset("ui.Reset", function);//设置一个按钮,用于调用function函数
while( !pangolin::ShouldQuit() )
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
d_cam.Activate(s_cam);
if (pangolin::Pushed(a_button)){//如果a_button按钮被点,就运行if里面的语句
std::cout << "You tough a_buttom" << std::endl;
a_double = 0;
a_int = 0;
}
glColor3f(1.0, 1.0, 1.0);
pangolin::glDrawColouredCube();
pangolin::FinishFrame();
}
return 0;
}
运行上面的代码,你会获得如下图所示的界面,试着点击左边的按钮,看看你的终端出现了什么。
下面的代码,前面几乎都出现过了,就不在细致解释了。
#include
#include
void setImageData(unsigned char * imageArray, int size){
for(int i = 0 ; i < size;i++) {
imageArray[i] = (unsigned char)(rand()/(RAND_MAX/255.0));
}
}
int main(/*int argc, char* argv[]*/)
{
pangolin::CreateWindowAndBind("Main",640,480);
glEnable(GL_DEPTH_TEST);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
pangolin::OpenGlMatrix proj = pangolin::ProjectionMatrix(640,480,420,420,320,240,0.1,1000);
pangolin::OpenGlRenderState s_cam(proj, pangolin::ModelViewLookAt(1,0.5,-2,0,0,0, pangolin::AxisY) );
pangolin::OpenGlRenderState s_cam2(proj, pangolin::ModelViewLookAt(0,0,-2,0,0,0, pangolin::AxisY) );
pangolin::View& d_cam1 = pangolin::Display("cam1")
.SetAspect(640.0f/480.0f)
.SetHandler(new pangolin::Handler3D(s_cam));
pangolin::View& d_cam2 = pangolin::Display("cam2")
.SetAspect(640.0f/480.0f)
.SetHandler(new pangolin::Handler3D(s_cam2));
pangolin::View& d_cam3 = pangolin::Display("cam3")
.SetAspect(640.0f/480.0f)
.SetHandler(new pangolin::Handler3D(s_cam));
pangolin::View& d_cam4 = pangolin::Display("cam4")
.SetAspect(640.0f/480.0f)
.SetHandler(new pangolin::Handler3D(s_cam2));
pangolin::View& d_img1 = pangolin::Display("img1")
.SetAspect(640.0f/480.0f);
pangolin::View& d_img2 = pangolin::Display("img2")
.SetAspect(640.0f/480.0f);
pangolin::Display("multi")
.SetBounds(0.0, 1.0, 0.0, 1.0)
.SetLayout(pangolin::LayoutEqual)
.AddDisplay(d_cam1)
.AddDisplay(d_img1)
.AddDisplay(d_cam2)
.AddDisplay(d_img2)
.AddDisplay(d_cam3)
.AddDisplay(d_cam4);
const int width = 64;
const int height = 48;
unsigned char* imageArray = new unsigned char[3*width*height];
pangolin::GlTexture imageTexture(width,height,GL_RGB,false,0,GL_RGB,GL_UNSIGNED_BYTE);
while( !pangolin::ShouldQuit() )
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
setImageData(imageArray,3*width*height);
imageTexture.Upload(imageArray,GL_RGB,GL_UNSIGNED_BYTE);
glColor3f(1.0,1.0,1.0);
d_cam1.Activate(s_cam);
pangolin::glDrawColouredCube();
d_cam2.Activate(s_cam2);
pangolin::glDrawColouredCube();
d_cam3.Activate(s_cam);
pangolin::glDrawColouredCube();
d_cam4.Activate(s_cam2);
pangolin::glDrawColouredCube();
d_img1.Activate();
glColor4f(1.0f,1.0f,1.0f,1.0f);
imageTexture.RenderToViewport();
d_img2.Activate();
glColor4f(1.0f,1.0f,1.0f,1.0f);
imageTexture.RenderToViewport();
pangolin::FinishFrame();
}
delete[] imageArray;
return 0;
}
以下代码完成的任务是,新开一个线程,然后显示一个立方体。
#include
#include
static const std::string window_name = "HelloPangolinThreads";
void setup() {
pangolin::CreateWindowAndBind(window_name, 640, 480);
glEnable(GL_DEPTH_TEST);
pangolin::GetBoundWindow()->RemoveCurrent();
//setup()函数是在主线程创建的,因此在主线程调用完之后,需要使用GetBoundWindow()->RemoveCurrent()对其解绑
}
void run() {
pangolin::BindToContext(window_name);
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, 0.0, 1.0, -640.0f/480.0f)
.SetHandler(&handler);
while( !pangolin::ShouldQuit() )
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
d_cam.Activate(s_cam);
pangolin::glDrawColouredCube();
pangolin::FinishFrame();
}
pangolin::GetBoundWindow()->RemoveCurrent();
}
int main()
{
setup();
std::thread render_loop;//新建一个线程
render_loop = std::thread(run);//然后将函数run加载到线程中
render_loop.join();
return 0;
}
Pangolin确实是一个很简单好用的3D显示库,加上GUI的一些交互操作简直飞起。如果你不会使用ROS或者闲ROS运行麻烦,使用Pangolin显示一些3D效果十分方便。
我相信看完我上面的总结的一些代码和用法,你基本已经掌握了Pangolin编程的思路了。如果你还有别的要求,或者本文的内容不能满足你的要求,你可以参考Pangolin官方例程进行学习。
Pangolin官方example:
https://github.com/stevenlovegrove/Pangolin/tree/master/examples