Game Programming using Qt 5 Beginner’s Guide, 2nd Edition - monkey

在这里插入图片描述

Qt 可以通过一组基于 QOpenGLShaderProgram 的类来使用着色器。 此类允许编译、链接和执行用 GLSL 编写的着色器程序。您可以通过检查接受指向 OpenGL 上下文的指针的静态 QOpenGLShaderProgram::hasOpenGLShaderPrograms() 调用的结果来检查您的 OpenGL 实现是否支持着色器。所有现代硬件和所有不错的图形驱动程序都应该对着色器有一定的支持。

Qt 支持各种着色器,最常见的是顶点着色器和片段着色器。 这些都是经典 OpenGL 管道的一部分。 您可以在下图中看到管道的图示:

在这里插入图片描述

单个着色器由 QOpenGLShader 类的实例表示。您需要在此类的构造函数中指定着色器的类型。然后,您可以通过调用 QOpenGLShader::compileSourceCode() 或 QOpenGLShader::compileSourceFile() 来编译着色器的源代码,它具有许多用于处理不同输入格式的重载。QOpenGLShader 对象存储已编译着色器的 ID 以备将来使用。

当您定义了一组着色器时,您可以使用 QOpenGLShaderProgram::addShader() 组装一个完整的程序。添加完所有着色器后,您可以 link() 程序并将其 bind() 到当前的 OpenGL 上下文。程序类有许多方法来设置不同输入参数的值——单一和数组版本的统一和属性。

shaderglscene.h

#ifndef SHADERGLSCENE_H
#define SHADERGLSCENE_H

#include <QObject>
#include <QOpenGLShaderProgram>
#include "abstractglscene.h"
#include "sceneglwindow.h"

class ShaderGLScene : public QObject, public AbstractGLScene 
{
    Q_OBJECT
    Q_PROPERTY(qreal angle READ angle WRITE setAngle)
public:
    ShaderGLScene(SceneGLWindow *window);
    void initialize();
    void paint();

    qreal angle() const;
    void paintObject(const QMatrix4x4& mvMatrix);

public slots:
    void setAngle(qreal a);

protected:
    void initializeCubeData();

private:
    bool m_initialized;
    qreal m_angle = 0;

    struct ScenePoint {
        QVector3D coords;
        QVector3D normal;

        ScenePoint(const QVector3D &c = QVector3D(),
                   const QVector3D &n = QVector3D()) :
            coords(c), normal(n)
        {

        }
    };

    QVector<ScenePoint> m_data;
    QOpenGLShaderProgram m_shader;
	//视图转换的三个矩阵
    QMatrix4x4 m_modelMatrix;
    QMatrix4x4 m_viewMatrix;
    QMatrix4x4 m_projectionMatrix;

};

#endif // SHADERGLSCENE_H

shaderglscene.cpp

#include "shaderglscene.h"
#include <QFile>
#include "plyreader.h"

ShaderGLScene::ShaderGLScene(SceneGLWindow *window) : AbstractGLScene(window) {
    window->setScene(this);
    m_initialized = false;
    initializeCubeData();
}

void ShaderGLScene::initialize() {
    //将场景的背景颜色设置为黑色
    AbstractGLScene::initialize();
    m_initialized = true;
    glClearColor(0, 0, 0, 0);
	//从这些文件中读取着色器并链接着色器程序
    m_shader.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/phong.vert");
    m_shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/phong.frag");
	//link() 函数返回一个布尔值,这里跳过错误检查
    m_shader.link();
    //为着色器准备所有输入数据
    //首先,着色器程序绑定到当前上下文
    //以便我们可以对其进行操作
    m_shader.bind();
    //然后,我们启用两个属性数组的设置
    //一个用于顶点坐标,另一个用于它们的法线
    //数据存储在 QVector<ScenePoint> 中
    //其中每个 ScenePoint 都有坐标和法线字段,因此坐标和法线没有单独的 C++ 数组
    m_shader.setAttributeArray("Vertex", GL_FLOAT,
                               &m_data[0].coords, 3, sizeof(ScenePoint));
    m_shader.enableAttributeArray("Vertex");

    m_shader.setAttributeArray("Normal", GL_FLOAT,
                               &m_data[0].normal, 3, sizeof(ScenePoint));
    m_shader.enableAttributeArray("Normal");

    m_shader.setUniformValue("mat.ka", QVector3D(0.1, 0, 0.0));
    m_shader.setUniformValue("mat.kd", QVector3D(0.7, 0.0, 0.0));
    m_shader.setUniformValue("mat.ks", QVector3D(1.0, 1.0, 1.0));
    m_shader.setUniformValue("mat.shininess", 128.0f);

    m_shader.setUniformValue("light.position", QVector3D(2, 1, 1));
    m_shader.setUniformValue("light.intensity", QVector3D(1, 1, 1));
}

void ShaderGLScene::paint() {
    //我们重置投影矩阵
    m_projectionMatrix.setToIdentity();
    float aspectRatio = qreal(window()->width()) / window()->height();
    //使用perspective()方法根据当前窗口大小给它一个透视变换
    m_projectionMatrix.perspective(90, aspectRatio, 0.5, 40);

    //视图矩阵也被重置,并使用lookAt()方法为相机准备变换
    //中心值表示眼睛正在看的视图的中心。 向上矢量指示相机的垂直方向
    m_viewMatrix.setToIdentity();
    QVector3D eye(0, 0, 2);
    QVector3D center(0, 0, 0);
    QVector3D up(0, 1, 0);
    m_viewMatrix.lookAt(eye, center, up);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glViewport(0, 0, window()->width(), window()->height());
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
	//我们首先设置模型矩阵,它决定了渲染对象相对于世界中心的位置
    //(在这种情况下,我们说它绕 y 轴旋转了 45 度)
    m_modelMatrix.setToIdentity();
    m_modelMatrix.scale(2);
    m_modelMatrix.rotate(m_angle, 0, 1, 0);
    m_modelMatrix.rotate(-90, 1, 0, 0);
	//然后我们构建模型-视图矩阵(表示对象相对于相机的位置)并将其传递给paintObject()方法
    QMatrix4x4 modelViewMatrix = m_viewMatrix * m_modelMatrix;
    paintObject(modelViewMatrix);
}

qreal ShaderGLScene::angle() const {
    return m_angle;
}
//激活着色器程序,然后将所有需要的矩阵设置为着色器的Uniform
void ShaderGLScene::paintObject(const QMatrix4x4 &mvMatrix) {
    m_shader.bind();
    m_shader.setUniformValue("projectionMatrix", m_projectionMatrix);
    m_shader.setUniformValue("modelViewMatrix", mvMatrix);
    m_shader.setUniformValue("mvpMatrix", m_projectionMatrix*mvMatrix);
    m_shader.setUniformValue("normalMatrix", mvMatrix.normalMatrix());
    //glDrawArrays() 的调用,告诉它使用活动数组以 GL_TRIANGLES 模式渲染
    glDrawArrays(GL_TRIANGLES, 0, m_data.size());
}

void ShaderGLScene::setAngle(qreal a) {
    m_angle = a;
    window()->update();
}

void ShaderGLScene::initializeCubeData() {
    PlyReader reader(":/monkey.ply");
    if(reader.read()) {
        // reconstruct faces
        const QVector<PlyReader::ScenePoint> vertices = reader.vertices();
        const QVector<QVector<int>> faces = reader.faces();
        for(const QVector<int>& face: faces) {
            ScenePoint pt;
            for(int vertexId: face) {
                const PlyReader::ScenePoint &scenePoint = vertices.at(vertexId);
                pt.coords = scenePoint.coord;
                pt.normal = scenePoint.normal;
                m_data << pt;
            }
        }
    }
}

phong.vert

//我们声明了四个矩阵,代表场景坐标映射的不同阶段
uniform highp mat4 modelViewMatrix;
uniform highp mat3 normalMatrix;
uniform highp mat4 projectionMatrix;
uniform highp mat4 mvpMatrix;
//我们还定义了两个输入属性——顶点和法线
//它们包含顶点数据
attribute highp vec4 Vertex;
attribute mediump vec3 Normal;
//着色器将输出两条数据
//归一化顶点法线和相机看到的变换顶点坐标
varying mediump vec3 N;
varying highp vec3 v;

void main(void)
{
    N = normalize(normalMatrix * Normal);
    v = vec3(modelViewMatrix * Vertex);
	//我们将 gl_Position 设置为最终的顶点坐标。
    gl_Position = mvpMatrix * Vertex;
}

phong.frag

//着色器还声明了两个结构来保存光和材质信息
struct Material {
    lowp vec3 ka;
    lowp vec3 kd;
    lowp vec3 ks;
    lowp float shininess;
};

struct Light {
    lowp vec4 position;
    lowp vec3 intensity;
};

uniform Material mat;

uniform Light light;
//插值法线 (N) 和片段 (v) 位置
varying mediump vec3 N;
varying highp vec3 v;
//由于每个顶点的所有输入数据都是针对每个片段进行插值的,因此会为每个像素单独计算最终颜色。
void main(void) {
    vec3 n = normalize(N);
    vec3 L = normalize(light.position.xyz - v);
    vec3 E = normalize(-v);
    vec3 R = normalize(reflect(-L, n));

    float LdotN = dot(L, n);
    float diffuse = max(LdotN, 0.0);
    vec3 spec = vec3(0, 0, 0);

    if(LdotN > 0.0) {
        float RdotE = max(dot(R, E), 0.0);
        spec = light.intensity * pow(RdotE, mat.shininess);
    }
    //它会计算三个分量——环境光、漫射光和镜面反射——将它们加在一起
    //并将其设置为gl_FragColor
    vec3 color = light.intensity * (mat.ka + mat.kd * diffuse + mat.ks * spec);
    gl_FragColor = vec4(color, 1.0);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A complete guide to designing and building fun games with Qt and Qt Quick 2 using associated toolsets About This Book Learn to create simple 2D to complex 3D graphics and games using all possible tools and widgets available for game development in Qt Understand technologies such as QML, Qt Quick, OpenGL, and Qt Creator, and learn the best practices to use them to design games Learn Qt with the help of many sample games introduced step-by-step in each chapter Who This Book Is For If you want to create great graphical user interfaces and astonishing games with Qt, this book is ideal for you. Any previous knowledge of Qt is not required, however knowledge of C++ is mandatory. What You Will Learn Install Qt on your system Understand the basic concepts of every Qt game and application Develop 2D object-oriented graphics using Qt Graphics View Build multiplayer games or add a chat function to your games with Qt's Network module Script your game with Qt Script Program resolution-independent and fluid UI using QML and Qt Quick Control your game flow as per the sensors of a mobile device See how to test and debug your game easily with Qt Creator and Qt Test In Detail Qt is the leading cross-platform toolkit for all significant desktop, mobile, and embedded platforms and is becoming more popular by the day, especially on mobile and embedded devices. Despite its simplicity, it's a powerful tool that perfectly fits game developers' needs. Using Qt and Qt Quick, it is easy to build fun games or shiny user interfaces. You only need to create your game once and deploy it on all major platforms like iOS, Android, and WinRT without changing a single source file. The book begins with a brief introduction to creating an application and preparing a working environment for both desktop and mobile platforms. It then dives deeper into the basics of creating graphical interfaces and Qt core concepts of data processing and display before you try creating a game. As you progress through the chapters, you'll learn to enrich your games by implementing network connectivity and employing scripting. We then delve into Qt Quick, OpenGL, and various other tools to add game logic, design animation, add game physics, and build astonishing UI for the games. Towards the final chapters, you'll learn to exploit mobile device features such as accelerators and sensors to build engaging user experiences. If you are planning to learn about Qt and its associated toolsets to build apps and games, this book is a must have. Style and approach This is an easy-to-follow, example-based, comprehensive introduction to all the major features in Qt. The content of each chapter is explained and organized around one or multiple simple game examples to learn Qt in a fun way. Table of Contents Chapter 1: Introduction to Qt Chapter 2: Installation Chapter 3: Qt GUI Programming Chapter 4: Qt Core Essentials Chapter 5: Graphics with Qt Chapter 6: Graphics View Chapter 7: Networking Chapter 8: Scripting Chapter 9: Qt Quick Basics Chapter 10: Qt Quick Appendix: Pop Quiz Answers
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值