glsl初步接触

在看计算机系的同学的代码时,我还不知道GLSL这种东西。我还以为OpenGL绘制就是使用glBegin和glEnd。我还深深地纳闷:OpenGL这货画东西完全不给力,是怎么做到跟DirectX相比的?然后我看到了代码里的glUseProgram,看到了程序里完全没有出现glBegin这种语句。后来我才知道,手里的OpenGL红宝书已经是过去时,NeHe的OpenGL教程也已经是过去时,我离地球太远了。

所以glsl才是所谓的Advanced OpenGL吧,然而在百度上搜索OpenGL相关信息,依然有许多文章在用glBegin和glEnd来画东西。但是看到的助于Open.gl这种网站里的教程,都已经彻底弃用这些老式代码,而将绘制工作完全交由显卡完成。现在学OpenGL不学glsl我看是说不过去了。即便是glsl,也经历了许多版本,有很多代码也开始过时了。

一个glsl的程序,最常用的有两个着色器,一个顶点着色器(vertex shader),一个片段着色器(fragement shader)。当然我们需要首先初始化着色器,比如用以下代码(参见https://open.gl/drawing):

GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
glCompileShader(fragmentShader);
这段代码用于生成并编译一个fragment shader。当然这里面可以换成vertex shader、geometry shader等,甚至最新的compute shader。这里面有个fragmentSource,是个字符串,用来存这些shader的源代码。可是有一件事情显而易见:用字符串来写shader的代码实在是太麻烦了。最好是把shader都存到文件里,然后把这个文件读入一个数组里。读取文件的方法多种多样,就不细说了。

显然上文中CompileShader这句就是用来编译着色器的,可是我们还想知道成功没成功。说实在的,跟显卡打交道很不方便,因为其中代码有什么错误都不太直观。

GLint status;
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status);

把所有要用到的shader都建立起来之后,就要建一个program,将所有的shader都绑到这个程序上来,比如一个夸张点的栗子:

GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glAttachShader(shaderProgram, geometryShader);
glAttachShader(shaderProgram, computeShader);
当然名字都是自己起的。

然后还需要连接一下,用:

glLinkProgram(shaderProgram);

最后,当需要使用或者停用这个shaderProgram时,分别对应下面两个语句:

glUseProgram(shaderProgram);
glUseProgram(0);

shader是建完了,我们还需要往里面传数据。首先当然是顶点坐标、颜色和纹理坐标,分别对应的是glBegin之后的glVertex*、glColor*和glTexCoord*这些命令。这次我们建三个数组来存这些数据。比如一个三角形总共三个顶点,对应三组坐标,可以分别存为
float vertex[9]={x1, y1, z1, x2, y2, z2, x3, y3, z3}; 
float color[9]={r1, g1, b1, r2, g2, b2, r3, g3, b3};
float texture[6]={u1, v1, u2, v2, u3, v3};
显然最常用的二维纹理坐标一个点只有两个数。

然后根据这三组数据建立VBO(Vertex Buffer Object):

GLuint vbo[3];
glGenBuffers(3, vbo);
//顶点坐标
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, 9*sizeof(float), vertex, GL_STATIC_DRAW);
//颜色
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, 9*sizeof(float), color, GL_STATIC_DRAW);
//纹理坐标
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, 6*sizeof(float), texture, GL_STATIC_DRAW);
缓存的大小应当与数组的大小相等。由于这些数据只是读取,不需要更改,因此最后一个参数就是GL_STATIC_DRAW。当然,如果不需要颜色或者纹理坐标,可以相应地改动vbo数组的大小。

为了绘制方便,我们还要建立一个顶点数组对象(VAO,Vertex Array Object):

GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

这些事情算是准备工作,已经做得差不多了。还有一些关键部分。这些部分需要先使用glUseProgram(shaderProgram)启用shader。然后需要指定我们这些数据的入口。有两种方法可以用于指定入口,一种类似open.gl教程里所说,可以用

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
GLint posAttrib = glGetAttribLocation(shaderProgram, "inVertex");
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(posAttrib);
我们假设传入的顶点坐标在shader里的名字是inVertex。这两句首先将要绑定的数据启用,然后获取shader里面inVertex变量的位置,然后将启用的数据绑到对应的位置上,最后启用这个绑好的数据。三组数据都可以类似地绑定好。

当然我们也可以很明确地指定这几个数据的位置,比如说

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
这就写明了vbo[0]这个对象(这里是顶点坐标)是绑在了0号位置上的。但是这样的话,在shader当中就需要明确指出所有传入变量的位置。假设传入的顶点坐标、颜色和纹理坐标分别绑定到0、1、2号位置,而shader里名称分别为inVertex、inColor、inTexture的话,那么在shader里就应该定义为:
layout (location = 0) in vec3 inVertex;
layout (location = 1) in vec3 inColor;
layout (location = 2) in vec2 inTexture;
这样三个变量就能对应得上了。

由于启用了VAO,绘制只需要一句:

glDrawArrays(GL_TRIANGLE, 0, 3);


这里还应该说说vertex和fragment这两个shader里应该写什么。以下是我某个vertex shader里的内容:

#version 400 compatibility

//此处两个location应当与程序当中绑定VBO时的参数对应
layout (location = 0) in vec2 VertexPosition;
layout (location = 1) in vec2 VertexTexcoord;

out vec2 vTexCoord;

void main()
{
	vTexCoord = VertexTexcoord;
	gl_Position = ftransform();		//不需要做任何变换,这个内置函数最简便

}

第一句是指定了所用glsl的版本。有些函数只在新版本里才能用。然后out vec2的这句是用来将vTexCoord的内容传给fragment shader。而主函数里除了将纹理坐标传递了一下之外,用了一个ftransform()函数。这个内置函数实际上是把OpenGL里的glViewport、glPerspective等函数生成的矩阵按照最常用的方式与顶点坐标相乘了。如果不对顶点坐标进行操作的话,这个函数是个不错的选择。

而另一个fragment shader里的内容为:

#version 400 compatibility

uniform sampler2D sampler0;
in vec2 vTexCoord;
out vec4 FragColor;

void main()
{
	FragColor=texture2D(sampler0, vTexCoord);
}

这个shader也啥都没干,texture2D一句表示从sampler0纹理中根据vTexCoord坐标获得相应的颜色值。

看起来glsl的框架搭起来还是相当麻烦,但是一旦搭好了框架,就能发挥glsl十分强大的功能了。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值