老规矩,我们先上效果。
这个GIF是不是显示有点问题啊。。。不过不管了。关键的地方我用红色标注了。
上代码。
首先是一个点云发动机,我们在这里面进行索引和缓存的定义,它继承自QOpenGLFunctions
class qScarlet_GLCloudEngin_ES2 : protected QOpenGLFunctions
{
public:
qScarlet_GLCloudEngin_ES2();
virtual ~qScarlet_GLCloudEngin_ES2();
void drawCubeGeometry(QOpenGLShaderProgram *program);
private:
void initCubeGeometry();
QOpenGLBuffer arrayBuf;
QOpenGLBuffer indexBuf;
};
它的CPP对应如下:
qScarlet_GLCloudEngin_ES2::qScarlet_GLCloudEngin_ES2()
: indexBuf(QOpenGLBuffer::IndexBuffer)
{
initializeOpenGLFunctions();
// Generate 2 VBOs
arrayBuf.create();
indexBuf.create();
// Initializes cube geometry and transfers it to VBOs
initCubeGeometry();
}
qScarlet_GLCloudEngin_ES2::~qScarlet_GLCloudEngin_ES2()
{
arrayBuf.destroy();
indexBuf.destroy();
}
void qScarlet_GLCloudEngin_ES2::initCubeGeometry()
{
// For cube we would need only 8 vertices but we have to
// duplicate vertex for each face because texture coordinate
// is different.
//这里是八个顶点的存储位置
CubeVertexData vertices[] = {
// Vertex data for face 0
{QVector3D(1.0f, 1.0f, 1.0f), QVector3D(0.0f, 0.0f,1.0f)}, // v0
{QVector3D(-1.0f, 1.0f, 1.0f), QVector3D(1.0f, 0.0f,1.0f)}, // v1
{QVector3D(-1.0f, -1.0f, 1.0f), QVector3D(0.0f, 1.0f,1.0f)}, // v2
{QVector3D( 1.0f, -1.0f, 1.0f), QVector3D(1.0f, 1.0f,1.0f)}, // v3
// Vertex data for face 1
{QVector3D( 1.0f, 1.0f, -1.0f), QVector3D( 1.0f, 0.0f,0.0f)}, // v4
{QVector3D( -1.0f, 1.0f, -1.0f), QVector3D(0.0f,1.0f, 0.0f)}, // v5
{QVector3D( -1.0f, -1.0f, -1.0f), QVector3D(1.0f, 1.0f,0.0f)}, // v6
{QVector3D( 1.0f, -1.0f, -1.0f), QVector3D(0.33f, 0.30f,0.60f)}, // v7
};
//它们的索引缓存。
GLushort indices[] = {
0,1,2,0,2,3,
4,0,3,4,3,7,
5,4,7,5,7,6,
1,5,6,1,6,2,
5,1,0,5,0,4,
6,7,3,6,3,2
};
// Transfer vertex data to VBO 0
arrayBuf.bind();
arrayBuf.allocate(vertices, 8 * sizeof(CubeVertexData));//这里放置顶点缓存的个数,立方体一共有八个顶点,而不是六个。。。
// Transfer index data to VBO 1
indexBuf.bind();
indexBuf.allocate(indices, 36 * sizeof(GLushort));//面片有3*2*6一共36个顶点。
}
void qScarlet_GLCloudEngin_ES2::drawCubeGeometry(QOpenGLShaderProgram *program)
{
// Tell OpenGL which VBOs to use
arrayBuf.bind();
indexBuf.bind();
// Offset for position
quintptr offset = 0;
// Tell OpenGL programmable pipeline how to locate vertex position data
int vertexLocation = program->attributeLocation("a_position");//这个名字一定要和glsl对应好,下面的colAttr也是。,他们是作为输入参数在glsl中被存储的。
program->enableAttributeArray(vertexLocation);
program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(CubeVertexData));
// Offset for texture coordinate
offset += sizeof(QVector3D);
// Tell OpenGL programmable pipeline how to locate vertex texture coordinate data
int texcoordLocation = program->attributeLocation("colAttr");
program->enableAttributeArray(texcoordLocation);
program->setAttributeBuffer(texcoordLocation, GL_FLOAT, offset,3, sizeof(CubeVertexData));
// Draw cube geometry using indices from VBO 1
//GL_TRIANGLE_FAN GL_TRIANGLE_STRIP GL_QUADS GL_TRIANGLE_STRIP GL_POINTS
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, 0);
}
然后是一个用于显示和控制旋转缩放的Wildget,这个借鉴了官方例子的相机,因为我觉得它的控制做得满足我的要求。
class qScarlet_GLWidget_ES2 : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit qScarlet_GLWidget_ES2(QWidget *parent = 0);
~qScarlet_GLWidget_ES2();
protected:
void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
void mouseReleaseEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
void mouseMoveEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
void timerEvent(QTimerEvent *e) Q_DECL_OVERRIDE;
void wheelEvent(QWheelEvent *e);
void initializeGL() Q_DECL_OVERRIDE;
void resizeGL(int w, int h) Q_DECL_OVERRIDE;
void paintGL() Q_DECL_OVERRIDE;
void initShaders();
void initTextures();
private:
QBasicTimer timer;
QOpenGLShaderProgram program;
qScarlet_GLCloudEngin_ES2 *geometries;
QOpenGLTexture *texture;
//we don't use texture but we leave it along.
QMatrix4x4 projection;
QVector2D mousePressPosition;
QVector3D rotationAxis;
qreal angularSpeed;
QQuaternion rotation;
GLfloat zoom;
};
它的cpp文件对应如下:
qScarlet_GLWidget_ES2::qScarlet_GLWidget_ES2(QWidget *parent) :
QOpenGLWidget(parent),
geometries(0),
texture(0),
angularSpeed(0)
{
//resourcescarlet2.qrc
//也不知道这句话是不是有用
//Q_INIT_RESOURCE(resourcescarlet2);
}
qScarlet_GLWidget_ES2::~qScarlet_GLWidget_ES2()
{
// Make sure the context is current when deleting the texture
// and the buffers.
makeCurrent();
delete texture;
delete geometries;
doneCurrent();
}
void qScarlet_GLWidget_ES2::mousePressEvent(QMouseEvent *e)
{
// Save mouse press position
mousePressPosition = QVector2D(e->localPos());
}
void qScarlet_GLWidget_ES2::mouseMoveEvent(QMouseEvent *e)
{
// Mouse release position - mouse press position
QVector2D diff = QVector2D(e->localPos()) - mousePressPosition;
// Rotation axis is perpendicular to the mouse position difference
// vector
QVector3D n = QVector3D(diff.y(), diff.x(), 0.0).normalized();
// Accelerate angular speed relative to the length of the mouse sweep
qreal acc = diff.length() / 100.0;
// Calculate new rotation axis as weighted sum
rotationAxis = (rotationAxis * angularSpeed + n * acc).normalized();
angularSpeed += acc;
}
void qScarlet_GLWidget_ES2::mouseReleaseEvent(QMouseEvent *e)
{
angularSpeed=0;
}
void qScarlet_GLWidget_ES2::timerEvent(QTimerEvent *)
{
// Decrease angular speed (friction)
angularSpeed *= 0.79;//使用乘法来降低速度666
// Stop rotation when speed goes below threshold
if (angularSpeed < 0.01)
{
angularSpeed = 0.0;
}
else
{
// Update rotation
rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed) * rotation;
}
update();
}
void qScarlet_GLWidget_ES2::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0, 0, 0, 1);
initShaders();
initTextures();
zoom =-5;
// Enable depth buffer
glEnable(GL_DEPTH_TEST);
// Enable back face culling
glEnable(GL_CULL_FACE);
geometries = new qScarlet_GLCloudEngin_ES2();
// Use QBasicTimer because its faster than QTimer
timer.start(12, this);
}
void qScarlet_GLWidget_ES2::initShaders()//加载shader,这里必须注意,新加入glsl文件后必须把QTcreator生成的Release文件夹删掉然后重新编译一下才会识别,作者为这个问题活活憋了一个晚上才知道,原来这是QT的bug。马丹。。。。
{
// Compile vertex shader
if (!program.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/glsl/glsl/qScarlet_vshader.glsl"))
close();
// Compile fragment shader
if (!program.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/glsl/glsl/qScarlet_fshader.glsl"))
close();
// Link shader pipeline
if (!program.link())
close();
// Bind shader pipeline for use
if (!program.bind())
close();
}
void qScarlet_GLWidget_ES2::initTextures()
{
// Load cube.png image
texture = new QOpenGLTexture(QImage(":/logo/alllogo/OfficialOpenglCube.png").mirrored());
// Set nearest filtering mode for texture minification
texture->setMinificationFilter(QOpenGLTexture::Nearest);
// Set bilinear filtering mode for texture magnification
texture->setMagnificationFilter(QOpenGLTexture::Linear);
// Wrap texture coordinates by repeating
// f.ex. texture coordinate (1.1, 1.2) is same as (0.1, 0.2)
texture->setWrapMode(QOpenGLTexture::Repeat);
}
void qScarlet_GLWidget_ES2::resizeGL(int w, int h)
{
// Calculate aspect ratio
qreal aspect = qreal(w) / qreal(h ? h : 1);
// Set near plane to 3.0, far plane to 7.0, field of view 45 degrees
const qreal zNear = 0.001, zFar = 1000.0, fov = 45.0;
// Reset projection
projection.setToIdentity();
// Set perspective projection
projection.perspective(fov, aspect, zNear, zFar);
}
void qScarlet_GLWidget_ES2::paintGL()
{
// Clear color and depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
texture->bind();
// Calculate model view transformation
QMatrix4x4 matrix;
matrix.translate(0.0, 0.0, -5.0);//zoom
matrix.translate(0.0, 0.0, zoom);//
matrix.rotate(rotation);
// Set modelview-projection matrix
program.setUniformValue("mvp_matrix", projection * matrix);
// Use texture unit 0 which contains cube.png
//program.setUniformValue("texture", 0);
// Draw cube geometry
geometries->drawCubeGeometry(&program);
}
void qScarlet_GLWidget_ES2::wheelEvent(QWheelEvent *e)
{
// 当鼠标滑轮在滚动时用于返回滑动的距离,该值等于鼠标旋转角度的8倍。正数值表示滑轮相对于用户在向前滑动,
// 相反,负数值表示滑轮相对于用户是向后滑动的。
//int mydelta2 = e->delta();
double ZoomSlowDown =2.5;
zoom += (double)(e->delta())/8.0/15.0/ZoomSlowDown;
update();
}
最后把两个最关键的Shader放上来:
顶点着色器:
#ifdef GL_ES
precision mediump int;
precision mediump float;
#endif
uniform mat4 mvp_matrix;
attribute vec4 a_position; //解释一下,attribute代表这是一个可由外部传入的变量
attribute lowp vec4 colAttr;
varying lowp vec4 col;//varying代表opengl的传出变量,这个变量会由顶点着色器传出,然后传入像素着色器。
void main()
{
gl_Position = mvp_matrix * a_position;//
col = colAttr;
}
然后下面是像素着色器。简单一句话就没了,但是记住这个传出的col的名字一定要和上面相同才行。
#ifdef GL_ES
precision mediump int;
precision mediump float;
#endif
varying lowp vec4 col;
void main()
{
gl_FragColor = col;
}
//至此所有代码完毕。