我猜你看名字一定不知道这是个什么东西,好吧……其实原版教程第一章就说了……只是我懒得记下来了。

GLSL就是指 OpenGL Shading Language 。所有的shader都是用这个种语言规范写出来的。本节就来介绍这个语言的一些操作。GLSL是一个类似于C的语言,大部分语法跟C很像。

代码构成:

一个shader的代码看起来差不多是下面这个样子:

#version version_number
in type in_variable_name;
in type in_variable_name;out type out_variable_name;uniform type uniform_name;void main()
{// process input(s) and do some weird graphics stuff...// output processed stuff to output variableout_variable_name = weird_stuff_we_processed;
}

总结一些就是有下面三个主要部分:

  • 版本声明:就是OpenGL的版本以及模式
  • 外部变量列表:可能有三种变量:in(输入变量),out(输出变量),uniform(后面再解释……)
  • main()函数:程序的入口,在这里会处理 输入变量,并为输出变量赋值。

特别的,对于vertex shader来说:输入变量就是顶点属性(vertex attribute)。它的数量并不能有无限多个,OpenGL确保了只有少可以有16个vec4类型的顶点属性,再多可能就不行了,不过有一些硬件也支持数量更多的vertex attribute。这个数量可以通过:GL_MAX_VERTEX_ATTRIBS得到,用下面的方法(不可以直接赋值,我试了,……结果给我出来个3万多……):

int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;

数据类型:

大体可以分为三类:1.常见的C的数据类型(如:int,float,uint,double,bool),2.vectors(向量),3.matrices(矩阵)

vectors:

对于所有基础类型,都有对应的vector,而且是且只能是1、2、3、4维的向量。把下面的n换成维数就可以了。

  • vecn:float类型的n维向量
  • bvecn:bool
  • ivecn:int
  • uvecn:uint
  • dvecn:double

例如:ivec3就是3维的整数向量。下面说一下用法,这东西有的地方不好描述,看例子吧。

获取一个分量:vec4 的变量 a。可以用a.x,a.y,a.z,a.w得到第1,2,3,4维的分量。对于颜色还可以用rgba,对于贴图坐标还可以用stpq。

重新组合新的向量:

vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;

部分分量当做参数:

vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);

有句废话……你不能对三维的向量使用.w去获取他的第四维的分量……因为压根就没有……。

matrices:

后面的教程会说~

In&Out:

shader虽然可以独立运行的很好,但是它也需要与外界进行信息交流。in,out所声明的变量就实现了这一点。在graphics pipeline中,上一步shaderd的out变量,在下一步有同名的in变量与之对应,那么这个变量就可以从上一个shader传递到下一个shader中。在vertex shader 与 fragment shader 中略略略略微有不同(我也不知道哪里不同,教程就这么写的)。

vertex shader:由于vertex shader直接从vertex data中读取数据(而不是通过对应名称的变量),所以必须告诉vertex shader,他所读取的数据在哪里,通过layout (location = 0)这种句子。并用glVertexAttrbPointer()配置好vertex data。

fragment shader:fragment shader需要一个 vec4 类型的变量,当做输出的颜色。

下面是例子,演示了如何把vertex shader 的信息(这里是颜色) 传到 fragment shader。

vertex shader:

#version 330 core
layout (location = 0) in vec3 aPos; // the position variable has attribute position 0out vec4 vertexColor; // specify a color output to the fragment shadervoid main()
{gl_Position = vec4(aPos, 1.0); // see how we directly give a vec3 to vec4's constructorvertexColor = vec4(0.5, 0.0, 0.0, 1.0); // set the output variable to a dark-red color
}

fragment shader:

#version 330 core
out vec4 FragColor;in vec4 vertexColor; // the input variable from the vertex shader (same name and same type)  void main()
{FragColor = vertexColor;
} 

Uniform:

目前我们把数据从CPU传给GPU的手段只有通过vertex shader读取vertex data而且数量还有限制。这……明显不够意思……。uniform就是另外一种手段,来将数据从CPU传给GPU。

注意:uniform的变量是“全局的”。意思就是,他独立于各个shader,也独立于各个shaderProgram(用glLinkProgram()的那个东西)(这个地方不是很好说,我试了好几次,每次都在刷新世界观,有时间我补张图吧【待补图】)

你如果想用一个uniform变量,需要:

shader中:1.声明变量

uniform vec4 ourColor;

.cpp中:1.得到uniform的地址(ID,具体是啥我也不知道,教程上画了个“/”我也不知道啥意思),2.启用需要修改这个变量的shaderProgram(也就是说,这个变量只会在这个shaderProgram中被修改,其他没有被激活的shaderProgram中的这个值,是会保持原来的值不变的。我试过了。我又试了试,很不好说,大体就是,如果你在另一个shaderProgram里声明了一个同名的uniform变量,那么他们实际上就是两个不同的变量。但是如果你没有声明,直接用了(对……你没看错,就是直接用了,反正我的电脑上这么做用是可以的,不知道跟不同显卡有没有关系),那他们就是一个变量。【待更正】),3.通过地址(ID),为其赋值。

这里只要记得:得到地址只需要在链接glLinkProgram()之后(不需要启动glUseProgram()),而赋值则是需要在启动glUseProgram()之后才可以。

float timeValue = glfwGetTime();
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);

glfwGetTime():就是运行了多长时间,单位是秒

glGetUniformLocation( shaderProgram,"变量名 字符串" ):得到给定的shaderProgram中指定变量的地址(然鹅返回的是个int类型,鹅不是指针……可能是为了不让你随便改吧,我自己瞎猜的……)

赋值的时候可以用glUniform什么()。“什么”的地方可以填如下的字符:

  • f:赋予float类型的值
  • i:int类型
  • ui:unsigned int 类型
  • 3f:3个float类型的值(这个3可以改成4,就是4个float,之类的。这东西……很容易理解吧……)
  • fv:float类型的vector
  • 等等…………

vertex attribute:

到目前为止,我们的vertex attribute只有一个:位置属性。下面就要正式的说一下,怎么传入多个属性了。

首先,我们在原始数据(传给VBO的那个float数组)里面加入一些东西:

float vertices[] = {// 位置              // 颜色0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 右下-0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // 左下0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 上
}; 

然后跟原来一样,把这个赋给VBO。由于我们数据的结构变了,所以要调整一下vertex shader的输入部分,以及glVertexAttribPointer():

vertex shader中需要增加一个输入变量,来读取每个点的颜色:(绿色部分是新加的代码)

#version 330 core
layout (location = 0) in vec3 aPos;   // the position variable has attribute position 0
layout (location = 1) in vec3 aColor; // the color variable has attribute position 1out vec3 ourColor; // output a color to the fragment shadervoid main()
{gl_Position = vec4(aPos, 1.0);ourColor = aColor; // set ourColor to the input color we got from the vertex data
}    

glVertexAttribPointer(),首先因为多加了一个属性,所以需要一个新的设置,存储在1的位置上。然后还需要调整步长:(黄色部分是需要注意的,做了更改的)

// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));
glEnableVertexAttribArray(1);

上面数据的输入已经做好了,最后不要忘了在fragment shader把颜色赋给每个点。

#version 330 core
out vec4 FragColor;
in vec3 ourColor;void main()
{FragColor = vec4(ourColor, 1.0);
}

注意

1.你应该能想到,这里我们没有用EBO,所以绘制图形的函数应该用glDrawArrays(),而不是glDrawElements()。(当然了,你用EBO也没有任何错误,只要用对了就行,用法跟前面完全一样)

2.在写shader的时候,由于我们是直接用字符串写的,所以很容易出错(我就是老在这里出错)。要注意加分号,要注意换行\n,等问题。在下一章,我们会写一个从文件里读取代码的类,然后就可以在vscode里来写glsl了(里面有高亮显示的插件)。

告一段落:

下面是vertex attribute那一节的代码:(uniform的代码……看原来文章里的代码吧,我有时间补上【待补】)

//  1.头文件:
#include <glad/glad.h>
#include <GLFW/glfw3.h>#include <iostream>//  5.OpenGL与窗口(main之前):
void CBK_framebuffer_size(GLFWwindow* window, int w, int h)
{glViewport(0, 0, w, h);
}void processInput(GLFWwindow* window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}int main()
{//  2.在创建窗口之前……:glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//  3.创建窗口:GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);//  4.在OpenGL之前……:if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}//  5.OpenGL与窗口:glViewport(0, 0, 800, 600);glfwSetFramebufferSizeCallback(window, CBK_framebuffer_size);//  OpenGL的配置:glClearColor(0.2f, 0.3f, 0.3f, 1.0f);//设置背景色//  绘图需要用的数据:float vertices[] = {// positions         // colors0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // bottom right-0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // bottom left0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // top };GLuint VBO;glGenBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);GLuint VAO;glGenVertexArrays(1, &VAO);glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);glBindVertexArray(0);//  Shader:const char *vsl = "#version 330 core\n""layout (location = 0) in vec3 aPos;\n""layout (location = 1) in vec3 myC_a;\n""out vec3 myC;""void main()\n""{\n""   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n""   myC = myC_a;\n""}\0";const char *fsl = "#version 330 core\n""in vec3 myC;\n""out vec4 C;\n""void main()\n""{\n""   C = vec4(myC,1.0f);\n""}\n\0";GLuint vshader;vshader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vshader, 1, &vsl, NULL);glCompileShader(vshader);GLuint fshader;fshader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fshader, 1, &fsl, NULL);glCompileShader(fshader);GLuint shadProg;shadProg = glCreateProgram();glAttachShader(shadProg,vshader);glAttachShader(shadProg,fshader);glLinkProgram(shadProg);glDeleteShader(vshader);glDeleteShader(fshader);//  6.Render Loop:while (!glfwWindowShouldClose(window)){processInput(window);glClear(GL_COLOR_BUFFER_BIT);//绘制背景色//  这下面就可以写绘图的代码了glUseProgram(shadProg);glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES,0,3);//  这上面是绘图的代码glfwSwapBuffers(window);glfwPollEvents();}//  7.程序结束之前:glfwTerminate();return 0;
}

运行结果:

啊~~~,这一章终于写完了~~~。拖了好久,因为我原来差不多就学到这里,这一章的内容并不是很熟悉(往后应该会更不熟悉2333)。而且主要原因……其实是……这几天有个活动要交了……我去整那个活动了………………,顺便……我周日晚上感受了第一次在教室刷夜的感受……总结一下就是……emmm,刷的时候很爽……刷完是……真tm困啊23333,周一的时候我一下睡了emm,6+7=13个小时啊,啧啧啧。

哦,说一下,我是把原来教程的一章分成了两章,因为我干事情……emmm喜欢留个尾巴……(好吧我承认这不好……)所以带小序号的章节,在原来教程里,也是正文……并不是说他不重要………………,只是因为我不是很想一次写完而已…………。比如这次的小序号就会写一下,如何读取外面的glsl文件,要不然老是用字符串写,一定会爽翻…………(前面的,002-01是讲了如何读取键盘、鼠标输入,如何重置背景色;003-01是讲了EBO。这三个……都是相当重要的内容…………)

老子不信我学不会OpenGL系列!004 GLSL!相关推荐

  1. 老子不信我学不会OpenGL系列!001 环境配置!

    附录: GLFW: Window Guide: GLFW的官方帮助 Building applications: 对 编译.链接 这个过程的一个解释,而且还有一个错误列表. GLFW with Cod ...

  2. OpenGL ES之GLSL实现仿抖音“缩放”“灵魂出窍”“抖动”“闪白”“毛刺”“幻觉”等动态滤镜效果

    无滤镜效果 "动态滤镜"效果的实现准备工作的代码与"无分屏滤镜"中的实现逻辑和流程一致,只需要修改相应的底部item数组及对应的着色器名称等,这里不再说明这部分 ...

  3. OpenGL ES之GLSL实现仿抖音“灰度滤镜”和“颠倒滤镜”效果

    无滤镜 "无滤镜"效果的实现准备工作的代码与"无分屏滤镜"中的实现逻辑和流程一致,只需要修改相应的底部item数组及对应的着色器名称等,这里不再说明这部分内容, ...

  4. 【转帖】绝版破解软件教程就不信你学不会!!

    绝版破解软件教程就不信你学不会!! www.hxhack.com 阅读: <script src="/Article/GetHits.asp?ArticleID=1191"& ...

  5. OpenGL ES之GLSL实现多种“马赛克滤镜”效果

    ⻢赛克效果 "⻢赛克效果"就是把图⽚的⼀个相当⼤⼩的区域⽤同⼀个点的颜⾊来表示,可以认为是⼤规模的降低图像的分辨率,⽽让图像的⼀些细节隐藏起来. 无马赛克滤镜 "无滤镜& ...

  6. 高质量实时渲染课程笔记(二)——图形学基础回顾(渲染管线、OpenGL入门、GLSL、渲染方程)

    文章目录 1 图形渲染管线 2 OpenGL 2.1 使用OpenGL过程的比喻: 油画过程 2.2 Place objects/models 放这些模型 模型这么摆放 2.3 Set up an e ...

  7. ADO.NET不信你学不会系列三

    提供程序组件(1)Connection类 Connection有两个构造函数一个是有参数的,一个是没有参数的.用的更多的是他带有参数的那个构造函数.构造函数中使用的连接字符串. 下面我们来介绍字符串的 ...

  8. 现代OpenGL系列教程(一)---旋转的三角形

    [写在前面] 本章主要内容: 1.基本的矩阵变换 2.基本的OpenGL Buffer Object 3.基本的GLSL(OpenGL着色语言)  [正文开始] 在正式开始学习之前,我必须要说明的是: ...

  9. OpenGL进阶(十三) - GLSL光照(Lighting)

    提要 在上一篇文章中,我们介绍了简单的Shading,同时提出了一个光照模型,模拟了一个点光源,但是,关于光的故事还没有结束... 今天要学习的是方向光源(Directional Light),聚光灯 ...

最新文章

  1. linux下batik-rasterizer.jar生成图片中文乱码
  2. SpringCloud:入门介绍
  3. Spring-AOP @AspectJ进阶之绑定抛出的异常
  4. 荣大速印机维修手册_荣大佳文一体机(速印机)故障及排除方法
  5. js事件里面套事件怎么不管用_原生js利用localstorage实现简易TODO list应用
  6. [转]Java 对象锁-synchronized()与线程的状态与生命周期
  7. android的log.v,Android Log.v(),Log.d(),Log.i(),Log.w(),Log.e() - 何时使用每一个?
  8. Virtual Member Functions(虚拟成员函数)
  9. 手机号码正则_中国大陆手机号码的正则表达式总结ChinaMobilePhoneNumberRegex
  10. 栅格数据灰度化并前端转换展示
  11. 微信小程序识别二维码
  12. chrome密码导出导入
  13. HTML和CSS实现京东首页(html和css详解)
  14. 苹果开发者账号续费不显示续费按钮的解决方法!
  15. zeroMQ支持PGM协议编译
  16. VoIP之Wireshark使用
  17. 计算机工作无法更改,win10系统计算机工作组名称无法更改的操作方案
  18. 10 Design – 旭辉成都万盛 TOD 项目 |卓美设计
  19. 小程序运营要善于做好活动推送
  20. css背景图片(第五次课)

热门文章

  1. picker-view中的view的高度修改
  2. Linux建立软连接
  3. 第60件事 关于产品运营的10个故事
  4. 多项目同时进行如何做好项目管理?
  5. window.performance
  6. 如何计算excel中单元格里面字符的个数
  7. JavaScript - 实例对象 与 new命令
  8. 选词的好坏直接影响着直通车的整体推广效果
  9. 图标设计有哪些技巧,用两个案例介绍下图标设计的技巧
  10. 谷歌浏览器如何导出书签【实用帖】