【OpenGL学习】(五)自定义着色器类

news2025/6/7 14:23:21

文章目录

  • 【OpenGL学习】(五)自定义着色器类
    • 着色器类
    • 插值着色
    • 统一着色

【OpenGL学习】(五)自定义着色器类

项目结构:
在这里插入图片描述

着色器类

// shader_s.h
#ifndef SHADER_H
#define SHADER_H

#include <glad/glad.h>

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

class Shader
{
public:
    unsigned int ID; // 存储着色器程序的唯一标识符
    // 参数 vertexPath - 顶点着色器文件的路径
    // 参数 fragmentPath - 片段着色器文件的路径
    Shader(const char* vertexPath, const char* fragmentPath)
    {
        // 1. 从文件路径中获取顶点/片段着色器源代码
        std::string vertexCode; // 存储顶点着色器源代码的字符串
        std::string fragmentCode; // 存储片段着色器源代码的字符串
        std::ifstream vShaderFile; // 用于读取顶点着色器文件的输入流
        std::ifstream fShaderFile; // 用于读取片段着色器文件的输入流
        // 确保ifstream对象可以抛出异常:
        vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
        fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
        try
        {
            // 打开文件
            vShaderFile.open(vertexPath);
            fShaderFile.open(fragmentPath);
            std::stringstream vShaderStream, fShaderStream;
            // 将文件内容读入流中
            vShaderStream << vShaderFile.rdbuf(); // 读取顶点着色器文件内容
            fShaderStream << fShaderFile.rdbuf(); // 读取片段着色器文件内容
            // 关闭文件句柄
            vShaderFile.close();
            fShaderFile.close();
            // 将流转换为字符串
            vertexCode = vShaderStream.str(); // 将顶点着色器流转换为字符串
            fragmentCode = fShaderStream.str(); // 将片段着色器流转换为字符串
        }
        catch (std::ifstream::failure& e)
        {
            std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() << std::endl;
        }
        const char* vShaderCode = vertexCode.c_str(); // 转换为C风格字符串的顶点着色器代码
        const char* fShaderCode = fragmentCode.c_str(); // 转换为C风格字符串的片段着色器代码
        // 2. 编译着色器
        unsigned int vertex, fragment;
        // 顶点着色器
        vertex = glCreateShader(GL_VERTEX_SHADER); // 创建顶点着色器对象
        glShaderSource(vertex, 1, &vShaderCode, NULL); // 将顶点着色器源代码附加到着色器对象
        glCompileShader(vertex); // 编译顶点着色器
        checkCompileErrors(vertex, "VERTEX"); // 检查顶点着色器编译错误
        // 片段着色器
        fragment = glCreateShader(GL_FRAGMENT_SHADER); // 创建片段着色器对象
        glShaderSource(fragment, 1, &fShaderCode, NULL); // 将片段着色器源代码附加到着色器对象
        glCompileShader(fragment); // 编译片段着色器
        checkCompileErrors(fragment, "FRAGMENT"); // 检查片段着色器编译错误
        // 着色器程序
        ID = glCreateProgram(); // 创建着色器程序对象
        glAttachShader(ID, vertex); // 将顶点着色器附加到程序
        glAttachShader(ID, fragment); // 将片段着色器附加到程序
        glLinkProgram(ID); // 链接着色器程序
        checkCompileErrors(ID, "PROGRAM"); // 检查着色器程序链接错误
        // 删除着色器,因为它们已经链接到我们的程序中,不再需要
        glDeleteShader(vertex); // 删除顶点着色器对象
        glDeleteShader(fragment); // 删除片段着色器对象
    }

    // 激活着色器
    // 使用当前着色器程序
    void use()
    {
        glUseProgram(ID); // 激活着色器程序
    }

    // setVec4
    // 参数 name - uniform变量的名称
    // 参数 v0, v1, v2, v3 - 要设置的四个浮点数值
    void setVec4(const std::string& name, float v0, float v1, float v2, float v3) const
    {
        glUniform4f(glGetUniformLocation(ID, name.c_str()), v0, v1, v2, v3); // 设置uniform vec4值
    }
private:
    // checkCompileErrors:用于检查着色器编译/链接错误。
    // 参数 shader - 要检查的着色器对象或程序对象
    // 参数 type - 着色器或程序的类型
    void checkCompileErrors(unsigned int shader, std::string type)
    {
        int success; // 用于存储编译或链接成功与否的标志
        char infoLog[1024]; // 用于存储错误信息的缓冲区
        if (type != "PROGRAM")
        {
            glGetShaderiv(shader, GL_COMPILE_STATUS, &success); // 获取着色器编译状态
            if (!success)
            {
                glGetShaderInfoLog(shader, 1024, NULL, infoLog); // 获取着色器编译错误信息
                std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
            }
        }
        else
        {
            glGetProgramiv(shader, GL_LINK_STATUS, &success); // 获取程序链接状态
            if (!success)
            {
                glGetProgramInfoLog(shader, 1024, NULL, infoLog); // 获取程序链接错误信息
                std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
            }
        }
    }
};
#endif

插值着色

顶点着色器:

// shader.vs
#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;
}

片段着色器:

// shader.fs
#version 330 core
out vec4 FragColor;

in vec3 ourColor;

void main()
{
    FragColor = vec4(ourColor, 1.0f);
}

主函数实现:

// Application.cpp
#include <glad/glad.h>     
#include <GLFW/glfw3.h>   
#include <shader_s.h>     
#include <iostream>    

// 函数声明
void framebuffer_size_callback(GLFWwindow* window, int width, int height); // 窗口大小变化时的回调函数
void processInput(GLFWwindow* window); // 处理用户输入

// 设置窗口的宽度和高度
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    // 初始化GLFW
    glfwInit();
    // 配置GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 设置OpenGL的主版本号为3
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 设置OpenGL的次版本号为3
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 在macOS上启用向前兼容
#endif

    // 创建GLFW窗口
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate(); // 如果窗口创建失败,终止GLFW
        return -1;
    }
    glfwMakeContextCurrent(window); // 将窗口的上下文设置为当前线程的上下文
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // 设置窗口大小变化的回调函数

    // 初始化GLAD,加载OpenGL函数指针
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // !!!创建并编译着色器程序
    Shader ourShader("shader.vs", "shader.fs"); // 使用指定的顶点着色器和片段着色器文件创建着色器对象

    // 设置顶点数据(和缓冲区)并配置顶点属性
    float vertices[] = {
        // positions         // colors
         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   // 顶部,蓝色
    };

    unsigned int VBO, VAO; // 定义顶点缓冲对象和顶点数组对象
    glGenVertexArrays(1, &VAO); // 生成一个顶点数组对象
    glGenBuffers(1, &VBO); // 生成一个顶点缓冲对象
    // 绑定顶点数组对象,然后绑定和设置顶点缓冲,最后配置顶点属性
    glBindVertexArray(VAO); // 绑定顶点数组对象

    glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定顶点缓冲对象
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 将顶点数据复制到缓冲中

    // 位置属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); // 指定顶点属性的格式
    glEnableVertexAttribArray(0); // 启用顶点属性数组
    // 颜色属性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); // 指定颜色属性的格式
    glEnableVertexAttribArray(1); // 启用颜色属性数组

    // 通常不需要解绑顶点数组对象,因为修改其他顶点数组对象时需要再次绑定
    // glBindVertexArray(0);

    // 渲染循环
    while (!glfwWindowShouldClose(window))
    {
        // 处理输入
        processInput(window);

        // 渲染
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f); // 设置清除颜色
        glClear(GL_COLOR_BUFFER_BIT); // 清除颜色缓冲

        // 渲染三角形
        ourShader.use(); //  !!!使用着色器程序
        glBindVertexArray(VAO); // 绑定顶点数组对象
        glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形

        // 交换缓冲并查询IO事件
        glfwSwapBuffers(window); // 交换颜色缓冲
        glfwPollEvents(); // 检查并调用事件
    }

    // 可选:释放所有资源
    glDeleteVertexArrays(1, &VAO); // 删除顶点数组对象
    glDeleteBuffers(1, &VBO); // 删除顶点缓冲对象

    // 终止GLFW,清除所有GLFW资源
    glfwTerminate();
    return 0;
}

// 处理输入:查询GLFW是否有按键被按下/释放,并执行相应的操作
void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) // 如果按下ESC键
        glfwSetWindowShouldClose(window, true); // 关闭窗口
}

// 窗口大小变化时的回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height); // 调整视口大小以匹配窗口的新尺寸
}

统一着色

顶点着色器:

#version 330 core
layout (location = 0) in vec3 aPos;

void main()
{
    gl_Position = vec4(aPos, 1.0);
}

片段着色器:

#version 330 core
out vec4 FragColor;
uniform vec4 ourColor; // 用于设置颜色的uniform变量

void main()
{
    FragColor = ourColor; // 使用uniform颜色
}

主函数:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <shader_s.h>
#include <iostream>

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    Shader ourShader("shader.vs", "shader.fs");

    float vertices[] = {
        // positions
         0.5f, -0.5f, 0.0f,  // bottom right
        -0.5f, -0.5f, 0.0f,  // bottom left
         0.0f,  0.5f, 0.0f   // top 
    };

    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    while (!glfwWindowShouldClose(window))
    {
        processInput(window);

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        ourShader.use();
        // 设置uniform颜色为绿色
        ourShader.setVec4("ourColor", 0.0f, 1.0f, 0.0f, 1.0f);

        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);

    glfwTerminate();
    return 0;
}

void processInput(GLFWwindow* window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
    glViewport(0, 0, width, height);
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2402997.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

408第一季 - 数据结构 - 栈与队列的应用

括号匹配 用瞪眼法就可以知道的东西 栈在表达式求值运用 先简单看看就行&#xff0c;题目做了就理解了 AB是操作符,也是被狠狠加入后缀表达式了&#xff0c;然后后面就是*&#xff0c;只要优先级比栈顶运算符牛逼就放里面&#xff0c;很显然&#xff0c;*比牛逼 继续前进&#…

超声波清洗设备的清洗效果如何?

超声波清洗设备是一种常用于清洗各种物体的技术&#xff0c;它通过超声波振荡产生的微小气泡在液体中破裂的过程来产生高能量的冲击波&#xff0c;这些冲击波可以有效地去除表面和细微裂缝中的污垢、油脂、污染物和杂质。超声波清洗设备在多个领域得到广泛应用&#xff0c;包括…

“草台班子”的成长路径分析

一、草台班子的起点&#xff1a;用最小成本验证价值 特点&#xff1a; 团队规模小&#xff08;通常3-5人&#xff09;&#xff0c;成员背景杂&#xff08;可能是程序员产品经理运营的混搭&#xff09;&#xff1b;资源匮乏&#xff08;无资金、无技术中台、无客户积累&#x…

软件测评服务如何依据标准确保品质?涵盖哪些常见内容?

软件测评服务涉及对软件的功能和性能等多维度进行评估和检验&#xff0c;这一过程有助于确保软件的品质&#xff0c;降低故障发生率及维护费用&#xff0c;对于软件开发和维护环节具有至关重要的价值。 测评标准依据 GB/T 25000.51 - 2016是软件测评的核心依据。依照这一标准…

Python打卡第46天

浙大疏锦行 注意力 注意力机制是一种让模型学会「选择性关注重要信息」的特征提取器&#xff0c;就像人类视觉会自动忽略背景&#xff0c;聚焦于图片中的主体&#xff08;如猫、汽车&#xff09;。 从数学角度看&#xff0c;注意力机制是对输入特征进行加权求和&#xff0c;…

Unity优化篇之DrawCall

当然可以&#xff01;以下是完整、详尽、可发布的博客文章&#xff0c;专注讲解 Unity 的静态合批与动态合批机制&#xff0c;并详细列出它们对 Shader 的要求和所有限制条件。文章结构清晰、技术深度足够&#xff0c;适合发布在 CSDN、掘金、知乎等技术平台。 urp默认隐藏动态…

SpringCloud学习笔记-2

说明&#xff1a;来源于网络&#xff0c;如有侵权请联系我删除 1.提问&#xff1a;如果注册中心宕机&#xff0c;远程调用还能成功吗 答&#xff1a;当微服务发起请求时&#xff0c;会向注册中心请求所有的微服务地址&#xff0c;然后在向指定的微服务地址发起请求。在设计实…

从混乱到秩序:探索管理系统如何彻底改变工作流程

内容摘要 在许多企业与组织中&#xff0c;工作流程混乱是阻碍发展的“绊脚石”。员工们常常被繁琐的步骤、模糊的职责和沟通不畅等问题搞得焦头烂额&#xff0c;工作效率低下&#xff0c;错误频发。而与之形成鲜明对比的是&#xff0c;一些引入了先进管理系统的团队&#xff0…

最新研究揭示云端大语言模型防护机制的成效与缺陷

一项全面新研究揭露了主流云端大语言模型&#xff08;LLM&#xff09;平台安全机制存在重大漏洞与不一致性&#xff0c;对当前人工智能安全基础设施现状敲响警钟。该研究评估了三大领先生成式AI平台的内容过滤和提示注入防御效果&#xff0c;揭示了安全措施在阻止有害内容生成与…

HTML5+CSS3+JS小实例:具有粘性重力的磨砂玻璃导航栏

实例:具有粘性重力的磨砂玻璃导航栏 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width…

Python爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…

Webpack的基本使用 - babel

Mode配置 Mode配置选项可以告知Webpack使用相应模式的内置优化 默认值是production&#xff08;什么都不设置的情况下&#xff09; 可选值有&#xff1a;none | development | production; 这几个选项有什么区别呢&#xff1f; 认识source-map 我们的代码通常运行在浏览器…

链游技术破壁:NFT资产确权与Play-to-Earn经济模型实战

链游技术破壁&#xff1a;NFT资产确权与Play-to-Earn经济模型实战 ——从「投机泡沫」到「可持续生态」的技术重构 一、NFT确权技术革新&#xff1a;从链上存证到动态赋权 跨链确权架构 全链互操作协议&#xff1a;采用LayerZero协议实现以太坊装备与Solana土地的跨链组合&…

为什么HDI叠孔比错孔设计难生产

摘要&#xff1a;本文深入探讨了HDI&#xff08;高密度互连&#xff09;技术中叠孔与错孔设计在生产难度上的差异。通过对两种设计在对位精度、制程复杂性、可靠性挑战等方面进行详细分析&#xff0c;阐述了叠孔设计在生产过程中面临的一系列难题&#xff0c;旨在为HDI产品的设…

数据分析实战2(Tableau)

1、Tableau功能 数据赋能&#xff08;让业务一线也可以轻松使用最新数据&#xff09; 分析师可以直接将数据看板发布到线上自动更新看板自由下载数据线上修改图表邮箱发送数据设置数据预警 数据探索&#xff08;通过统计分析和数据可视化&#xff0c;从数据发现问题&#xf…

游戏开发中的CI/CD优化案例:知名游戏公司Gearbox使用TeamCity简化CI/CD流程

案例背景 关于Gearbox&#xff1a; Gearbox 是一家美国电子游戏公司&#xff0c;总部位于德克萨斯州弗里斯科&#xff0c;靠近达拉斯。Gearbox 成立于1999年&#xff0c;推出过多款史上最具代表性的视频游戏&#xff0c;包括《半衰期》、《战火兄弟连》以及《无主之地》。 团队…

Linux --TCP协议实现简单的网络通信(中英翻译)

一、什么是TCP协议 1.1 、TCP是传输层的协议&#xff0c;TCP需要连接&#xff0c;TCP是一种可靠性传输协议&#xff0c;TCP是面向字节流的传输协议&#xff1b; 二、TCPserver端的搭建 2.1、我们最终好实现的效果是 客户端在任何时候都能连接到服务端&#xff0c;然后向服务…

LlamaIndex 工作流简介以及基础工作流

什么是工作流&#xff1f; 工作流是一种由事件驱动、基于步骤的应用程序执行流程控制方式。 你的应用程序被划分为多个称为“步骤&#xff08;Steps&#xff09;”的部分&#xff0c;这些步骤由“事件&#xff08;Events&#xff09;”触发&#xff0c;并且它们自身也会发出事…

如何利用Elastic Stack(ELK)进行安全日志分析

在以下文章中&#xff0c;我将解释如何使用Elastic Stack&#xff08;ELK&#xff09;进行安全日志分析&#xff0c;以提高安全性和监控网络活动。ELK是一个功能强大的开源日志管理和分析平台&#xff0c;由Elasticsearch、Logstash和Kibana组成&#xff0c;适用于各种用例&…

创客匠人:以 AI 利器赋能创始人 IP 打造,加速知识变现新路径

在知识付费与个人 IP 崛起的时代&#xff0c;创客匠人作为行业领先的技术服务商&#xff0c;正通过 AI 工具重构创始人 IP 打造与知识变现的生态。其推出的三大 AI 利器 ——AI 销售信、免训数字人、AI 智能客服&#xff0c;精准解决 IP 运营中的核心痛点。 以 AI 销售信为例&…