着色器

网友投稿 534 2022-09-01

着色器

着色器

链接程序做的事情就是从一个着色器像另一个着色器发送数据

着色器用in out数据传递

const GLchar* vertexShaderSource = "#version 330 core\n""layout(location = 0) in vec3 position;\n""out vec4 vertexColor;\n""void main()\n""{\n"" gl_Position = vec4(position.x, position.y, position.z, 1.0);\n""vertexColor = vec4(0.5f, 0.0f, 0.0f, 1.0f);\n""}\0";const GLchar* fragmentShaderSource = "#version 330 core\n""in vec4 vertexColor;\n""out vec4 color;\n""void main()\n""{\n""color = vertexColor;\n""}\0";

Uniform:相当于去全局的顶点属性。

const GLchar* fragmentShaderSource = "#version 330 core\n""out vec4 color;\n""uniform vec4 ourColor;\n""void main()\n""{\n""color = ourColor;\n""}\0";

GLfloat timeValue = glfwGetTime();GLfloat greenValue = (sin(timeValue) / 2) + 0.5;GLint vertexColorLocation = glGetUniformLocation(shaderProgramOrange, "ourColor");glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);/*首先我们通过glfwGetTime()获取运行的秒数。然后我们使用sin函数让颜色在0.0到1.0之间改变,最后将结果储存到greenValue里接着,我们用glGetUniformLocation查询uniform ourColor的位置值。我们为查询函数提供着色器程序和uniform的名字(这是我们希望获得的位置值的来源)。如果glGetUniformLocation返回-1就代表没有找到这个位置值。最后,我们可以通过glUniform4f函数设置uniform值。注意,查询uniform地址不要求你之前使用过着色器程序,但是更新一个unform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置unform的。*/

之后颜色会随时间变化。

之后是添加更多属性:

更新了属性后的VBO内存中的数据:

// GLEW#define GLEW_STATIC#include // GLFW#include #include void key_callback(GLFWwindow* windows, int key, int scancode, int action, int mode);//顶点着色器源码:const GLchar* vertexShaderSource = "#version 330 core\n""layout(location = 0) in vec3 position;\n""layout(location = 1) in vec3 color;\n"//这里用layout标识符把color属性设置为1"out vec3 ourColor;\n""void main()\n""{\n"" gl_Position = vec4(position.x, position.y, position.z, 1.0);\n""ourColor = color;\n""}\0";const GLchar* fragmentShaderSource = "#version 330 core\n""in vec3 ourColor;\n""out vec4 color;\n""void main()\n""{\n""color = vec4(ourColor, 1.0f);\n""}\n\0";int main(){ std::cout << "Starting GLFW context, OpenGL 3.3" << std::endl; //这都是初始化GLFW,后面的要看文档 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(800, 600, "LearnOpenGL", nullptr, nullptr); if (window == nullptr) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window);//通知GLFW将我们窗口的上下文设置为当前线程的主上下文 //不知道回调函数为什么要加在这里?? //glfwSetKeyCallback(window, key_callback2);回调函数只有最下面那个有作用 glfwSetKeyCallback(window, key_callback); //初始化GLEW(管理OpenGL的函数指针) glewExperimental = GL_TRUE; if (glewInit() != GLEW_OK) { std::cout << "Failed to initialize GLEW" << std::endl; return -1; } //视口,OpenGl知道相对于窗口大小显示数据和坐标 int width, height; glfwGetFramebufferSize(window, &width, &height); glViewport(0, 0, width, height);//前两个控制左下角的位置,后面是窗口宽度高度(像素) //动态编译着色器源码: GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); GLuint fragmentShaderOrange = glCreateShader(GL_FRAGMENT_SHADER); GLuint shaderProgramOrange = glCreateProgram(); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); glShaderSource(fragmentShaderOrange, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShaderOrange); glAttachShader(shaderProgramOrange, vertexShader); glAttachShader(shaderProgramOrange, fragmentShaderOrange); glLinkProgram(shaderProgramOrange); GLfloat 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 // 顶部 }; GLuint VBO, VAO; //使用glGenBuffers函数和一个缓冲ID生成一个VBO对象?? glGenBuffers(1, &VBO); glGenVertexArrays(1, &VAO); //绑定VAO glBindVertexArray(VAO); //复制顶点数组到缓冲中,glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数 glBindBuffer(GL_ARRAY_BUFFER, VBO);//glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //设置顶点属性指针,和颜色属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3*sizeof(GLfloat))); glEnableVertexAttribArray(1); //因为现在是两个属性,需要向右移动步长,6个 glBindVertexArray(0); //安全? //防止退出的循环 while (!glfwWindowShouldClose(window)) { glfwPollEvents(); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); //glClearColor是一个状态设置函数,glClear是一个状态应用函数 //绘图 glUseProgram(shaderProgramOrange); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3);//绘制图形函数,0-3是数组索引 glBindVertexArray(0); glfwSwapBuffers(window); } //回调函数 //释放: glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glfwTerminate(); //释放/删除之前的分配的所有资源 return 0;}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode){ std::cout << key << std::endl; //用户按下ESC键,我们设置window窗口WindowShouldClose属性为true //关闭应用程序 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GL_TRUE);}

效果图:

其中的渐变色就是片段插值(Fragment Interpolation)的结果。

练习1:Adjust the vertex shader so that the triangle is upside down:

这个被脑筋急转弯了,修改y轴为负即可。

练习2:Specify a horizontal offset via a uniform and move the triangle to the right side of the screen in the vertex shader using this offset value:

const GLchar* vertexShaderSource = "#version 330 core\n""layout(location = 0) in vec3 position;\n""layout(location = 1) in vec3 color;\n"//这里用layout标识符把color属性设置为1"out vec3 ourColor;\n""uniform float xOffset;\n""void main()\n""{\n"" gl_Position = vec4(position.x+xOffset, -position.y, position.z, 1.0);\n""ourColor = color;\n""}\0";

float offset = 0.5f; glUniform1f(glGetUniformLocation(shaderProgramOrange, "xOffset"), offset);

3.Output the vertex position to the fragment shader using the out keyword and set the fragment's color equal to this vertex position (see how even the vertex position values are interpolated across the triangle). Once you managed to do this; try to answer the following question: why is the bottom-left side of our triangle black?

const GLchar* vertexShaderSource = "#version 330 core\n""layout(location = 0) in vec3 position;\n"//这里用layout标识符把color属性设置为1"out vec3 outp;\n""void main()\n""{\n"" gl_Position = vec4(position.x, position.y, position.z, 1.0);\n""outp = position;\n""}\0";

注意这里out的时候需要在mian里新建变量进行转接。

效果图:

左下角是黑色的原因也就是,把负的左边都变成了0.

如何封装目前所学的知识到一个抽象对象中。 写自己的着色器类。 预处理指令的作用:预处理指令会告知你的编译器只在它没被包含过的情况下才包含和编译这个头文件, 即使多个文件都包含了这个着色器头文件。 它是用来防止链接冲突的。

封装好的类直接用就可以了。

Shader.h:

#ifndef SHADER_H#define SHADER_H#include #include #include #include #include ; // 包含glew来获取所有的必须OpenGL头文件class Shader{public: // 程序ID GLuint Program; // 构造器读取并构建着色器 Shader(const GLchar* vertexPath, const GLchar* fragmentPath) { //从文件路径获取顶点,片段着色器 std::string vertexCode; std::string fragmentCode; std::ifstream vShaderFile; std::ifstream fShaderFile; // 保证ifstream对象可以抛出异常: vShaderFile.exceptions(std::ifstream::badbit); fShaderFile.exceptions(std::ifstream::badbit); try { // 打开文件 vShaderFile.open(vertexPath); fShaderFile.open(fragmentPath); std::stringstream vShaderStream, fShaderStream; // 读取文件的缓冲内容到流中 vShaderStream << vShaderFile.rdbuf(); fShaderStream << fShaderFile.rdbuf(); // 关闭文件 vShaderFile.close(); fShaderFile.close(); // 转换流至GLchar数组 vertexCode = vShaderStream.str(); fragmentCode = fShaderStream.str(); } catch (std::ifstream::failure e) { std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl; } const GLchar* vShaderCode = vertexCode.c_str(); const GLchar* fShaderCode = fragmentCode.c_str(); //编译和链接着色器。 GLuint vertex, fragment; GLint success; GLchar 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); // Print compile errors if any glGetShaderiv(fragment, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(fragment, 512, NULL, infoLog); std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; } this->Program = glCreateProgram(); glAttachShader(this->Program, vertex); glAttachShader(this->Program, fragment); glLinkProgram(this->Program); // 打印连接错误(如果有的话) glGetProgramiv(this->Program, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(this->Program, 512, NULL, infoLog); std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; } // 删除着色器,它们已经链接到我们的程序中了,已经不再需要了 glDeleteShader(vertex); glDeleteShader(fragment); } void Use() { glUseProgram(this->Program); }};#endif

shader.vs和shader.frag直接写在资源里。

用的时候可以这样写:

// GLEW#define GLEW_STATIC#include // GLFW#include #include #include "Shader.h"void key_callback(GLFWwindow* windows, int key, int scancode, int action, int mode);//顶点着色器源码:int main(){ std::cout << "Starting GLFW context, OpenGL 3.3" << std::endl; //这都是初始化GLFW,后面的要看文档 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(800, 600, "LearnOpenGL", nullptr, nullptr); if (window == nullptr) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window);//通知GLFW将我们窗口的上下文设置为当前线程的主上下文 //不知道回调函数为什么要加在这里?? //glfwSetKeyCallback(window, key_callback2);回调函数只有最下面那个有作用 glfwSetKeyCallback(window, key_callback); //初始化GLEW(管理OpenGL的函数指针) glewExperimental = GL_TRUE; if (glewInit() != GLEW_OK) { std::cout << "Failed to initialize GLEW" << std::endl; return -1; } //视口,OpenGl知道相对于窗口大小显示数据和坐标 int width, height; glfwGetFramebufferSize(window, &width, &height); glViewport(0, 0, width, height);//前两个控制左下角的位置,后面是窗口宽度高度(像素) Shader ourShader("shader.vs", "shader.frag"); GLfloat 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 // 顶部 }; GLuint VBO, VAO; //使用glGenBuffers函数和一个缓冲ID生成一个VBO对象?? glGenBuffers(1, &VBO); glGenVertexArrays(1, &VAO); //绑定VAO glBindVertexArray(VAO); //复制顶点数组到缓冲中,glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数 glBindBuffer(GL_ARRAY_BUFFER, VBO);//glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //设置顶点属性指针,和颜色属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(0); //因为现在是两个属性,需要向右移动步长,6个 glBindVertexArray(0); //安全? //防止退出的循环 while (!glfwWindowShouldClose(window)) { glfwPollEvents(); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); //glClearColor是一个状态设置函数,glClear是一个状态应用函数 //绘图 ourShader.Use(); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3);//绘制图形函数,0-3是数组索引 glBindVertexArray(0); glfwSwapBuffers(window); } //回调函数 //释放: glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glfwTerminate(); //释放/删除之前的分配的所有资源 return 0;}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode){ std::cout << key << std::endl; //用户按下ESC键,我们设置window窗口WindowShouldClose属性为true //关闭应用程序 if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GL_TRUE);}

shader.vs

#version 330 corelayout(location = 0) in vec3 position;out vec3 outp;void main(){ gl_Position = vec4(position.x, position.y, position.z, 1.0);outp = position;}

shader.frag

#version 330 corein vec3 outp;out vec4 color;void main(){color = vec4(outp,1.0);}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:螺旋队列
下一篇:php接口隔离原则实例分析(接口隔离原则举例)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~