GLSL 内建变量
内建变量为着色器无需手动定义、可直接使用的系统变量,自带固定语义,可理解为系统自动填充或等待管线填充的内置常量/变量。
*示例:*顶点着色器中裁剪空间输出位置向量 gl_Position。
着色器接口块
接口块用于规范化组织着色器的输入与输出数据,实现顶点着色器与片元着色器的数据传递。
使用规则
- 顶点着色器:使用
out关键字定义输出接口块 - 片元着色器:使用
in关键字定义输入接口块 - 核心要求:两端块名必须完全一致,块实例对象名无需一致
- 访问块内成员变量时,必须添加块名作为前缀
普通 Uniform 变量共享规则
跨着色器程序
多个独立的着色器程序(各自包含一套顶点+片元着色器),即使定义了同名 uniform 变量,必须分别单独设置,相互不共享。
同着色器程序内部
对于同一个着色器程序,若顶点着色器、片元着色器使用了同一个 uniform 变量,只需 CPU 设置一次即可全局共用。
Uniform 缓冲对象
UBO(Uniform Buffer Object):类似于 VBO,VBO 用于存储顶点数据,UBO 专门用于存储多个着色器程序可共享的批量 Uniform 变量。
核心工作原理
GPU 预设有若干 UBO 绑定点,通过绑定点编号对齐实现数据互通:
- CPU 侧:将创建好的 UBO 绑定到指定编号的绑定点
- 着色器侧:Uniform 块声明绑定到同一编号的绑定点
- 编号匹配后,所有关联该绑定点的着色器均可读取同一份
UBO数据
着色器端 UBO 声明(OpenGL4.2+)
// std140:固定内存对齐规则// binding=0:绑定至0号UBO绑定点layout (std140,binding=0) uniform GlobalMatrices{ mat4 view; mat4 project;};
// 普通独有Uniform,仅属于当前着色器uniform mat4 model;C++ 端创建 UBO
unsigned int ubo;glGenBuffers(1,&ubo);glBindBuffer(GL_UNIFORM_BUFFER,ubo);// 开辟对应大小显存,暂不填充数据glBufferData(GL_UNIFORM_BUFFER,2*sizeof(glm::mat4),NULL,GL_STATIC_DRAW);glBindBuffer(GL_UNIFORM_BUFFER,0);C++ 端绑定 UBO 到绑定点
一个 UBO 只需绑定一次,全局生效:// 方式1:指定偏移与大小,支持一个缓冲区绑定多段UBOglBindBufferRange(GL_UNIFORM_BUFFER,0,ubo,0,2*sizeof(glm::mat4));
// 方式2:整体绑定整个UBO缓冲区glBindBufferBase(GL_UNIFORM_BUFFER,0,ubo);低版本 OpenGL 绑定方式(4.2 以下)
不支持着色器内 binding 布局,需要代码手动关联块与绑定点:// 获取Uniform块索引unsigned int index=glGetUniformBlockIndex(shader.ID,"GlobalMatrices");// 将该块关联到0号绑定点glUniformBlockBinding(shader.ID,index,0);需要使用该 Uniform 块的所有着色器,均需执行一次该绑定。
Uniform 三种内存布局规则
CPU 与 GPU 内存对齐策略不同,必须指定布局规则,才能精准匹配数据偏移。常用三种:std140、shared、packed。
std140
强制固定对齐规则,偏移可提前预判计算,适合UBO多着色器共享。
基础对齐规范
- float / int / bool / uint:基准对齐 4 字节,占用 4 字节
- vec2:基准对齐 8 字节,占用 8 字节
- vec3:基准对齐 16 字节,占用 12 字节
- vec4:基准对齐 16 字节,占用 16 字节
- matN 矩阵:按列拆分为多个 vec4,每列基准对齐 16 字节、占用 16 字节
- 数组:每个元素基准对齐强制 16 字节,元素自身占用按类型计算
- 结构体:整体基准对齐 = 内部最大成员的基准对齐,内部成员按规则排布填充
layout (std140) uniform ExampleBlock{ // 基准对齐 // 字节偏移 float value; // 4 // 0 vec3 vector; // 16 // 16 mat4 matrix; // 16 // 32、48、64、80(四列) float values[3]; // 16 // 96、112、128 bool boolean; // 4 // 144 int integer; // 4 // 148};整体块占用 152 字节,开辟显存时至少分配该大小。
shared
无强制固定对齐规则,编译器自动优化内存排布,变量偏移无法预计算。 使用时需要运行时查询偏移量再写入数据:
// 1. 获取块索引GLuint blockIndex=glGetUniformBlockIndex(shader.ID,"TestBlock");// 2. 定义成员名数组const char* names[]={"f","v3","arr[0]"};GLuint indices[3];// 3. 获取成员索引glGetUniformIndices(shader.ID,3,names,indices);// 4. 查询各成员偏移GLuint offsets[3];glGetActiveUniformsiv(shader.ID,3,indices,GL_UNIFORM_OFFSET,offsets);packed
编译器会自动优化剔除未使用的 Uniform 变量,内存布局不固定。
禁止用于 UBO 共享场景:不同着色器的变量使用情况不同,布局不一致,无法跨程序共享数据。
UBO 数据填充方式
使用glBufferSubData局部更新 UBO 数据:
glBufferSubData(GL_UNIFORM_BUFFER, 内存偏移, 数据真实大小, 数据指针);参数说明:偏移量为 std140 规则计算出的精准字节偏移,大小为变量真实数据大小,不包含内存填充空字节。
Uniform 块变量访问规则
在着色器代码中,Uniform 块内部成员变量无需加任何前缀,可直接访问使用。
UBO 优势
- **提升渲染效率:**批量合并内存传输,大幅减少 CPU-GPU API 调用次数
- **多着色器共享、易维护:**全局一份统一数据,切换着色器无需重复赋值
- **突破原生 Uniform 数量限制:**原生 Uniform 组件数量上限极低(mat4、vec 数量受限),无法满足骨骼动画等大量矩阵需求;UBO 限制宽松,可承载大批量矩阵数据
example1: GLSL内建变量
顶点着色器的输出变量gl_PointSize。要实现的是离得越远,点越大。那么首先在main.cpp我们需要构建一些点,点没有顶点数据,所以无需准备(位置、法线或纹理等顶点属性信息),但是点有位置数据,所以可以定义一个点位置数组(如果要绘制多个点的话)。把这个位置数据直接放入顶点缓冲区,配置顶点指针,然后在渲染循环内使用glDrawArrays(GL_POINT,0,8),第一个参数指明了绘制图元类型为点,第二个参数指明了从顶点缓冲区的第一个顶点开始取数据,第三个参数指明了要取多少个数据(即要画多少个点)。如果需要配置点大小,则需要开启GL_PROGRAM_POINT_SIZE,然后在顶点着色器中配置gl_PointSize。这里为了实现离得越远,点越大,那么gl_PointSize=abs(gl_Position.z),因为gl_Position正是裁剪空间坐标,其z坐标的绝对值代表了物体到相机的非线性深度(相机朝-z看,如果物体在相机前方,其z坐标必定为负数,且离得越远,z坐标的绝对值越大)(教程中这里没有加abs,但也没有出错,我也不知道为什么,按道理说gl_Position.z是负数,赋值给pointSize肯定是不合适的。但是我可视化了一下gl_Position.z发现它并不是负数,这就更奇怪了)
#define STB_IMAGE_IMPLEMENTATION#include <glad/glad.h>#include <GLFW/glfw3.h>
#include <glm/glm.hpp>#include <glm/gtc/matrix_transform.hpp>#include <glm/gtc/type_ptr.hpp>
#include <stb_image.h>#include <iostream>#include <vector>
#include <myShader.h>#include <myCamera.h>#include <Model.h>
using namespace std;
const unsigned int SCR_WIDTH = 800;const unsigned int SCR_HEIGHT = 400;
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));float lastX = (float)SCR_WIDTH / 2.0f;float lastY = (float)SCR_HEIGHT / 2.0f;float deltaTime = 0.0f;float lastFrame = 0.0f;bool firstCamera = true;
void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height);}void processInput(GLFWwindow* window) { if(glfwGetKey(window,GLFW_KEY_ESCAPE)==GLFW_PRESS){ glfwSetWindowShouldClose(window, true); } if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { camera.ProcessKeyboard(FORWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { camera.ProcessKeyboard(BACKWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { camera.ProcessKeyboard(LEFT, deltaTime); } if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { camera.ProcessKeyboard(RIGHT, deltaTime); }}void processMovement(GLFWwindow* window, double xpos, double ypos) { if (firstCamera) { lastX = xpos; lastY = ypos; firstCamera = false; } float xoffset = xpos - lastX; float yoffset = lastY - ypos; lastX = xpos; lastY = ypos; camera.ProcessMouseMovement(xoffset, yoffset);}void processScroll(GLFWwindow* window, double xoffset, double yoffset) { camera.ProcessScroll(yoffset);}unsigned int loadTexture(const char* path) { unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture);
int width, height, nrChannels; unsigned char* data = stbi_load(path, &width, &height, &nrChannels, 0); if (data) { GLenum format; if (nrChannels == 1) { format = GL_RED; } else if (nrChannels == 3) { format = GL_RGB; } else if (nrChannels == 4) { format = GL_RGBA; } glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { cout << "Failed to load texture" << endl; } stbi_image_free(data); return texture;}unsigned int loadCubemap(vector<string> faces) { unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
int width, height, nrChannels; for (int i = 0; i < faces.size(); i++) { unsigned char* data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0); if(data) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); } else { cout << "failed to load cubemap" << endl; } stbi_image_free(data); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return texture;}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) { cout << "Failed to initialize GLFW" << endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glfwSetScrollCallback(window, processScroll); glfwSetCursorPosCallback(window, processMovement);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { cout << "failed to load glad" << endl; glfwTerminate(); return -1; }
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
//stbi_set_flip_vertically_on_load(true);
glEnable(GL_DEPTH_TEST); glEnable(GL_PROGRAM_POINT_SIZE);
float vertices[] = { -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f };
float pointPositions[] = { 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
-1.0f,0.0f,0.0f,
0.0f, 1.0f, 0.0f,
0.0f,-1.0f,0.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f,-1.0f,
2.0f, 0.0f, 0.0f };
unsigned int pointVAO, pointVBO; glGenVertexArrays(1, &pointVAO); glGenBuffers(1, & pointVBO); glBindVertexArray(pointVAO); glBindBuffer(GL_ARRAY_BUFFER, pointVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(pointPositions), pointPositions, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0);
glBindVertexArray(0);
Shader shader("src/Shader/vertexShader.txt", "src/Shader/fragmentShader.txt");
while (!glfwWindowShouldClose(window)) { float currentFrame = (float)glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame;
processInput(window);
glClearColor(0.1, 0.1, 0.1, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.use(); glm::mat4 view = camera.GetCameraView(); glm::mat4 project = glm::perspective(glm::radians(camera.Zoom),(float)SCR_WIDTH / SCR_HEIGHT, 0.1f, 100.0f); glm::mat4 model = glm::mat4(1.0f); shader.setMatrix("model", model); shader.setMatrix("view", view); shader.setMatrix("project", project);
glBindVertexArray(pointVAO); glDrawArrays(GL_POINTS, 0, 8); glBindVertexArray(0);
glfwSwapBuffers(window); glfwPollEvents(); } glDeleteVertexArrays(1, &pointVAO); glDeleteBuffers(1, &pointVBO);
glfwTerminate(); return 0;}//vertexShader#version 330 corelayout (location = 0) in vec3 aPos;
uniform mat4 model;uniform mat4 view;uniform mat4 project;
void main(){ gl_Position=project*view*model*vec4(aPos,1.0); gl_PointSize=gl_Position.z;}//fragmentShader#version 330 core
out vec4 FragColor;
void main(){ FragColor=vec4(vec3(0.5),1.0);}
顶点着色器的输入变量gl_VertexID。表示当前正在处理的顶点在本次绘制调用中的索引。从0开始递增,每处理一个顶点,值+1.对使用glDrawArrays绘制而言,gl_VertexID是顶点的全局序号,从start开始,每画一个顶点,gl_VertexID+1。对使用glDrawELements绘制而言,gl_VertexID是绘制顶点本身的索引。它的用途可以是:不需要在C++里准备完整顶点数据,靠gl_VertexID在着色器中区分顶点,减少CPU-GPU的数据传输。
#define STB_IMAGE_IMPLEMENTATION#include <glad/glad.h>#include <GLFW/glfw3.h>
#include <glm/glm.hpp>#include <glm/gtc/matrix_transform.hpp>#include <glm/gtc/type_ptr.hpp>
#include <stb_image.h>#include <iostream>#include <vector>
#include <myShader.h>#include <myCamera.h>#include <Model.h>
using namespace std;
const unsigned int SCR_WIDTH = 800;const unsigned int SCR_HEIGHT = 400;
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));float lastX = (float)SCR_WIDTH / 2.0f;float lastY = (float)SCR_HEIGHT / 2.0f;float deltaTime = 0.0f;float lastFrame = 0.0f;bool firstCamera = true;
void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height);}void processInput(GLFWwindow* window) { if(glfwGetKey(window,GLFW_KEY_ESCAPE)==GLFW_PRESS){ glfwSetWindowShouldClose(window, true); } if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { camera.ProcessKeyboard(FORWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { camera.ProcessKeyboard(BACKWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { camera.ProcessKeyboard(LEFT, deltaTime); } if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { camera.ProcessKeyboard(RIGHT, deltaTime); }}void processMovement(GLFWwindow* window, double xpos, double ypos) { if (firstCamera) { lastX = xpos; lastY = ypos; firstCamera = false; } float xoffset = xpos - lastX; float yoffset = lastY - ypos; lastX = xpos; lastY = ypos; camera.ProcessMouseMovement(xoffset, yoffset);}void processScroll(GLFWwindow* window, double xoffset, double yoffset) { camera.ProcessScroll(yoffset);}unsigned int loadTexture(const char* path) { unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture);
int width, height, nrChannels; unsigned char* data = stbi_load(path, &width, &height, &nrChannels, 0); if (data) { GLenum format; if (nrChannels == 1) { format = GL_RED; } else if (nrChannels == 3) { format = GL_RGB; } else if (nrChannels == 4) { format = GL_RGBA; } glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { cout << "Failed to load texture" << endl; } stbi_image_free(data); return texture;}unsigned int loadCubemap(vector<string> faces) { unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
int width, height, nrChannels; for (int i = 0; i < faces.size(); i++) { unsigned char* data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0); if(data) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); } else { cout << "failed to load cubemap" << endl; } stbi_image_free(data); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return texture;}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) { cout << "Failed to initialize GLFW" << endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glfwSetScrollCallback(window, processScroll); glfwSetCursorPosCallback(window, processMovement);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { cout << "failed to load glad" << endl; glfwTerminate(); return -1; }
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
//stbi_set_flip_vertically_on_load(true);
glEnable(GL_DEPTH_TEST); glEnable(GL_PROGRAM_POINT_SIZE);
float vertices[] = { -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f };
//只存了一个顶点的数据 float pointPositions[] = { 0.0f, 0.0f, 0.0f };
unsigned int pointVAO, pointVBO; glGenVertexArrays(1, &pointVAO); glGenBuffers(1, & pointVBO); glBindVertexArray(pointVAO); glBindBuffer(GL_ARRAY_BUFFER, pointVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(pointPositions), pointPositions, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0);
glBindVertexArray(0);
Shader shader("src/Shader/vertexShader.txt", "src/Shader/fragmentShader.txt");
while (!glfwWindowShouldClose(window)) { float currentFrame = (float)glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame;
processInput(window);
glClearColor(0.1, 0.1, 0.1, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.use(); glm::mat4 view = camera.GetCameraView(); glm::mat4 project = glm::perspective(glm::radians(camera.Zoom),(float)SCR_WIDTH / SCR_HEIGHT, 0.1f, 100.0f); glm::mat4 model = glm::mat4(1.0f); shader.setMatrix("model", model); shader.setMatrix("view", view); shader.setMatrix("project", project);
glBindVertexArray(pointVAO); glDrawArrays(GL_POINTS, 0, 15);//要求GPU连续处理15个顶点,而缓冲区只有1组数据,那么就循环复用缓冲区里的顶点数据 glBindVertexArray(0);
glfwSwapBuffers(window); glfwPollEvents(); } glDeleteVertexArrays(1, &pointVAO); glDeleteBuffers(1, &pointVBO);
glfwTerminate(); return 0;}//vertexShader#version 330 corelayout (location = 0) in vec3 aPos;
uniform mat4 model;uniform mat4 view;uniform mat4 project;
out vec3 ourColor;
void main(){ //根据变化的gl_VertexID计算顶点的差异化位置、大小、颜色 float offsetX=gl_VertexID*0.6; vec3 newPos=aPos+vec3(offsetX,0.0,0.0); gl_Position=project*view*model*vec4(newPos,1.0); gl_PointSize=8.0+gl_VertexID*1.2; ourColor=vec3(gl_VertexID/15.0,0.3,0.8);}//fragmentShader#version 330 corein vec3 ourColor;out vec4 FragColor;
void main(){ FragColor=vec4(ourColor,1.0);}
还可以是构建程序化网格:根据gl_VertexID计算点的行数和列数,分别赋予不同的颜色
//vertexShader#version 330 corelayout (location = 0) in vec3 aPos;
uniform mat4 model;uniform mat4 view;uniform mat4 project;
out vec3 ourColor;
void main(){ //根据变化的gl_VertexID计算顶点的差异化位置、颜色 float row=floor(gl_VertexID/10.0); float col=mod(gl_VertexID,10.0); vec3 gridPos=vec3(col*0.6,row*0.6,0.0); gl_Position=project*view*model*vec4(gridPos,1.0); gl_PointSize=8.0; ourColor=vec3(gl_VertexID/20.0,0.3,0.8);}
还可以是CPU传入顶点颜色/纹理数组,然后使用gl_VertexID作为下标取值。
片元着色器的输入gl_FragCoord。是屏幕空间的坐标,其x和y分量分别的范围是[0,screen_width]和[0,screenHeight],其z分量是片元的真实非线性深度值,范围是[0,1],可用于深度测试。此外,gl_FragCoord还可以用于技术演示,划分屏幕范围,特定的区域显示特定的颜色。如下例中屏幕左半部分显示红色,右半部分显示绿色。
#define STB_IMAGE_IMPLEMENTATION#include <glad/glad.h>#include <GLFW/glfw3.h>
#include <glm/glm.hpp>#include <glm/gtc/matrix_transform.hpp>#include <glm/gtc/type_ptr.hpp>
#include <stb_image.h>#include <iostream>#include <vector>
#include <myShader.h>#include <myCamera.h>#include <Model.h>
using namespace std;
const unsigned int SCR_WIDTH = 800;const unsigned int SCR_HEIGHT = 400;
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));float lastX = (float)SCR_WIDTH / 2.0f;float lastY = (float)SCR_HEIGHT / 2.0f;float deltaTime = 0.0f;float lastFrame = 0.0f;bool firstCamera = true;
void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height);}void processInput(GLFWwindow* window) { if(glfwGetKey(window,GLFW_KEY_ESCAPE)==GLFW_PRESS){ glfwSetWindowShouldClose(window, true); } if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { camera.ProcessKeyboard(FORWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { camera.ProcessKeyboard(BACKWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { camera.ProcessKeyboard(LEFT, deltaTime); } if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { camera.ProcessKeyboard(RIGHT, deltaTime); }}void processMovement(GLFWwindow* window, double xpos, double ypos) { if (firstCamera) { lastX = xpos; lastY = ypos; firstCamera = false; } float xoffset = xpos - lastX; float yoffset = lastY - ypos; lastX = xpos; lastY = ypos; camera.ProcessMouseMovement(xoffset, yoffset);}void processScroll(GLFWwindow* window, double xoffset, double yoffset) { camera.ProcessScroll(yoffset);}unsigned int loadTexture(const char* path) { unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture);
int width, height, nrChannels; unsigned char* data = stbi_load(path, &width, &height, &nrChannels, 0); if (data) { GLenum format; if (nrChannels == 1) { format = GL_RED; } else if (nrChannels == 3) { format = GL_RGB; } else if (nrChannels == 4) { format = GL_RGBA; } glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { cout << "Failed to load texture" << endl; } stbi_image_free(data); return texture;}unsigned int loadCubemap(vector<string> faces) { unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
int width, height, nrChannels; for (int i = 0; i < faces.size(); i++) { unsigned char* data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0); if(data) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); } else { cout << "failed to load cubemap" << endl; } stbi_image_free(data); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return texture;}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) { cout << "Failed to initialize GLFW" << endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glfwSetScrollCallback(window, processScroll); glfwSetCursorPosCallback(window, processMovement);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { cout << "failed to load glad" << endl; glfwTerminate(); return -1; }
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
//stbi_set_flip_vertically_on_load(true);
glEnable(GL_DEPTH_TEST); glEnable(GL_PROGRAM_POINT_SIZE);
float vertices[] = { -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f };
unsigned int cubeVAO, cubeVBO; glGenVertexArrays(1, &cubeVAO); glGenBuffers(1, &cubeVBO);
glBindVertexArray(cubeVAO); glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); 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);
Shader shader("src/Shader/vertexShader.txt", "src/Shader/fragmentShader.txt");
while (!glfwWindowShouldClose(window)) { float currentFrame = (float)glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame;
processInput(window);
glClearColor(0.1, 0.1, 0.1, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.use(); glm::mat4 view = camera.GetCameraView(); glm::mat4 project = glm::perspective(glm::radians(camera.Zoom),(float)SCR_WIDTH / SCR_HEIGHT, 0.1f, 100.0f); glm::mat4 model = glm::mat4(1.0f); shader.setMatrix("model", model); shader.setMatrix("view", view); shader.setMatrix("project", project);
glBindVertexArray(cubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0);
glfwSwapBuffers(window); glfwPollEvents(); } glDeleteVertexArrays(1, &cubeVAO); glDeleteBuffers(1, &cubeVBO);
glfwTerminate(); return 0;}//vertexShader#version 330 corelayout (location = 0) in vec3 aPos;layout (location = 1) in vec3 aNormal;
uniform mat4 model;uniform mat4 view;uniform mat4 project;
void main(){ gl_Position=project*view*model*vec4(aPos,1.0);}//fragmentShader#version 330 coreout vec4 FragColor;
void main(){ if(gl_FragCoord.x<400){ FragColor=vec4(1.0,0.0,0.0,1.0); }else{ FragColor=vec4(0.0,1.0,0.0,1.0); }}
还可以用于测试不同的光照模型:下例中左半部分采用phong光照模型(反射向量和视角方向接近度),右半部分采用blinn-phong光照模型(半角向量和法向量接近度),观察高光部分的不同。可以看到blinn-phong光照模型下高光形状更自然。
//fragmentShader#version 330 corein vec3 FragPos;in vec3 Normal;
out vec4 FragColor;
uniform vec3 lightPos;uniform vec3 viewPos;void main(){ vec2 uv=gl_FragCoord.xy/vec2(800,400); vec3 worldNormal=normalize(Normal); vec3 worldLightDir=normalize(lightPos-FragPos); vec3 worldViewDir=normalize(viewPos-FragPos);
float diff=max(dot(worldNormal,worldLightDir),0.0); vec3 lightDiffuse=vec3(0.8f); vec3 diffuseColor=vec3(0.1,0.6,0.2); vec3 diffuse=lightDiffuse*diff*diffuseColor;
vec3 lightAmbient=vec3(0.1f); vec3 ambient=lightAmbient*diffuseColor;
float spec=0.0f; if(gl_FragCoord.x<400){ vec3 reflectDir=reflect(-worldLightDir,worldNormal); spec=pow(max(dot(worldViewDir,reflectDir),0.0f),32.0f); }else{ vec3 halfDir=normalize(worldLightDir+worldViewDir); spec=pow(max(dot(worldNormal,halfDir),0.0f),32.0f); } vec3 lightSpecular=vec3(1.0f); vec3 specular=lightSpecular*spec*vec3(1.0f);
FragColor=vec4(ambient+diffuse+specular,1.0);}
片元着色器的输入变量gl_FrontFacing,返回一个bool类型的值,反应当前片元是正面面的一部分,还是背面面的一部分。仅在不开启面剔除时使用,因为开启面剔除之后,有一类面(正向面/背向面)总是会被剔除,那么gl_FrontFacing总是返回true/false,没有使用的意义了。下例中为模型的正向面和背向面分别赋予不同的纹理。编写代码时一定要注意关闭面剔除,以及顶点的定义需要遵循背向面顺时针,正向面逆时针(或者反过来,关键是要全部背面统一,全部正面统一)
#define STB_IMAGE_IMPLEMENTATION#include <glad/glad.h>#include <GLFW/glfw3.h>
#include <glm/glm.hpp>#include <glm/gtc/matrix_transform.hpp>#include <glm/gtc/type_ptr.hpp>
#include <stb_image.h>#include <iostream>#include <vector>
#include <myShader.h>#include <myCamera.h>#include <Model.h>
using namespace std;
const unsigned int SCR_WIDTH = 800;const unsigned int SCR_HEIGHT = 400;
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));float lastX = (float)SCR_WIDTH / 2.0f;float lastY = (float)SCR_HEIGHT / 2.0f;float deltaTime = 0.0f;float lastFrame = 0.0f;bool firstCamera = true;
void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height);}void processInput(GLFWwindow* window) { if(glfwGetKey(window,GLFW_KEY_ESCAPE)==GLFW_PRESS){ glfwSetWindowShouldClose(window, true); } if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { camera.ProcessKeyboard(FORWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { camera.ProcessKeyboard(BACKWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { camera.ProcessKeyboard(LEFT, deltaTime); } if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { camera.ProcessKeyboard(RIGHT, deltaTime); }}void processMovement(GLFWwindow* window, double xpos, double ypos) { if (firstCamera) { lastX = xpos; lastY = ypos; firstCamera = false; } float xoffset = xpos - lastX; float yoffset = lastY - ypos; lastX = xpos; lastY = ypos; camera.ProcessMouseMovement(xoffset, yoffset);}void processScroll(GLFWwindow* window, double xoffset, double yoffset) { camera.ProcessScroll(yoffset);}unsigned int loadTexture(const char* path) { unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture);
int width, height, nrChannels; unsigned char* data = stbi_load(path, &width, &height, &nrChannels, 0); if (data) { GLenum format; if (nrChannels == 1) { format = GL_RED; } else if (nrChannels == 3) { format = GL_RGB; } else if (nrChannels == 4) { format = GL_RGBA; } glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { cout << "Failed to load texture" << endl; } stbi_image_free(data); return texture;}unsigned int loadCubemap(vector<string> faces) { unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
int width, height, nrChannels; for (int i = 0; i < faces.size(); i++) { unsigned char* data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0); if(data) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); } else { cout << "failed to load cubemap" << endl; } stbi_image_free(data); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return texture;}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) { cout << "Failed to initialize GLFW" << endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glfwSetScrollCallback(window, processScroll); glfwSetCursorPosCallback(window, processMovement);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { cout << "failed to load glad" << endl; glfwTerminate(); return -1; }
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
stbi_set_flip_vertically_on_load(true);
glEnable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE);
float vertices[] = { // Back face -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // Bottom-left 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, // bottom-right 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // bottom-left -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left // Front face -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-right 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // top-right -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, // top-left -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left // Left face -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-right -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-left -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-left -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-left -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-right -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-right // Right face 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // bottom-right 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // top-left 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-left // Bottom face -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, // top-left 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // bottom-left -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // bottom-right -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, // top-right // Top face -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // top-right 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // bottom-right -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // top-left -0.5f, 0.5f, 0.5f, 0.0f, 0.0f // bottom-left };
unsigned int cubeVAO, cubeVBO; glGenVertexArrays(1, &cubeVAO); glGenBuffers(1, &cubeVBO);
glBindVertexArray(cubeVAO); glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1);
Shader shader("src/Shader/vertexShader.txt", "src/Shader/fragmentShader.txt");
unsigned int frontTexture = loadTexture("resources/textures/container.jpg"); unsigned int backTexture = loadTexture("resources/textures/marble.jpg");
shader.use(); shader.setInt("frontTexture", 0); shader.setInt("backTexture", 1);
while (!glfwWindowShouldClose(window)) { float currentFrame = (float)glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame;
processInput(window);
glClearColor(0.1, 0.1, 0.1, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.use(); glm::mat4 view = camera.GetCameraView(); glm::mat4 project = glm::perspective(glm::radians(camera.Zoom),(float)SCR_WIDTH / SCR_HEIGHT, 0.1f, 100.0f); glm::mat4 model = glm::mat4(1.0f); shader.setMatrix("model", model); shader.setMatrix("view", view); shader.setMatrix("project", project);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, frontTexture); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, backTexture);
glBindVertexArray(cubeVAO); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0);
glfwSwapBuffers(window); glfwPollEvents(); } glDeleteVertexArrays(1, &cubeVAO); glDeleteBuffers(1, &cubeVBO);
glfwTerminate(); return 0;}//vertexShader#version 330 corelayout (location = 0) in vec3 aPos;layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
uniform mat4 model;uniform mat4 view;uniform mat4 project;
void main(){ gl_Position=project*view*model*vec4(aPos,1.0); TexCoords=aTexCoords;}//fragmentShader#version 330 core
in vec2 TexCoords;out vec4 FragColor;
uniform sampler2D frontTexture;uniform sampler2D backTexture;
void main(){ if(gl_FrontFacing){ FragColor=texture(frontTexture,TexCoords); }else{ FragColor=texture(backTexture,TexCoords); }}
gl_FragCoord还可以用于实现双面材质的物体、双面光照,以及描边轮廓(检测到背面,输出纯色描边)。此外,默认是剔除背面,也可以通过glCullFace设置要剔除的面。
片元着色器的输出变量gl_FragDepth,默认值等于gl_FragCoord.z,但是gl_FragCoord作为一个输入变量,它是只读的,所以如果我们要修改片元深度,就只能使用gl_FragDepth。但之前说过,如果要在片元着色器修改深度,那么early-z就禁用了,因为early-z时并不知道当前片元是否会被剔除,无法预知修改后的片元深度。但是opengl4.2版本以上给我们提供了有条件的修改。
//fragmentShader//片元着色器中修改后的深度只能比原始深度(即gl_FragCoord.z大)//这种情况下,如果原始深度>缓冲深度,那么修改后的深度必定也>缓冲深度,一定可以被early-z剔除layout (depth_greater) out float gl_FragDepth;//片元着色器中修改后的深度只能比原始深度小//这种情况下,如果原始深度<缓冲深度,那么修改后的深度必定也<缓冲深度,一定可以被early-z保留layout (depth_less) out float gl_FragDepth;//片元着色器中修改后的深度和原始深度关系未知,可大可小layout (depth_any) out float gl_FragDepth;//片元着色器中未对深度进行修改layout (depth_unchanged) out float gl_FragDepth;example2: 接口块
**使用接口块的原因:使代码模块化,**当传递的变量很少时,零散in/out无所谓,但当渲染管线复杂时,可能有十几二十个变量,接口块可以把所有传递数据收拢到一个结构体,语义清晰。
//vertexShader#version 330 corelayout (location = 0) in vec3 aPos;layout (location = 1) in vec2 aTexCoords;
out VS_OUT{ vec2 TexCoords;}vs_out;
uniform mat4 model;uniform mat4 view;uniform mat4 project;
void main(){ gl_Position=project*view*model*vec4(aPos,1.0); vs_out.TexCoords=aTexCoords;}//fragmentShader#version 330 core
in VS_OUT{ vec2 TexCoords;}fs_in;out vec4 FragColor;
uniform sampler2D frontTexture;uniform sampler2D backTexture;
void main(){ if(gl_FrontFacing){ FragColor=texture(frontTexture,fs_in.TexCoords); }else{ FragColor=texture(backTexture,fs_in.TexCoords); }}example3: uniform缓冲
使用多个着色器绘制不同的模型,但所有着色器共享一套matrices uniform结构块,对其中的变量view和project矩阵只需赋值一次,体现UBO跨着色器共享uniform的价值。
**首先是创建UBO缓冲,**与创建VBO的步骤类似。在使用glBufferData开辟缓冲占据内存时,开辟的大小由缓冲内所有uniform块的内存决定,而uniform块的内存由它要存储的变量决定,本例中只有一个uniform块,且uniform块内只是两个mat矩阵。
然后是将创建的UBO缓冲绑定到绑定点。
然后使用glBufferSubData写入数据,需要知道数据的起始偏移位置和数据的大小。
然后创建若干个着色器程序,分别将其中的uniform块绑定到与UBO一样的绑定点。
最后在渲染循环中分别激活这些着色器程序,设置变化的变量或私有的变量,绘制。
#define STB_IMAGE_IMPLEMENTATION#include <glad/glad.h>#include <GLFW/glfw3.h>
#include <glm/glm.hpp>#include <glm/gtc/matrix_transform.hpp>#include <glm/gtc/type_ptr.hpp>
#include <stb_image.h>#include <iostream>#include <vector>
#include <myShader.h>#include <myCamera.h>#include <Model.h>
using namespace std;
const unsigned int SCR_WIDTH = 800;const unsigned int SCR_HEIGHT = 400;
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));float lastX = (float)SCR_WIDTH / 2.0f;float lastY = (float)SCR_HEIGHT / 2.0f;float deltaTime = 0.0f;float lastFrame = 0.0f;bool firstCamera = true;
void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height);}void processInput(GLFWwindow* window) { if(glfwGetKey(window,GLFW_KEY_ESCAPE)==GLFW_PRESS){ glfwSetWindowShouldClose(window, true); } if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { camera.ProcessKeyboard(FORWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { camera.ProcessKeyboard(BACKWARD, deltaTime); } if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { camera.ProcessKeyboard(LEFT, deltaTime); } if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { camera.ProcessKeyboard(RIGHT, deltaTime); }}void processMovement(GLFWwindow* window, double xpos, double ypos) { if (firstCamera) { lastX = xpos; lastY = ypos; firstCamera = false; } float xoffset = xpos - lastX; float yoffset = lastY - ypos; lastX = xpos; lastY = ypos; camera.ProcessMouseMovement(xoffset, yoffset);}void processScroll(GLFWwindow* window, double xoffset, double yoffset) { camera.ProcessScroll(yoffset);}unsigned int loadTexture(const char* path) { unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture);
int width, height, nrChannels; unsigned char* data = stbi_load(path, &width, &height, &nrChannels, 0); if (data) { GLenum format; if (nrChannels == 1) { format = GL_RED; } else if (nrChannels == 3) { format = GL_RGB; } else if (nrChannels == 4) { format = GL_RGBA; } glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { cout << "Failed to load texture" << endl; } stbi_image_free(data); return texture;}unsigned int loadCubemap(vector<string> faces) { unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
int width, height, nrChannels; for (int i = 0; i < faces.size(); i++) { unsigned char* data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0); if(data) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); } else { cout << "failed to load cubemap" << endl; } stbi_image_free(data); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return texture;}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) { cout << "Failed to initialize GLFW" << endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); glfwSetScrollCallback(window, processScroll); glfwSetCursorPosCallback(window, processMovement);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { cout << "failed to load glad" << endl; glfwTerminate(); return -1; }
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
//stbi_set_flip_vertically_on_load(true);
glEnable(GL_DEPTH_TEST);
float vertices[] = { // positions -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, };
unsigned int cubeVAO, cubeVBO; glGenVertexArrays(1, &cubeVAO); glGenBuffers(1, &cubeVBO);
glBindVertexArray(cubeVAO); glBindBuffer(GL_ARRAY_BUFFER, cubeVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0);
//创建uniform缓冲对象 unsigned int uboMatrices; glGenBuffers(1, &uboMatrices); glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices); glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0);
//关联缓冲对象到绑定点 glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboMatrices, 0, 2 * sizeof(glm::mat4));
//填充缓冲对象 //先填充渲染过程中不会变的成员变量 glm::mat4 project = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);//相机没有缩放了,固定fov为45度 glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(project));//为什么需要value_ptr?glm::mat4是C++类/结构体,而glBufferSubData等OpenGL API只接收原生指针,value_ptr会返回矩阵内部底层float数组首地址 glBindBuffer(GL_UNIFORM_BUFFER, 0);
Shader shader1("src/Shader/vertexShader.txt", "src/Shader/fragmentShader.txt"); Shader shader2("src/Shader/vertexShader.txt", "src/Shader/fragmentShader2.txt"); Shader shader3("src/Shader/vertexShader.txt", "src/Shader/fragmentShader3.txt"); Shader shader4("src/Shader/vertexShader.txt", "src/Shader/fragmentShader4.txt");
//为顶点着色器中的uniform块关联绑定点 //获得uniform块的位置值 unsigned int uniformBlockIndexRed = glGetUniformBlockIndex(shader1.ID, "Matrices"); unsigned int uniformBlockIndexGreen = glGetUniformBlockIndex(shader2.ID, "Matrices"); unsigned int uniformBlockIndexBlue = glGetUniformBlockIndex(shader3.ID, "Matrices"); unsigned int uniformBlockIndexYellow = glGetUniformBlockIndex(shader4.ID, "Matrices"); //关联绑定点 glUniformBlockBinding(shader1.ID, uniformBlockIndexRed, 0); glUniformBlockBinding(shader2.ID, uniformBlockIndexGreen, 0); glUniformBlockBinding(shader3.ID, uniformBlockIndexBlue, 0); glUniformBlockBinding(shader4.ID, uniformBlockIndexYellow, 0);
while (!glfwWindowShouldClose(window)) { float currentFrame = (float)glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame;
processInput(window);
glClearColor(0.1, 0.1, 0.1, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 view = camera.GetCameraView(); glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices); glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(view)); glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindVertexArray(cubeVAO); shader1.use(); glm::mat4 model; model = glm::translate(model, glm::vec3(-0.75f, 0.75f, 0.0f)); shader1.setMatrix("model", model); glDrawArrays(GL_TRIANGLES, 0, 36);
shader2.use(); model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(0.75f, 0.75f, 0.0f)); shader2.setMatrix("model", model); glDrawArrays(GL_TRIANGLES, 0, 36);
shader3.use(); model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(-0.75f, -0.75f, 0.0f)); shader3.setMatrix("model", model); glDrawArrays(GL_TRIANGLES, 0, 36);
shader4.use(); model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(0.75f, -0.75f, 0.0f)); shader4.setMatrix("model", model); glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glfwSwapBuffers(window); glfwPollEvents(); } glDeleteVertexArrays(1, &cubeVAO); glDeleteBuffers(1, &cubeVBO);
glfwTerminate(); return 0;}//vertexShader#version 330 corelayout (location = 0) in vec3 aPos;layout (std140) uniform Matrices{ mat4 project; mat4 view;};
uniform mat4 model;
void main(){ gl_Position=project*view*model*vec4(aPos,1.0);}//fragmentShader#version 330 coreout vec4 FragColor;
void main(){ FragColor=vec4(1.0,0.0,0.0,1.0);}//fragmentShader2#version 330 coreout vec4 FragColor;
void main(){ FragColor=vec4(0.0,1.0,0.0,1.0);}//fragmentShader3#version 330 coreout vec4 FragColor;
void main(){ FragColor=vec4(0.0,0.0,1.0,1.0);}//fragmentShader4#version 330 coreout vec4 FragColor;
void main(){ FragColor=vec4(1.0,1.0,0.0,1.0);}