我决定开个新坑了。以后每周五更新。
这是GLSL的学习周记!
GLSL就是OPENGL SHADER LANGUAGE的简称,就是着色器语言。
着色器是一种交给显卡运行的小程序,这种小程序可以用GLSL来写,写好后交给OPENGL编译,就可以在显卡上运行了。
 那么问题来了!为什么要给显卡运行呢?
显卡是一种特殊的处理器,有核心,有寄存器,还有内存,不过对比CPU,最大的特点就是显卡的核心更多。多多少呢?一般CPU有4-8个核心,而显卡则是100个左右的核心!不过由于造价还有空间的限制,显卡的某些功能会被削弱,例如逻辑判断功能(if) 。但是它的浮点运算能力却非常强大哦!
为什么显卡要制作这么多核心呢?因为,在图像处理方面,是非常适合用并行计算的。所谓并行计算,就是两个并行计算之间不需要彼此的数据。而图像正是非常符合这个特点!想象一下,假如要在屏幕上显示一幅图画,那么可以交给这些核心这么一个工作:每个核心处理图画的每一行,把图画的每一行的像素画到屏幕上面。这种工作并行计算再适合不过了!因为这里面不包括任何的逻辑处理部分。 
好了,是不是也想让显卡帮我们并行计算呢?

**使用着色器功能会暂时关闭大量的OPENGL原有功能,包括光照等。你也可以在一次渲染中使用着色器和原有功能,在需要着色器的时候使用着色器,不需要的时候关闭就可以了

要使用GLSL先要这样:
#include <glew.h> 
// 链接glew.lib (用工程属性或者预编译命令)
// 创建OPENGL窗口 (用你最熟悉的创建方法)
glewInit();
GLuint program = glCreateProgram(); // 创建着色器程序
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
// 你需要两个着色器,顶点着色器和片段着色器
glShaderSource(vertex_shader, 1, &"这里是你的顶点着色器代码", &1);
glShaderSource(fragment_shader, 1, &"这里是你的片段着色器代码", &1);
// 别急着纠错,上面两行写的只是伪代码。正确的glShaderSource用法请自行搜索。在后面我会教你写基础的着色器代码。
glCompileShader(vertex_shader);
glCompileShader(fragment_shader);
// 编译,可以用glGetShaderiv,指定GL_COMPILE_STATUS知道究竟有没有错误。如果你写的代码没有错,那么你的着色器就可以使用了,如果有错,可以用glGetShaderiv指定GL_INFO_LOG_LENGTH获得错误文本的长度,glGetShaderInfoLog获得错误文本
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
// 关联你的着色器到程序上,删除关联用glDetachShader,这都是动态的
glLinkProgram(program);
// 链接你的程序,用所有着色器生成一个完整的着色器程序,可以用glGetProgramiv指定GL_LINK_STATUS知道究竟有没有错误。如果有错,可以用glGetProgramiv指定GL_INFO_LOG_LENGTH获取错误文本的长度,glGetProgramInfoLog获取错误文本
glUseProgram(program);
// 使用你的着色器程序,直到使用 glUseProgram(NULL)所有的OPENGL固定功能被关闭,使用你的着色器来处理

好了,接下来我们来学习着色器语言,并且测试一下它
如果你成功的话,你会看到这样的画面 
图片

这是一个光照的效果,根据公式光照能量跟距离光源的距离的平方成反比做出来的。你也可以不用着色器写,用你的CPU写也能达到一样的效果,但是着色器的速度更快(快得不止一点~)

要实现这个效果我们需要知道光照的坐标、光照的颜色、光照的能量,因为是初学,这些都是定值就好了。假设光照在屏幕的正中央,颜色是黄色,能量是3000

要达到这种效果,需要遍历屏幕上面的所有的像素,这就要用到一种叫做像素着色器的技术。
像素着色器是这样一种着色器:顶点着色器只负责简单地传递坐标,主要的代码在片段着色器上面

顶点着色器看起来是这样的:
#version 440 // 关于version:这个是GLSL的version。我使用的是4.4版本,如果你的电脑不支持,请使用更低的版本,不过低版本有的语句需要修改,也有可能是你的高性能显卡没有打开,请到你的控制面板为你的程序选择高性能显卡,或者更改全局设置
 
in vec4 vertex_position; // in表示由上层传递过来的数据,传递给顶点着色器的数据有坐标等,这里用到一个,就是坐标
 
void main()
{
gl_Position = vertex_position; // 把接受到的坐标传递给片段着色器


不要问我顶点着色器在一次渲染中执行了几次,因为我也不知道,在今后的学习中我会把这个问题搞懂的。我只知道片段着色器至少执行屏幕上像素个数的次数。

片段着色器看起来是这样的:

#version 440
 
void main()
{
float r = 1.0f;
float g = 1.0f;
float b = 0.5f;
        // 黄色 
vec2 p;
p.x = gl_FragCoord.x - 500;
p.y = gl_FragCoord.y - 500;
        // glFragCoord表示这个点的坐标
        // 在(500,500) 的地方生成光照。注意:因为OPENGL的计算方式,屏幕底部的Y坐标为0
float length = p.x * p.x + p.y * p.y; // 距离的平方
float rate = 3000.0f / length; // 比值
gl_FragColor = vec4(rate * r, rate * g, rate * b, 1.0f); // glFragColor表示这个点的最终颜色


**vec4等类型的用法可以自行搜索,都是挺简单的。

接下来,你需要激活屏幕上面的所有像素,以便像素着色器的运行,用画一个矩形的方法再适合不过了
 
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glBegin(GL_QUADS);
glVertex2f(-1, -1);
glVertex2f(-1, 1);
glVertex2f(1, 1);
glVertex2f(1, -1);
glEnd();
// SwapBuffers 或者 glFlush

参考:《OpenGL编程指南》(原书第7版)