之前我们将着色器的代码用glsl写好之后,保存为字符串指针,然后用一个函数去编译它,这是一种手段,对于简单的着色器代码可以这样。但当我们针对复杂的着色器,我们发现编写、编译、管理着色器是一件麻烦事。我们用一个类将着色器的所有编译,链接,管理都放在一个文件里。再将着色器源码单独设置成.glsl文件用来,从文件流读取,不再放到c++编译器里了。这样主函数就比较简洁了。 我们建立一个类shader,将一切着色器的步骤都在这个类里封装了,这样我们在主函数实例化它,我们就直接可以使用着色器不用在意内部的具体情况(从文件流读取,编译,链接)。

因为我们是在.h文件里面实现的这些步骤,包括函数的具体实现我们都放到.h文件里了,所以我们还需要一些特殊处理

#ifndef SHADER_H  //先测试x是否被宏定义过
#define SHADER_H //如果没有宏定义下面就宏定义x并编译下面的语句
#include <glad/glad.h>; // 包含glad来获取所有的必须OpenGL头文件
#include <string>
#include <fstream> //file stream ,fstream是C++ STL中对文件操作的合集,包含了常用的所有文件操作。
#include <sstream> //字符串流,可以支持C风格的串流的输入输出操作。
#include <iostream> #endif   //如果已经定义了则编译#endif后面的语句

然后我们可以声明这个类的结构了:

class Shader { 
public:
// 程序ID
unsigned int ID; // 构造器读取并构建着色器
Shader(const GLchar* vertexPath, const GLchar* fragmentPath);
// 使用/激活程序
void use();
// uniform工具函数
void setBool(const std::string &name, bool value) const;
void setInt(const std::string &name, int value) const;
void setFloat(const std::string &name, float value) const;
};

看着比较简单,接下来我们对函数的具体实现,分析,首先是最重要的构造函数,这一个函数里面,将一切编译链接都做完了。构造函数需要传入两个参数的文件地址(针对vs的.sln文件目录来说,我们把文件放到那里,就只需要一个名字就可以了),不用管第三个,那是一个几何地址,设为NULL。

Shader(const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr)
{
// 1. 从文件路径中获取顶点/片段着色器
//声明一些对象,ifstream 的意思是 从硬盘到内存的文件流对象,这个用来存储我们的着色器源码,并且对其进行处理
std::string vertexCode;
std::string fragmentCode;
std::string geometryCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
std::ifstream gShaderFile;
//保证ifstream对象可以抛出异常,防止错误读取,出现故障,即使停止。
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
gShaderFile.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();
// 转换数据流到string
stringvertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str(); // if geometry shader path is present, also load a geometry shader//这一段不用管。
if (geometryPath != nullptr)
{
gShaderFile.open(geometryPath);
std::stringstream gShaderStream;
gShaderStream << gShaderFile.rdbuf();
gShaderFile.close();
geometryCode = gShaderStream.str();
}
}
//如果捕捉到异常,输出一些文字告诉程序员
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
//声明两个字符串数组指针,用来存放从上面读下来的源码,用于接下来的编译
const char* vShaderCode = vertexCode.c_str();
const char * fShaderCode = fragmentCode.c_str();
//下面的就是编译,链接,删除,这都是类似的了。
// 2. compile shaders
unsigned int vertex, fragment;
// vertex shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// if geometry shader is given, compile geometry shader
unsigned int geometry;
if (geometryPath != nullptr)
{
const char * gShaderCode = geometryCode.c_str();
geometry = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(geometry, 1, &gShaderCode, NULL);
glCompileShader(geometry);
checkCompileErrors(geometry, "GEOMETRY");
}
// shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
if (geometryPath != nullptr)
glAttachShader(ID, geometry);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// delete the shaders as they're linked into our program now and no longer necessery
glDeleteShader(vertex);
glDeleteShader(fragment);
//不管这个
if (geometryPath != nullptr)
glDeleteShader(geometry); }

好了,现在我们已经成功创造出来了这个类的构造函数,接下来我们设计一下几个公有函数就好了,它们都很简单。 激活程序:

void use() { 
glUseProgram(ID);
}

// uniform工具函数:

void setBool(const std::string &name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}

void setInt(const std::string &name, int value) const
{

glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}

void setFloat(const std::string &name, float value) const { glUniform1f(glGetUniformLocation(ID, name.c_str()), value); } 好了,这样我们就解决了所有的问题,它让我们的使用变得非常简单易。

Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.fs"); 

... while(...) { 

ourShader.use(); 

ourShader.setFloat("someUniform", 1.0f); DrawStuff(); 

}
												

LearnOpenGL学习笔记(四)——着色器类编写的更多相关文章

  1. java之jvm学习笔记四(安全管理器)

    java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...

  2. 《深入理解java虚拟机》学习笔记四/垃圾收集器GC学习/一

    Grabage Collection      GC GC要完毕的三件事情: 哪些内存须要回收? 什么时候回收? 怎样回收? 内存运行时区域的各个部分中: 程序计数器.虚拟机栈.本地方法栈这3个区域随 ...

  3. ROS学习笔记四:用C++编写ROS发布与订阅

    1 创建并编译功能包 1.1 创建功能包 在工作空间的 src 目录下创建功能包: $ cd ~/dev/catkin_ws/src $ catkin_create_pkg chapter2_tuto ...

  4. Typescript 学习笔记四:回忆ES5 中的类

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  5. C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...

  6. JVM学习笔记-第六章-类文件结构

    JVM学习笔记-第六章-类文件结构 6.3 Class类文件的结构 本章中,笔者只是通俗地将任意一个有效的类或接口锁应当满足的格式称为"Class文件格式",实际上它完全不需要以磁 ...

  7. IOS学习笔记(四)之UITextField和UITextView控件学习

    IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ...

  8. java学习笔记07--日期操作类

    java学习笔记07--日期操作类   一.Date类 在java.util包中定义了Date类,Date类本身使用非常简单,直接输出其实例化对象即可. public class T { public ...

  9. java学习笔记13--比较器(Comparable、Comparator)

    java学习笔记13--比较器(Comparable.Comparator) 分类: JAVA 2013-05-20 23:20 3296人阅读 评论(0) 收藏 举报 Comparable接口的作用 ...

随机推荐

  1. 状态机学习(六)解析JSON2

    来自 从零开始的 JSON 库教程 从零开始教授如何写一个符合标准的 C 语言 JSON 库 作者 Milo Yip https://zhuanlan.zhihu.com/json-tutorial ...

  2. ABP 权限拦截 第二篇

    由于访问人数过多,我今天从新整理一下ABP权限认证机制,帮助大家更容易读懂 1.Abp 的权限拦截主要通过过滤器,    public class AbpAuthorizationFilter : I ...

  3. .net 资源释放(托管资源和非托管资源)

    1.托管资源 像int.float.DateTime等都是托管资源:net中80%的资源都是托管资源: 托管资源的回收通过GC(垃圾回收器)自动释放分配给该对象的内存,但无法预测进行垃圾回收的时间,我 ...

  4. 【转】《深入理解C# 3.x的新特性》博文系列汇总

    [转]<深入理解C# 3.x的新特性>博文系列汇总 较之C# 2.0, C# 3.x引入了一系列新的特性,为我们编程带来很大的便利,通过有效地利用这些新特性,我们可以编写出更加简洁.优雅的 ...

  5. JSP内置对象seesion

    什么是session session表示客户端与服务器的一次会话 Web中的session指的是用户在浏览某网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间 从上述定 ...

  6. Python踩坑之 sys.argv[-1]代表什么

    平台:win10+python 3.7.0 一.sys说明: sys.argv这个函数是我们写python脚本中最常用的一个函数. sys是Python的一个「标准库」,也就是官方出的「模块」,是「S ...

  7. css变换transform 以及 行内元素的一些说明

    变换transform的用法比较简单:[变换其实和普通的css属性,如color等没什么区别,可以为变换应用过渡或动画,就像其他普通css属性一样]#test { transform: transla ...

  8. shp文件和地理数据库文件的区别

    存储文件结构不同.所能进行的计算也不同. https://blog.csdn.net/lucahan/article/details/51761610 对数据库操作更快更方便,如何证明?尤其是数据量比 ...

  9. freeRTOSConfig.h文件对FreeRTOS进行系统配置

    FreeRTOS内核是高度可定制的,使用配置文件FreeRTOSConfig.h进行定制.每个FreeRTOS应用都必须包含这个头文件,用户根据实际应用来裁剪定制FreeRTOS内核.这个配置文件是针 ...

  10. 配置docker官方源并用yum安装docker

    一.docker的官方安装文档: https://docs.docker.com/engine/installation/linux/centos/ 由docker给的文档可以看出它也只是去配置了一个 ...