我之前有简单的解释fragment shader的作用,实际上还有一个必须的shader,就是vertex shader。
vertex shader就跟它的名字一样是用来处理顶点的,在新潮的opengl中三维的坐标系是需要我们自己建立的,而且都是通过vertex shader来处理的,在这个shader中你可以定义每个顶点的位置,这是主要的功能,如果有需要还可以为后续的fragment shader做一些其它的计算并传递结果,例如,基于vertex位置的颜色。
这里的ripple deformer就是通过使用
http://adrianboeing.blogspot.com/2011/02/ripple-effect-in-webgl.html
的方程式来做一个ripple effect的动画
http://v.youku.com/v_show/id_XNjk5MjE2MDM2.html
这个vertex shader很简单
先是定义使用的版本
#version 430
然后是位置属性,它代表着我们传递进来的顶点的x,y, z,所以是一个向量
layout(location=0) in vec3 pos;
接着是坐标空间矩阵和时间变量
uniform mat4 MVP;
uniform float time;
然后是一些可变和恒定的参数,这个例子里我把它们都定义成了恒定的
const float amplitude = 0.125;
const float frequency = 4;
const float PI = 3.14159;
最后就是main函数了,就像c和c++程序一样,每个shader里都必须有一个main函数,当这个shader被运行时就会调用这个函数
void main()
{
float r = length(pos);
float y = amplitude * sin(-PI * r * frequency+time) / r;
gl_Position = MVP * vec4(pos.x, y, pos.z, 1.);
}
需要注意的是gl_Position是一个全局变量,只要vertex shader一创建它就存在,它就是用来保存顶点最终的位移的。
完整的代码:
https://github.com/mackst/opengl-samples/tree/master/rippleDeformer
如果你细心点会发现,并没有for loop出现,就是
for pos in vertexPositions:
pass
毕竟我们传递了好几万的位移信息到gpu内存
如果你看过之前的fragment shader也会发现是没有for loop的,然而却是能操作成千上百的像素。
这就是图形处理的强大之处,跟准确的说是gpu的强大之处:并行处理
gpu的原本设计就是做图形处理的,为了能高效的处理,它们选择了并行架构。
怎么并行法呢?
假设我们的gpu能一次处理1万个顶点数据,而我们有10万的数据,gpu会自己for loop 10次,每次处理1万个顶点数据,这每次处理1万个顶点可不是从0或1开始的for loop直到1万,而是同时进行处理的。
如果你了解多线程编程,你就更能明白并行处理,只是gpu和cpu的线程数量是差别非常大的,cpu可能最多只能同时运行上百个线程,而gpu则可以是成千上万的线程。
当然上面的解释只是我自己觉得好理解的方式,要了解gpu的运作方式还得靠你自己。
这就是为什么gup计算能这么快,当人们发现这点后就有人尝试使用opengl来进行一些非图形化的数据计算,实际也证明了这种可行性,之后nvidia和apple都看到了不少人都在使用这种经典的gpu加速,于是CUDA和OpenCL就诞生。