建设网站所需要的技术,备案平台新增网站,高级搜索百度,有没有做家纺类的网站一.GLSL 着色器是使用一种叫GLSL的类C语言写成的。GLSL着色器编码顺序#xff1a;声明版本》定义输入输出》uniform》main函数。每个着色器的入口点是main函数#xff0c;在main函数中我们处理所有的输入变量#xff0c;并将结果输出到输出变量中。如下图#xff1a;
#ver…一.GLSL 着色器是使用一种叫GLSL的类C语言写成的。GLSL着色器编码顺序声明版本》定义输入输出》uniform》main函数。每个着色器的入口点是main函数在main函数中我们处理所有的输入变量并将结果输出到输出变量中。如下图
#version version_number
in type in_variable_name;
in type in_variable_name;out type out_variable_name;uniform type uniform_name;int main()
{// 处理输入并进行一些图形操作...// 输出处理过的结果到输出变量out_variable_name weird_stuff_we_processed;
}
二.顶点着色器(补充) 顶点着色器的每个输入变量也叫顶点属性我们能够声明的定点属性是有上限的它通常由硬件决定。OpenGL确保至少有16个包含4分量的顶点属性可用但是有些硬件或许允许更多的顶点属性你可以查询GL_MAX_VERTEX_ATTRIBS来获取具体的上限
int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, nrAttributes);
std::cout Maximum nr of vertex attributes supported: ;
三.GLSL数据类型 GLSL包含默认基础数据类型int、float、double、uint和bool。GLSL也有两种容器类型分别是向量(Vector)和矩阵(Matrix)。这里先介绍向量后面在学矩阵。
四.向量 GLSL中的向量是一个可以包含有2、3或者4个分量的容器分量的类型可以是前面默认基础类型的任意一个。它们可以是下面的形式n代表分量的数量
类型含义vecn包含n个float分量的默认向量bvecn包含n个bool分量的向量ivecn包含n个int分量的向量uvecn包含n个unsigned int分量的向量dvecn包含n个double分量的向量 一个向量的分量可以通过vec.x这种方式获取这里x是指这个向量的第一个分量。你可以分别使用.x、.y、.z和.w来获取它们的第1、2、3、4个分量。GLSL也允许你对颜色使用rgba或是对纹理坐标使用stpq访问相同的分量。 向量这一数据类型也允许一些有趣而灵活的分量选择方式叫做重组(Swizzling)。重组允许这样的语法
vec2 someVec;
vec4 differentVec someVec.xyxx;
vec3 anotherVec differentVec.zyw;
vec4 otherVec someVec.xxxx anotherVec.yxzy; 你可以使用上面4个字母任意组合来创建一个和原来向量一样长的同类型新向量只要原来向量有那些分量即可然而你不允许在一个vec2向量中去获取.z元素。我们也可以把一个向量作为一个参数传给不同的向量构造函数以减少需求参数的数量
vec2 vect vec2(0.5, 0.7);
vec4 result vec4(vect, 0.0, 0.0);
vec4 otherResult vec4(result.xyz, 1.0);
四.输入和输出 着色器是许多独立的小程序GLSL定义了in和out关键字专门来实现这个目的每个着色器使用这两个关键字设定输入和输出。只要一个输出变量与下一个着色器阶段的输入匹配它就会传递下去。但在顶点和片段着色器中会有点不同。顶点着色器应该接收的是一种特殊形式的输入否则就会效率低下。顶点着色器的输入特殊在它从顶点数据中直接接收输入。它从顶点数据中直接接收输入。为了定义顶点数据该如何管理我们使用location这一元数据指定输入变量这样我们才可以在CPU上配置顶点属性。我们已经在前面的教程看过这个了layout (location 0)。顶点着色器需要为它的输入提供一个额外的layout标识这样我们才能把它链接到顶点数据。片段着色器它需要一个vec4颜色输出变量因为片段着色器需要生成一个最终输出的颜色。如果你在片段着色器没有定义输出颜色OpenGL会把你的物体渲染为黑色或白色。 如果我们打算从一个着色器向另一个着色器发送数据我们必须在发送方着色器中声明一个输出在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候OpenGL就会把两个变量链接到一起它们之间就能发送数据了这是在链接程序对象时完成的。我们用lesson2的示例修改顶点着色器和片段着色器的代码得到 测试 着色器之间的参数传递
// 注意我们如何把一个vec3作为vec4的构造器的参数
// 把输出变量设置为暗红色
const char *vertexShaderSource #version 330 core\nlayout (location 0) in vec3 aPos;\nout vec4 vertexColor;\nvoid main()\n{\n gl_Position vec4(aPos, 1.0);\n vertexColor vec4(0.5, 0.0, 0.0, 1.0);\n}\0;const char *fragmentShaderSource #version 330 core\nout vec4 FragColor;\nin vec4 vertexColor;\nvoid main()\n{\nFragColor vertexColor;\n}\n\0;测试 着色器之间的参数传递 end 五.Uniform关键字 Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式但uniform和顶点属性有些不同。首先uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的而且它可以被着色器程序的任意着色器在任意阶段访问。第二无论你把uniform值设置成什么uniform会一直保存它们的数据直到它们被重置或更新。
下面演示如何设置uniform的值我们让六边形的渲染颜色动起来。 测试 Uniform
const char *fragmentShaderSource #version 330 core\nout vec4 FragColor;\nuniform vec4 customColor;\nvoid main()\n{\nFragColor customColor;\n}\n\0;/// 测试 end // 着色器处理float timeValue glfwGetTime(); //获取运行的秒数float greenValue (sin(timeValue) / 2.0f) 0.5f; //sin函数让颜色在0.0到1.0之间改变///glGetUniformLocation查询uniform ourColor的位置值。/// 我们为查询函数提供着色器程序和uniform的名字这是我们希望获得的位置值的来源。/// 如果glGetUniformLocation返回-1就代表没有找到这个位置值。/// 最后我们可以通过glUniform4f函数设置uniform值。注意查询uniform地址不要求你之前使用过着色器程序/// 但是更新一个uniform之前你必须先使用程序调用glUseProgram)因为它是在当前激活的着色器程序中设置uniform的。int vertexColorLocation glGetUniformLocation(shaderProgram, customColor);glUseProgram(shaderProgram);glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);//glUseProgram(shaderProgram);// 着色器end 先设置片段着色器的uniform值我们在main函数中渲染之前使用着色器通过获取着色器的位置然后设置颜色值。效果如下
live.csdn.net/v/364740
六.更多属性 在lesson2里面我们知道了解了VBOVAO等关键字这次我们学习如何设置顶点的颜色以及加入着色器中。 1.先把顶点数组里面每个坐标后面添加一个颜色值。 // 顶点输入六边形float vertices[] {-0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f,1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f,-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};2. 顶点着色器使它能够接收颜色值作为一个顶点属性输入。需要注意的是我们用layout标识符来把aColor属性的位置值设置为1
/// 测试将定点着色器添加颜色值
const char *vertexShaderSource #version 330 core\nlayout (location 0) in vec3 aPos;\nlayout (location 1) in vec3 aColor;\nout vec3 ourColor;\nvoid main()\n{\n gl_Position vec4(aPos.x, aPos.y, aPos.z, 1.0);\n ourColor aColor;\n}\0;const char *fragmentShaderSource #version 330 core\nin vec3 ourColor;\nout vec4 FragColor;\nvoid main()\n{\nFragColor vec4(ourColor, 1.0f);\n}\n\0;
/// 测试 end 3.计算VAO里顶点着色器解析的位置和颜色。 // 解析顶点数据// 第一个参数: 0, 着色器的location 0,对应这里的0// 第二个参数: 3, 顶点数据是3个坐标构成这里是3// 第三个参数: 数据类型GL_FLOAT// 第四个参数: 是否标准化标准化就会映射到0~1之间// 第五个参数步长: 表示每个顶点的所占空间的大小// 第六个参数: 代表偏移默认位置为0//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);// 启动顶点属性//glEnableVertexAttribArray(0);// 位置属性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); 4.得到的效果如下 这是原作者给出的图像解释原作者是3角形我们是六边形下面的颜色数量为6
这个图片可能不是你所期望的那种因为我们只提供了3个颜色而不是我们现在看到的大调色板。这是在片段着色器中进行的所谓片段插值(Fragment Interpolation)的结果。当渲染一个三角形时光栅化(Rasterization)阶段通常会造成比原指定顶点更多的片段。光栅会根据每个片段在三角形形状上所处相对位置决定这些片段的位置。 基于这些位置它会插值(Interpolate)所有片段着色器的输入变量。比如说我们有一个线段上面的端点是绿色的下面的端点是蓝色的。如果一个片段着色器在线段的70%的位置运行它的颜色输入属性就会是一个绿色和蓝色的线性结合更精确地说就是30%蓝 70%绿。
这正是在这个三角形中发生了什么。我们有3个顶点和相应的3个颜色从这个三角形的像素来看它可能包含50000左右的片段片段着色器为这些像素进行插值颜色。如果你仔细看这些颜色就应该能明白了红首先变成到紫再变为蓝色。片段插值会被应用到片段着色器的所有输入属性上。
七.编写我们自己的着色器重点来啦 编写、编译、管理着色器是件麻烦事。在着色器主题的最后我们会写一个类来让我们的生活轻松一点它可以从硬盘读取着色器然后编译并链接它们并对它们进行错误检测这就变得很好用了。这也会让你了解该如何封装目前所学的知识到一个抽象对象中。
编写定点和片段着色器代码文件
#version 330 core
layout (location 0) in vec3 aPos;
out vec4 vertexColor;
void main()
{gl_Position vec4(aPos, 1.0);vertexColor vec4(0.5, 0.0, 0.0, 1.0);
}
#version 330 core
out vec4 FragColor;
uniform vec4 customColor;
void main()
{FragColor customColor;
} 封装着色器类
shader.h
#pragma once
// 包含glad来获取所有的必须OpenGL头文件
#include glad/glad.h
#include string
#include fstream
#include sstream
#include iostreamclass Shader
{
public:// 程序IDunsigned int ID;// 构造器读取并构建着色器Shader(const char* vertexPath, const char* fragmentPath);// 使用/激活程序void use();// uniform工具函数void setFloat(const std::string name, float value) const;
};shader.cpp
#include shader.hShader::Shader(const char* vertexPath, const char* fragmentPath)
{// 1. 从文件路径中获取顶点/片段着色器std::string vertexCode;std::string fragmentCode;std::ifstream vShaderFile;std::ifstream fShaderFile;// 保证ifstream对象可以抛出异常vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit);try{// 打开文件vShaderFile.open(vertexPath);fShaderFile.open(fragmentPath);std::stringstream vShaderStream, fShaderStream;// 读取文件的缓冲内容到数据流中vShaderStream vShaderFile.rdbuf();fShaderStream fShaderFile.rdbuf();// 关闭文件处理器vShaderFile.close();fShaderFile.close();// 转换数据流到stringvertexCode vShaderStream.str();fragmentCode fShaderStream.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. 编译着色器unsigned int vertex, fragment;int success;char infoLog[512];// 顶点着色器vertex glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertex, 1, vShaderCode, NULL);glCompileShader(vertex);// 打印编译错误如果有的话glGetShaderiv(vertex, GL_COMPILE_STATUS, success);if(!success){glGetShaderInfoLog(vertex, 512, NULL, infoLog);std::cout ERROR::SHADER::VERTEX::COMPILATION_FAILED\n infoLog std::endl;}// 片段着色器fragment glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragment, 1, fShaderCode, NULL);glCompileShader(fragment);glGetShaderiv(fragment, GL_COMPILE_STATUS, success);if(!success){glGetShaderInfoLog(fragment, 512, NULL, infoLog);std::cout ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n infoLog std::endl;}// 着色器程序ID glCreateProgram();glAttachShader(ID, vertex);glAttachShader(ID, fragment);glLinkProgram(ID);// 打印连接错误如果有的话glGetProgramiv(ID, GL_LINK_STATUS, success);if(!success){glGetProgramInfoLog(ID, 512, NULL, infoLog);std::cout ERROR::SHADER::PROGRAM::LINKING_FAILED\n infoLog std::endl;}// 删除着色器它们已经链接到我们的程序中了已经不再需要了glDeleteShader(vertex);glDeleteShader(fragment);
}void Shader::use()
{glUseProgram(ID);
}void Shader::setFloat(const std::string name, float value) const
{glUniform4f(glGetUniformLocation(ID, name.c_str()), 0.0f, value, 0.0f, 1.0f);
}在main函数中使用着色器类: 真正实现的代码就两句 Shader ourShader(D:/opengl/learning-opengl/lesson3/createtriangle/shader.vs, D:/opengl/learning-opengl/lesson3/createtriangle/shader.fs); // 测试从文件读取着色器 ourShader.use(); ourShader.setFloat(customColor, greenValue);
int main()
{// 告知opengl我们使用的版本和渲染模式glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// mac 下需要这句话glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);// end// 创建opengl窗口GLFWwindow* window glfwCreateWindow(800, 600, 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);// glad加载if(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout failed to init glad! std::endl;}// // 构建顶点着色器// // 1.创建着色器对象// unsigned int vertexShader;// vertexShader glCreateShader(GL_VERTEX_SHADER);// // 2.将着色器代码关联到对象上并编译// glShaderSource(vertexShader, 1, vertexShaderSource, NULL);// glCompileShader(vertexShader);// // 3.检查编译是否成功// int success;// char infoLog[512];// glGetShaderiv(vertexShader, GL_COMPILE_STATUS, success);// if(!success)// {// glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);// std::cout ERROR::SHADER::VERTEX::COMPILATION_FAILED\n infoLog std::endl;// }// // 构建片段着色器// unsigned int 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);// std::cout ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n infoLog std::endl;// }// // 编译完成之后链接到程序对象// unsigned int shaderProgram;// shaderProgram glCreateProgram();// glAttachShader(shaderProgram, vertexShader);// glAttachShader(shaderProgram, fragmentShader);// glLinkProgram(shaderProgram);// glGetProgramiv(shaderProgram, GL_LINK_STATUS, success);// // 判断是否链接程序成功// if(!success) {// glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);// }// // 使用程序// //glUseProgram(shaderProgram);// // 删除着色器对象在把着色器对象链接到程序对象以后记得删除着色器对象我们不再需要它们// glDeleteShader(vertexShader);// glDeleteShader(fragmentShader);Shader ourShader(D:/opengl/learning-opengl/lesson3/createtriangle/shader.vs, D:/opengl/learning-opengl/lesson3/createtriangle/shader.fs);// 顶点输入六边形float vertices[] {-0.5f, 0.5f, 0.0f,0.5f, 0.5f, 0.0f,1.0f, 0.0f, 0.0f,0.5f, -0.5f, 0.0f,-0.5f, -0.5f, 0.0f,-1.0f, 0.0f, 0.0f};// float vertices[] {// -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,// 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f,// 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,// 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f,// -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,// -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f// };unsigned int indices[] {0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5};// 建立缓冲对象unsigned int VBO;unsigned int VAO;unsigned int EBO;glGenVertexArrays(1, VAO);glGenBuffers(1, VBO);glGenBuffers(1, EBO);glBindVertexArray(VAO);// 绑定到缓冲bufferglBindBuffer(GL_ARRAY_BUFFER, VBO);// 刷入缓冲bufferglBufferData(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);// 解析顶点数据// 第一个参数: 0, 着色器的location 0,对应这里的0// 第二个参数: 3, 顶点数据是3个坐标构成这里是3// 第三个参数: 数据类型GL_FLOAT// 第四个参数: 是否标准化标准化就会映射到0~1之间// 第五个参数步长: 表示每个顶点的所占空间的大小// 第六个参数: 代表偏移默认位置为0glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);// 启动顶点属性glEnableVertexAttribArray(0);// // 位置属性// 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);// 解绑VAOglBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);// 等待用户关闭窗口while(!glfwWindowShouldClose(window)){processInput(window);glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// 着色器处理uniformfloat timeValue glfwGetTime(); //获取运行的秒数float greenValue (sin(timeValue) / 2.0f) 0.5f; //sin函数让颜色在0.0到1.0之间改变// ///glGetUniformLocation查询uniform ourColor的位置值。// /// 我们为查询函数提供着色器程序和uniform的名字这是我们希望获得的位置值的来源。// /// 如果glGetUniformLocation返回-1就代表没有找到这个位置值。// /// 最后我们可以通过glUniform4f函数设置uniform值。注意查询uniform地址不要求你之前使用过着色器程序// /// 但是更新一个uniform之前你必须先使用程序调用glUseProgram)因为它是在当前激活的着色器程序中设置uniform的。// int vertexColorLocation glGetUniformLocation(shaderProgram, customColor);// glUseProgram(shaderProgram);// glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);// 着色器end//glUseProgram(shaderProgram);// 测试从文件读取着色器ourShader.use();ourShader.setFloat(customColor, greenValue);// 3. 绘制物体glBindVertexArray(VAO);glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);// 双缓冲交换glfwSwapBuffers(window);// 响应各种交互事件glfwPollEvents();}// 释放资源glfwTerminate();return 0;
}
效果同uniform的动画一样。截图如下 到这里基本就简单的学习了着色器。
八.学习地址(感谢原作者的讲解)
着色器 - LearnOpenGL CN (learnopengl-cn.github.io)
九.demo地址
learningOpengl: 一起学习opengl