[OpenGL] 体积雾

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ZJU_fish1996/article/details/83049949

开发环境: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();
}

猜你喜欢

转载自blog.csdn.net/ZJU_fish1996/article/details/83049949