[OpenGL] 体积雾
开发环境:Qt, OpenGL
(注 : 水平有限,实现细节不一定完全正确,可能相比一般的体积雾实现过程会显得过于复杂,所以仅供参考;动图有闪烁和条纹现象是录屏软件的问题)
概念引入
体积雾,简单来说就是有体积的区域雾,在体积雾内的物体,会显得模糊;而在体积雾外的物体,则是物体的原颜色。
现在我们已经明确了,如果物体落在体积雾内,我们需要在雾的颜色和物体原颜色之间做一个混合。这个混合比例不是固定的,而是取决于体积雾的厚度。而这个厚度,和物体以及体积雾的深度是相关的,需要用物体的深度减去离眼睛更近的雾的深度以得到体积雾的厚度。厚度越小,物体也就会越清晰。
实现原理
本次项目实现体积雾的方式比较直观,但也比较耗费性能。首先需要渲染三张深度纹理,用的并非opengl自己的深度纹理,而是自定义的深度纹理,直接写入帧缓冲。
三张深度纹理分别是体积雾正面的深度(此时剔除背面渲染),体积雾背面的深度(此时剔除正面渲染),场景中物体的深度。准备好这三张纹理后,再把整个场景渲染到一张纹理上。
最后,先根据三张深度纹理来计算哪里没有雾,哪里有雾。如果有雾,再去计算雾的厚度,从而计算雾和场景的融合程度。以上计算完成后,根据有无雾以及雾的融合程度,把场景纹理和雾进行混合,最终结果输出到屏幕上。
关于纹理精度
我们创建的帧缓冲使用的格式是GL_RGB,数据格式是unsigned byte,一个字节也就是8位,相当于我们的纹理精度为0~255。我最初使用了255位的纹理做了一版效果,可以看得出有明显的条纹,体积雾边缘也有明显锯齿,这都是精度过低导致的。在最后改良版本中效果得到了改善,仍有条纹闪烁是动图压缩导致的,实际效果已经比较好了。
OpenGL的扩展支持了浮点纹理,这个没有具体实践过,不知道在帧缓冲是否支持。由于只需要存储深度这一个数据,目前采取的改善措施是把浮点数据存储到rgb三个通道中,相当于把纹理的精度从8位提升到了24位。rgb通道依次存储1,2^(-8),2^(-16)精度的数据。
图:8位精度的效果
具体实现
vShader0.glsl
存储体积雾正面/背面以及场景物体深度的顶点着色器。其中深度取的是裁剪空间的z值。
uniform mat4 ProjectMatrix;
uniform mat4 ModelViewMatrix;
attribute vec4 a_position;
varying float depth;
void main()
{
gl_Position = ModelViewMatrix * a_position;
gl_Position = ProjectMatrix * gl_Position;
depth = gl_Position.z;
}
fShader0.glsl
存储体积雾正面/背面以及场景物体深度的片元着色器。需要在此处对深度进行归一化,此处farPlane是在OpenGL中设置的远裁剪面值。归一化后需要编码数据,将其存储到rgb分量中。
uniform sampler2D texture;
varying float depth;
float farPlane = 40;
void main(void)
{
float fColor = depth / farPlane;
float fR, fB, fG;
fColor = modf(fColor * 256, fR);
fColor = modf(fColor * 256, fG);
fColor = modf(fColor * 256, fB);
gl_FragColor = vec4(fR/256,fG/256,fB/256,1);
}
vShader1.glsl
将场景物体渲染到纹理上的顶点着色器。
uniform mat4 ProjectMatrix;
uniform mat4 ModelViewMatrix;
attribute vec4 a_position;
attribute vec2 a_texcoord;
varying vec2 v_texcoord;
void main()
{
gl_Position = ProjectMatrix * (ModelViewMatrix * a_position);
v_texcoord = a_texcoord;
}
fShader1.glsl
将场景物体渲染到纹理上的片元着色器。
uniform sampler2D texture;
varying vec2 v_texcoord;
void main(void)
{
gl_FragColor = texture2D(texture,v_texcoord);
}
vShader2.glsl
进行场景和体积雾混合的顶点着色器,由于这里绘制的只是一个面片,所以不需要做坐标空间转化,直接传给片元着色器就可以了。
attribute vec4 a_position;
attribute vec2 a_texcoord;
varying vec2 v_texcoord;
void main()
{
gl_Position = a_position;
v_texcoord = a_texcoord;
}
fShader2.glsl
进行场景和体积雾混合的片元着色器。我们首先定义雾是灰色的,然后解码三个深度纹理的深度数值,记录为back,front,dist。
接下来,开始进行混合因子的计算,此时我们是在图像空间进行的计算。大概的逻辑是:
(1) 如果图像的当前位置没有物体,有雾,则体积雾的厚度为背面深度减去正面;
(2) 如果图像的当前位置没有物体也没有雾,体积雾的厚度为0;
(3) 如果图像的当前位置有物体,且物体在雾的前面,体积雾的厚度为0;
(4) 如果图像的当前位置有物体,且物体在雾中间,体积雾的厚度为物体深度减去体积雾正面深度;
(5) 如果图像的当前位置有物体,且物体在体积雾后面,体积雾的厚度为背面深度减去正面;
计算完毕之后,根据雾的浓度,对厚度做一定的映射得到混合因子,在这里将其放大为原来的20倍。
varying vec2 v_texcoord;
uniform sampler2D frontDepth;
uniform sampler2D backDepth;
uniform sampler2D sceneDepth;
uniform sampler2D finalColor;
void main()
{
vec4 fogColor = vec4(0.7,0.7,0.7,1);
gl_FragColor = texture2D(finalColor, v_texcoord);
float alpha = 0;
vec3 fFactor = vec3(1,256.0/65536.0,1.0/65536.0);
vec4 backColor = texture2D(backDepth, v_texcoord);
vec4 frontColor = texture2D(frontDepth, v_texcoord);
vec4 distColor = texture2D(sceneDepth, v_texcoord);
float back = dot(backColor.rgb, fFactor);
float front = dot(frontColor.rgb, fFactor);
float dist = dot(distColor.rgb, fFactor);
if(dist == 0.0)
{
if(back != 0)
{
alpha = back - front;
}
else
{
alpha = 0;
}
}
else
{
if(dist < front)
{
alpha = 0;
}
else if(dist > back)
{
alpha = back - front;
}
else if(dist > front)
{
alpha = dist - front;
}
}
alpha *= 20;
alpha = clamp(alpha,0,1);
gl_FragColor = gl_FragColor * (1 - alpha) + fogColor * alpha;
}
mainwidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include "geometryengine.h"
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QMatrix4x4>
#include <QVector2D>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QOpenGLShader>
#include <QBasicTimer>
class GeometryEngine;
class MainWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit MainWidget(QWidget *parent = nullptr);
~MainWidget() override;
protected:
void keyPressEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void timerEvent(QTimerEvent *e) override;
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
private:
QQuaternion rotation;
QBasicTimer timer;
QVector2D mousePressPosition;
QVector3D rotationAxis;
qreal angularSpeed;
// 需要三个深度缓冲区
GLuint FrontDepth; // 体积雾正面深度纹理
GLuint BackDepth; // 体积雾背面深度纹理
GLuint SceneDepth; // 物体深度纹理
GLuint FinalTexture; // 渲染场景纹理
GLuint FrontFBO; // 体积雾正面帧缓冲
GLuint BackFBO; // 体积雾背面帧缓冲
GLuint SceneFBO; // 场景帧缓冲
GLuint FinalFBO; // 渲染场景帧缓冲
int screenX = 640; // 屏幕大小
int screenY = 480;
QMatrix4x4 viewMatrix; // 视点(相机)矩阵
QMatrix4x4 projection; // 投影矩阵
// 眼睛位置,望向位置
QVector3D eyeLocation = QVector3D(0, 0, 10);
QVector3D lookAtLocation = QVector3D(0, 0, 0);
GeometryEngine *geometries; // 绘制Engine
QOpenGLTexture *texture; // 立方体贴的纹理
QOpenGLShaderProgram program0;
QOpenGLShaderProgram program1;
QOpenGLShaderProgram program2;
void GetViewMatrix(QMatrix4x4& matrix);
};
#endif // MAINWIDGET_H
mainwidget.cpp
#include "mainwidget.h"
#include <QMouseEvent>
#include <math.h>
MainWidget::MainWidget(QWidget *parent) :
QOpenGLWidget(parent),
angularSpeed(0),
geometries(nullptr)
{
}
MainWidget::~MainWidget()
{
makeCurrent();
delete geometries;
doneCurrent();
}
void MainWidget::keyPressEvent(QKeyEvent* event)
{
const float step = 0.3f;
if(event->key() == Qt::Key_W)
{
eyeLocation.setZ(eyeLocation.z() - step);
lookAtLocation.setZ(lookAtLocation.z() - step);
update();
}
else if(event->key() == Qt::Key_S)
{
eyeLocation.setZ(eyeLocation.z() + step);
lookAtLocation.setZ(lookAtLocation.z() + step);
update();
}
else if(event->key() == Qt::Key_A)
{
eyeLocation.setX(eyeLocation.x() - step);
lookAtLocation.setX(lookAtLocation.x() - step);
update();
}
else if(event->key() == Qt::Key_D)
{
eyeLocation.setX(eyeLocation.x() + step);
lookAtLocation.setX(lookAtLocation.x() + step);
update();
}
else if(event->key() == Qt::Key_D)
{
eyeLocation.setX(eyeLocation.x() + step);
lookAtLocation.setX(lookAtLocation.x() + step);
update();
}
else if(event->key() == Qt::Key_Q)
{
eyeLocation.setY(eyeLocation.y() - step);
lookAtLocation.setY(lookAtLocation.y() - step);
update();
}
else if(event->key() == Qt::Key_E)
{
eyeLocation.setY(eyeLocation.y() + step);
lookAtLocation.setY(lookAtLocation.y() + step);
update();
}
}
void MainWidget::mousePressEvent(QMouseEvent *e)
{
// Save mouse press position
mousePressPosition = QVector2D(e->localPos());
}
void MainWidget::mouseReleaseEvent(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();
// Increase angular speed
angularSpeed += acc;
}
void MainWidget::timerEvent(QTimerEvent *)
{
// Decrease angular speed (friction)
angularSpeed *= 0.99;
// Stop rotation when speed goes below threshold
if (angularSpeed < 0.01) {
angularSpeed = 0.0;
} else {
// Update rotation
rotation = QQuaternion::fromAxisAndAngle(rotationAxis, angularSpeed) * rotation;
// Request an update
update();
}
}
void MainWidget::initializeGL()
{
initializeOpenGLFunctions();
// 清屏颜色
glClearColor(0, 0, 0, 0);
// 开启剔除
glEnable(GL_CULL_FACE);
// add shader 0
QOpenGLShader* vShader0 = new QOpenGLShader(QOpenGLShader::Vertex);
QOpenGLShader* fShader0 = new QOpenGLShader(QOpenGLShader::Fragment);
vShader0->compileSourceFile(":/vShader0.glsl");
fShader0->compileSourceFile(":/fShader0.glsl");
program0.addShader(vShader0);
program0.addShader(fShader0);
program0.link();
// add shader 1
QOpenGLShader* vShader = new QOpenGLShader(QOpenGLShader::Vertex);
QOpenGLShader* fShader = new QOpenGLShader(QOpenGLShader::Fragment);
vShader->compileSourceFile(":/vShader1.glsl");
fShader->compileSourceFile(":/fShader1.glsl");
program1.addShader(vShader);
program1.addShader(fShader);
program1.link();
QOpenGLShader* vShader2 = new QOpenGLShader(QOpenGLShader::Vertex);
QOpenGLShader* fShader2 = new QOpenGLShader(QOpenGLShader::Fragment);
vShader2->compileSourceFile(":/vShader2.glsl");
fShader2->compileSourceFile(":/fShader2.glsl");
program2.addShader(vShader2);
program2.addShader(fShader2);
program2.link();
geometries = new GeometryEngine;
// 加载立方体的纹理
texture = new QOpenGLTexture(QImage(":/cube.png").mirrored());
texture->setMinificationFilter(QOpenGLTexture::Nearest);
texture->setMagnificationFilter(QOpenGLTexture::Linear);
texture->setWrapMode(QOpenGLTexture::Repeat);
// 雾正面的深度
glGenFramebuffers(1, &FrontFBO);
glBindFramebuffer(GL_FRAMEBUFFER, FrontFBO);
glGenTextures(1, &FrontDepth);
glBindTexture(GL_TEXTURE_2D, FrontDepth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, screenX, screenY, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, FrontDepth, 0);
// 雾背面的深度
glGenFramebuffers(1, &BackFBO);
glBindFramebuffer(GL_FRAMEBUFFER, BackFBO);
glGenTextures(1, &BackDepth);
glBindTexture(GL_TEXTURE_2D, BackDepth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, screenX, screenY, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, BackDepth, 0);
// 场景物体的深度
glGenFramebuffers(1, &SceneFBO);
glBindFramebuffer(GL_FRAMEBUFFER, SceneFBO);
glGenTextures(1, &SceneDepth);
glBindTexture(GL_TEXTURE_2D, SceneDepth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, screenX, screenY, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, SceneDepth, 0);
// 最终渲染场景
glGenFramebuffers(1, &FinalFBO);
glBindFramebuffer(GL_FRAMEBUFFER, FinalFBO);
glGenTextures(1, &FinalTexture);
glBindTexture(GL_TEXTURE_2D, FinalTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, screenX, screenY, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, FinalTexture, 0);
timer.start(12, this);
}
// 计算view矩阵
void MainWidget::GetViewMatrix(QMatrix4x4& matrix)
{
QVector3D upDir(0, 1, 0);
QVector3D N = eyeLocation - lookAtLocation; // 这里是和OpenGL的z轴方向保持一致
QVector3D U = QVector3D::crossProduct(upDir, N);
QVector3D V = QVector3D::crossProduct(N, U);
N.normalize();
U.normalize();
V.normalize();
matrix.setRow(0, {U.x(), U.y(), U.z(), -QVector3D::dotProduct(U, eyeLocation)}); // x
matrix.setRow(1, {V.x(), V.y(), V.z(), -QVector3D::dotProduct(V, eyeLocation)}); // y
matrix.setRow(2, {N.x(), N.y(), N.z(), -QVector3D::dotProduct(N, eyeLocation)}); // z
matrix.setRow(3, {0, 0, 0, 1});
}
void MainWidget::resizeGL(int w, int h)
{
screenX = w;
screenY = h;
float aspect = float(w) / float(h ? h : 1);
const qreal zNear = 1.0, zFar = 40.0, fov = 45.0;
projection.setToIdentity();
projection.perspective(fov, aspect, zNear, zFar);
}
void MainWidget::paintGL()
{
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
GetViewMatrix(viewMatrix); // 计算view矩阵
glBindTexture(GL_TEXTURE_2D, 0);
{
glCullFace(GL_BACK);
// 将雾正面的深度渲染到纹理
glBindFramebuffer(GL_FRAMEBUFFER, FrontFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
program0.bind();
QMatrix4x4 mvMatrix;
mvMatrix.translate(0,-2,0);
mvMatrix.scale(3,2,2);
mvMatrix = viewMatrix * mvMatrix;
program0.setUniformValue("ModelViewMatrix", mvMatrix);
program0.setUniformValue("ProjectMatrix", projection);
geometries->drawCube(&program0);
}
{
glCullFace(GL_FRONT);
// 将雾背面的深度渲染到纹理
glBindFramebuffer(GL_FRAMEBUFFER, BackFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
program0.bind();
QMatrix4x4 mvMatrix;
mvMatrix.translate(0,-2,0);
mvMatrix.scale(3,2,2);
mvMatrix = viewMatrix * mvMatrix;
program0.setUniformValue("ModelViewMatrix", mvMatrix);
program0.setUniformValue("ProjectMatrix", projection);
geometries->drawCube(&program0);
}
{
glCullFace(GL_BACK);
// 场景物体的深度
glBindFramebuffer(GL_FRAMEBUFFER, SceneFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
program0.bind();
QMatrix4x4 mvMatrix;
mvMatrix.rotate(rotation);
mvMatrix.scale(1,1,1);
mvMatrix = viewMatrix * mvMatrix;
program0.setUniformValue("ModelViewMatrix", mvMatrix);
program0.setUniformValue("ProjectMatrix", projection);
geometries->drawCube(&program0);
}
{
// 渲染场景
glBindFramebuffer(GL_FRAMEBUFFER, FinalFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
program1.bind();
texture->bind();
QMatrix4x4 mvMatrix;
mvMatrix.rotate(rotation);
mvMatrix.scale(1,1,1);
mvMatrix = viewMatrix * mvMatrix;
program1.setUniformValue("texture", 0);
program1.setUniformValue("ModelViewMatrix", mvMatrix);
program1.setUniformValue("ProjectMatrix", projection);
geometries->drawCubeGeometry(&program1);
}
{
// 计算并绘制体积雾
glDisable(GL_DEPTH_TEST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
program2.bind();
QMatrix4x4 mvMatrix;
mvMatrix.scale(1,1,1);
mvMatrix = viewMatrix * mvMatrix;
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, SceneDepth);
program2.setUniformValue("sceneDepth",1);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, FrontDepth);
program2.setUniformValue("frontDepth",2);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, BackDepth);
program2.setUniformValue("backDepth", 3);
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, FinalTexture);
program2.setUniformValue("finalColor", 4);
geometries->drawScreen(&program2);
}
}
geometryengine.h
#ifndef GEOMETRYENGINE_H
#define GEOMETRYENGINE_H
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
class GeometryEngine : protected QOpenGLFunctions
{
public:
GeometryEngine();
virtual ~GeometryEngine();
void drawCube(QOpenGLShaderProgram *program);
void drawCubeGeometry(QOpenGLShaderProgram *program);
void drawScreen(QOpenGLShaderProgram *program);
private:
void initCubeGeometry();
QOpenGLBuffer screenArrayBuf;
QOpenGLBuffer screenIndexBuf;
QOpenGLBuffer arrayBuf;
QOpenGLBuffer indexBuf;
};
#endif // GEOMETRYENGINE_H
geometryengine.cpp
#include "geometryengine.h"
#include <QVector2D>
#include <QVector3D>
struct VertexData
{
QVector3D position;
QVector2D texture;
};
//! [0]
GeometryEngine::GeometryEngine()
: screenIndexBuf(QOpenGLBuffer::IndexBuffer),indexBuf(QOpenGLBuffer::IndexBuffer)
{
initializeOpenGLFunctions();
arrayBuf.create();
indexBuf.create();
screenArrayBuf.create();
screenIndexBuf.create();
initCubeGeometry();
}
GeometryEngine::~GeometryEngine()
{
arrayBuf.destroy();
indexBuf.destroy();
screenArrayBuf.destroy();
screenIndexBuf.destroy();
}
void GeometryEngine::initCubeGeometry()
{
// For cube we would need only 8 vertices but we have to
// duplicate vertex for each face because texture coordinate
// is different.
VertexData vertices[] = {
// Vertex data for face 0
{QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(0.0f, 0.0f)}, // v0
{QVector3D( 1.0f, -1.0f, 1.0f), QVector2D(0.33f, 0.0f)}, // v1
{QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(0.0f, 0.5f)}, // v2
{QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v3
// Vertex data for face 1
{QVector3D( 1.0f, -1.0f, 1.0f), QVector2D( 0.0f, 0.5f)}, // v4
{QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.33f, 0.5f)}, // v5
{QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.0f, 1.0f)}, // v6
{QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.33f, 1.0f)}, // v7
// Vertex data for face 2
{QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.5f)}, // v8
{QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(1.0f, 0.5f)}, // v9
{QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.66f, 1.0f)}, // v10
{QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(1.0f, 1.0f)}, // v11
// Vertex data for face 3
{QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.0f)}, // v12
{QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(1.0f, 0.0f)}, // v13
{QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(0.66f, 0.5f)}, // v14
{QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(1.0f, 0.5f)}, // v15
// Vertex data for face 4
{QVector3D(-1.0f, -1.0f, -1.0f), QVector2D(0.33f, 0.0f)}, // v16
{QVector3D( 1.0f, -1.0f, -1.0f), QVector2D(0.66f, 0.0f)}, // v17
{QVector3D(-1.0f, -1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v18
{QVector3D( 1.0f, -1.0f, 1.0f), QVector2D(0.66f, 0.5f)}, // v19
// Vertex data for face 5
{QVector3D(-1.0f, 1.0f, 1.0f), QVector2D(0.33f, 0.5f)}, // v20
{QVector3D( 1.0f, 1.0f, 1.0f), QVector2D(0.66f, 0.5f)}, // v21
{QVector3D(-1.0f, 1.0f, -1.0f), QVector2D(0.33f, 1.0f)}, // v22
{QVector3D( 1.0f, 1.0f, -1.0f), QVector2D(0.66f, 1.0f)}, // v23
};
GLushort indices[] = {
0, 1, 2, 3, 3, // Face 0 - triangle strip ( v0, v1, v2, v3)
4, 4, 5, 6, 7, 7, // Face 1 - triangle strip ( v4, v5, v6, v7)
8, 8, 9, 10, 11, 11, // Face 2 - triangle strip ( v8, v9, v10, v11)
12, 12, 13, 14, 15, 15, // Face 3 - triangle strip (v12, v13, v14, v15)
16, 16, 17, 18, 19, 19, // Face 4 - triangle strip (v16, v17, v18, v19)
20, 20, 21, 22, 23 // Face 5 - triangle strip (v20, v21, v22, v23)
};
// Transfer vertex data to VBO 0
arrayBuf.bind();
arrayBuf.allocate(vertices, 24 * sizeof(VertexData));
// Transfer index data to VBO 1
indexBuf.bind();
indexBuf.allocate(indices, 34 * sizeof(GLushort));
VertexData screenVertices[] =
{
{{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f}},
{{1.0f, -1.0f, 0.0f}, {1.0f, 0.0f}},
{{1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
{{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f}},
};
GLushort screenIndices[] = {
0, 1, 2, 2, 3, 0
};
screenArrayBuf.bind();
screenArrayBuf.allocate(screenVertices, 4 * sizeof(VertexData));
screenIndexBuf.bind();
screenIndexBuf.allocate(screenIndices, 6 * sizeof(GLushort));
}
void GeometryEngine::drawCubeGeometry(QOpenGLShaderProgram *program)
{
arrayBuf.bind();
indexBuf.bind();
int offset = 0;
int vertexLocation = program->attributeLocation("a_position");
program->enableAttributeArray(vertexLocation);
program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(VertexData));
offset += sizeof(QVector3D);
int texcoordLocation = program->attributeLocation("a_texcoord");
program->enableAttributeArray(texcoordLocation);
program->setAttributeBuffer(texcoordLocation, GL_FLOAT, offset, 2, sizeof(VertexData));
glDrawElements(GL_TRIANGLE_STRIP, 34, GL_UNSIGNED_SHORT, nullptr);
}
void GeometryEngine::drawCube(QOpenGLShaderProgram *program)
{
arrayBuf.bind();
indexBuf.bind();
int offset = 0;
int vertexLocation = program->attributeLocation("a_position");
program->enableAttributeArray(vertexLocation);
program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(VertexData));
glDrawElements(GL_TRIANGLE_STRIP, 34, GL_UNSIGNED_SHORT, nullptr);
}
void GeometryEngine::drawScreen(QOpenGLShaderProgram *program)
{
screenArrayBuf.bind();
screenIndexBuf.bind();
int offset = 0;
int vertexLocation = program->attributeLocation("a_position");
program->enableAttributeArray(vertexLocation);
program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(VertexData));
offset += sizeof(QVector3D);
int texcoordLocation = program->attributeLocation("a_texcoord");
program->enableAttributeArray(texcoordLocation);
program->setAttributeBuffer(texcoordLocation, GL_FLOAT, offset, 2, sizeof(VertexData));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
}
main.cpp
#include <QApplication>
#include <QLabel>
#include <QSurfaceFormat>
#ifndef QT_NO_OPENGL
#include "mainwidget.h"
#endif
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QSurfaceFormat format;
format.setDepthBufferSize(24);
QSurfaceFormat::setDefaultFormat(format);
app.setApplicationName("cube");
app.setApplicationVersion("0.1");
#ifndef QT_NO_OPENGL
MainWidget widget;
widget.show();
#else
QLabel note("OpenGL Support required");
note.show();
#endif
return app.exec();
}