1.5.2 练习

一、更改笑脸朝向

题目:修改片段着色器,仅让笑脸图案朝另一个方向看。

更改片段着色器代码如下:

#version 330 core

out vec4 FragColor;

in vec3 ourColor;
in vec2 ourTexture; //纹理坐标

uniform sampler2D textureWall; //第0个采样器
uniform sampler2D textureSmile; //第1个采样器
uniform sampler2D textureSmall; //第2个采样器

void main()
{
    FragColor = mix(texture(textureWall, ourTexture),
                   texture(textureSmile, vec2(-ourTexture.x, ourTexture.y)), 0.5);
}

在paintGL函数中激活纹理单元

void MyOpenGLWidget::paintGL()
{
    //设置墨绿色背景
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //状态设置
    glClear(GL_COLOR_BUFFER_BIT); //状态使用

    //绘制
    m_shaderProgram.bind(); //激活程序对象
    glBindVertexArray(VAO); //绑定VAO
    m_textureWall->bind(0); //绑定激活纹理单元0
    m_textureSmile->bind(1); //绑定激活纹理单元1
    m_textureSmall->bind(2); //绑定激活纹理单元2
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL); //绘图
}

运行结果如下:

二、纹理环绕方式

题目:尝试用不同的纹理环绕方式,设定一个从0.0f到2.0f范围内的(而不是原来的0.0f到1.0f)纹理坐标。试试看能不能在箱子的角落放置4个笑脸。

更改顶点数据:

//顶点数据
float vertices[] = {
        //位置              //颜色             //纹理
        0.9f,  0.9f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 2.0f, //右上角
        0.9f, -0.9f, 0.0f, 0.0f, 1.0f, 0.0f, 2.0f, 0.0f, //右下角
       -0.9f, -0.9f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, //左下角
       -0.9f,  0.9f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 2.0f //左上角
};

给glTexParameteri()函数传不同的参数

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

三、GL_NEAREST

题目:尝试在矩形上只显示纹理图像的中间一部分,修改纹理坐标,达到能看见单个的像素的效果。尝试使用GL_NEAREST的纹理过滤方式让像素显示得更清晰。

更改顶点数据:

    //顶点数据
    float vertices[] = {
        //位置              //颜色             //纹理
        0.9f,  0.9f, 0.0f, 1.0f, 0.0f, 0.0f, 0.55f, 0.55f, //右上角
        0.9f, -0.9f, 0.0f, 0.0f, 1.0f, 0.0f, 0.55f, 0.45f, //右下角
       -0.9f, -0.9f, 0.0f, 0.0f, 0.0f, 1.0f, 0.45f, 0.45f, //左下角
       -0.9f,  0.9f, 0.0f, 1.0f, 1.0f, 0.0f, 0.45f, 0.55f //左上角
    };

更改initializeGL()函数代码

m_textureSmile->bind(1);
//纹理环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

四、纹理可见度

题目:使用一个uniform变量作为mix函数的第三个参数来改变两个纹理可见度,使用上和下键来改变箱子或笑脸的可见度。

更改着色器程序,创建一个变量mixValue,用来接收纹理可见度

#version 330 core

out vec4 FragColor;

in vec3 ourColor;
in vec2 ourTexture; //纹理坐标

uniform sampler2D textureWall; //第0个采样器
uniform sampler2D textureSmile; //第1个采样器
uniform sampler2D textureSmall; //第2个采样器

uniform float mixValue;

void main()
{
    FragColor = mix(texture(textureWall, ourTexture),
                   texture(textureSmile, vec2(-ourTexture.x, ourTexture.y)), mixValue);
}

更改myopenglwidget.h代码,创建接收鼠标事件函数keyPressEvent和纹理可见度变量mixValue

#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>

class MyOpenGLWidget : public QOpenGLWidget,QOpenGLFunctions_3_3_Core
{
    Q_OBJECT

public:
    explicit MyOpenGLWidget(QWidget *parent = nullptr);
    ~MyOpenGLWidget();

protected:
    virtual void initializeGL();
    virtual void resizeGL(int w, int h);
    virtual void paintGL();

    void keyPressEvent(QKeyEvent *event);

private:
    QOpenGLShaderProgram m_shaderProgram;
    QOpenGLTexture *m_textureWall;
    QOpenGLTexture *m_textureSmile;
    QOpenGLTexture *m_textureSmall;

    float mixValue = 0.5;
};

#endif // MYOPENGLWIDGET_H

更改myopenglwidget.cpp代码

#include "myopenglwidget.h"
#include <QDebug>
#include <QKeyEvent>

unsigned int VBO; //顶点缓冲对象
unsigned int VAO; //顶点数组对象
unsigned int EBO; //元素缓冲对象

MyOpenGLWidget::MyOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
    setFocusPolicy(Qt::StrongFocus);
}

MyOpenGLWidget::~MyOpenGLWidget()
{
    makeCurrent();
    glDeleteBuffers(1, &VBO);
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &EBO);
    doneCurrent();
}

void MyOpenGLWidget::initializeGL()
{
    //初始化OpenGL函数
    initializeOpenGLFunctions();

    //创建VBO,并赋予ID
    glGenBuffers(1, &VBO);
    //绑定VBO对象
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    //顶点数据
    float vertices[] = {
        //位置              //颜色             //纹理
        0.9f,  0.9f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, //右上角
        0.9f, -0.9f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, //右下角
       -0.9f, -0.9f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, //左下角
       -0.9f,  0.9f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f //左上角
    };
    //把顶点数据复制到显存中
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    //创建VAO对象,并赋予ID
    glGenVertexArrays(1, &VAO);
    //绑定VAO对象
    glBindVertexArray(VAO);

    //创建EBO对象,并赋予ID
    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    unsigned int indices[] = {
                               0, 1, 3, //第一个三角形
                               1, 2, 3 //第二个三角形
                             };
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    //位置属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0); //开启VAO管理的第一个属性值

    //颜色属性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1); //开启VAO管理的第二个属性值

    //纹理属性
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2); //开启VAO管理的第三个属性值

    //解绑VBO
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    //解绑VAO
    glBindVertexArray(0);

    //创建一个程序对象
    m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/shaders/shapes.vert");
    m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/shaders/shapes.frag");
    bool success = m_shaderProgram.link();
    if(!success)
        qDebug()<<"ERR:" << m_shaderProgram.log();

    m_shaderProgram.bind();
    m_shaderProgram.setUniformValue("vertexColor", 0.0, 1.0, 0.0, 1.0);
    
    m_textureWall = new QOpenGLTexture(QImage(":/images/wall.jpg").mirrored()); //mirrored消除镜像
    m_shaderProgram.setUniformValue("textureWall", 0); //把纹理单元传给片段着色器中的采样器

    m_textureSmile = new QOpenGLTexture(QImage(":/images/awesomeface.png").mirrored()); //mirrored消除镜像
    m_shaderProgram.setUniformValue("textureSmile", 1); //把纹理单元传给片段着色器中的采样器

    m_textureSmall = new QOpenGLTexture(QImage(":/images/small.png").mirrored()); //mirrored消除镜像
    m_shaderProgram.setUniformValue("textureSmall", 2); //把纹理单元传给片段着色器中的采样器

    m_shaderProgram.setUniformValue("mixValue", mixValue);
}

void MyOpenGLWidget::resizeGL(int w, int h)
{
    Q_UNUSED(w);

    Q_UNUSED(h);
    //glViewport(0, 0, w, h);
}

void MyOpenGLWidget::paintGL()
{
    //设置墨绿色背景
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //状态设置
    glClear(GL_COLOR_BUFFER_BIT); //状态使用

    //绘制
    m_shaderProgram.bind(); //激活程序对象
    glBindVertexArray(VAO); //绑定VAO
    m_textureWall->bind(0); //绑定激活纹理单元0
    m_textureSmile->bind(1); //绑定激活纹理单元1
    m_textureSmall->bind(2); //绑定激活纹理单元2
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL); //绘图
}

void MyOpenGLWidget::keyPressEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_Up)
        mixValue+=0.1;
    else if(event->key() == Qt::Key_Down)
        mixValue-=0.1;
    else
        return;

    if(mixValue > 1.0)
        mixValue = 1.0;

    if(mixValue < 0.0)
        mixValue = 0.0;

    makeCurrent();
    m_shaderProgram.setUniformValue("mixValue", mixValue);
    doneCurrent();
    update();

    QOpenGLWidget::keyPressEvent(event);
}

在函数initializeGL()最后一行给片段着色器中的mixValue变量设置一个初始值

m_shaderProgram.setUniformValue("mixValue", mixValue);

keyPressEvent函数接收键盘事件,当是上键时mixValue值+0.1,下键时-0.1,且维持mixValue值在0.0到1.0之间;

在自定义函数中修改GUP的值之前需要调用makeCurrent()函数,更改数值后调用doneCurrent()函数,之后调用update()函数进行界面更新,会自动调用paintGL()函数

makeCurrent();
m_shaderProgram.setUniformValue("mixValue", mixValue);
doneCurrent();
update();

运行结果如下:

通过摁上键或下键改变纹理可见度的值

注:观看OpenGL中文官网(https://learnopengl-cn.github.io/)和阿西拜的现代OpenGL入门(https://ke.qq.com/course/3999604#term_id=104150693)学习OpenGL

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值