【OpenGL学习】(五)自定义着色器类
文章目录
- 【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()
{// 初始化GLFWglfwInit();// 配置GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 设置OpenGL的主版本号为3glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 设置OpenGL的次版本号为3glfwWindowHint(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(); // 如果窗口创建失败,终止GLFWreturn -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 // colors0.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);
#endifGLFWwindow* 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[] = {// positions0.5f, -0.5f, 0.0f, // bottom right-0.5f, -0.5f, 0.0f, // bottom left0.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);
}
相关文章:

【OpenGL学习】(五)自定义着色器类
文章目录 【OpenGL学习】(五)自定义着色器类着色器类插值着色统一着色 【OpenGL学习】(五)自定义着色器类 项目结构: 着色器类 // shader_s.h #ifndef SHADER_H #define SHADER_H#include <glad/glad.h>#inc…...

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

超声波清洗设备的清洗效果如何?
超声波清洗设备是一种常用于清洗各种物体的技术,它通过超声波振荡产生的微小气泡在液体中破裂的过程来产生高能量的冲击波,这些冲击波可以有效地去除表面和细微裂缝中的污垢、油脂、污染物和杂质。超声波清洗设备在多个领域得到广泛应用,包括…...
k8s部署dify
以前部署过,最近重新部署发现还是存在很多问题,这里进行记录 1.基础配置内容 配置信息和账号密码 # dify-deployment.yaml--- # Namespace apiVersion: v1 kind: Namespace metadata:name: dify-min--- # ConfigMap for shared environment variables…...

“草台班子”的成长路径分析
一、草台班子的起点:用最小成本验证价值 特点: 团队规模小(通常3-5人),成员背景杂(可能是程序员产品经理运营的混搭);资源匮乏(无资金、无技术中台、无客户积累&#x…...
RAG技术解析:实现高精度大语言模型知识增强
RAG技术解析:实现高精度大语言模型知识增强 RAG概述 RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合检索系统和生成模型的技术架构,旨在提高大语言模型回答问题的准确性和相关性。当遇到如"如何退…...

软件测评服务如何依据标准确保品质?涵盖哪些常见内容?
软件测评服务涉及对软件的功能和性能等多维度进行评估和检验,这一过程有助于确保软件的品质,降低故障发生率及维护费用,对于软件开发和维护环节具有至关重要的价值。 测评标准依据 GB/T 25000.51 - 2016是软件测评的核心依据。依照这一标准…...
大数据学习(131)-Hive数据分析函数总结
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
SCAU数值计算OJ
18957.计算自然对数ln(x)的导数 Description 求自然对数ln(x)的导数,输入双精度实数x>1,输出自然对数ln(x)的导数(精确到小数点后2位有效数,小数点后第2位四舍五入所得)。输入格式 m(整数,实验数据总…...
c++ 基于openssl MD5用法
基于openssl MD5用法 #include <iostream> #include <openssl/md5.h> using namespace std; int main(int argc, char* argv[]) { cout << "Test Hash!" << endl; unsigned char data[] "测试md5数据"; unsigned char out[1024…...

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

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

SpringCloud学习笔记-2
说明:来源于网络,如有侵权请联系我删除 1.提问:如果注册中心宕机,远程调用还能成功吗 答:当微服务发起请求时,会向注册中心请求所有的微服务地址,然后在向指定的微服务地址发起请求。在设计实…...
C++.OpenGL (9/64)复习(Review)
复习(Review) 核心概念快速回顾 #mermaid-svg-MMSQf7gXQlHqiqfM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-MMSQf7gXQlHqiqfM .error-icon{fill:#552222;}#mermaid-svg-MMSQf7gXQlHqiqfM .error-text{fill:#…...
Spring Boot-面试题(52)
摘要: 1、通俗易懂,适合小白 2、仅做面试复习用,部分来源网络,博文免费,知识无价,侵权请联系! 1. 什么是 Spring Boot 框架? Spring Boot 是基于 Spring 框架的快速开发框架&#…...

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

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

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…...
CVAT标注服务
CVAT 是一个交互式的视频和图像标注工具,适用于计算机视觉,是一个典型的现代Web应用架构,可以实现大部分情况的标注工作,可以通过serveless CVAT-github cvat文档 下面将就其配置介绍一下几个服务: 1. 核心服务 (C…...
SpringBoot+Mybatisplus配置多数据源(超级简单!!!!)
今天分享配置多数据源的另外一种方式,SpringBoMybatisplus配置多数据源,此种方式配置相对简单,都是苞米豆封装好的,配置容易;此篇分享比较简单的方式配置数据源,多个固定的数据源,通过注解选择使…...
Git Svn
github一般需要科学上网,通过SourceTree通过URL克隆,会提示无效URL或者SLL Timeout之类,如果电脑开启了VPN,在系统设置-网络-DNS查看代理端口,如:127.0.0.1:7890 手动配置git代理 git config --global ht…...

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

Webpack的基本使用 - babel
Mode配置 Mode配置选项可以告知Webpack使用相应模式的内置优化 默认值是production(什么都不设置的情况下) 可选值有:none | development | production; 这几个选项有什么区别呢? 认识source-map 我们的代码通常运行在浏览器…...
LLaMA-Factory的5种推理方式总结
LLaMA-Factory 作为一款开源的大语言模型微调与推理框架,提供了 5 种核心推理方式,覆盖从本地调试到生产部署的全流程需求。以下是具体方式及示例: 1. 交互式命令行推理 适用场景:快速测试模型效果或进行简单对话。 示例命令&am…...

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

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

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

游戏开发中的CI/CD优化案例:知名游戏公司Gearbox使用TeamCity简化CI/CD流程
案例背景 关于Gearbox: Gearbox 是一家美国电子游戏公司,总部位于德克萨斯州弗里斯科,靠近达拉斯。Gearbox 成立于1999年,推出过多款史上最具代表性的视频游戏,包括《半衰期》、《战火兄弟连》以及《无主之地》。 团队…...

Linux --TCP协议实现简单的网络通信(中英翻译)
一、什么是TCP协议 1.1 、TCP是传输层的协议,TCP需要连接,TCP是一种可靠性传输协议,TCP是面向字节流的传输协议; 二、TCPserver端的搭建 2.1、我们最终好实现的效果是 客户端在任何时候都能连接到服务端,然后向服务…...

LlamaIndex 工作流简介以及基础工作流
什么是工作流? 工作流是一种由事件驱动、基于步骤的应用程序执行流程控制方式。 你的应用程序被划分为多个称为“步骤(Steps)”的部分,这些步骤由“事件(Events)”触发,并且它们自身也会发出事…...