[OpenGL] 屏幕后处理:景深效果
开发环境:Qt, OpenGL
立方体纹理是Qt官方教程Cube里自带的纹理。
概念引入
景深是摄像中的术语。相机聚焦后,镜头前远近平面之间的物体能够清晰成像,这一段清晰成像的距离也就是景深,我们可以从下图更直观地理解景深。
为了在绘制中模拟景深这一效果,一个直观的想法是,获取物体和相机的距离,根据这一距离和远近平面的关系,来决定物体的清晰以及模糊状态,以及它的模糊程度。
对场景中的物体进行模糊实际上是图像空间的技术,这个时候,如果能够把场景渲染到一张纹理上,然后对这个纹理进行逐像素处理,就会非常方便。为了达到这一目的,我们可以使用屏幕后处理技术,简单来说,先把场景渲染到帧缓冲中,然后再把这个帧缓冲渲染到一个和屏幕同样大小的面片上,在第二次渲染的过程中,同时进行后期处理。
模糊是一个比较常见的技术,比如我们最常用的高斯模糊,用一个滤波算子将临近像素按照一定比例混合,达到模糊的效果。但是在屏幕后处理的过程中,我们并不知道相机的深度信息,也就无法根据深度来确定模糊因子。为了拿到这一变量,一个比较简单的方法是,在将场景渲染到纹理的同时,获取深度,并计算对应的模糊因子,然后将这一数据写入纹理的alpha通道,在第二次渲染处理的过程中,就能很直接的获取相关信息。
总而言之,景深是一个比较耗时的技术,它将至少消耗两个pass。
帧缓冲技术
OpenGL底层对场景渲染到纹理提供了支持, 它的基本步骤如下:
(1) 创建一个帧缓冲对象
(2) 创建一个和屏幕大小相同的纹理
(3) 将纹理附加到帧缓冲上
(4) 渲染时,指定将其渲染到特定的帧缓冲上。不需要渲染到帧缓冲时,我们需要关闭这一效果。
由此可见,我们一共需要创建两个对象,帧缓冲对象和纹理,并且需要建立两者的对应的关系。接下来,我们来看一下每一步对应的OpenGL实现。
1. 创建帧缓冲
glGenFramebuffers(1, &fBO);
glBindFramebuffer(GL_FRAMEBUFFER, fBO);
在以上代码中,我们仅创建一个帧缓冲,并将对应的索引值存在fBO (类型为GLuint) 变量里,然后把这一帧缓冲绑定到下文环境中。
2. 创建纹理
glGenTextures(1, &sceneBuffer);
glBindTexture(GL_TEXTURE_2D, sceneBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screenX, screenY, 0, GL_RGBA, 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);
创建方式和普通纹理创建方式基本一致,指定了基本的参数后,再指定纹理缩放的过滤模式,稍微需要注意的地方有:
(1) 创建的纹理大小和屏幕大小保持一致(*)
(2) 为了写入深度数据,纹理的通道为RGBA
之后。场景将会被写入这一纹理,通过sceneBuffer可索引到该纹理。
(*) 本例未考虑窗口缩放的问题,如果需要考虑的话,每次渲染都去创建和销毁纹理会比较耗时,我的想法是,先创建一张比较大的纹理,然后再第二次渲染到面片上时,调整纹理的uv,比如长宽均为一半时,纹理uv取(0.5,0.5),该想法未经实践,不确定是否可行。
3. 纹理附加到帧缓冲
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sceneBuffer, 0);
4. 指定使用当前帧缓冲
glBindFramebuffer(GL_FRAMEBUFFER, fBO); // 之后的操作写入帧缓存
// ... Render_Pass0() ...
glBindFramebuffer(GL_FRAMEBUFFER, 0); // 取消写入帧缓存
// ... Render_Pass1() ...
以上代码运行在每帧调用的paintGL中。
模糊效果
本例中使用的算子参考的是《DirectX 3D HLSL高级实例精讲》中使用的算子,该算子包含了12个数据,记录的是纹理的偏移值,利用这一偏移数据,获取当前像素的周围12个像素,并计算这12个像素的平均值,最终得到一张模糊的图像。需要注意的是,偏移算子本身记录的是绝对值,我们需要除以屏幕的大小,以排除屏幕大小对偏移效果的影响。
之后,根据模糊因子(取值范围在0~1) 之间,在原图像和模糊图像之间进行插值,得到最终的数据。
着色器部分
1. 第一次渲染
Vertex Shader:
uniform mat4 ProjectMatrix;
uniform mat4 ModelViewMatrix;
attribute vec4 a_position;
attribute vec2 a_texcoord;
varying vec2 v_texcoord;
varying float v_depth;
void main()
{
gl_Position = ModelViewMatrix * a_position; // 转换到视点空间
v_texcoord = a_texcoord;
v_depth = gl_Position.z; // 记录一下相机深度信息
gl_Position = ProjectMatrix * gl_Position; // 转换到投影空间
}
这里唯一特别的是,需要先把数据转换到相机空间再去记录深度数据。
Fragment Shader:
uniform sampler2D texture;
varying float v_depth;
varying vec2 v_texcoord;
void main(void)
{
gl_FragColor = texture2D(texture, v_texcoord);
float blur = 0;
float near_distance = 10.0; // 近平面的模糊衰减范围
float far_distance = 10.0; // 远平面的模糊衰减范围
float near_plane = -20.0; // 近平面
float far_plane = -25.0; // 远平面
// 根据深度计算模糊因子
if(v_depth <= near_plane && v_depth >= far_plane)
{
blur = 0;
}
else if(v_depth > near_plane)
{
blur = clamp(v_depth, near_plane, near_plane + near_distance);
blur = (blur - near_plane) / near_distance;
}
else if(v_depth < far_plane)
{
blur = clamp(v_depth, far_plane - far_distance, far_plane);
blur = (far_plane - blur) / far_distance;
}
// 将模糊因子写入alpha通道
gl_FragColor.a = blur;
}
首先,我们需要指定一下近、远平面以及衰减范围,可以在上文中的景深概念图中找到它们具体的含义,衰减范围即对应图中的模糊区,在这一区域中,模糊因子往靠近远/近平面的方向从1递减到0,在景深范围内为0,也就代表这一区域是完全清晰的。
在这里远近平面取值均为负值,这是因为在上下文中的view矩阵的z轴方向是从物体方向指向视点方向,也就是说,视点处z轴值为0,在视点前,物体的z值均为负,离视点越远,z轴的值越小。这一数据的取值和具体的实现方式是有关联的,要根据具体情况进行考虑。
计算模糊因子的部分看起来比较长,其实本质是在计算一个分段函数,在此例中,为了方便用的线性插值,在实际应用中,可用其它曲线来计算。
ps. 关于模糊因子的计算应该放到顶点着色器还是片元着色器,目前放在片元里计算主要是为了比较精确,主要担心的是,如果两个顶点落在分段函数的不同段处结果可能处理的不对,在简化景深计算方式或者对效果要求不高的情况下,也可以直接在顶点中计算。
2. 第二次渲染
Vertex Shader:
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif
attribute vec3 a_position;
attribute vec2 a_texcoord;
varying vec2 v_texcoord;
void main()
{
gl_Position = vec4(a_position, 1.0);
v_texcoord = a_texcoord;
}
顶点着色器只是为了传一下数据,没有什么特别需要计算的。
Fragment Shader:
#ifdef GL_ES
// Set default precision to medium
precision mediump int;
precision mediump float;
#endif
varying vec4 v_color;
varying vec2 v_texcoord;
uniform sampler2D texture;
uniform vec2 screenSize;
int kernelNum = 12;
vec2 g_v2TwelveKernelBase[] =
{
{1.0,0.0},{0.5,0.866},{-0.5,0.866},
{-1.0,0.0},{-0.5,-0.866},{0.5,-0.866},
{1.5,0.866},{0, 1.732},{-1.5,0.866},
{-1.5,0.866},{0,-1.732},{1.5,-0.866},
};
void main()
{
vec4 v4Original = texture2D(texture, v_texcoord);
vec2 v4ScreenSize = screenSize / 5;
vec3 v3Blurred = vec3(0, 0, 0);
for(int i = 0; i < kernelNum; i++)
{
vec2 v2Offset = vec2(g_v2TwelveKernelBase[i].x / v4ScreenSize.x,
g_v2TwelveKernelBase[i].y / v4ScreenSize.y);
vec4 v4Current = texture2D(texture, v_texcoord + v2Offset);
v3Blurred += lerp(v4Original.rgb, v4Current.rgb, v4Original.a);
}
gl_FragColor = vec4 (v3Blurred / kernelNum , 1.0f);
}
具体的计算已经在前文中的模糊效果中提及,主要思想是对原图像和模糊图像按照模糊因子进行混合。
入口代码
基本框架来自qt opengl的官方教程,在此基础上修改的。
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>
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 initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
private:
GLuint sceneBuffer; // 场景渲染纹理
GLuint fBO; // 帧缓冲
int screenX = 640; // 屏幕大小
int screenY = 480;
QMatrix4x4 viewMatrix; // 视点(相机)矩阵
QMatrix4x4 projection; // 投影矩阵
// 眼睛位置,望向位置
QVector3D eyeLocation = QVector3D(0, 0, 20);
QVector3D lookAtLocation = QVector3D(0, 0, 0);
GeometryEngine *geometries; // 绘制Engine
QOpenGLTexture *texture; // 立方体贴的纹理
// 两个pass的program
QOpenGLShaderProgram program0;
QOpenGLShaderProgram program;
void RenderScene();
void GetViewMatrix(QMatrix4x4& matrix);
};
#endif // MAINWIDGET_H
mainwidget.cpp
#include "mainwidget.h"
#include <QMouseEvent>
#include <math.h>
MainWidget::MainWidget(QWidget *parent) :
QOpenGLWidget(parent),
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();
}
}
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(":/vShader.glsl");
fShader->compileSourceFile(":/fShader.glsl");
program.addShader(vShader);
program.addShader(fShader);
program.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, &fBO);
glBindFramebuffer(GL_FRAMEBUFFER, fBO);
// 生成纹理图像,附加到帧缓冲
glGenTextures(1, &sceneBuffer);
glBindTexture(GL_TEXTURE_2D, sceneBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screenX, screenY, 0, GL_RGBA, 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, sceneBuffer, 0);
}
// 计算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 = 200.0, fov = 45.0;
projection.setToIdentity();
projection.perspective(fov, aspect, zNear, zFar);
}
void MainWidget::RenderScene()
{
// 传入cube的纹理
program0.setUniformValue("texture", 0);
QMatrix4x4 mvMatrix;
// 转换到世界坐标系
mvMatrix.scale(QVector3D(2,2,1));
// 转换到相机坐标系
mvMatrix = viewMatrix * mvMatrix;
program0.setUniformValue("ModelViewMatrix", mvMatrix);
program0.setUniformValue("ProjectMatrix", projection);
geometries->drawCubeGeometry(&program0);
}
void MainWidget::paintGL()
{
// pass 0 : 将场景渲染到纹理
glBindFramebuffer(GL_FRAMEBUFFER, fBO); // 之后的操作写入帧缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓存
glEnable(GL_DEPTH_TEST); // 开启深度测试
GetViewMatrix(viewMatrix); // 计算view矩阵
texture->bind(); // 等价于 glBindTexture
program0.bind(); // 使用program0绑定的shader
RenderScene(); // 渲染立方体
// pass 1 : 后处理
glBindFramebuffer(GL_FRAMEBUFFER, 0); // 取消写入帧缓存
glBindTexture(GL_TEXTURE_2D, sceneBuffer); // 使用之前场景渲染到的纹理
program.bind(); // 使用program0绑定的shader
glDisable(GL_DEPTH_TEST); // 关闭深度测试
QVector2D ScreenSize(screenX, screenY);
program.setUniformValue("screenSize", ScreenSize); // 传入屏幕大小和纹理
program.setUniformValue("texture", 0);
geometries->drawScreen(&program); // 把纹理渲染到一张面片上
}
geometryengine.h
#ifndef GEOMETRYENGINE_H
#define GEOMETRYENGINE_H
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
class GeometryEngine : protected QOpenGLFunctions
{
public:
GeometryEngine();
virtual ~GeometryEngine();
void drawCubeGeometry(QOpenGLShaderProgram *program);
void drawScreen(QOpenGLShaderProgram *program);
private:
void initCubeGeometry();
QOpenGLBuffer screenArrayBuf;
QOpenGLBuffer screenIndexBuf;
QOpenGLBuffer arrayBuf;
QOpenGLBuffer indexBuf;
};
#endif // GEOMETRYENGINE_H
geometryengine.cpp
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#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::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();
}