当前位置: 首页 > news >正文

【OpenGL】LearnOpenGL学习笔记03 - 着色器

上接: https://blog.csdn.net/weixin_44506615/article/details/149864439?spm=1001.2014.3001.5501
完整代码: https://gitee.com/Duo1J/learn-open-gl

一、着色器

在之前的三角形和矩形的绘制中,我们用到了顶点和片段着色器,接下来我们再稍微深入的了解一下着色器
我们之前写的顶点和片段着色器

// 顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;void main()
{gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}// 片段着色器
#version 330 core
out vec4 FragColor;void main()
{FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
} 

1. 数据类型
GLSL包含的基本数据类型有

int、uint、floatdoublebool

除此之外,GLSL还包含两种容器类型: 向量矩阵
现在先了解一下向量,矩阵等之后用到再聊

类型含义
vecn包含n个float分量的默认向量
bvecn包含n个bool分量的向量
ivecn包含n个int分量的向量
uvecn包含n个unsigned int分量的向量
dvecn包含n个double分量的向量

你可以像下面这样访问向量的分量

vec4 v;
vec3 v2 = v.xyz;
vec3 v3 = v.xxx;
vec4 v4 = v.xyzw + v.zwxy;
vec2 v5 = v.yz;

2. 输入和输出
GLSL定义了inout关键字来表示这个变量是否是输入或是输出变量
我们之前还用到了layout (location = 0)来将我们声明的变量链接到顶点数据中 (glVertexAttribPointer的第一个参数设置了顶点的location)

// 顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;
//定义输出变量
out vec4 vertexColor;void main()
{gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);// 输出变量vectexColor设置为暗红色vertexColor = vec4(0.5, 0.0, 0.0, 1.0);
}// 片段着色器
#version 330 core
out vec4 FragColor;
// 顶点着色器传来的输入变量,需要名称相同、类型相同
in vec4 vertexColor;void main()
{FragColor = vertexColor;
} 

3. uniform
如果我们想要从CPU传递数据到GPU上,我们可以使用uniform关键字
unform是全局变量,意味着在每个着色器程序中都是独一无二的,同时uniform在被赋值之后它会一直保存他们的数据,直到被重置或是更新

// 片段着色器
#version 330 core
out vec4 FragColor;
// 定义uniform变量
uniform vec4 ourColor;void main()
{FragColor = ourColor;
}
// 获取时间值
float timeValue = glfwGetTime();
// 通过sin函数计算随时间变化的g值
float greenValue = sin(timeValue) / 2.0f + 0.5f;
// 获取我们声明的uniform变量的位置
int ourUniformColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUseProgram(shaderProgram);
// 设置uniform变量
glUniform4f(ourUniformColorLocation, 0, greenValue, 0, 1);

编译运行,顺利的话我们可以看见我们绘制的图形在一直变换颜色
4. 传入更多属性
前面提到,我们可以通过location = n来将我们声明的变量链接到顶点数据中,这意味着我们可以传入更多不同类型的顶点数据
修改一下vertices

float 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    // 顶部
};

接下来调整一下我们的着色器

// 顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos;
// 从location = 1 传入颜色值
layout (location = 1) in vec3 aColor;out vec3 vertexColor;void main()
{gl_Position = vec4(aPos, 1.0);vertexColor = aColor;
}// 片段着色器
#version 330 core
out vec4 FragColor;
in vec3 vertexColor;void main()
{FragColor = vec4(vertexColor, 1.0);
}

最后再修改一下我们的顶点属性
对于颜色属性,我们需要注意第一位参数会变为1,以此来链接到location = 1的变量,同时最后一位参数需要设置为3 * sizeof(float)的偏移量
由于多了3个float的颜色值,所以步长将变为6 * sizeof(float)

// 位置属性
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);

编译运行,顺利的话可以看见一个彩色的三角形
顶点色
这里虽然我们只提供了三个顶点处的颜色值,但片段着色器会进行片段插值,其会根据每个片段在三角形形状上所处的相对位置来决定这些片段的颜色

二、着色器类

接下来我们封装一下我们的着色器类,并且将着色器抽离成单独文件,以便后续使用
直接上代码
Shader.h

#pragma once#include <glad/glad.h>;#include <string>
#include <fstream>
#include <sstream>
#include <iostream>/**
* 着色器
*/
class Shader
{
public:/*** 着色器程序ID*/unsigned int ProgramID;/*** @param vertexPath: 顶点着色器路径* @param fragmentPath: 片段着色器路径*/Shader(const char* vertexPath, const char* fragmentPath);/*** 使用这个着色器程序*/void Use();/*** 销毁这个着色器程序*/void Delete();/*** 设置bool类型uniform值*/void SetBool(const std::string& name, bool value) const;/*** 设置int类型uniform值*/void SetInt(const std::string& name, int value) const;/*** 设置float类型uniform值*/void SetFloat(const std::string& name, float value) const;
};

Shader.cpp

#include "Shader.h"Shader::Shader(const char* vertexPath, const char* fragmentPath)
{std::string vertexCode;std::string fragmentCode;std::ifstream vertexShaderFile;std::ifstream fragmentShaderFile;vertexShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);fragmentShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);try{// 打开着色器代码文件vertexShaderFile.open(vertexPath);fragmentShaderFile.open(fragmentPath);std::stringstream vShaderStream, fShaderStream;// 读取文件到字符串流中vShaderStream << vertexShaderFile.rdbuf();fShaderStream << fragmentShaderFile.rdbuf();// 关闭文件流vertexShaderFile.close();fragmentShaderFile.close();// 流转换为字符串vertexCode = vShaderStream.str();fragmentCode = fShaderStream.str();}catch (std::ifstream::failure e){std::cout << "[Error] Failed to read shader file" << std::endl;}// 转为c-style字符串const char* vertexShaderCode = vertexCode.c_str();const char* fragmentShaderCode = fragmentCode.c_str();// 创建、赋值、编译、链接着色器unsigned int vertexShader, fragmentShader;int success;char infoLog[512];// 顶点着色器vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderCode, NULL);glCompileShader(vertexShader);glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "[Error] Vertex shader compile failed!\n" << infoLog << std::endl;};// 片段着色器fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderCode, NULL);glCompileShader(fragmentShader);glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "[Error] Fragment shader compile failed!\n" << infoLog << std::endl;}ProgramID = glCreateProgram();glAttachShader(ProgramID, vertexShader);glAttachShader(ProgramID, fragmentShader);glLinkProgram(ProgramID);glGetProgramiv(ProgramID, GL_LINK_STATUS, &success);if (!success){glGetProgramInfoLog(ProgramID, 512, NULL, infoLog);std::cout << "[Error] Shader program link failed!\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(fragmentShader);
}void Shader::Use()
{glUseProgram(ProgramID);
}void Shader::Delete()
{glDeleteProgram(ProgramID);
}void Shader::SetBool(const std::string& name, bool value) const
{glUniform1i(glGetUniformLocation(ProgramID, name.c_str()), (int)value);
}void Shader::SetInt(const std::string& name, int value) const
{glUniform1i(glGetUniformLocation(ProgramID, name.c_str()), value);
}void Shader::SetFloat(const std::string& name, float value) const
{glUniform1f(glGetUniformLocation(ProgramID, name.c_str()), value);
}

VertexShader.vs

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;out vec3 vertexColor;void main()
{gl_Position = vec4(aPos, 1.0);vertexColor = aColor;
}

FragmentShader.fs

#version 330 core
out vec4 FragColor;
in vec3 vertexColor;void main()
{FragColor = vec4(vertexColor, 1.0);
}

Main.cpp

#include <iostream>#include <glad/glad.h>
#include <GLFW/glfw3.h>#include "Shader.h"float 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    // 顶部
};unsigned int indices[] = {0, 1, 3, // 第一个三角形1, 2, 3  // 第二个三角形
};void ProcessInput(GLFWwindow* window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE)){glfwSetWindowShouldClose(window, true);}
}void OnSetFrameBufferSize(GLFWwindow* window, int width, int height)
{glViewport(0, 0, width, height);
}int main()
{glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);GLFWwindow* window = glfwCreateWindow(1280, 720, "OpenGLRenderer", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}glfwSetFramebufferSizeCallback(window, OnSetFrameBufferSize);unsigned int VAO;glGenVertexArrays(1, &VAO);glBindVertexArray(VAO);unsigned int VBO;glGenBuffers(1, &VBO);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);unsigned int EBO;glGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);// 替换为Shader类Shader shader("VertexShader.vs", "FragmentShader.fs");while (!glfwWindowShouldClose(window)){glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);ProcessInput(window);// 使用着色器程序shader.Use();glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 3);//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);glfwSwapBuffers(window);glfwPollEvents();}// 销毁着色器程序shader.Delete();glfwTerminate();return 0;
}

编译运行,顺利的话可以看见之前绘制的彩色三角形

http://www.dtcms.com/a/314248.html

相关文章:

  • 2025年半导体探针卡市场深度调研:规模数据、竞争格局
  • 防火墙的进阶练习
  • PVE环境对网口和wifi的配置
  • Neo4j 基础语法指南
  • 基于Spring Cloud Gateway和Resilience4j的微服务容错与流量控制实战经验分享
  • javacc学习笔记 03、编译原理实践 - JavaCC解析表达式并生成抽象语法树
  • MySQL5.0数据库管理系统安装部署
  • PCB反焊盘的样子越诡异,高速过孔的性能越好?
  • [自动化Adapt] 父子事件| 冗余过滤 | SQLite | SQLAlchemy | 会话工厂 | Alembic
  • 【物联网】基于树莓派的物联网开发【23】——树莓派安装SQLite嵌入式数据库
  • 秋招笔记-8.4
  • 小实验:按键点灯(中断法)
  • QT的UDP
  • 【数据结构入门】链表
  • Solidity智能合约开发全攻略
  • Java基础-斗地主游戏
  • ArrayDeque双端队列--底层原理可视化
  • ubuntu修改时区
  • 【HZ-T536开发板免费体验】Cangjie Magic调用视觉语言大模型(VLM)真香,是不是可以没有YOLO和OCR了?
  • 通用 PDF 文件流 OCR 到文本 API 接口
  • 使用 Aspose.OCR 将图像文本转换为可编辑文本
  • 基础14-Java集合框架:掌握List、Set和Map的使用
  • 十字滑台:精密制造的“心脏“如何跳动?
  • 耘瞳科技国产化点云处理软件,开启智能化三维测量新时代
  • [机器学习]01-构建简单的贝叶斯分类器
  • 抓包相关知识学习
  • 项目复盘:Arena Of Furnace
  • vtkSSAAPass代码解析
  • [自动化Adapt] 回放策略 | AI模型驱动程序
  • Python异常捕获全指南