如果想要完整的代码,可以用上一章最下面那份代码,然后进行局部替换就OK了
一、着色器结构
一个简单的着色器如下:
#version 3.4 //版本号
in type name1 //输入数据
out type name2 //输出数据
uniform type name3 //全局属性
int main()
{
// 处理输入并进行一些图形操作
...
// 输出处理过的结果到输出变量
name2 = xxx;
}
其中对于顶点着色器,输入数据叫做顶点属性(Vertex Attribute),也就是颜色位置这些,一般来讲顶点属性能声明的上限为16个,有些硬件可能更高,可以通过以下代码查询:(注意要在初始化之后)
GLint nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;
两个着色器数据的连接:
如果我们打算从一个着色器向另一个着色器发送数据,我们只需要在当前着色器中声明一个输出,在下一个着色器中声明一个同类型同名字的输入,OpenGL就会将这两个变量链接到一起,例如我们可以来个比较奇葩的操作:让顶点着色器为片段着色器决定颜色,如下:
// Shaders
const GLchar* VShader =
"#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"out vec4 colorIn;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
"colorIn = vec4(0.2f, 0.3f, 0.4f, 1);\n"
"}\n\0";
const GLchar* FShaderY =
"#version 330 core\n"
"in vec4 colorIn;\n"
"out vec4 colorOut;\n"
"void main()\n"
"{\n"
"colorOut = colorIn;\n"
"}\n\0";
对于片段着色器,我们必须要一个vec4的向量输出作为最终颜色,如果没有就会默认全黑或者全白,注意如果你的GLSL编译失败,也会出现全黑或者全白的情况
对于顶点着色器,它从顶点数据中直接接收输入,我们需要使用location这一元数据指定输入变量,也就是使用 "layout (location = 0)" 设置位置值,这样我们才可以在CPU上配置顶点属性
二、向量数据类型
GLSL中几种常见向量如下,其中n≤4
- vecn:包含n个float分量的默认向量
- bvecn:包含n个bool分量的向量
- ivecn:包含n个int分量的向量
- uvecn:包含n个unsigned int分量的向量
- dvecn:包含n个double分量的向量
我们可以用xyzw表示其所有分量,对于颜色来讲,我们也可以用rgba
向量可以随意组合,这种趣而灵活的分量选择方式,叫做重组(Swizzling),如下:
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
vec2 vect = vec2(0.5f, 0.7f);
vec4 result = vec4(vect, 0.0f, 0.0f);
vec4 otherResult = vec4(result.xyz, 1.0f);
当然要遵循基本原则,例如参数要对的上,不能对类似于vec2之类的向量访问其zw属性等
三、全局统一变量 Uniform
Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,也是GLSLshader中的全局变量,可以在任意的shader中访问,这也意味着uniform变量必须在每个着色器程序对象中都是独一无二的
如下,我们在片段着色器中声明了一个uniform变量,并把片段着色器的输出颜色设置为uniform值的内容。因为uniform是全局变量,我们可以在任何着色器中定义它们,而无需通过顶点着色器作为中介
#version 330 core
out vec4 color;
uniform vec4 ourColor; // 在OpenGL程序代码中设定这个变量
void main()
{
color = ourColor;
}
注意:如果你声明了一个uniform却在GLSL代码中没用过,编译器会静默移除这个变量,最后编译出的版本中就并不会包含它
接下来就是为uniform添加数据了,一个例子:
glUseProgram(shaderYellow);
GLfloat timeValue = glfwGetTime();
GLfloat greenValue = (sin(timeValue) / 2) + 0.5;
GLint vertexColorLocation = glGetUniformLocation(shaderYellow, "colorOut");
glUniform4f(vertexColorLocation, greenValue, greenValue, 0.0f, 1.0f);
- glUseProgram(shaderYellow):激活着色器
- glfwGetTime():获取当前运行的时间
- glGetUniformLocation(shaderYellow, "colorOut"):获取当前uniform变量colorOut的位置值,如果返回-1表示没有找到
- glUniform4f(vertexColorLocation, greenValue, greenValue, 0.0f, 1.0f):设置uniform值,其中第一个参数为位置值
注意上面的函数名glUniform4f,后面的4f就表示需要传入4个float,因为C不支持重载,所以只要参数不同就必须要一个新的函数,如下:你可以找到好多个一样前缀的方法
完整代码如下:
#pragma comment(lib,"glew32.lib")
#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include<opengl/freeglut.h>
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
const GLuint WIDTH = 800, HEIGHT = 600;
const GLchar* VShader =
"#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(position, 1.0);\n" //这里就用到了上面所说的“重组”
"}\n\0";
const GLchar* FShaderY =
"#version 330 core\n"
"out vec4 color;\n"
"uniform vec4 colorOut;\n"
"void main()\n"
"{\n"
"color = colorOut;\n"
"}\n\0";
const GLchar* FShaderB =
"#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
"color = vec4(0.1f, 0.1f, 1.0f, 1.0f);\n"
"}\n\0";
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
glewExperimental = GL_TRUE;
glewInit();
int width, height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &VShader, NULL);
glCompileShader(vertexShader);
GLuint fShaderY = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fShaderY, 1, &FShaderY, NULL);
glCompileShader(fShaderY);
GLuint fShaderB = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fShaderB, 1, &FShaderB, NULL);
glCompileShader(fShaderB);
GLuint shaderYellow = glCreateProgram();
GLuint shaderBlue = glCreateProgram();
glAttachShader(shaderYellow, vertexShader);
glAttachShader(shaderYellow, fShaderY);
glLinkProgram(shaderYellow);
glAttachShader(shaderBlue, vertexShader);
glAttachShader(shaderBlue, fShaderB);
glLinkProgram(shaderBlue);
glDeleteShader(vertexShader);
glDeleteShader(fShaderY);
glDeleteShader(fShaderB);
GLfloat trangleY[] =
{
-0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
0.0f, 0.0f, 0.0f
};
GLfloat trangleB[] =
{
0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
0.0f, 0.0f, 0.0f
};
GLuint VBO[2], VAO[2];
glGenVertexArrays(2, VAO);
glGenBuffers(2, VBO);
glBindVertexArray(VAO[0]);
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(trangleY), trangleY, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(VAO[1]);
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(trangleB), trangleB, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderYellow);
GLfloat timeValue = glfwGetTime();
GLfloat greenValue = (sin(timeValue) / 2) + 0.5;
GLint vertexColorLocation = glGetUniformLocation(shaderYellow, "colorOut");
//std::cout << vertexColorLocation << std::endl;
glUniform4f(vertexColorLocation, greenValue, greenValue, 0.0f, 1.0f);
glUseProgram(shaderYellow);
glBindVertexArray(VAO[0]);
glDrawArrays(GL_TRIANGLES, 0, 3);
glUseProgram(shaderBlue);
glBindVertexArray(VAO[1]);
glDrawArrays(GL_TRIANGLES, 0, 3);
glBindVertexArray(0);
glfwSwapBuffers(window);
}
glDeleteVertexArrays(2, VAO);
glDeleteBuffers(2, VBO);
glfwTerminate();
return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
}
运行后可以看到左边的三角形颜色会随时间变化