首页 > 其他分享 >OpenGL 着色器详解

OpenGL 着色器详解

时间:2023-11-04 17:35:38浏览次数:36  
标签:std 片段 变量 OpenGL 详解 include 着色器 ifstream

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. 数据类型

向量

 向量重组

 6. glVertexAttribPointer

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

 

 顶点数据

 顶点着色器

 片段着色器

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

着色器配置完成,我们还要设置顶点数据的属性。

 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 */

 

标签:std,片段,变量,OpenGL,详解,include,着色器,ifstream
From: https://www.cnblogs.com/rmb999/p/17809545.html

相关文章

  • C/C++连接mysql(api接口方法详解)
      前言本篇记录C/C++连接mysql利用mysql的api接口的方法:这个方法的代码基本上很久都没有变过了,这里做个笔记来简单学习一下,还有一种方法等有时间了解后再来更新使用API的方式连接,需要先做环境配置,加载mysql的头文件和lib文件。可以看我之前的一篇文章VS中C/C++访问MySQL数据......
  • 详解主席树与二维数点问题
    主席树与二维数点问题前言:自己在网上搜索了很久,都没有看到具体是怎么维护的,下课问了下,一下就点醒了。正文:先考虑主席树和二维数点有什么关系。我们可以将y轴看成一个时间轴。我们查询y1-y2之间的数字时,其实就是查询这些版本下的x1-x2的区间和,最后由于可加性,直接差分相减即可......
  • Android动态代理详解
    动态代理在java里面算是一种比常用的技术,它和静态代理的区别在于静态代理需在编译的时候代理类就已经确定了,而动态代理的代理类是在运行的时候动态生成的。例如使用retrofit的时候我们只需要定义好interface:publicinterfaceGitHubService{@GET("users/{user}/repos")Ca......
  • 猫树详解
    一、猫树的作用学一个算法当然得先了解它的用处,那么猫树的作用嘛...简单来讲,线段树能维护的信息猫树基本都能维护比如什么区间和、区间gcd、最大子段和等 满足结合律且支持快速合并的信息二、猫树的算法实现什么都别说,我知道你想先知道猫树是怎么实现的我们就以区间和查......
  • Python 中的 __init__.py 和__all__ 详解(抄袭的joker) 因为写的实在是太好了
    Python中的__init__.py和__all__详解JOKER没意思先生 之前不论是自己写代码还是用别人的代码,都没有注意过这个东西,今天忽然看了一下,网上的教程感觉讲的都不是很清楚,自己又研究了研究,总结一下,如果有不对的地方,大家帮忙指正一下。在Python工程里,当pyth......
  • CentOS7中firewall防火墙详解和配置
    提示修改防火墙配置文件之前,需要对之前防火墙做好备份重启防火墙后,需要确认防火墙状态和防火墙规则是否加载,若重启失败或规则加载失败,则所有请求都会被防火墙拒绝firewalld的基本使用#停止firewallsystemctlstopfirewalld.service#禁止firewall开机启动systemctldisablefi......
  • T端与R端详解:光纤收发器接口区分与作用
    在光纤通信系统中,了解光纤收发器的T端(Transmit,发送端)与R端(Receive,接收端)对于保障数据传输的正确性至关重要。本文将对这两个接口进行详细解析。T端与R端的定义T端(Transmit端):这是光纤收发器用来发送信号的接口。它将电信号转换为光信号,通过光纤线路传送给对端设备。R端(Receive端):此接......
  • HashMap源码详解
    HashMap简介HashMap是Java语言中的一种集合类,它实现了Map接口,用于存储Key-Value对。它基于哈希表数据结构,通过计算Key的哈希值来快速定位Value的位置,从而实现高效的插入、删除和查找操作。下面我们对照着JAVA1.8中的HashMap源码来分析一下它的内部实现逻辑基本的结构在开始分析......
  • c#中工厂模式详解
    总体介绍:  工厂模式主要有三种类型:简单工厂、工厂方法和抽象工厂,该模式用于封装和管理对象的创建,是一种创建型模式。  万物皆对象,创建对象时必然需要new该对象,当需要更改对象时,需要把项目中所有地方都修改一遍,这显然违背了软件设计的开闭原则。  如果使用工厂来生成对象,......
  • C++ 移动构造函数详解
    目录移动构造函数是什么?复制构造和移动构造对比改进的拷贝构造移动构造实现移动构造优点左值、右值、左值引用、右值引用std::move参考移动构造函数是什么?移动构造是C++11标准中提供的一种新的构造方法。先举个生活例子,你有一本书,你已经不想看了,但我非常想看,那么我有哪......