前言
在前面几节的学习中,发现每次都需要编写着色程序,特别麻烦,还有没有更好的方法呢?答案是肯定的,本节内容主要就是将着色器程序分装为着色器类,以后需要用到时可以直接调用,就很方便。
本文主要参考:https://learnopenglcn.github.io/01%20Getting%20started/05%20Shaders/
一、着色器是什么?
GLSL着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。
着色器的开头总是要声明版本,接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。
二、着色器
1.着色器类
#ifndef SHADER_H
#define SHADER_H
#include <glad/glad.h> // 包含glad来获取所有的必须OpenGL头文件
#include<glm/glm.hpp>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
using namespace std;
class Shader
{
public:
// 程序ID
unsigned int ID;
// 构造 读取并构建着色器
Shader(const char* vertexPath, const char* fragmentPath,const char* geometryPath=nullptr)
{
//从路径中获取顶点或片段着色器
string vertexCode,fragmentCode,geometryCode;
ifstream vShaderFile;
ifstream fShaderFile;
ifstream geometryFile;
//保证ifstream对象可以抛出异常
vShaderFile.exceptions(ifstream::failbit | ifstream::badbit);
fShaderFile.exceptions(ifstream::failbit | ifstream::badbit);
geometryFile.exceptions(ifstream::failbit | ifstream::badbit);
try
{
//打开文件
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
stringstream vShaderStream, fShaderStream;
//读取文件的缓存到数据流
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
//关闭文件
vShaderFile.close();
fShaderFile.close();
//转换到string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
if (geometryPath != nullptr)
{
geometryFile.open(geometryPath);
stringstream gShaderStream;
gShaderStream << geometryFile.rdbuf();
geometryFile.close();
geometryCode = gShaderStream.str();
}
}
catch (ifstream::failure &e)
{
cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << endl;
}
const char *vShaderCode = vertexCode.c_str();
const char *fShaderCode = fragmentCode.c_str();
//编译
unsigned int vertex, fragment;
int success;
char infoLog[512];
//顶点着色器
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertex, 512, NULL, infoLog);
cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << endl;
}
//片段着色器
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragment, 512, NULL, infoLog);
cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << endl;
}
unsigned int geometry;
if (geometryPath != nullptr)
{
const char *gShaderCode = geometryCode.c_str();
geometry = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(geometry, 1, &gShaderCode, NULL);
glCompileShader(geometry);
glGetShaderiv(geometry, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(geometry, 512, NULL, infoLog);
cout << "ERROR::SHADER::GEOMRTRY::COMPILATION_FAILED\n" << infoLog << endl;
}
}
//着色程序
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
if (geometryPath != nullptr)
glAttachShader(ID, geometry);
glLinkProgram(ID);
//打印链接错误
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(ID, 512, NULL, infoLog);
cout << "ERROR::SHADER::PROGRAM::LINK_FAILED\n" << infoLog << endl;
}
//删除顶点着色器、片段着色器
glDeleteShader(vertex);
glDeleteShader(fragment);
if (geometryPath != nullptr)
glDeleteShader(geometry);
}
// 使用/激活程序
void use()
{
glUseProgram(ID);
}
// uniform工具函数
void setBool(const string &name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
void setInt(const string &name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
void setFloat(const string &name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
void setVec2(const string &name, const glm::vec2 &value) const
{
glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1,&value[0]);
}
void setVec2(const string &name, float x,float y) const
{
glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);
}
void setVec3(const string &name, const glm::vec3 &value) const
{
glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec3(const string &name, float x,float y,float z) const
{
glUniform3f(glGetUniformLocation(ID, name.c_str()), x,y,z);
}
void setVec4(const string &name, const glm::vec4 &value) const
{
glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec4(const string &name, float x,float y,float z,float w) const
{
glUniform4f(glGetUniformLocation(ID, name.c_str()),x,y,z,w);
}
void setMat2(const string &name, const glm::mat2 &mat) const
{
glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1,GL_FALSE,&mat[0][0]);
}
void setMat3(const string &name, const glm::mat3 &mat) const
{
glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
void setMat4(const string &name, const glm::mat4 &mat) const
{
glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
};
#endif
2.主程序(三个角颜色不同的三角形)
#include<glad/glad.h>
#include<GLFW/glfw3.h>
#include"shader.h"
#include<iostream>
using namespace std;
const unsigned int SCR_HEIGHT = 600;
const unsigned int SCR_WIDTH = 800;
void framebuff_size_callback(GLFWwindow *window, int width, int height)
{
glViewport(0, 0, width, height);
}
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
int main()
{
//GLFW初始化
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//创建窗口
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "OpenglLearn", NULL, NULL);
if (window == NULL)
{
cout << "Failed to create GLFW window" << endl;
glfwTerminate();
return -1;
}
//窗口链接上下文
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuff_size_callback);
//检测GLAD初始化
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
cout << "Failed to initialize GLAD" << endl;
return -1;
}
//编译、链接的着色器程序
Shader ourShader("shader.vs","shader.fs");
//三角形坐标
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 VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//赋值,将数据从cpu发送到gpu的顶点缓存中
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);
//解绑
glBindBuffer(GL_ARRAY_BUFFER, 0);
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);
//交互缓冲,以及有无触发事件
glfwSwapBuffers(window);
glfwPollEvents();
}
//删除顶点数组、顶点缓冲
glDeleteVertexArrays(0, &VAO);
glDeleteBuffers(0, &VBO);
//释放内存
glfwTerminate();
system("pause");
return 0;
}