目录
一、实现功能
1、网格显示
2、坐标轴显示
3、鼠标操作旋转、平移、缩放
4、点云显示
二、实现效果
三、实现方法
基于QGLWidget实现
光源设置
/**
* https://learn.microsoft.com/zh-cn/windows/win32/opengl/gllightfv
* @brief glLightfv 设置单个光源参数
* @param GLenum light 光线的标识符(GL_LIGHT0 ~ GL_LIGHT7)
GLenum pname 参数名称
GL_AMBIENT 环境光
GL_DIFFUSE 散射光
GL_SPECULAR 镜面反射光
GL_POSITION 光源位置齐次坐标(x,y,z,w)
GL_SPOT_DIRECTION 点光源聚光方向矢量(x,y,z)
GL_SPOT_EXPONENT 点光源聚光指数
GL_SPOT_CUTOFF 点光源聚光截至角
GL_CONSTANT_ATTENUATION 常数衰减因子K0,衰减系数=1/(K0+K1*D+K2*D2)
GL_LINEAR_ATTENUATION 线性衰减因子K1
GL_QUADRATIC_ATTENUATION 二次衰减因子K2
const GLfloat *params
*/
void APIENTRY glLightfv (GLenum light, GLenum pname, const GLfloat *params);
//设置好以上光源的属性之后,要使用glEnable(GL_LIGHTING)和glEnable(GL_LIGHTX)来启用光照和第X号光源。
示例:
GLfloat ambientLight[] = {0.7f, 0.7f, 0.7f, 1.0f}; //光源环境光强度数组
GLfloat diffuseLight[] = {0.7f, 0.8f, 0.8f, 1.0f}; //光源散射光强度数组
GLfloat specularLight[] = {0.4f, 0.4f, 0.4f, 1.0f}; //光源镜面反射光强度数组
GLfloat positionLight[] = {20.0f, 20.0f, 20.0f, 0.0f}; //光源位置数组
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight); //设置0号光源的环境光属性
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight); //设置0号光源的散射光属性
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight); //设置0号光源的镜面反射光属性
glLightfv(GL_LIGHT0, GL_POSITION, positionLight); //设置0号光源的位置属性
材料设置
/**
* https://learn.microsoft.com/zh-cn/windows/win32/opengl/glmaterialfv
* @brief glMaterialfv 设置材料属性
* @param GLenum face, 指出材质属性将应用于物体的哪面
GL_FRONT 前面
GL_BACK 背面
GL_FRONT_AND_BACK 前面和背面
GLenum pname, 设置的哪种材质属性
GL_AMBIENT 材料的环境光颜色
GL_DIFFUSE 材料的漫射光颜色
GL_SPECULAR 材料的镜面反射光颜色
GL_EMISSION 材料的辐射光颜色
GL_SHININESS 镜面指数(光亮度)
GL_AMBIENT_AND_DIFFUSE 材质的环境颜色和散射颜色
GL_COLOR_INDEXES 材料的环境光、漫射光和镜面光颜色
const GLfloat *params
*/
void APIENTRY glMaterialfv (GLenum face, GLenum pname, const GLfloat *params);
示例:
GLfloat color[] = {r / 255.0f, g / 255.0f, b / 255.0f};
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
平移旋转缩放
/**
* https://learn.microsoft.com/zh-cn/windows/win32/opengl/gltranslated
* @brief glTranslated 将当前矩阵乘以平移矩阵,与glTranslatef函数功能一样
* @param x,y,z分别指定沿x,y,z轴方向的平移分量
*/
void APIENTRY glTranslated (GLdouble x, GLdouble y, GLdouble z);
/**
* https://learn.microsoft.com/zh-cn/windows/win32/opengl/glrotated
* @brief glRotated 当前矩阵乘以旋转矩阵,绕着向量(cx, cy, cz)逆时针旋转,与glRotatef函数功能一样
* @param GLdouble angle, 旋转角度(以度为单位)
GLdouble x, 向量的 x 坐标
GLdouble y, 向量的 y 坐标
GLdouble z 向量的 z 坐标
*/
void APIENTRY glRotated (GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
示例:
glTranslated(0, 0, m_zoom); /* 不清楚为什么可以达到缩放的效果 */
glTranslated(m_xTrans, m_yTrans, 0);
glRotated(m_xRotate / 16.0, 1.0, 0.0, 0.0); /* 绕x轴旋转 */
glRotated(m_yRotate / 16.0, 0.0, 1.0, 0.0); /* 绕y轴旋转 */
绘制
// 使用glPushMatrix和glPopMatrix保存和还原未启动的坐标系
void APIENTRY glPushMatrix (void);
void APIENTRY glPopMatrix (void);
/**
* https://learn.microsoft.com/zh-cn/windows/win32/opengl/glbegin
* @brief glBegin 与glEnd组合使用
* @param GLenum mode 创建元素的类型
GL_POINTS 将每个顶点视为单个点
GL_LINES 将顶点的每个配对视为独立的线段
GL_LINE_LOOP 绘制从第一个顶点到最后一个顶点的连接线段组,然后返回到第一个
GL_LINE_STRIP 绘制从第一个顶点到最后一个顶点的连接线段组
GL_TRIANGLES 将每个三元顶点视为独立的三角形
GL_TRIANGLE_STRIP 绘制一组连接的三角形
GL_TRIANGLE_FAN 绘制一组连接的三角形
GL_QUADS 将四个顶点的每个组视为独立的四边形
GL_QUAD_STRIP 绘制一组连接的四边形
GL_POLYGON 绘制单个凸面多边形
*/
WINGDIAPI void APIENTRY glBegin (GLenum mode);
WINGDIAPI void APIENTRY glEnd (void);
示例:画点云
glPushMatrix();
glBegin(GL_POINTS);
for(auto vec3d : m_pointVector)
{
glVertex3d(vec3d.x(), vec3d.y(), vec3d.z());
}
glEnd();
glPopMatrix();
完整代码
BaseGLWidget.h
#ifndef RRGLWIDGET_H
#define RRGLWIDGET_H
#include <QGLWidget>
#include <QWidget>
#include "opengllib_global.h"
class OPENGLLIBSHARED_EXPORT BaseGLWidget : public QGLWidget
{
Q_OBJECT
public:
BaseGLWidget(QWidget *parent = 0);
~BaseGLWidget();
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int w, int h) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void setGLMaterialColor(int r, int g, int b);
void setXRotation(int angle);
void setYRotation(int angle);
void setXYTranslate(int dx, int dy);
void setZoom(int zoom);
virtual void drawGL() = 0;
virtual void drawMeshline(float size = 50, int count = 15);
virtual void drawCoordAxis(float length = 900);
protected:
GLdouble m_xRotate; /**< x旋转角度 */
GLdouble m_yRotate; /**< y旋转角度 */
GLdouble m_zoom; /**< 缩放 */
GLdouble m_xTrans; /**< 界面显示X位置 */
GLdouble m_yTrans; /**< 界面显示Y位置 */
QPoint lastPos;
};
#endif // RRGLWIDGET_H
BaseGLWidget.cpp
#include "BaseGLWidget.h"
#include <GL/glu.h>
#include <QDebug>
#include <QMouseEvent>
BaseGLWidget::BaseGLWidget(QWidget *parent)
: QGLWidget(parent)
{
m_xRotate = -2276;
m_yRotate = 1736;
m_zoom = -20;
m_xTrans = 0;
m_yTrans = 0;
}
BaseGLWidget::~BaseGLWidget() {}
void BaseGLWidget::initializeGL()
{
//用来初始化这个OpenGL窗口部件的,可以在里面设定一些有关选项
GLfloat ambientLight[] = {0.7f, 0.7f, 0.7f, 1.0f}; //光源环境光强度数组
GLfloat diffuseLight[] = {0.7f, 0.8f, 0.8f, 1.0f}; //光源散射光强度数组
GLfloat specularLight[] = {0.4f, 0.4f, 0.4f, 1.0f}; //光源镜面反射光强度数组
GLfloat positionLight[] = {20.0f, 20.0f, 20.0f, 0.0f}; //光源位置数组
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight); //设置0号光源的环境光属性
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight); //设置0号光源的散射光属性
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight); //设置0号光源的镜面反射光属性
glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 1.0);
glLightfv(GL_LIGHT0, GL_POSITION, positionLight); //设置0号光源的位置属性
glEnable(GL_LIGHTING); //启用光照
glEnable(GL_LIGHT0); //打开光源
glEnable(GL_DEPTH_TEST); //隐藏表面消除
glEnable(GL_NORMALIZE);
glClearColor(0.0, 0.0, 0.0, 1.0);
}
void BaseGLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* 清除屏幕和深度缓存 */
glPushMatrix();
glTranslated(0, 0, m_zoom);
glTranslated(m_xTrans, m_yTrans, 0);
glRotated(m_xRotate / 16.0, 1.0, 0.0, 0.0); /* 绕x轴旋转 */
glRotated(m_yRotate / 16.0, 0.0, 1.0, 0.0); /* 绕y轴旋转 */
glRotated(+90.0, 1.0, 0.0, 0.0);
drawGL();
glPopMatrix();
}
void BaseGLWidget::resizeGL(int w, int h)
{
if (w < 0 || h < 0)
{
return;
}
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(35.0, w / float(h), 1.0, 20000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslated(0.0, 0.0, -40.0);
}
void BaseGLWidget::mousePressEvent(QMouseEvent *event)
{
lastPos = event->pos();
}
void BaseGLWidget::mouseMoveEvent(QMouseEvent *event)
{
int dx = event->x() - lastPos.x();
int dy = event->y() - lastPos.y();
if (event->buttons() & Qt::LeftButton)
{
setXRotation(m_xRotate + 4 * dy);
setYRotation(m_yRotate - 4 * dx);
}
else if (event->buttons() & Qt::MidButton)
{
setXYTranslate(0.5 * dx, 0.5 * dy);
}
lastPos = event->pos();
}
void BaseGLWidget::wheelEvent(QWheelEvent *event)
{
auto scroll_offest = event->angleDelta().y() / 120;
setZoom(m_zoom + (float)scroll_offest);
}
void BaseGLWidget::setGLMaterialColor(int r, int g, int b)
{
/*
* 参数1的取值可以是GL_FRONT、GL_BACK或GL_FRONT_AND_BACK,指出材质属性将应用于物体的哪面
* 参数2指出要设置的哪种材质属性,GL_AMBIENT_AND_DIFFUSE材质的环境颜色和散射颜色
*/
GLfloat color[] = {r / 255.0f, g / 255.0f, b / 255.0f};
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
}
void BaseGLWidget::setXRotation(int angle)
{
int tangle = angle;
if (tangle != m_xRotate)
{
m_xRotate = tangle;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
updateGL();
}
}
void BaseGLWidget::setYRotation(int angle)
{
int tangle = angle;
if (tangle != m_yRotate)
{
m_yRotate = tangle;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
}
void BaseGLWidget::setXYTranslate(int dx, int dy)
{
m_xTrans += dx;
m_yTrans -= dy;
updateGL();
}
void BaseGLWidget::setZoom(int zoom)
{
m_zoom = zoom;
if(m_zoom < -300)
{
m_zoom = -300;
}
if(m_zoom > 35)
{
m_zoom = 35;
}
updateGL();
}
void BaseGLWidget::drawMeshline(float size, int count)
{
glPushMatrix();
GLfloat color[] = {8.0f / 255, 108.0f / 255, 162.0f / 255};
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
float start = count * (size / 2);
float posX = start, posZ = start;
for (int i = 0; i <= count; ++i)
{
glBegin(GL_LINES);
glVertex3f(posX, start, 0);
glVertex3f(posX, -start, 0);
glVertex3f(start, posZ, 0);
glVertex3f(-start, posZ, 0);
glEnd();
posX = posX - size;
posZ = posZ - size;
}
glPopMatrix();
}
void BaseGLWidget::drawCoordAxis(float length)
{
glPushMatrix();
glLineWidth(4.0f);
glBegin(GL_LINES);
setGLMaterialColor(255, 0, 0); // X axis is red.
glVertex3f(0, 0, 0);
glVertex3f(length, 0, 0);
setGLMaterialColor(0, 255, 0); // Y axis is green.
glVertex3f(0, 0, 0);
glVertex3f(0, length, 0);
setGLMaterialColor(0, 0, 255); // Z axis is blue.
glVertex3f(0, 0, 0);
glVertex3f(0, 0, length);
glEnd();
// Draw labels
glColor3f(0.0, 1.0, 1.0);
renderText(0, 0, length, "+Z", QFont("helvetica", 12, QFont::Bold, true));
glColor3f(1.0, 0.0, 0.0);
renderText(length, 0, 0, "+X", QFont("helvetica", 12, QFont::Bold, true));
glColor3f(0.0, 1.0, 0.0);
renderText(0, length, 0, "+Y", QFont("helvetica", 12, QFont::Bold, true));
glLineWidth(1.0f);
glPopMatrix();
}
PointCloudGLWidget.h
#ifndef POINTCLOUD3DWIDGET_H
#define POINTCLOUD3DWIDGET_H
#include "BaseGLWidget.h"
#include <QVector3D>
class OPENGLLIBSHARED_EXPORT PointCloudGLWidget: public BaseGLWidget
{
Q_OBJECT
public:
explicit PointCloudGLWidget(QWidget *parent = 0);
~PointCloudGLWidget();
void updatePoints(const QVector<QVector3D> &points);
void loadCsvFile(const QString &path);
protected:
virtual void drawGL() override;
private:
void drawPointdata();
private:
QVector<QVector3D> m_pointVector;
};
#endif // POINTCLOUD3DWIDGET_H
PointCloudGLWidget.cpp
#include "PointCloudGLWidget.h"
#include <QFile>
#include <QTextStream>
#include <QDebug>
PointCloudGLWidget::PointCloudGLWidget(QWidget *parent)
: BaseGLWidget(parent)
{
}
PointCloudGLWidget::~PointCloudGLWidget()
{
}
void PointCloudGLWidget::updatePoints(const QVector<QVector3D> &points)
{
m_pointVector.clear();
m_pointVector = points;
}
void PointCloudGLWidget::loadCsvFile(const QString &path)
{
m_pointVector.clear();
QFile inFile(path);
if (inFile.open(QIODevice::ReadOnly))
{
QTextStream stream_text(&inFile);
while (!stream_text.atEnd())
{
QString line = stream_text.readLine();
QStringList strSplit = line.split(",");
double x = strSplit.value(0).toDouble();
double y = strSplit.value(1).toDouble();
double z = strSplit.value(2).toDouble();
m_pointVector.append(QVector3D(x, y, z));
}
inFile.close();
}
}
void PointCloudGLWidget::drawGL()
{
drawMeshline(2, 16);
drawCoordAxis(4.0);
drawPointdata();
}
void PointCloudGLWidget::drawPointdata()
{
glPushMatrix();
setGLMaterialColor(255 * 0.5, 255, 255);
glBegin(GL_POINTS);
for(auto vec3d : m_pointVector)
{
glVertex3d(vec3d.x(), vec3d.y(), vec3d.z());
}
glEnd();
glPopMatrix();
}