openGL之着色器程序的使用
1 #define GLEW_STATIC 2 #include <GL\glew.h> 3 #include <GLFW\glfw3.h> 4 #include<iostream> 5 using namespace std; 6 7 //函数原型 8 void key_callback(GLFWwindow* window, int key, int scancode, 9 int action, int mode); 10 11 //窗口大小 12 const GLuint WIDTH = 800, HEIGHT = 600; 13 14 const GLchar* vertexShaderSource = "#version 330 core\n" 15 "layout (location = 0) in vec3 position;\n" 16 "void main()\n" 17 "{\n" 18 "gl_Position = vec4(position.x,position.y,position.z,1.0);\n" 19 "}\0"; 20 21 const GLchar* fragmentShaderSource = "#version 330 core\n" 22 "out vec4 color;\n" 23 "void main()\n" 24 "{\n" 25 "color = vec4(1.0f,0.5f,0.2f,1.0f);\n" 26 "}\n\0"; 27 28 int main(){ 29 //初始化 GLFW 30 glfwInit(); 31 32 //设置GLFW需要的选项 33 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 34 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 35 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 36 glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); 37 38 //创建一个窗口对象 39 GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "test2", nullptr, nullptr); 40 glfwMakeContextCurrent(window); 41 42 //设置徐亚的回调函数 43 glfwSetKeyCallback(window, key_callback); 44 45 glewExperimental = GL_TRUE; 46 47 glewInit(); 48 49 int width, height; 50 glfwGetFramebufferSize(window, &width, &height); 51 glViewport(0, 0, width, height); 52 53 //定点着色器 54 GLuint vertextShader; 55 vertextShader = glCreateShader(GL_VERTEX_SHADER); 56 glShaderSource(vertextShader, 1, &vertexShaderSource, NULL); 57 glCompileShader(vertextShader); 58 59 //用于判断一个着色器是否编译成功 60 GLint success; 61 GLchar infoLog[512]; 62 glGetShaderiv(vertextShader, GL_COMPILE_STATUS, &success); 63 if (!success){ 64 glGetShaderInfoLog(vertextShader, 512, NULL, infoLog); 65 cout << "vertextShader COMPILE FAILED" << endl; 66 } 67 68 //像素着色器 69 GLuint fragmentShader; 70 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 71 glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); 72 glCompileShader(fragmentShader); 73 74 //用于判断一个着色器是否编译成功 75 glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); 76 if (!success){ 77 glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); 78 cout << "fragmentShader COMPILE FAILED" << endl; 79 } 80 81 //创建一个着色器程序对象:多个着色器最后链接的版本 82 GLuint shaderProgram; 83 shaderProgram = glCreateProgram(); 84 glAttachShader(shaderProgram, vertextShader); 85 glAttachShader(shaderProgram, fragmentShader); 86 glLinkProgram(shaderProgram); 87 88 glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); 89 90 if (!success){ 91 glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); 92 } 93 glDeleteShader(vertextShader); 94 glDeleteShader(fragmentShader); 95 96 //创建定点数据 97 GLfloat vertices[] = { 98 -0.5f, -0.5f, 0.0f, 99 0.5f, -0.5f, 0.0f, 100 0.0f, 0.5f, 0.0f 101 }; 102 //创建 VBO 顶点缓冲数据,并把数据赋值到内存中 103 GLuint VBO; 104 glGenBuffers(1, &VBO); 105 //VAO顶点数组对象 106 GLuint VAO; 107 glGenVertexArrays(1, &VAO); 108 109 //绑定VAO 110 glBindVertexArray(VAO); 111 112 //复制顶点数据到缓冲中提供给OpenGL使用 113 glBindBuffer(GL_ARRAY_BUFFER, VBO); 114 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 115 116 //设置顶点属性指针 117 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid *)0); 118 glEnableVertexAttribArray(0); 119 120 //解绑VBO 121 glBindBuffer(GL_ARRAY_BUFFER, 0); 122 123 //解绑VAO 124 glBindVertexArray(0); 125 126 while (!glfwWindowShouldClose(window)){ 127 glfwPollEvents(); 128 129 //释放颜色缓存 130 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); 131 glClear(GL_COLOR_BUFFER_BIT); 132 133 //绘制物体 134 //当打算渲染一个物体使用使用着色器程序 135 glUseProgram(shaderProgram); 136 glBindVertexArray(VAO); 137 138 //绘制函数 139 glDrawArrays(GL_TRIANGLES, 0, 3); 140 glBindVertexArray(0); 141 142 glfwSwapBuffers(window); 143 } 144 145 glDeleteVertexArrays(1, &VAO); 146 glDeleteBuffers(1, &VBO); 147 148 glfwTerminate(); 149 return 0; 150 } 151 void key_callback(GLFWwindow* window, int key, int scancode, 152 int action, int mode){ 153 if (key == GLFW_KEY_L && action == GLFW_PRESS){ 154 glfwSetWindowShouldClose(window, GL_TRUE); 155 } 156 }
首先,在发该贴的时候,这个程序依旧没有跑起来,因为GLFW、GLEW等库的原因,鉴于GLUT是上个时代的产物,所以学到后面看到的一些案例都是用的GLEW、GLFW、GLAD等库,一时半会儿没有配置成功,但是,这并不能影响我们根据其中的代码来理解着色器程序(shader)。
下面,我们主要来看一下其中的着色器代码部分:
一、两个着色器程序
const GLchar* vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 position;\n" "void main()\n" "{\n" "gl_Position = vec4(position.x,position.y,position.z,1.0);\n" "}\0"; const GLchar* fragmentShaderSource = "#version 330 core\n" "out vec4 color;\n" "void main()\n" "{\n" "color = vec4(1.0f,0.5f,0.2f,1.0f);\n" "}\n\0";
首先第一个是顶点着色器(vertexShader):
顶点着色器用于读取顶点(坐标)数据,所以这个position参数是从外部数据源读取的,在main方法中将外部读取的顶点数据转化为四维坐标(x,y,z,w),并且赋值给全局变量:gl_Position。
第二个是片元着色器(fragmentShader):
这里注意了,片元着色器隐式地对所有的gl_Position中的坐标点进行着色并且将颜色输出。所以这个color参数是输出的,可能你也看到了,输出的颜色是个vec4,分别代表RGBA,最后一个1.0f表示alpha通道值为1.0f(浮点型)
二、着色器程序的编译
//定点着色器 GLuint vertextShader; vertextShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertextShader, 1, &vertexShaderSource, NULL); glCompileShader(vertextShader); //用于判断一个着色器是否编译成功 GLint success; GLchar infoLog[512]; glGetShaderiv(vertextShader, GL_COMPILE_STATUS, &success); if (!success){ glGetShaderInfoLog(vertextShader, 512, NULL, infoLog); cout << "vertextShader COMPILE FAILED" << endl; } //片元着色器 GLuint fragmentShader; fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); //用于判断一个着色器是否编译成功 glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); if (!success){ glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); cout << "fragmentShader COMPILE FAILED" << endl; }
这一块也不难理解:
1st,定义一个着色器(顶点着色器或者片元着色器);
2nd,为这个着色器对象加载着色器程序片段;
3rd,编译这个着色器;
当然,案例程序还加了一段编译成功与否的判断,这是有必要的,方便调试。
三、多个着色器连接
//创建一个着色器程序对象:多个着色器最后链接的版本 GLuint shaderProgram; shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertextShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if (!success){ glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); } glDeleteShader(vertextShader); glDeleteShader(fragmentShader);
这块也不难理解,新建一个shaderProgram,并且通过glAttachShader() API将之前的两个着色器进行连接,这样顶点着色器输出的四维坐标就能供片元着色器使用了。
两个着色器进行连接之后,对之前的着色器进行删除。
四、将外部顶点数据进行绑定(用户自定义数据)
//创建定点数据 GLfloat vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f }; //创建 VBO 顶点缓冲数据,并把数据赋值到内存中 GLuint VBO; glGenBuffers(1, &VBO); //VAO顶点数组对象 GLuint VAO; glGenVertexArrays(1, &VAO); //绑定VAO glBindVertexArray(VAO); //复制顶点数据到缓冲中提供给OpenGL使用 glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //设置顶点属性指针 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid *)0); glEnableVertexAttribArray(0); //解绑VBO glBindBuffer(GL_ARRAY_BUFFER, 0); //解绑VAO glBindVertexArray(0);
不难看出,用户自定义顶点数据为三个顶点(-0.5,-0.5,0)(0.5,-0.5,0)和(0,0.5,0);
新建VAO、VBO对象并将其放到内存中;
将用户自定义数据赋值到VBO中;
后面的VAO相关操作没有看懂,这一块不是直接用VBO(顶点缓存对象)么,并没有用到VAO啊???
glVertexAttribPointer()我的理解就是为这些顶点(坐标)设置相应的指针好让程序知道如何操作。
五、渲染
//释放颜色缓存 glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); //绘制物体 //当打算渲染一个物体使用使用着色器程序 glUseProgram(shaderProgram); glBindVertexArray(VAO); //绘制函数 glDrawArrays(GL_TRIANGLES, 0, 3); glBindVertexArray(0); glfwSwapBuffers(window);
首先,清空屏幕;
然后调用之前的shaderProgram,并为其绑定数据(??不是已经绑定数据了么)