[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中。

模糊效果

       本例中使用的算子包含了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

#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();
}

 

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值