workPlane.h

#pragma once
#include <qopenglfunctions_4_5_core.h>
#include <qvector3d.h>

const float GRID_COLOR_R = 0.3f;
const float GRID_COLOR_G = 0.3f;
const float GRID_COLOR_B = 0.3f;

struct PlanePara
{
QVector3D origin; // 平面原点
QVector3D normal; // 平面法向量
QVector3D localX; // 平面本地X轴
QVector3D localY; // 平面本地Y轴
float halfLength; // 平面半长(原参数保留)
unsigned int gridCntPerEdge; // 每边网格线数量(原参数保留)
float offset; // 平面偏移量(原参数保留)
};

class WorkPlane : public QOpenGLFunctions_4_5_Core
{
public:
WorkPlane() {}
~WorkPlane() {}

void initPlane(const PlanePara& para)
{
initializeOpenGLFunctions();
m_planePara = para;

initLocalXY(m_planePara.normal, m_planePara.localX, m_planePara.localY);
generateGridLines(m_planePara);
}

void drawPlane()
{
glBindVertexArray(m_vao);
glDrawArrays(GL_LINES, 0, m_gridVertexCount);
glBindVertexArray(0);
}

private:
void initLocalXY(const QVector3D& normal, QVector3D& localX, QVector3D& localY)
{
QVector3D yAxis(0, 1, 0);
QVector3D xAxis(1, 0, 0);

QVector3D ref = (QVector3D::crossProduct(normal, xAxis).length() < 1e-6) ? yAxis : xAxis;
localX = QVector3D::crossProduct(normal, ref).normalized();
localY = QVector3D::crossProduct(normal, localX).normalized();
}

void generateGridLines(const PlanePara& para)
{
std::vector<float> gridVertices;
float step = (2.0f * para.halfLength) / para.gridCntPerEdge;

QVector3D start, end;
float offset;

//平行于X轴的线
for (int i = 0; i <= para.gridCntPerEdge; i++)
{
offset = -para.halfLength + i * step;
start = para.origin - para.halfLength * para.localX + offset * para.localY;
end = para.origin + para.halfLength * para.localX + offset * para.localY;

start += para.offset * para.normal;
end += para.offset * para.normal;

gridVertices.insert(gridVertices.end(),
{
start.x(), start.y(), start.z(), GRID_COLOR_R, GRID_COLOR_G, GRID_COLOR_B,
end.x(), end.y(), end.z(), GRID_COLOR_R, GRID_COLOR_G, GRID_COLOR_B,
});
}

//平行于y轴的线
for (int i = 0; i <= para.gridCntPerEdge; i++)
{
offset = -para.halfLength + i * step;
start = para.origin - para.halfLength * para.localY + offset * para.localX;
end = para.origin + para.halfLength * para.localY + offset * para.localX;

start += para.offset * para.normal;
end += para.offset * para.normal;

gridVertices.insert(gridVertices.end(),
{
start.x(), start.y(), start.z(), GRID_COLOR_R, GRID_COLOR_G, GRID_COLOR_B,
end.x(), end.y(), end.z(), GRID_COLOR_R, GRID_COLOR_G, GRID_COLOR_B,
});
}

m_gridVertexCount = gridVertices.size() / 6;

glGenVertexArrays(1, &m_vao);
glGenBuffers(1, &m_vbo);

glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, gridVertices.size() * sizeof(float), gridVertices.data(), GL_STATIC_DRAW);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

glBindVertexArray(0);
}

private:
unsigned int m_vao, m_vbo;
PlanePara m_planePara;
int m_gridVertexCount; // 网格顶点总数
};

glview.h

#include <qopenglwidget.h>
#include <qopenglfunctions_4_5_core.h>
#include <qopenglshaderprogram.h>
#include <qopenglvertexarrayobject.h>
#include <qopenglbuffer.h>
#include <QMouseEvent>
#include "camera.h"
#include "workPlane.h"

class GLWidget : public QOpenGLWidget ,protected QOpenGLFunctions_4_5_Core
{
Q_OBJECT

public:
GLWidget(QWidget* parent = nullptr)
{
setMouseTracking(true);
}
~GLWidget() {}

void initializeGL() override
{
initializeOpenGLFunctions();

initShader(m_shader, QString("./shader/LoadingShader.vert"), QString("./shader/LoadingShader.frag"));
initTriangle();
initPlane();
}

void resizeGL(int width,int height) override
{
glViewport(0, 0, width, height);
m_camera.Init(width, height);

m_projectionMat.setToIdentity();
m_projectionMat.perspective(45.0f, width / height, 0.1f, 100.0f);
}

void paintGL() override
{
glEnable(GL_DEPTH_TEST);

m_shader.bind();

m_modelMat.setToIdentity();

m_viewMat = m_camera.getViewMatrix();

//m_viewMat.lookAt(QVector3D(0.0f, 0.0f, 5.0f), QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, 1.0f, 0.0f));
m_shader.setUniformValue("model", m_modelMat);
m_shader.setUniformValue("view", m_viewMat);
m_shader.setUniformValue("projection", m_projectionMat);
drawTriangle();

m_workPlane.drawPlane();
m_shader.release();
}

void initShader(QOpenGLShaderProgram& shader,const QString& vertShaderPath,const QString& fragShaderPath)
{
if (!shader.addShaderFromSourceFile(QOpenGLShader::Vertex, vertShaderPath) ||
!shader.addShaderFromSourceFile(QOpenGLShader::Fragment, fragShaderPath) ||
!shader.link())
{
qDebug() << shader.log();
}
}

void initPlane()
{
PlanePara para;
para.origin = QVector3D(0.0f, -1.0f, 0.0f);
para.normal = QVector3D(0.0f, 1.0f, 0.0f); // 向上的法向量(Y轴)
para.halfLength = 25.0f;
para.gridCntPerEdge = 32;
para.offset = 0.0f; // 平面基础偏移(Y=0.1f)

m_workPlane.initPlane(para);
}

void initTriangle()
{
float vertices[] =
{
//position color
-0.5f,0.0f,0.5f, 0.5f,0.5f,0.0f,
0.0f,0.5f,0.5f, 0.5f,0.5f,0.5f,
0.5f,0.0f,0.5f, 0.5f,0.0f,0.5f,
};

glGenVertexArrays(1, &m_vao);
glGenBuffers(1, &m_vbo);

glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);

glBindVertexArray(0);
}

void drawTriangle()
{
glBindVertexArray(m_vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
}

protected:
bool event(QEvent* e) override
{
m_camera.handle(e);
update();

return QWidget::event(e);
}

private:
QOpenGLShaderProgram m_shader;
unsigned int m_vao, m_vbo;

QMatrix4x4 m_modelMat;
QMatrix4x4 m_viewMat;
QMatrix4x4 m_projectionMat;
Camera m_camera = Camera(QVector3D(0.0f, 0.0f, 5.0f));
WorkPlane m_workPlane;
};

图-1

[源码下载]