3D Computer Grapihcs Using OpenGL - 06 Vertex and Fragment Shaders

从这里就接触到了可编程图形渲染管线。 

下面介绍使用Vertex Shader (顶点着色器)和 Fragment Shader(像素着色器)的方法。

我们的目标是使用这两个着色器给三角形填充绿色。

添加了一个cpp文件存放Shader文件,MyShaderCode.cpp:

 1 const char* vertexShaderCode =
 2 "    #version 430                            \r\n"
 3 "                                            \r\n"
 4 "    in layout(location=0) vec2 position;    \r\n"
 5 "                                            \r\n"
 6 "                                            \r\n"
 7 "                                            \r\n"
 8 "    void main()                             \r\n"
 9 "    {                                       \r\n"
10 "      gl_Position= vec4(position,0.0,1.0);  \r\n"
11 "    }                                       \r\n"
12 "                                            \r\n"
13 "                                            \r\n";
14 
15 const char* fragmentShaderCode =
16 "    #version 430                            \r\n"
17 "                                            \r\n"
18 "    out vec4 finalColor;                    \r\n"
19 "                                            \r\n"
20 "                                            \r\n"
21 "    void main()                             \r\n"
22 "    {                                       \r\n"
23 "      finalColor = vec4(0.0,1.0,0.0,1.0);   \r\n"
24 "    }                                       \r\n"
25 "                                            \r\n"
26 "                                            \r\n";

书写Shader使用的是GLSL语言,和C语言语法非常相似。

Vertex Shader

#version 430 表示opengl的版本号,要使用显卡支持的版本,否则会编译出错

vertex Shader第四行的 in 关键字表示这里定义的是一个输入。layout(location=0)表示这里接收的是位置信息。vec2表示这里是一个2维向量。position是自己定义的接受数据的变量名称。

gl_Position是GLSL定义的关键字,使用在Vertex shader中就表示它将接收一个四维向量来表示顶点位置信息。

vec4(position,0.0,1.0)是GLSL的一种特殊语法,用于方便的构造一个四维向量,因为position是二维的,后面再添加两个数字就表示了四维向量。最后一位是1.0,表示没有缩放。

Fragment Shader

第18行使用out关键字,表示这里是一个输出。

而Fragment Shader在渲染管线中只有一个输出,就是每个Fragment(暂时可以理解为像素,实际上不等同于像素)的颜色。

在main中直接将所有的像素颜色都设置成绿色。

 

 

MyGlWindow.cpp

 1 #include <gl\glew.h>
 2 #include "MyGlWindow.h"
 3 
 4 extern const char* vertexShaderCode;
 5 extern const char* fragmentShaderCode;
 6 
 7 GLuint programID;
 8 
 9 void MyGlWindow::sendDataToOpenGL()
10 {
11     GLfloat verts[] =
12     {
13         +0.0f, +0.0f, //0
14         +1.0f, +1.0f, //1
15         -1.0f, +1.0f, //2
16         -1.0f, -1.0f, //3
17         +1.0f, -1.0f, //4
18     };
19 
20     GLuint vertexBufferID;
21     glGenBuffers(1, &vertexBufferID);
22     glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
23     glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
24 
25     GLushort indices[] =
26     {
27         0,1,2,
28         0,3,4,
29     };
30     GLuint indexBufferID;
31     glGenBuffers(1, &indexBufferID);
32     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
33     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
34 
35     glEnableVertexAttribArray(0);
36     glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
37 }
38 
39 void MyGlWindow::installShaders()
40 {
41     GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
42     GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
43 
44     glShaderSource(vertexShaderID, 1, &vertexShaderCode, 0);
45     glShaderSource(fragmentShaderID, 1, &fragmentShaderCode, 0);
46 
47     glCompileShader(vertexShaderID);
48     glCompileShader(fragmentShaderID);
49 
50     programID = glCreateProgram();
51     glAttachShader(programID, vertexShaderID);
52     glAttachShader(programID, fragmentShaderID);
53 
54     glLinkProgram(programID);
55 
56     glDeleteShader(vertexShaderID);
57     glDeleteShader(fragmentShaderID);
58     
59     glUseProgram(programID);
60 }
61 
62 void MyGlWindow::initializeGL()
63 {
64     glewInit();
65     sendDataToOpenGL();
66     installShaders();
67 }
68 
69 void MyGlWindow::paintGL()
70 {
71     glViewport(0, 0, width(), height());
72     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
73 }
74 
75 MyGlWindow::~MyGlWindow()
76 {
77     glUseProgram(0);
78     glDeleteProgram(programID);
79 }

 

MyGlWindow.h

 

 1 #pragma once
 2 #include <QtOpenGL\qgl.h>
 3 class MyGlWindow :public QGLWidget
 4 {
 5 protected:
 6     void sendDataToOpenGL();
 7     void installShaders();
 8     void initializeGL();
 9     void paintGL();
10 
11 public:
12     ~MyGlWindow();
13 };

 

对MyGlWindow.cpp文件进行了一个重构,把上节initializeGL()中的内容除了第一句glewInit()之外全部封装进了sendDataToOpenGL()函数,因为这些代码都是向OpenGL函数发送数据的。

为了使用Shader,我们新增加了一个installShaders()函数,所有使用Shader的功能都写在其中。

在initializeGL函数中先调用sendDataToOpengGL(),然后调用installShaders()。

 

使用Shader的步骤清单

  1. 创建Shader并分配ID,glCreateShader
  2. 设置Shader内容, glSourceShader
  3. 编译Shader, glCompileShader
  4. 创建Program,glCreateProgram
  5. 附加Shader,glAttachShader
  6. 链接Shader, glLinkProgram
  7. 删除Shader,glDeleteShader
  8. 使用Program, glUseProgram
  9. 删除Program,glDeleteProgram

下面就按照这六个步骤逐步解析代码。

1. 创建Shader并分配ID

39 - 40 行使用glCreateShader分别创建了vertex shader和 fragment shader,并分别将ID保存在GLuint对象里(和前面使用GLuint存储Buffer对象类似)

函数的参数是固定的宏

2. 设置Shader内容

42 - 43 行使用glShaderSource函数给Shader提供具体内容。

我们额外增加了一个文件MyShaderCode用来存放Shader的代码,代码具体内容放在两个较长的字符串里。

然后在MyGlWindow.cpp的开头使用extern 声明了vertexShaderCode和fragmentShaderCode字符串变量。

glShaderSource的

  • 第一个参数表示Shader的ID
  • 第二个参数表示Shader内容的字符串有几个数组,这里只有1个
  • 第三个参数表示字符串数组本身,但是需要注意参数的类型是const char **,所以还要在前面增加一个取地址符号
  • 第四个参数使用0表示由OpenGL自己计算字符串的长度

3. 编译Shader

45 - 46 行使用glCompileShader分别对两个Shader进行编译

4.创建Program

进行后续操作之前需要创建一个Program,这里使用glCreateProgram()创建了一个Porgram,并将其ID保存在GLuint对象里。

5.附加Shader

glAttachShader有两个参数,第一个参数是Program的ID,第二个参数是Shader的ID

6.链接Shader

虽然通常称为“链接SHader”, 但其实是链接Program, 函数glLinkProgram的参数就是Program的ID

7.删除Shader

Shader使用完成后一定要删除Shader,第56和57行进行了这步操作。在useProgram之前进行也没问题,只要编译和链接了Shader就行。

8.使用Program

glUseProgram的参数是Program的ID

9. 删除Program

使用完成Program之后要删除Program,这里在析构函数里进行是比较合适的,分两步,如77和78行所示。

 

 

完成后编译,我们将得到两个绿色的三角形。

posted @ 2018-01-23 19:22  Ken_An  阅读(419)  评论(0编辑  收藏  举报