OpenGL(4)着色器

文章目录

前言:

在OpenGL开发中,着色器(Shader)是用于控制图形渲染管线各个阶段的小程序。它们是用GLSL(OpenGL Shading Language)编写的,GLSL是一种类似于C的语言。着色器在GPU上执行,负责处理顶点、片段等数据,最终生成图像。

一、着色器

1、什么是着色器?

概念:

着色器(Shader)是运行在 GPU 上的小程序,用于处理图形渲染过程中的特定任务。传统的 OpenGL 渲染流程中,CPU 需要承担大量的图形计算任务,而引入着色器后,将这些计算任务转移到 GPU 上,利用 GPU 的并行计算能力,大大提高了渲染效率。

工作原理:

当 OpenGL 进行图形渲染时,会将顶点数据(如顶点坐标、颜色、纹理坐标等)传递给着色器进行处理。着色器根据预设的算法对这些数据进行计算和转换,最终生成像素的颜色值,用于显示在屏幕上。整个渲染过程可以分为多个阶段,每个阶段由不同类型的着色器负责处理。

2、着色器类型

2.1、顶点着色器(Vertex Shader)

  • 功能:顶点着色器是处理顶点数据的第一个阶段。它接收顶点的原始数据(如顶点坐标、法线、颜色等),并对这些数据进行变换和处理,例如将顶点坐标从模型空间转换到裁剪空间。

  • 示例代码

    cpp 复制代码
    #version 330 core
    layout (location = 0) in vec3 aPos;  // 输入的顶点位置
    layout (location = 1) in vec3 aColor; // 输入的顶点颜色
    out vec3 ourColor;  // 输出的颜色,传递给片段着色器
    void main()
    {
        gl_Position = vec4(aPos, 1.0); // 将顶点位置转换为齐次坐标
        ourColor = aColor; // 传递颜色数据
    }

2.2、片段着色器(Fragment Shader)

  • 功能:片段着色器负责计算每个像素的最终颜色。它接收顶点着色器传递过来的数据(如颜色、纹理坐标等),并根据这些数据计算出每个像素的颜色值。

  • 示例代码

    cpp 复制代码
    #version 330 core
    in vec3 ourColor; // 从顶点着色器接收的颜色数据
    out vec4 FragColor; // 输出的最终像素颜色
    void main()
    {
        FragColor = vec4(ourColor, 1.0); // 将颜色数据转换为 RGBA 格式
    }

3、着色器属性

在基于 Qt 进行 OpenGL 开发时,理解 OpenGL 着色器语言(GLSL)中的 layoutinout 等属性是非常重要的,这些属性用于定义着色器之间的数据传递和属性的存储位置。下面详细介绍这些属性。

3.1、layout 属性

  • 功能:

    layout 属性主要用于指定着色器输入输出变量的存储布局,比如在顶点着色器中指定顶点属性的位置,或者在片段着色器中指定颜色输出的位置等。通过 layout 属性,我们可以精确地控制数据在内存中的存储和访问方式,方便 OpenGL 正确地读取和处理数据。

  • 示例及解释:

    glsl 复制代码
    #version 330 core
    // 顶点着色器中,使用 layout 指定顶点位置属性的位置为 0
    layout (location = 0) in vec3 aPos;
    // 使用 layout 指定顶点颜色属性的位置为 1
    layout (location = 1) in vec3 aColor;
    out vec3 ourColor;
    
    void main()
    {
        gl_Position = vec4(aPos, 1.0);
        ourColor = aColor;
    }

    在上述顶点着色器代码中,layout (location = 0) 表明 aPos 这个顶点位置属性在顶点数据中的索引为 0,layout (location = 1) 则表示 aColor 顶点颜色属性的索引为 1。在 C++ 代码中,我们可以根据这些索引来设置顶点属性指针,示例如下:

    cpp 复制代码
    // 设置顶点位置属性指针
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    // 设置顶点颜色属性指针
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));

3.2、in 属性

  • 功能:

    in 属性用于声明着色器的输入变量。在不同类型的着色器中,in 变量的来源不同。例如,在顶点着色器中,in 变量通常是从 CPU 传递过来的顶点属性数据;在片段着色器中,in 变量一般是从顶点着色器经过光栅化插值后传递过来的数据。

  • 示例及解释:

    glsl 复制代码
    #version 330 core
    // 顶点着色器
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec3 aColor;
    out vec3 ourColor;
    
    void main()
    {
        gl_Position = vec4(aPos, 1.0);
        ourColor = aColor;
    }
    
    // 片段着色器
    in vec3 ourColor;
    out vec4 FragColor;
    
    void main()
    {
        FragColor = vec4(ourColor, 1.0);
    }

在顶点着色器中,aPosaColor 是从 CPU 传递过来的顶点属性,使用 in 关键字声明。而 ourColor是输出变量,会传递给片段着色器。在片段着色器中,ourColor 作为输入变量接收来自顶点着色器的数据,然后将其用于计算最终的像素颜色。

3.3、out 属性

  • 功能:

    out 属性用于声明着色器的输出变量。这些变量会传递给下一个阶段的着色器进行处理。例如,顶点着色器的输出变量会经过光栅化插值后传递给片段着色器。

  • 示例及解释:

    glsl 复制代码
    #version 330 core
    // 顶点着色器
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec3 aColor;
    // 声明输出变量,传递给片段着色器
    out vec3 ourColor;
    
    void main()
    {
        gl_Position = vec4(aPos, 1.0);
        ourColor = aColor;
    }
    
    // 片段着色器
    // 接收来自顶点着色器的输出变量
    in vec3 ourColor;
    // 声明片段着色器的输出变量,即最终的像素颜色
    out vec4 FragColor;
    
    void main()
    {
        FragColor = vec4(ourColor, 1.0);
    }

在顶点着色器中,ourColor 使用 out 关键字声明,它会将顶点的颜色信息传递给片段着色器。在片段着色器中,FragColor 使用 out 关键字声明,它表示最终的像素颜色,会被写入帧缓冲区。

3.4、总结

  • layout 属性用于指定变量的存储位置,方便 OpenGL 正确解析数据。
  • in 属性用于声明着色器的输入变量,接收来自上一个阶段的数据。
  • out 属性用于声明着色器的输出变量,将数据传递给下一个阶段的着色器。

通过合理使用这些属性,我们可以实现不同着色器之间的数据传递和交互,从而完成复杂的图形渲染任务。

4、示例

创建一个顶点着色器与片段着色器,并使用QOpenGLShaderProgram类对象应用着色器,如下:

  • 顶点着色器
cpp 复制代码
// vertex_shader.glsl
#version 330 core
layout (location = 0) in vec3 aPos;      // 顶点位置
layout (location = 1) in vec3 aColor;    // 顶点颜色

out vec3 ourColor; // 传递给片段着色器的颜色

void main()
{
    gl_Position = vec4(aPos, 1.0); // 设置顶点位置
    ourColor = aColor;             // 传递颜色
}
  • 片段着色器
cpp 复制代码
#version 330 core
in vec3 ourColor;      // 从顶点着色器传入的颜色
out vec4 FragColor;    // 输出颜色

void main()
{
    FragColor = vec4(ourColor, 1.0); // 使用传入的颜色
}
  • 示例代码
cpp 复制代码
#include <QOpenGLFunctions_3_3_Core>
#include "OpenGLWidget.h"

// 顶点数据结构
struct Vertex {
    float position[3];       // 位置
    float color[3];          // 颜色
};

// 顶点数据
Vertex vertices[] = {
    { { -0.5f, -0.5f, 0.0f },{ 1.0, 0.0, 0.0 } },
    { { 0.5f, -0.5f, 0.0f },{ 0.0, 1.0, 0.0 } },
    { { 0.0f,  0.5f, 0.0f },{ 0.0, 0.0, 1.0 } }
};

// 构造函数
OpenGLWidget::OpenGLWidget(QWidget* parent) : QOpenGLWidget(parent)
{
}

// 析构函数
OpenGLWidget::~OpenGLWidget()
{
}

void OpenGLWidget::initializeGL()
{
    // 初始化OpenGL相关的接口
    initializeOpenGLFunctions();

    // 顶点数据:坐标 + 颜色
    GLfloat vertices[] = {
        // 位置              // 颜色
        -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下角 (红色)
         0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 右下角 (绿色)
         0.0f,  0.5f, 0.0f, 0.0f, 0.0f, 1.0f  // 顶部 (蓝色)
    };

    // 创建并绑定VAO
    glGenVertexArrays(1, &m_vao);
    glBindVertexArray(m_vao);

    // 创建并绑定m_vbo
    glGenBuffers(1, &m_vbo);
    glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 设置顶点属性指针
    // 位置属性 (location = 0)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)0);
    glEnableVertexAttribArray(0);

    // 颜色属性 (location = 1)
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    // 解绑VAO
    glBindVertexArray(0);

    // 编译着色器
    m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/OpenGL_Demo_Shader/vertex_shader.glsl");
    m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/OpenGL_Demo_Shader/fragment_shader.glsl");
    m_shaderProgram.link();
}

void OpenGLWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h); // 设置视口大小
}

void OpenGLWidget::paintGL()
{
    // 清除颜色缓冲
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置背景颜色
    glClear(GL_COLOR_BUFFER_BIT);

    // 使用着色器程序
    m_shaderProgram.bind();

    // 绑定VAO
    glBindVertexArray(m_vao);

    // 绘制三角形
    glDrawArrays(GL_TRIANGLES, 0, 3);

    // 解绑VAO
    glBindVertexArray(0);
}
  • 运行结果
相关推荐
向上的车轮4 小时前
数据中台工作流编排引擎:Apache Airflow
apache
雾迟sec4 小时前
Web安全-文件上传漏洞-黑白名单及其它绕过思路(附思维导图)
javascript·安全·web安全·网络安全·apache·安全威胁分析
yumgpkpm7 小时前
CMP(类Cloudera CDP 7.3 404版华为泰山Kunpeng)和Apache Doris的对比
大数据·hive·hadoop·spark·apache·hbase·cloudera
zhangkaixuan4568 小时前
Apache Paimon 查询全流程深度分析
java·apache·paimon
A-刘晨阳1 天前
时序数据库选型指南:从大数据视角切入,聚焦 Apache IoTDB
大数据·apache·时序数据库·iotdb
迦蓝叶1 天前
使用 Apache Jena 构建 Java 知识图谱
java·apache·知识图谱·图搜索·关系查询·关系推理
zhangkaixuan4562 天前
Apache Paimon 写入流程
java·大数据·apache·paimon
DolphinScheduler社区2 天前
Apache DolphinScheduler 3.3.2 正式发布!性能与稳定性有重要更新
大数据·开源·apache·任务调度·海豚调度·发版
SeaTunnel2 天前
Apache SeaTunnel 支持 Metalake 开发了!避免任务配置敏感信息暴露
大数据·开源·apache·个人开发·数据集成·seatunnel·看开源之夏
Mr_Art892 天前
金融行业湖仓实践:Apache Paimon 小文件治理之道
数据仓库·金融·apache