OpenGL着色器语言(GLSL)看上去很像C语言,它由OpenGL实现进行编译和连接,并且(经常是)完全在图形硬件中运行。

我们有顶点着色器、片段着色器和几何着色器,前两种是必需的,后一种是可选的。

有三种向顶点着色器传递数据的方式:(1)参数,是对每个顶点而言;

(2)统一值,是针对整个顶点数据批次的常量(所以是一致的)

(3)加载和使用纹理数据

可以为片段着色器设置统一值和纹理数据。将顶点属性发送到片段着色器毫无意义,因为片段着色器只是用来在图元进行光栅化后对片段(最基本的是像素)进行填充。不过,每个顶点数据都可以通过顶点程序传递到片段着色器。但是在着这种情况下,这些数据可能是常量(每个片段都是这样的值),或者这些值也可以用不同的方式在图元表面进行插值。

着色器程序和C语言类似,从入口点main函数开始,并且使用同样的字符集和注释约定,以及很多相同的处理指令。

顶点着色器实例

//顶点着色器
#version 330
in vec4 vVertex;  //顶点位置属性
in vec4 vColor;   //顶点颜色属性
out vec4 vVaryingColor; //传递到片段着色器的颜色值
void main(void)
{
vVaryingColor=vColor; //简单复制颜色值
gl_Position=vVertex; //简单传递顶点位置}

片段着色器实例

#version 330
out vec4 vFragColor; //将要进行光栅化的片段颜色
in vec4 vVaryingColor; //从顶点阶段得到的颜色
void main(void)
{
vFragColor=vVaryingColor; //对片段进行颜色插值}

每个着色器的第一行非命令行都是指定版本。

#version 330

这一行指定这个着色器要求的OpenGL着色语言的最低版本为3.3。如果OpenGL驱动不支持3.3版,那么着色器将不会编译。

顶点着色器分析

属性声明

属性是由C/C++客户端OpenGL代码逐个顶点进行指定的。在顶点着色器中,这些属性只是简单地声明为in。

in vec4 vVertex;in vec4 vColor;

vVertex和vColor是两个导入属性,即一个4分量顶点位置和一个4分量顶点颜色值。在GLSL中,每个顶点程序都最多可以有16个属性。

标记为in的变量是只读。对变量名进行重用,在着色器中进行一些中间计算看起来比较聪明,但是如果试图这样做的话,那么驱动中的GLSL编译器就会产生一个错误。

声明输出

out  vec4 vVaryingColor;

这个变量将成为将要传送到片段着色器的顶点的颜色值。实际上,这个变量必须为片段着色器声明为一个in变量,否则在我们试图将着色器编译和连接到一起时,就会得到一个连接错误。

当在一个顶点着色器中将一个值声明为out,并在片段着色器中将其声明为in时,这个片段着色器接受的变量值为一个插补值。在默认情况下,这些工作将以一种正确透视的方式进行,并且在变量之前指定另一个额外的限定符smooth,以确保完成了这些工作。还可以指定flat声明不应进行插值,或者指定noperspective来声明在各个值之间进行直接线性插值。当使用flat时,还有必要进行一些额外的考虑。

main函数

void amin(void)
{
vVaryingColor=vColor;
gl_Position=vVertex;}

我们将颜色输入属性分配给即将发出的插补值,并且未经变换直接将输入的顶点值分配给gl_Position。

变量gl_Position是一个预定义的内建4分量向量,它包含顶点着色器要求的一个输出。输入gl_Position的值是几何图形阶段用来装配图元。

片元着色器分析

在渲染一个图元(例如三角形)时,一旦3个顶点由顶点程序进行了处理,那么它们将组装成一个三角形,而这个三角形将由硬件进行光栅化。图形硬件确定独立片段属于(或者更精确地,在颜色缓冲区中)的什么位置,并且为三角形中的每个片段(如果不进行任何多重采样的话则只是一个点)执行片段程序的一个实例。

片段程序的最终输出颜色是一个4分量浮点向量:out vec4 vFragColor;

如果片段程序只有一个输出,那么它在内部将分配为“输出0”。这是片段着色器的第一个输出,并且将传输到由glDrawBuffer设置的缓冲区目标,默认情况下为GL_BLACK,即黑色缓冲区(对于双重缓冲环境来说是这样的)。实际颜色缓冲区常常并不包含4个浮点分量,这样输出值就会映射到目标缓冲区的范围内。

输入片段着色器是经过平滑插值的颜色值,由顶点上游传入。这只是作为一个in变量进行声明的。

in vec4 vVaryingColor;

片段着色器的主程序是将平滑插值颜色值直接分配给片段颜色。

vFragColor=vVaryingColor;

编译、绑定和连接

着色器源代码被传递给驱动程序,然后进行编译,最后进行连接,就像我们要对所有的C/C++程序做的一样。此外,着色器中的属性名需要绑定到由GLSL提供的16个预分配属性槽中的某一个。在整个过程中,我们可以检查错误,甚至可以接收从驱动程序传回的关于试图简历着色器时为什么会失败的诊断信息。

OpenGL API不支持任何类型的文件I/O操作。着色器的源代码采用什么样的方式,由程序员根据哪种方式对应用程序有利来进行编译。最简单的方式是将存储在ASCII纯文本文件中。这样要使用典型的文件系统函数从磁盘中加载文本文件就是一件很简单的事了。我们还可以使用VS2017的扩展与更新功能,下载GLSL包,并将顶点着色器和片元着色器的后缀分别改为.vert和.frag,就可以高亮显示着色器语言。

我们创建一个Shder类来加载和初始化着色器。

Shader.h

#pragma once
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader
{
public:unsigned int ID;   //程序IDenum  Slot{DIFFUSE,SPECULAR};//构造器读取并构建着色器Shader(const GLchar *vertexPath, const GLchar * fragmentPath, const char* geometryPath = nullptr);//使用(激活)程序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;void setMat4(const std::string &name, const glm::mat4 &mat) const;void setVec3(const std::string & name, float x, float y, float z)const;void SetUniform3f(const char* paramNameString, glm::vec3 param);void SetUniform1f(const char* paraNameString, float param);void SetUniform1i(const char* paraNameString, int slot);
private:void checkCompileErrors(GLuint shader, std::string type);
};

Shader.cpp

#include <glad/glad.h>
#include<glm\glm.hpp>
#include<string>
#include<fstream>
#include<sstream>
#include<iostream>
#include "Shader.h"Shader::Shader(const GLchar * vertexPath, const GLchar * fragmentPath, const char * geometryPath)
{// 1. retrieve the vertex/fragment source code from filePathstd::string vertexCode;std::string fragmentCode;std::string geometryCode;std::ifstream vShaderFile;std::ifstream fShaderFile;std::ifstream gShaderFile;// ensure ifstream objects can throw exceptions: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{// open filesvShaderFile.open(vertexPath);fShaderFile.open(fragmentPath);std::stringstream vShaderStream, fShaderStream;// read file's buffer contents into streamsvShaderStream << vShaderFile.rdbuf();fShaderStream << fShaderFile.rdbuf();// close file handlersvShaderFile.close();fShaderFile.close();// convert stream into stringvertexCode = vShaderStream.str();fragmentCode = fShaderStream.str();// if geometry shader path is present, also load a geometry shaderif (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 shadersunsigned int vertex, fragment;// vertex shadervertex = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertex, 1, &vShaderCode, NULL);glCompileShader(vertex);checkCompileErrors(vertex, "VERTEX");// fragment Shaderfragment = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragment, 1, &fShaderCode, NULL);glCompileShader(fragment);checkCompileErrors(fragment, "FRAGMENT");// if geometry shader is given, compile geometry shaderunsigned 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 ProgramID = 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 necesseryglDeleteShader(vertex);glDeleteShader(fragment);if (geometryPath != nullptr){glDeleteShader(geometry);}
}
void Shader::use()
{glUseProgram(ID);
}
// utility uniform functions
// ------------------------------------------------------------------------
void Shader::setBool(const std::string &name, bool value) const
{glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
// ------------------------------------------------------------------------
void Shader::setInt(const std::string &name, int value) const
{glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
// ------------------------------------------------------------------------
void Shader::setFloat(const std::string &name, float value) const
{glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}void Shader::checkCompileErrors(GLuint shader, std::string type)
{GLint success;GLchar infoLog[1024];if (type != "PROGRAM"){glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(shader, 1024, NULL, infoLog);std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;}}else{glGetProgramiv(shader, GL_LINK_STATUS, &success);if (!success){glGetProgramInfoLog(shader, 1024, NULL, infoLog);std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;}}
}void Shader::setMat4(const std::string &name, const glm::mat4 &mat) const
{glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}void Shader::setVec3(const std::string & name, float x,float y,float z) const
{glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);
}void Shader::SetUniform3f(const char * paramNameString, glm::vec3 param)
{glUniform3f(glGetUniformLocation(ID, paramNameString), param.x,param.y,param.z);
}void Shader::SetUniform1f(const char * paraNameString, float param)
{glUniform1f(glGetUniformLocation(ID, paraNameString), param);
}void Shader::SetUniform1i(const char * paraNameString, int slot)
{glUniform1i(glGetUniformLocation(ID, paraNameString), slot);
}

指定属性

使用我们的Shader类来获取顶点程序文件和片段程序文件。

Shader *ourShader = new Shader("VertexSource.vert", "fragmentSource.frag");

初始化并将模型数据加载到VAO和VBO

unsigned int  VAO;glGenVertexArrays(1, &VAO);glBindVertexArray(VAO);unsigned int VBO;glGenBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// position attributeglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);

连接着色器

这一步我们已经在Shader类中实现了,将着色器连接之后,我们就可以丢弃顶点着色器对象。

// shader ProgramID = 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 necesseryglDeleteShader(vertex);glDeleteShader(fragment);if (geometryPath != nullptr){glDeleteShader(geometry);}

使用着色器

我们将此功能放在Shader类的use函数中实现

void Shader::use()
{glUseProgram(ID);
}

使用ourSahder->use来调用该功能。

然后设置绑定模型

glBindVertexArray(VAO);

绘制图形(此处为三角形)

glDrawArrays(GL_TRIANGLES, 0, 3);

此部分也可以参考文章OpenGL学习之绘制三角形

OpenGL学习之着色器详解相关推荐

  1. OpenGL ES _ 着色器_片断着色器详解

    OpenGL ES _ 入门_01 OpenGL ES _ 入门_02 OpenGL ES _ 入门_03 OpenGL ES _ 入门_04 OpenGL ES _ 入门_05 OpenGL ES ...

  2. 顶点着色器详解 (Vertex Shaders)

    顶点着色器详解 (Vertex Shaders) 2014-2-9 20:56| 发布者: 隐龙| 查看: 1631| 评论: 0 摘要: 学习了顶点处理,你就知道固定功能流水线怎么将顶点从模型空间坐 ...

  3. Tomcat - 深度学习 - 类加器详解

    前言 Tomcat如何实现不同的应用程序,使用不同的第三方类库?带着疑问学下去 打破双亲委派 以Tomcat类加载为例,Tomcat 如果使用默认的双亲委派类加载机制行不行? 我们思考一下: Tomc ...

  4. 着色器_片断着色器详解

    本节学习目标 输入值和输出值 如何渲染多个输出缓冲区 输入值和输出值 片段着色器内置变量 输入值:片段着色器接受顶点管线最终输出的迭代值,这些值包括片段的位置,已解析的主颜色和辅助颜色,一系列的纹理坐 ...

  5. unity曲面细分着色器详解

     前言:本文翻译自catlikecoding上一篇十分详细的英文blog并修改了几处错误,逐行解释了如何在自己的shader中添加曲面细分支持,并给出了多种计算细分因子的方案以及它们的优缺点. 原文链 ...

  6. Three.js-着色器加工材质及材质着色器详解

    在Three中,我们可以使用着色器对材质进行加工,例如在对物体材质进行设置时,我们可以通过对顶点着色器的更改,从而实现物体的运动或变化.使用着色器加工材质,主要依赖于Material材质基类中的onB ...

  7. 深度学习各类优化器详解(动量、NAG、adam、Adagrad、adadelta、RMSprop、adaMax、Nadam、AMSGrad)

    深度学习梯度更新各类优化器详细介绍 文章目录 <center>深度学习梯度更新各类优化器详细介绍 一.前言: 二.梯度下降变形形式 1.批量归一化(BGD) 2.随机梯度下降(SGD) 3 ...

  8. 【深度学习】优化器详解

    优化器 深度学习模型通过引入损失函数,用来计算目标预测的错误程度.根据损失函数计算得到的误差结果,需要对模型参数(即权重和偏差)进行很小的更改,以期减少预测错误.但问题是如何知道何时应更改参数,如果要 ...

  9. android平台下OpenGL ES 3.0实例详解顶点属性、顶点数组

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

最新文章

  1. SpringMVC_数据校验
  2. 微信小程序 request请求数据 模块输出
  3. 【Oracle】Redhat6.5环境下安装oracle11G R2
  4. 学习《html5.css3.0》网页布局和样式精粹(第二天)
  5. matlab窗函数 响应,matlab窗函数设计方案.doc
  6. 1.5 编程基础之循环控制 20 球弹跳高度的计算
  7. 设计师灵感交流社区|给你的作品一个舞台
  8. RPA目前在中国的发展怎么样?
  9. 《C++ 进阶心法》书籍修正记录
  10. 问题五十一:怎么用ray tracing画tear drop
  11. oracle 11g rac 环境(1)
  12. 我对未来技术趋势的一些看法
  13. 如何管理计算机的字体,请同事吃了顿饭才要来的字体管理神器,电脑里的几百个字体有救了!...
  14. 三分钟学会数据库, INSERT INTO 插入
  15. 广东工业大学化学工程考研情况
  16. 事件模型-温度预警问题
  17. 白色网站,看久了不爽,segmentfault.com的夜色模式....
  18. 微信小程序获取用户真实信息
  19. Android 调用系统打开相机,打开相册获取图片路径
  20. 用matlab计算信源信息熵,计算离散信源的熵matlab实现

热门文章

  1. [益智]:有3箱水果,一箱是苹果,一箱是橘子,一箱是两种水果的混装
  2. 精通Windows API(一)Windows应用程序开发入门
  3. 交换机启用光口命令_交换机常用命令
  4. 劳动仲裁流程怎么走?
  5. 微信小程序云开发之云函数的调用
  6. 3分钟带你秒懂OA办公系统
  7. 晶体结构与对称群Lecture0
  8. 记录一个自动发送QQ邮件的python脚本。
  9. 仿微信字母快速查找联系人界面
  10. windows cmd命令进入文件夹目录