定义自己的着色器

编写、编译、管理着色器是件麻烦事。在着色器的最后主题里,我们会写一个类来让我们的生活轻松一点,这个类从硬盘读着色器,然后编译和链接它们,对它们进行错误检测,这就变得很好用了。这也会给你一些关于如何把我们目前所学的知识封装到一个抽象的对象里的灵感。
       我们会在头文件里创建整个类,主要为了学习,也可以方便移植。我们先来添加必要的include,定义类结构:

#ifndef SHADER_H
#define SHADER_H
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
using namespace std;
#include <GL/glew.h>; // 包含glew获取所有的OpenGL必要headers
class Shader
{
public:
// 程序ID
GLuint Program;
// 构造器读取并创建Shader
Shader(const GLchar * vertexSourcePath, const GLchar * fragmentSourcePath);
// 使用Program
void Use();
};
#endif

注:
在上面,我们用了几个预处理指令(Preprocessor Directives)。这些预处理指令告知你的编译器,只在没被包
含过的情况下才包含和编译这个头文件,即使多个文件都包含了这个shader头文件,它是用来防止链接冲突的。

shader类保留了着色器程序的ID。它的构造器需要顶点和片段着色器源代码的文件路径,我们可以把各自的文本文件储存在硬盘上。 Use 函数看似平常,但是能够显示这个自造类如何让我们的生活变轻松(虽然只有一点)。

从文件读取

我们使用C++文件流读取着色器内容,储存到几个string对象里着色器

Shader(const GLchar * vertexPath, const GLchar * fragmentPath)
{
// 1. 从文件路径获得vertex/fragment源码
std::string vertexCode;
std::string fragmentCode;
try {
// 打开文件
std::ifstream vShaderFile(vertexPath);
std::ifstream fShaderFile(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// 读取文件缓冲到流
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// 关闭文件句柄
vShaderFile.close();
fShaderFile.close();
// 将流转为GLchar数组
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch(std::exception e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}

下一步,我们需要编译和链接着色器。注意,我们也要检查编译/链接是否失败,如果失败,打印编译错误,调试的时候这及其重要(这些错误日志你总会需要的):

着色器

// 2. 编译着色器
GLuint vertex, fragment;
GLint success;
GLchar 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);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl
;
};
// 对片段着色器进行类似处理
[...]
// 着色器程序
this->Program = glCreateProgram();
glAttachShader(this->Program, vertex);
glAttachShader(this->Program, fragment);
glLinkProgram(this->Program);
// 打印连接错误
glGetProgramiv(this->Program, GL_LINK_STATUS, &success);
if(!success)
{
glGetProgramInfoLog(this->Program, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// 删除着色器
glDeleteShader(vertex);
glDeleteShader(fragment);

最后我们也要实现Use函数:

void Use()
{
glUseProgram(this->Program);
}

现在我们写完了一个完整的着色器类。使用着色器类很简单;我们创建一个着色器对象以
后,就可以简单的使用了:
着色器

Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.frag");
...
while(...)
{
ourShader.Use();
glUniform1f(glGetUniformLocation(ourShader.Program, "someUniform"), 1.0f);
DrawStuff();
}

我们把顶点和片段着色器储存为两个叫做 shader.vs 和 shader.frag 的文件。你可以使用自己喜欢的名字命名着色器文件;我自己觉得用 .vs 和 .frag 作为扩展名很直观。

Learn OpenGL(五)——定义自己的着色器相关推荐

  1. Learn OpenGL (三):着色器

    GLSL 着色器是使用一种叫GLSL的类C语言写成的.GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性. 着色器的开头总是要声明版本,接着是输入和输出变量.uniform和mai ...

  2. Learn OpenGL(三)——顶点着色器(Vertext Shader)

    顶点着色器是几个着色器中的一个, 它是可编程的. 现代OpenGL需要我们至少设置一个顶点着色器和一个片段着色器, 如果我们打算做渲染的话. 我们会简要介绍一下着色器以及配置两个非常简单的着色器来绘制 ...

  3. Learn OpenGL(四)——片段着色器(Fragment Shader)

    片段着色器(Fragment Shader) 片段着色器是第二个也是最终我们打算创建的用于渲染三角形的着色器. 片段着色器的全部, 都是用来计算你的像素的最后颜色输出. 为了让事情比较简单, 我们的片 ...

  4. OpenGL ES 2.0 for Android教程(二):定义顶点和着色器

    OpenGL ES 2 第二章:定义顶点和着色器 文章传送门 OpenGL ES 2.0 for Android教程(一) OpenGL ES 2.0 for Android教程(三) OpenGL ...

  5. Android OpenGl Es 学习(二):定义顶点和着色器

    概述 这是一个新的系列,学习OpengGl Es,其实是<OpenGl Es 应用开发实践指南 Android卷>的学习笔记,感兴趣的可以直接看这本书,当然这个会记录自己的理解,以下只作为 ...

  6. OpenGL从入门到精通--着色器的使用

    着色器 github源码仓库 opengl环境准备 opengl编程从入门到精通-hello,window OpenGL从入门到精通–你好三角形 OpenGL从入门到精通–着色器的使用 着色器(Sha ...

  7. OpenGL ES (三)着色器和程序

    OpenGL ES学习系列文章: 上一篇:OpenGL ES (二)EGL介绍和使用 下一篇:OpenGL ES (二)EGL介绍和使用 着色器和程序 前言 1.创建Shader 2.加载Shader ...

  8. opengl顶点数据传送和着色器处理(vao,vbo)

    OpenGL学习脚印: 顶点数据传送和着色器处理1 写在前面 本节内容翻译和整理自<Learning Modern 3D Graphics Programming>Chapter1内容.作 ...

  9. OpenGL ES3.1使用计算着色器(Compute Shader)

    OpenGL ES3.1使用计算着色器(Compute Shader) 1.基本介绍 OpenGL ES从3.1版本开始支持计算着色器         工作模型有全局工作组和本地工作组,全局工作组包含 ...

最新文章

  1. javascript 表达式和运算符 (二)
  2. 李飞飞:为什么计算机视觉对机器人如此重要?
  3. python判断、创建文件夹
  4. ubuntu设置vim语法高亮显示和自动缩进
  5. lnmp编译安装mysql_LNMP编译安装教程
  6. 揭秘百万人围观的Facebook视频直播
  7. ORB-SLAM3 yaml文件介绍
  8. C++ begin( ) cbegin( ) end() cend()区别
  9. 禁用的灰色文本框、按钮的克星
  10. android 刷机 zip,ZipInstaller(ZIP刷机神器)
  11. 共模信号与差模信号(差分信号)
  12. pip 安装包成功 但是import 失败
  13. 蓝桥杯练习题——数列求和
  14. Android JNI总结
  15. no such file or directory, open '/Users/anna/package.json'的解决
  16. 1660_MIT 6.828 JOS初始化boot_alloc的初步实现
  17. 艾司博讯:拼多多新手如何正确使用多多进宝?
  18. Mac zshrc文件找不到问题
  19. [C语言]expected declaration or statement at end of input
  20. Integer源码 转2进制转16进制

热门文章

  1. 【李宏毅2020 ML/DL】P45-50 Network Compression
  2. WebStorm配置github
  3. 【项目篇】Android团队项目开发之统一代码规范
  4. Android中使用HttpURLConnection实现GET POST JSON数据与下载图片
  5. mysql单库tps_简单计算mysql 的QPS,TPS
  6. 字符串处理 BestCoder Round #43 1001 pog loves szh I
  7. 【转】MongoDB介绍及下载与安装
  8. Android:ViewFlipper、幻灯片
  9. 工业用计算机使用环境温度范围,IEC 61000-2-2
  10. Log4Net Layout使用以及扩展