学习来源:https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/
还是老话吧,为了学过就忘,准备写点东西,或者摘抄点东西。
注:本文所有代码教程都有,若是看不惯本文的可以直接去教程里找。
(出现任何差错的,欢迎在评论区与我讨论!)
目录
着色器是一种把输入转化为输出的程序。
GLSL
√GLSL:OpenGL着色器语言。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。
√着色器的开头总是要声明版本,接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。
√谈论顶点着色器的时候,每个输入变量也叫顶点属性(Vertex Attribute)。
数据类型
√默认基础数据类型:int、float、double、uint和bool。
√两种容器类型:向量(Vector)和矩阵(Matrix)。
向量
√GLSL中的向量是一个可以包含有1、2、3或者4个分量的容器,分量的类型可以是前面默认基础类型的任意一个。
类型 | 含义 |
vecn | 包含n个float分量的默认向量 |
bvecn | 包含n个bool分量的向量 |
ivecn | 包含n个int分量的向量 |
uvecn | 包含n个unsigned int分量的量 |
dvecn | 包含n个double分量的向量 |
√大多数时候我们使用vecn,因为float足够满足大多数要求了。
√可以分别使用vec.x、.y、.z和.w来获取向量的第1、2、3、4个分量。
向量的重组(Swizzling)
√允许:
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
√把一个向量作为一个参数传给不同的向量构造函数,以减少需求参数的数量:
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);
输入和输出
√每个着色器使用这in和out关键字设定输入和输出,只要一个输出变量与下一个着色器阶段的输入匹配,它就会传递下去。但在顶点和片段着色器中会有点不同。
√①顶点着色器接收一种特殊形式的输入,否则就会效率低下。使用location这一元数据指定输入变量,这样才可以在CPU上配置顶点属性:layout (location = 0)。顶点着色器需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据。
√②片段着色器,它需要一个vec4颜色输出变量。从一个着色器向另一个着色器发送数据的时候,我们必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了(这是在链接程序对象时完成的)。
√改动之前“一个三角形”的程序。
顶点着色器
#version 330 core
layout (location = 0) in vec3 aPos; // 位置变量的属性位置值为0
out vec4 vertexColor; // 为片段着色器指定一个颜色输出
void main(){
gl_Position = vec4(aPos, 1.0); // 注意我们如何把一个vec3作为vec4的构造器的参数
vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // 把输出变量设置为暗红色
}
片段着色器
#version 330 core
out vec4 FragColor;
in vec4 vertexColor; // 从顶点着色器传来的输入变量(名称相同、类型相同)
void main(){
FragColor = vertexColor;
}
√在顶点着色器中声明了一个vertexColor变量作为vec4输出,并在片段着色器中声明了一个类似的vertexColor。由于它们名字相同且类型相同,片段着色器中的vertexColor就和顶点着色器中的vertexColor链接了。
√我们在“一个三角形”的代码基础上更改代码的一部分,更改如下:
(“一个三角形”代码见我上一篇文章:OpenGL学习——第4天(绘制三角形)
代码在文末:https://blog.csdn.net/weixin_40851250/article/details/85038950
这边的所有代码往下翻一翻就好了)
/*着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"out vec4 vertexColor; // 为片段着色器指定一个颜色输出\n"/*添加*/
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0); "// 注意我们如何把一个vec3作为vec4的构造器的参数
" vertexColor = vec4(0.5, 0.0, 0.0, 1.0);"// 把输出变量设置为暗红色
"}\0";
/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec4 vertexColor;\n"// 从顶点着色器传来的输入变量(名称相同、类型相同)
"void main()\n"
"{\n"
" FragColor = vertexColor;\n"//作了更改
"}\n\0";
/*着色器源码*/
//const char *vertexShaderSource = "#version 330 core\n"
//"layout (location = 0) in vec3 aPos;\n"
//"void main()\n"
//"{\n"
//" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
//"}\0";
///*片段着色器源码*/
//const char *fragmentShaderSource = "#version 330 core\n"
//"out vec4 FragColor;\n"
//"void main()\n"
//"{\n"
//" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
//"}\n\0";
√更改后三角形变了颜色:
√我们来看一下颜色变化:原来的代码中,FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);,(1.0f,0.5f,0.2f)就是rgb的配色,末尾1.0f是不透明度。后来颜色变成了:vertexColor = vec4(0.5, 0.0, 0.0, 1.0),也就是我们的暗红色。过程:在着色器中新建了一个vertexColor的vec4变量,然后将其传给片段着色器使用。
→
√只要我们改变这个变量,比如:我们将vertexColor = vec4(0.5, 0.0, 0.0, 1.0);改为vertexColor = vec4(1.0, 0.7, 0.6, 1.0);,就看到三角形变成了肉粉色。
√到这里,我们成功地从顶点着色器向片段着色器发送数据。
代码:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
const unsigned int SCR_WIDTH = 800;/*窗口宽*/
const unsigned int SCR_HEIGHT = 600;/*窗口高*/
/*着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"out vec4 vertexColor; // 为片段着色器指定一个颜色输出\n"/*添加*/
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0); "// 注意我们如何把一个vec3作为vec4的构造器的参数
" vertexColor = vec4(1.0, 0.7, 0.6, 1.0);"// 把输出变量设置为暗红色
"}\0";
/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec4 vertexColor;\n"// 从顶点着色器传来的输入变量(名称相同、类型相同)
"void main()\n"
"{\n"
" FragColor = vertexColor;\n"//作了更改
"}\n\0";
/*着色器源码*/
//const char *vertexShaderSource = "#version 330 core\n"
//"layout (location = 0) in vec3 aPos;\n"
//"void main()\n"
//"{\n"
//" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
//"}\0";
///*片段着色器源码*/
//const char *fragmentShaderSource = "#version 330 core\n"
//"out vec4 FragColor;\n"
//"void main()\n"
//"{\n"
//" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
//"}\n\0";
void framebuffer_size_callback(GLFWwindow* window, int width, int height);/*窗口回调函数*/
void processInput(GLFWwindow *window);/*输入检测函数*/
/*输入函数:检查用户是否按下了返回键(Esc)*/
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
/*把WindowShouldClose属性设置为 true的方法关闭GLFW*/
glfwSetWindowShouldClose(window, true);
}
/*每当窗口改变大小,GLFW会调用这个函数并填充相应的参数供你处理*/
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Mac OS系统就加上这一句
#endif
/*创建一个宽SCR_WIDTH,高SCR_HEIGHT,名为"LearnOpenGL"的窗口*/
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
/*如果创建窗口失败*/
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
/*通知GLFW将我们窗口的上下文设置为当前线程的主上下文*/
glfwMakeContextCurrent(window);
/*需要注册这个函数,告诉GLFW我们希望每当窗口调整大小的时候调用这个函数*/
/*framebuffer_size_callback:窗口大小改变时,调用这个函数*/
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
/*初始化GLAD:GLFW给我们的是glfwGetProcAddress*/
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
/*用glCreateShader创建一个顶点着色器对象:传递的参数是GL_VERTEX_SHADER*/
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
/*把这个着色器源码vertexShaderSource附加到着色器对象上*/
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
/*然后编译它*/
glCompileShader(vertexShader);
/*检测在调用glCompileShader后编译是否成功*/
int success;
char infoLog[512];/*储存错误消息(如果有的话)的容器*/
/*用glGetShaderiv检查是否编译成功*/
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
/*如果编译失败,我们会用glGetShaderInfoLog获取错误消息*/
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
/*编译片段着色器的过程与顶点着色器类似,只不过我们使用GL_FRAGMENT_SHADER常量作为着色器类型*/
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
/*把这个着色器源码fragmentShaderSource附加到着色器对象上*/
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
/*用glCreateProgram建一个程序对象*/
int shaderProgram = glCreateProgram();
/*把之前编译的着色器附加到程序对象上*/
glAttachShader(shaderProgram, vertexShader);/*顶点着色器*/
glAttachShader(shaderProgram, fragmentShader);/*片段着色器*/
/*然后用glLinkProgram链接它们*/
glLinkProgram(shaderProgram);
/*检测链接着色器程序是否失败,并获取相应的日志*/
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
/*把着色器对象链接到程序对象以后,删除着色器对象*/
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
/*开始绘制图形之前,先给OpenGL输入一些顶点数据:△的三个顶点*/
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};
//float vertices[] = {
// 0.5f, 0.5f, 0.0f, // top right
// 0.5f, -0.5f, 0.0f, // bottom right
// -0.5f, -0.5f, 0.0f, // bottom left
// -0.5f, 0.5f, 0.0f // top left
//};
//unsigned int indices[] = { // note that we start from 0!
// 0, 1, 3, // first Triangle
// 1, 2, 3 // second Triangle
//};
/*使用glGenBuffers函数和一个缓冲ID生成一个VBO对象*/
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
/*使用glBindVertexArray绑定VAO*/
glBindVertexArray(VAO);
/*把顶点数组复制到缓冲中供OpenGL使用*/
glBindBuffer(GL_ARRAY_BUFFER, VBO);
/*glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数
第一个参数是目标缓冲的类型
第二个参数指定传输数据的大小(以字节为单位)
第三个参数是我们希望发送的实际数据
第四个参数指定了我们希望显卡如何管理给定的数据*/
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//unsigned int VBO, VAO, EBO;/*多了一个EBO*/
//glGenVertexArrays(1, &VAO);
//glGenBuffers(1, &VBO);
//glGenBuffers(1, &EBO);/*添加*/
//
//glBindVertexArray(VAO);
//glBindBuffer(GL_ARRAY_BUFFER, VBO);
//glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);/*添加*/
//glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);/*添加*/
/*设置顶点属性指针*/
/*第一个参数指定我们要配置的顶点属性,第二个参数指定顶点属性的大小,第三个参数指定数据的类型
下个参数定义我们是否希望数据被标准化(Normalize),第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔
最后一个参数的类型是void*,所以需要我们进行这个奇怪的强制类型转换*/
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
/*通过glVertexAttribPointer设置的顶点属性配置。*/
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
/*渲染循环*/
while (!glfwWindowShouldClose(window))
{
/*迭代调用processInput输入函数*/
processInput(window);
/*每个新的渲染迭代开始的时候清屏*/
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
/*画三角形*/
/*通过glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)函数配置OpenGL如何绘制图元
第一个参数表示我们打算将其应用到所有的三角形的正面和背面,第二个参数告诉我们用线来绘制
之后的绘制调用会一直以线框模式绘制三角形,直到我们
用glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)将其设置回默认模式*/
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);/*线框模式*/
glUseProgram(shaderProgram);/*程序对象*/
glBindVertexArray(VAO);
/*glDrawArrays函数,它使用当前激活的着色器,之前定义的顶点属性配置,
和VBO的顶点数据(通过VAO间接绑定)来绘制图元
glDrawArrays函数第一个参数是我们打算绘制的OpenGL图元的类型*/
glDrawArrays(GL_TRIANGLES, 0, 3);/*绘制一个三角形时*/
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);/*绘制两个三角形时*/
/*检查并调用事件,交换缓冲*/
glfwSwapBuffers(window);
glfwPollEvents();
}
/*当渲染循环结束后我们需要正确释放/删除之前的分配的所有资源*/
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
//glDeleteBuffers(1, &EBO);/*添加*/
/*调用glfwTerminate函数来完成*/
glfwTerminate();
return 0;
}
Uniform
√Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同:uniform是全局的(Global)。
√声明一个GLSL的uniform:在一个着色器中添加uniform关键字至类型和变量名前。
√如果声明了一个uniform却在GLSL代码中没用过,编译器会静默移除这个变量,导致最后编译出的版本中并不会包含它,这可能导致几个非常麻烦的错误
√这个uniform现在还是空的;我们还没有给它添加任何数据,首先需要找到着色器中uniform属性的索引/位置值,然后更新它的值,让它随着时间改变颜色。
√接下来可能就是唯一需要看的地方了,那就是Uniform使用之后的区别。
√修改地点①:着色器源码(修改的地方用不同的颜色标志了)
/*着色器源码*/ const char *vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "void main()\n" "{\n" " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" "}\0"; | /*着色器源码*/ const char *vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "void main()\n" "{\n" " gl_Position = vec4(aPos, 1.0);\n"//vec4(aPos.x, aPos.y, aPos.z, 1.0) "}\0"; |
/*片段着色器源码*/ const char *fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" "void main()\n" "{\n" " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\n\0"; | /*片段着色器源码*/ const char *fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" "uniform vec4 ourColor;\n"// 声明了一个uniform vec4的ourColor "void main()\n" "{\n" " FragColor = ourColor;\n"//vec4(1.0f, 0.5f, 0.2f, 1.0f) "}\n\0"; |
√修改地点②:while循环里
/*激活着色器*/
glUseProgram(shaderProgram);
/*更新uniform颜色*/
float timeValue = glfwGetTime();/*通过glfwGetTime()获取运行的秒数*/
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;/*使用sin函数让颜色在0.0到1.0之间改变*/
/*用glGetUniformLocation查询uniform ourColor的位置值*/
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
/*更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的*/
glUseProgram(shaderProgram);/*程序对象:这一句原来就存在*/
/*通过glUniform4f函数设置uniform值。*/
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);/*后面四位是rgba*/
√最后三角形由绿变黑,再又黑变绿,循环往复,代码就是正确的了。
√一点小改变:在上面的代码中,我们可以看到这一句:
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
我加的注释里也写明了,后面四位就是控制颜色与透明度的,greenValue是我的一个随时间变化的float变量,我们如果把这句话改成:
glUniform4f(vertexColorLocation, 1.0f-greenValue, greenValue, 1.0f, 1.0f);
三角形颜色就会由蓝变粉,再由粉变蓝。
√代码:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
const unsigned int SCR_WIDTH = 800;/*窗口宽*/
const unsigned int SCR_HEIGHT = 600;/*窗口高*/
/*着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n"//vec4(aPos.x, aPos.y, aPos.z, 1.0)
"}\0";
/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec4 ourColor;\n"// 声明了一个uniform vec4的ourColor
"void main()\n"
"{\n" //并把片段着色器的输出颜色设置为uniform值的内容
" FragColor = ourColor;\n"//vec4(1.0f, 0.5f, 0.2f, 1.0f)
"}\n\0";
void framebuffer_size_callback(GLFWwindow* window, int width, int height);/*窗口回调函数*/
void processInput(GLFWwindow *window);/*输入检测函数*/
/*输入函数:检查用户是否按下了返回键(Esc)*/
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
/*把WindowShouldClose属性设置为 true的方法关闭GLFW*/
glfwSetWindowShouldClose(window, true);
}
/*每当窗口改变大小,GLFW会调用这个函数并填充相应的参数供你处理*/
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Mac OS系统就加上这一句
#endif
/*创建一个宽SCR_WIDTH,高SCR_HEIGHT,名为"LearnOpenGL"的窗口*/
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
/*如果创建窗口失败*/
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
/*通知GLFW将我们窗口的上下文设置为当前线程的主上下文*/
glfwMakeContextCurrent(window);
/*需要注册这个函数,告诉GLFW我们希望每当窗口调整大小的时候调用这个函数*/
/*framebuffer_size_callback:窗口大小改变时,调用这个函数*/
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
/*初始化GLAD:GLFW给我们的是glfwGetProcAddress*/
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
/*用glCreateShader创建一个顶点着色器对象:传递的参数是GL_VERTEX_SHADER*/
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
/*把这个着色器源码vertexShaderSource附加到着色器对象上*/
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
/*然后编译它*/
glCompileShader(vertexShader);
/*检测在调用glCompileShader后编译是否成功*/
int success;
char infoLog[512];/*储存错误消息(如果有的话)的容器*/
/*用glGetShaderiv检查是否编译成功*/
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
/*如果编译失败,我们会用glGetShaderInfoLog获取错误消息*/
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
/*编译片段着色器的过程与顶点着色器类似,只不过我们使用GL_FRAGMENT_SHADER常量作为着色器类型*/
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
/*把这个着色器源码fragmentShaderSource附加到着色器对象上*/
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
/*用glCreateProgram建一个程序对象*/
int shaderProgram = glCreateProgram();
/*把之前编译的着色器附加到程序对象上*/
glAttachShader(shaderProgram, vertexShader);/*顶点着色器*/
glAttachShader(shaderProgram, fragmentShader);/*片段着色器*/
/*然后用glLinkProgram链接它们*/
glLinkProgram(shaderProgram);
/*检测链接着色器程序是否失败,并获取相应的日志*/
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
/*把着色器对象链接到程序对象以后,删除着色器对象*/
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
/*开始绘制图形之前,先给OpenGL输入一些顶点数据:△的三个顶点*/
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f // top
};
/*使用glGenBuffers函数和一个缓冲ID生成一个VBO对象*/
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
/*使用glBindVertexArray绑定VAO*/
glBindVertexArray(VAO);
/*把顶点数组复制到缓冲中供OpenGL使用*/
glBindBuffer(GL_ARRAY_BUFFER, VBO);
/*glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数
第一个参数是目标缓冲的类型
第二个参数指定传输数据的大小(以字节为单位)
第三个参数是我们希望发送的实际数据
第四个参数指定了我们希望显卡如何管理给定的数据*/
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
/*设置顶点属性指针*/
/*第一个参数指定我们要配置的顶点属性,第二个参数指定顶点属性的大小,第三个参数指定数据的类型
下个参数定义我们是否希望数据被标准化(Normalize),第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔
最后一个参数的类型是void*,所以需要我们进行这个奇怪的强制类型转换*/
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
/*通过glVertexAttribPointer设置的顶点属性配置。*/
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
/*渲染循环*/
while (!glfwWindowShouldClose(window))
{
/*迭代调用processInput输入函数*/
processInput(window);
/*每个新的渲染迭代开始的时候清屏*/
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
/*激活着色器*/
glUseProgram(shaderProgram);
/*更新uniform颜色*/
float timeValue = glfwGetTime();/*通过glfwGetTime()获取运行的秒数*/
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;/*使用sin函数让颜色在0.0到1.0之间改变*/
/*用glGetUniformLocation查询uniform ourColor的位置值*/
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
/*更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的*/
glUseProgram(shaderProgram);/*程序对象:这一句原来就存在*/
/*通过glUniform4f函数设置uniform值。*/
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);/*后面四位是rgba*/
/*绘制三角形*/
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);/*绘制一个三角形时*/
/*交换缓冲并查询IO事件*/
glfwSwapBuffers(window);
glfwPollEvents();
}
/*当渲染循环结束后我们需要正确释放/删除之前的分配的所有资源*/
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
//glDeleteBuffers(1, &EBO);/*添加*/
/*调用glfwTerminate函数来完成*/
glfwTerminate();
return 0;
}
更多属性
√在前面的教程中,我们了解了如何填充VBO、配置顶点属性指针以及如何把它们都储存到一个VAO里。
√把颜色数据加进顶点数据中:我们将把颜色数据添加为3个float值至vertices数组,把三角形的三个角分别指定为红色、绿色和蓝色
√修改地点①:
float vertices[] = {
// 位置 // 颜色
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 顶部
};
√由于现在有更多的数据要发送到顶点着色器,所以要调整一下顶点着色器,使它能够接收颜色值作为一个顶点属性输入。用layout标识符来把aColor属性的位置值设置为1。
√修改地点②:顶点着色器
/*着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n"//vec4(aPos.x, aPos.y, aPos.z, 1.0)
"}\0";
↓
/*顶点着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"// 位置变量的属性位置值为 0
"layout (location = 1) in vec3 aColor;\n"// 颜色变量的属性位置值为 1
"out vec3 ourColor; \n"// 向片段着色器输出一个颜色
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n"//vec4(aPos.x, aPos.y, aPos.z, 1.0)
" ourColor=aColor;\n"//将ourColor设置为我们从顶点数据那里得到的输入颜色
"}\0";
√由于我们不再使用uniform来传递片段的颜色了,现在使用ourColor输出变量,我们必须再修改一下片段着色器
√修改地点③:片段着色器源码
/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec4 ourColor;\n"// 声明了一个uniform vec4的ourColor
"void main()\n"
"{\n" //并把片段着色器的输出颜色设置为uniform值的内容
" FragColor = ourColor;\n"//vec4(1.0f, 0.5f, 0.2f, 1.0f)
"}\n\0";
↓
/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(ourColor, 1.0);\n"
"}\n\0";
√因为我们添加了另一个顶点属性,并且更新了VBO的内存,我们就必须重新配置顶点属性指针。使用glVertexAttribPointer函数更新顶点格式。
√修改地点④:属性指针配置。(while循环上面的4行)
/*设置顶点属性指针*/
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
/*通过glVertexAttribPointer设置的顶点属性配置。*/
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
↓
/*位置属性*/
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
/*通过glVertexAttribPointer设置的顶点属性配置。*/
glEnableVertexAttribArray(0);
/*颜色属性*/
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glUseProgram(shaderProgram);
√修改地点⑤:while循环里删除uniform相关的那一段:
/*激活着色器*/
glUseProgram(shaderProgram);
/*更新uniform颜色*/
float timeValue = glfwGetTime();/*通过glfwGetTime()获取运行的秒数*/
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;/*使用sin函数让颜色在0.0到1.0之间改变*/
/*用glGetUniformLocation查询uniform ourColor的位置值*/
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
/*更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的*/
glUseProgram(shaderProgram);/*程序对象:这一句原来就存在*/
/*通过glUniform4f函数设置uniform值。*/
glUniform4f(vertexColorLocation, 1.0f-greenValue, greenValue, 1.0f, 1.0f);/*后面四位是rgba*/
√最后会出现一个这样的彩色三角形:
√全部代码:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
const unsigned int SCR_WIDTH = 800;/*窗口宽*/
const unsigned int SCR_HEIGHT = 600;/*窗口高*/
/*顶点着色器源码*/
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"// 位置变量的属性位置值为 0
"layout (location = 1) in vec3 aColor;\n"// 颜色变量的属性位置值为 1
"out vec3 ourColor;\n"// 向片段着色器输出一个颜色
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n"//vec4(aPos.x, aPos.y, aPos.z, 1.0)
" ourColor=aColor;\n"//将ourColor设置为我们从顶点数据那里得到的输入颜色
"}\0";
/*片段着色器源码*/
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec3 ourColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(ourColor, 1.0f);\n"
"}\n\0";
void framebuffer_size_callback(GLFWwindow* window, int width, int height);/*窗口回调函数*/
void processInput(GLFWwindow *window);/*输入检测函数*/
/*输入函数:检查用户是否按下了返回键(Esc)*/
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
/*把WindowShouldClose属性设置为 true的方法关闭GLFW*/
glfwSetWindowShouldClose(window, true);
}
/*每当窗口改变大小,GLFW会调用这个函数并填充相应的参数供你处理*/
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Mac OS系统就加上这一句
#endif
/*创建一个宽SCR_WIDTH,高SCR_HEIGHT,名为"LearnOpenGL"的窗口*/
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
/*如果创建窗口失败*/
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
/*通知GLFW将我们窗口的上下文设置为当前线程的主上下文*/
glfwMakeContextCurrent(window);
/*需要注册这个函数,告诉GLFW我们希望每当窗口调整大小的时候调用这个函数*/
/*framebuffer_size_callback:窗口大小改变时,调用这个函数*/
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
/*初始化GLAD:GLFW给我们的是glfwGetProcAddress*/
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
/*用glCreateShader创建一个顶点着色器对象:传递的参数是GL_VERTEX_SHADER*/
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
/*把这个着色器源码vertexShaderSource附加到着色器对象上*/
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
/*然后编译它*/
glCompileShader(vertexShader);
/*检测在调用glCompileShader后编译是否成功*/
int success;
char infoLog[512];/*储存错误消息(如果有的话)的容器*/
/*用glGetShaderiv检查是否编译成功*/
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
/*如果编译失败,我们会用glGetShaderInfoLog获取错误消息*/
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
/*编译片段着色器的过程与顶点着色器类似,只不过我们使用GL_FRAGMENT_SHADER常量作为着色器类型*/
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
/*把这个着色器源码fragmentShaderSource附加到着色器对象上*/
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
/*用glCreateProgram建一个程序对象*/
int shaderProgram = glCreateProgram();
/*把之前编译的着色器附加到程序对象上*/
glAttachShader(shaderProgram, vertexShader);/*顶点着色器*/
glAttachShader(shaderProgram, fragmentShader);/*片段着色器*/
/*然后用glLinkProgram链接它们*/
glLinkProgram(shaderProgram);
/*检测链接着色器程序是否失败,并获取相应的日志*/
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
/*把着色器对象链接到程序对象以后,删除着色器对象*/
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
/*开始绘制图形之前,先给OpenGL输入一些顶点数据:△的三个顶点*/
float vertices[] = {
// 位置 // 颜色
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 顶部
};
/*使用glGenBuffers函数和一个缓冲ID生成一个VBO对象*/
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
/*使用glBindVertexArray绑定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, 6 * sizeof(float), (void*)0);
/*通过glVertexAttribPointer设置的顶点属性配置。*/
glEnableVertexAttribArray(0);
/*颜色属性*/
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glUseProgram(shaderProgram);
/*渲染循环*/
while (!glfwWindowShouldClose(window))
{
/*迭代调用processInput输入函数*/
processInput(window);
/*每个新的渲染迭代开始的时候清屏*/
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
/*绘制三角形*/
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);/*绘制一个三角形时*/
/*交换缓冲并查询IO事件*/
glfwSwapBuffers(window);
glfwPollEvents();
}
/*当渲染循环结束后我们需要正确释放/删除之前的分配的所有资源*/
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
//glDeleteBuffers(1, &EBO);/*添加*/
/*调用glfwTerminate函数来完成*/
glfwTerminate();
return 0;
}
我们自己的着色器类
√自己写着色器显得很麻烦,这是总有英雄拯救世界——封装。
√把着色器类全部放在在头文件里,添加必要的include,并定义类结构(具体见教程):
#ifndef SHADER_H#define SHADER_H
#include <glad/glad.h>; // 包含glad来获取所有的必须OpenGL头文件
#include <string>#include <fstream>#include <sstream>#include <iostream>
class Shader
{public:
// 程序ID
unsigned int ID;
// 构造器读取并构建着色器
Shader(const GLchar* vertexPath, const GLchar* fragmentPath);
// 使用/激活程序
void use();
// uniform工具函数
void setBool(const std::string &name, bool value) const;
void setInt(const std::string &name, int value) const;
void setFloat(const std::string &name, float value) const;
};
#endif
从文件读取
①配置GLM
http://www.pianshen.com/article/130332661/
②配置文件路径
在D盘根目录下(随便哪里都可以,反正到时候路径写正确就好了),新建两个txt文件,然后分别写入(两个着色器的源码):
#version 330 corelayout (location = 0) in vec3 aPos;layout (location = 1) in vec3 aColor;
out vec3 ourColor;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
}
上面这个保存为:3.3.shader.vs (随便什么名字,到时候自己传参的时候要一致)
另一个如下:
#version 330 coreout vec4 FragColor;
in vec3 ourColor;
void main()
{
FragColor = vec4(ourColor, 1.0f);
}
这个保存为:3.3.shader.fs(随便什么名字,到时候自己传参的时候要一致)
新建一个名为shader_s.h的头文件,代码(教程也有)如下:
#ifndef SHADER_H
#define SHADER_H
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader
{
public:
unsigned int ID;
// constructor generates the shader on the fly
// ------------------------------------------------------------------------
Shader(const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr)
{
// 1. retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::string geometryCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
std::ifstream gShaderFile;
// ensure ifstream objects can throw exceptions:
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
gShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
// open files
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// close file handlers
vShaderFile.close();
fShaderFile.close();
// convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
// if geometry shader path is present, also load a geometry shader
if (geometryPath != nullptr)
{
gShaderFile.open(geometryPath);
std::stringstream gShaderStream;
gShaderStream << gShaderFile.rdbuf();
gShaderFile.close();
geometryCode = gShaderStream.str();
}
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char * fShaderCode = fragmentCode.c_str();
// 2. compile shaders
unsigned int vertex, fragment;
// vertex shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// if geometry shader is given, compile geometry shader
unsigned int geometry;
if (geometryPath != nullptr)
{
const char * gShaderCode = geometryCode.c_str();
geometry = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(geometry, 1, &gShaderCode, NULL);
glCompileShader(geometry);
checkCompileErrors(geometry, "GEOMETRY");
}
// shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
if (geometryPath != nullptr)
glAttachShader(ID, geometry);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// delete the shaders as they're linked into our program now and no longer necessery
glDeleteShader(vertex);
glDeleteShader(fragment);
if (geometryPath != nullptr)
glDeleteShader(geometry);
}
// activate the shader
// ------------------------------------------------------------------------
void use()
{
glUseProgram(ID);
}
// utility uniform functions
// ------------------------------------------------------------------------
void setBool(const std::string &name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
// ------------------------------------------------------------------------
void setInt(const std::string &name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
// ------------------------------------------------------------------------
void setFloat(const std::string &name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
// ------------------------------------------------------------------------
void setVec2(const std::string &name, const glm::vec2 &value) const
{
glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec2(const std::string &name, float x, float y) const
{
glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y);
}
// ------------------------------------------------------------------------
void setVec3(const std::string &name, const glm::vec3 &value) const
{
glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec3(const std::string &name, float x, float y, float z) const
{
glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z);
}
// ------------------------------------------------------------------------
void setVec4(const std::string &name, const glm::vec4 &value) const
{
glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]);
}
void setVec4(const std::string &name, float x, float y, float z, float w)
{
glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w);
}
// ------------------------------------------------------------------------
void setMat2(const std::string &name, const glm::mat2 &mat) const
{
glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
// ------------------------------------------------------------------------
void setMat3(const std::string &name, const glm::mat3 &mat) const
{
glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
// ------------------------------------------------------------------------
void setMat4(const std::string &name, const glm::mat4 &mat) const
{
glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
private:
// utility function for checking shader compilation/linking errors.
// ------------------------------------------------------------------------
void checkCompileErrors(GLuint shader, std::string type)
{
GLint success;
GLchar infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
#endif
然后是我们的cpp代码:
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "shader_s.h"
#include <iostream>
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
Shader ourShader("D:\\3.3.shader.vs", "D:\\3.3.shader.fs");
float vertices[] = {
// positions // colors
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom left
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // top
};
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
ourShader.use();
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window);
glfwPollEvents();
}
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glfwTerminate();
return 0;
}
√区别:
①我们可以看到,两个着色器源码都用外部文件(那两个vs与fs文件)替代了。
②中间非常冗长的一段(float之前)都被封装了,然后使用
Shader ourShader("D:\\3.3.shader.vs", "D:\\3.3.shader.fs"); 调用。最后还是一个彩色的三角形。