OpenGL 着色器详解
1. GLSL语言
glsl语言是用来编写着色器的,通过一段一段包含main函数的程序片段,告诉渲染引擎怎么去渲染内容。
glsl语言的语法有点类似c语言风格,只是增加了一些特有的关键字来修饰变量,下面是一个着色器基本的程序结构:

首先声明的是GLSL的版本号和模式,然后就是声明变量。像其他语言一样,声明变量我们要包括数据类型和变量名两个部分。而在GLSL中,我们还要指定变量的类型。
in,通过in关键字来声明,现在声明的变量是一个输入变量。out,对应的out关键字表明这是一个输出变量。uniform,而uniform就比较厉害了,他代表生成的变量是一个全局变量。
最后一部分就是主函数main。我们在这里处理输入输出值。
2. 输入变量in
首先我们可以定义的输入变量的个数是有限的,这一般取决于硬件,但OpenGL至少还是会为我们保留16个输入变量可用。我们可以查询GL_MAX_VERTEX_ATTRIBS来获取具体的上限:

3. 输出变量out
由于片段着色器直接输出色值交个显卡渲染,下一个阶段不会对色值进行处理,所以我们生成的输出变量无需与下一个阶段产生联系。但是试想一下,如果我们在顶点着色器中,想传递一个参数给片段着色器怎么办呢?**我们要建立起输入变量和输出变量间的关系。**怎么建立呢,GLSL给了我们一个很简单的方式来建立联系,一致的数据类型及变量名称即可。
顶点着色器

片段着色器

4 . 全局变量
全局的作用范围作用于整个着色器程序上。也就是你把带有全局变量x的着色器源码编译在着色器A上,再把A添加到着色器程序B上,那么B中的所有着色器都可以使用变量x。并且在GL编码中,也可以通过对应的着色器程序获取到指定的全局变量x。
这里我们仅提供一个在片段着色器中使用全局变量的示例:
片段着色器

渲染循环

5. 数据类型
向量

向量重组

这是一个设置顶点变量属性的函数,通过它可以设置步长,偏移量和位置

顶点数据

顶点着色器

片段着色器

我们看到,顶点着色器中,我们声明了两个输入变量。一个代表位置,一个代表颜色。然后我们要把颜色值传给片段着色器,所以要声明一个输出变量,在片段着色器中在声明一个以一样的输入变量,如此,颜色也就从顶点着色器传到片段着色器中了。
着色器配置完成,我们还要设置顶点数据的属性。

7. Shader Class
一个着色器程序就是一个最小的绘制单元。其绘制结果直接取决于两个着色器。基于面向对象的思想,我们希望对其进行封装。
我们来梳理一下加载着色器的代码:
创建着色器对象->附加着色器源码并编译->创建着色器程序->绑定着色器->链接着色器
基于上述过程,我们封装了一下着色器程序的类:
1 #ifndef Shader_hpp
2 #define Shader_hpp
3
4 #include <glad/glad.h>
5 #include <GLFW/glfw3.h>
6 #include <string>
7 #include <fstream>
8 #include <sstream>
9 #include <iostream>
10
11 class Shader {
12 public:
13 ///着色器程序ID
14 unsigned int ID;
15 ///构造器读取着色器
16 Shader(const GLchar * vertexPath,const GLchar * fragmentPath) {
17 std::string vertexCode;
18 std::string fragmentCode;
19 std::ifstream vShaderFile;
20 std::ifstream fShaderFile;
21 // ensure ifstream objects can throw exceptions:
22 vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
23 fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);
24 try
25 {
26 ///打开文件
27 vShaderFile.open(vertexPath);
28 fShaderFile.open(fragmentPath);
29 std::stringstream vShaderStream, fShaderStream;
30 ///读取文件
31 vShaderStream << vShaderFile.rdbuf();
32 fShaderStream << fShaderFile.rdbuf();
33 ///关闭文件
34 vShaderFile.close();
35 fShaderFile.close();
36 ///获取源码
37 vertexCode = vShaderStream.str();
38 fragmentCode = fShaderStream.str();
39 }
40 catch (std::ifstream::failure e)
41 {
42 std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
43 }
44 ///获取c字符串
45 const char* vShaderCode = vertexCode.c_str();
46 const char * fShaderCode = fragmentCode.c_str();
47 ///编译
48 unsigned int vertex, fragment;
49 ///顶点着色器
50 vertex = glCreateShader(GL_VERTEX_SHADER);
51 glShaderSource(vertex, 1, &vShaderCode, NULL);
52 glCompileShader(vertex);
53 checkCompileErrors(vertex, "VERTEX");
54 ///片段着色器
55 fragment = glCreateShader(GL_FRAGMENT_SHADER);
56 glShaderSource(fragment, 1, &fShaderCode, NULL);
57 glCompileShader(fragment);
58 checkCompileErrors(fragment, "FRAGMENT");
59 ///着色器程序
60 ID = glCreateProgram();
61 glAttachShader(ID, vertex);
62 glAttachShader(ID, fragment);
63 glLinkProgram(ID);
64 checkCompileErrors(ID, "PROGRAM");
65 ///释放着色器
66 glDeleteShader(vertex);
67 glDeleteShader(fragment);
68 }
69
70 ///激活主色器程序
71 void use() {
72 glUseProgram(ID);
73 }
74
75 ///uniform setter
76 void setBool(const std::string &name ,GLboolean value) const {
77 glUniform1i(glGetUniformLocation(ID,name.c_str()),(GLint)value);
78 }
79 void setInt(const std::string &name ,GLint value) const {
80 glUniform1i(glGetUniformLocation(ID,name.c_str()),value);
81 }
82 void setFloat(const std::string &name ,GLfloat value) const {
83 glUniform1f(glGetUniformLocation(ID,name.c_str()),value);
84 }
85 void setVec4f(const std::string &name ,GLfloat x,GLfloat y,GLfloat z,GLfloat w) const {
86 glUniform4f(glGetUniformLocation(ID,name.c_str()),x,y,z,w);
87 }
88
89 private:
90 ///检查编译错误
91 void checkCompileErrors(unsigned int shader, std::string type)
92 {
93 int success;
94 char infoLog[1024];
95 if (type != "PROGRAM")
96 {
97 glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
98 if (!success)
99 {
100 glGetShaderInfoLog(shader, 1024, NULL, infoLog);
101 std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
102 }
103 }
104 else
105 {
106 glGetProgramiv(shader, GL_LINK_STATUS, &success);
107 if (!success)
108 {
109 glGetProgramInfoLog(shader, 1024, NULL, infoLog);
110 std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
111 }
112 }
113 }
114 };
115
116 #endif /* Shader_hpp */
OpenGL 着色器详解的更多相关文章
- GLSL-几何着色器详解跟实例(GS:Geometry Shader)[转]
		
[OpenGL4.0]GLSL-几何着色器详解和实例(GS:Geometry Shader) 一.什么是几何着色器(GS:Geometry Shader) Input Assembler(IA)从顶点 ...
 - 顶点着色器详解 (Vertex Shaders)
		
学习了顶点处理,你就知道固定功能流水线怎么将顶点从模型空间坐标系统转化到屏幕空间坐标系统.虽然固定功能流水线也可以通过设置渲染状态和参数来改变最终输出的结果,但是它的整体功能还是受限.当我们想实现一个 ...
 - C编译器、链接器、加载器详解
		
摘自http://blog.csdn.net/zzxian/article/details/16820035 C编译器.链接器.加载器详解 一.概述 C语言的编译链接过程要把我们编写的一个c程序(源代 ...
 - Java类加载器详解
		
title: Java类加载器详解date: 2015-10-20 18:16:52tags: JVM--- ## JVM三种类型的类加载器- 我们首先看一下JVM预定义的三种类型类加载器,当一个 J ...
 - JMeter 后置处理器之正则表达式提取器详解
		
后置处理器之正则表达式提取器详解 by:授客 QQ:1033553122 1. 添加正则表达式提取器 右键线程组->添加->后置处理器->正则表达式提取器 2. 提取器配置介绍 ...
 - OpenGL着色器入门简介
		
说明:本文翻译自LearnOpengl经典教程,OpenGL着色器基础介绍的比较通俗易懂,特总结分享一下! 为什么要使用着色器?我们知道,OpenGL一般使用经典的固定渲染管线来渲染对象,但是随着Op ...
 - Solr系列五:solr搜索详解(solr搜索流程介绍、查询语法及解析器详解)
		
一.solr搜索流程介绍 1. 前面我们已经学习过Lucene搜索的流程,让我们再来回顾一下 流程说明: 首先获取用户输入的查询串,使用查询解析器QueryParser解析查询串生成查询对象Query ...
 - Lucene系列三:Lucene分词器详解、实现自己的一个分词器
		
一.Lucene分词器详解 1. Lucene-分词器API (1)org.apache.lucene.analysi.Analyzer 分析器,分词器组件的核心API,它的职责:构建真正对文本进行分 ...
 - python设计模式之装饰器详解(三)
		
python的装饰器使用是python语言一个非常重要的部分,装饰器是程序设计模式中装饰模式的具体化,python提供了特殊的语法糖可以非常方便的实现装饰模式. 系列文章 python设计模式之单例模 ...
 - Jmeter 正则表达式提取器详解(Regular Expression Exactor)
		
Jmeter 正则表达式提取器详解(Regular Expression Exactor) Name(名称):随意设置,最好有业务意义. Comments(注释):随意设置,可以为空 Apply to ...
 
随机推荐
- 树莓派命令——linux命令tips
			
sudo python3 test.py 和 python3 test.py 完全不是一个东西,有时候是链接的编译器不同,环境是完全不同,sudo会调用一些无关资源,反而容易造成程序运行失败或浪费cp ...
 - 抽象语法树AST必知必会
			
1 介绍 AST 打开前端项目中的 package.json,会发现众多工具已经占据了我们开发日常的各个角落,例如 JavaScript 转译.CSS 预处理.代码压缩.ESLint.Prettier ...
 - Cilium 系列-2-Cilium 快速安装
			
系列文章 Cilium 系列文章 前言 在本章中,我们将直接将 Cilium 安装到 Kubernetes 集群中. 在实验中,我们用到的组件及版本为: Cilium 1.13.4 K3s v1.26 ...
 - 获取客户端真实 IP 地址的最佳实践
			
一.背景 1. 业务上云带来性能收益 公司从去年全面推动业务上云,而以往 IDC 架构部署上,接入层采用典型的 4 层 LVS 多机房容灾架构,在业务高峰时期,扩容困难(受限于物理机资源和 LVS 内 ...
 - volatile是如何保证有序性的?
			
为什么需要保证有序性? 有如下代码,在int i = a;执行了的情况下,i的值最终会为几? public class NoVolatileExample { int a = 0; boolean f ...
 - 小白终于解决了在学习Go中不知道Makefile是什么的难题
			
如何在Go中使用Makefile 1.Makefile是什么 Makefile是一种构建工具,用于在项目中定义和执行一系列命令.它通常包含了一些规则和目标,用于编译.测试.运行和清理项目. 2.Mak ...
 - Vu3+Element-Plus根据路由配置生成菜单导航栏
			
先看效果,整体界面结构如下 点击左侧菜单栏,右侧切换显示不同页面内容. Vue3使用路由–南河小站 1 路由配置 路由配置如下: const routes = [ { path: "&quo ...
 - 结果过滤器—MVC项目中结果过滤器(Result Filter)使用
			
一.什么是结果过滤器? 结果过滤器(ResultFilter),是对执行的Action结果进行处理的一种AOP思想,适用于任何需要直接环绕 View 或格式化处理的逻辑.结果过滤器可以替换或更改 Ac ...
 - [ABC140E] Second Sum
			
2023-02-13 题目 题目传送门 翻译 翻译 难度&重要性(1~10):4 题目来源 AtCoder 题目算法 双向链表 解题思路 \(1.\) 当我们用从小到大的顺序来求解时,把原来求 ...
 - Web项目如何配置Eslint
			
介绍 ESLint 是一个根据方案识别并报告 ECMAScript/JavaScript 代码问题的工具,其目的是使代码风格更加一致并避免错误.在很多地方它都与 JSLint 和 JSHint 类似, ...