OpenGL学习记录二(着色器)
着色器 -> 运行在gpu上的程序
顶点缓冲 ->只是一个内存缓冲区 字节数组缓冲区
顶点缓冲区和c++中字符数组缓冲区不一样,顶点缓冲区实际上实在gpu上(显卡vram)
上下文会有自己的流,缓冲区会有自己的流
Opengl中生成的所有东西都会被分配一个唯一的标识符(整数) (比如着色器id等)
一个完整的顶点(vertex):不止包含坐标,还包含颜色,纹理其他东西
顶点着色器(Vertex Shader)是几个可编程着色器中的一个。如果我们打算做渲染的话,现代OpenGL需要我们至少设置一个顶点和一个片段着色器。
顶点着色器允许我们指定任何以顶点属性为形式的输入。
stride--每个顶点之间的字节数量--偏移,顶点开始到顶点结束的跨度长度
pointer---数组,顶点里,位置、纹理、法线的偏移量(字节长度)
着色器:shader
在计算机图形中颜色被表示为有4个元素的数组:红色、绿色、蓝色和alpha(透明度)分量,通常缩写为RGBA。
当在OpenGL或GLSL中定义一个颜色的时候,我们把颜色每个分量的强度设置在0.0到1.0之间。
比如说我们设置红为1.0f,绿为1.0f,我们会得到两个颜色的混合色,即黄色。
这三种颜色分量的不同调配可以生成超过1600万种不同的颜色
顶点着色器:指定顶点的位置(需要能够给他们提供转换),每一个顶点调用一次顶点着色器
片段(像素)着色器:基本目的是决定像素的颜色是什么,会为每个像素运行一次去光栅化(画在屏幕上) --损耗大
glShaderSource() --length-> nullptr,以null结尾----每个字符都认定以null结尾
截图 learnopengl
创建着色器
/** * 创建着色器 */ static int CompileShader(unsigned int type, const std::string& source) { unsigned id = glCreateShader(type); const char* src = source.c_str(); /* 指定着色器 */ glShaderSource(id, 1, &src, nullptr); glCompileShader(id); //TODO Eroor Handling int result; glGetShaderiv(id, GL_COMPILE_STATUS, &result); if (result == GL_FALSE) { int length; glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length); char* message = (char*)alloca(length * sizeof(char)); glGetShaderInfoLog(id, length, &length, message); std::cout << "Failed to compile " << (type == GL_VERTEX_SHADER ? "vertex" : "fragment") << "shader!" << std::endl; std::cout << message << std::endl; glDeleteShader(id); return 0; } return id; } static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader) { unsigned int program = glCreateProgram(); unsigned int vs = CompileShader(GL_VERTEX_SHADER,vertexShader); unsigned int fs = CompileShader(GL_FRAGMENT_SHADER,fragmentShader); /* 链接着色器 */ glAttachShader(program, vs); glAttachShader(program, fs); /* 链接程序 */ glLinkProgram(program); /* 检索 */ glValidateProgram(program); /* 已经链接到程序,可以删除shader,但不是很必要,不利于调试 */ //glDeleteShader(vs); //glDeleteShader(fs); return program; }
/** * Opengl中生成的所有东西都会被分配一个唯一的标识符(整数) */ //3个顶点坐标 float positions[6] = { -0.5f, -0.5f, 0.0f, 0.5f, 0.5f, -0.5f }; /* 创建缓冲区 */ unsigned int buffer; glGenBuffers(1, &buffer); /* 选择缓冲区 */ glBindBuffer(GL_ARRAY_BUFFER,buffer);//数组,缓冲区 /* STREAM : The data store contents will be modified once and used at most a few times.(数据存储内容将被修改一次,最多使用几次) STATIC : The data store contents will be modified once and used many times.(数据存储内容将被修改一次并使用多次) DYNAMIC : The data store contents will be modified repeatedly and used many times.(数据存储内容将被重复修改和多次使用) GL_STATIC_DRAW :数据不会或几乎不会改变。 GL_DYNAMIC_DRAW:数据会被改变很多。 GL_STREAM_DRAW :数据每次绘制时都会改变。 */ /* 指定数据 :大小以字节为单位 , 数据可以传入null*/ glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(float), positions, GL_STATIC_DRAW); /* 启用顶点属性 */ glEnableVertexAttribArray(0); /* 链接顶点属性 */ /* (TERPROCGLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) */ /* (TERPROCGLuint 索引,GLint 大小,GLenum 类型,GLboolean 归一化,GLsizei stride, const void *指针) */ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0); std::string vertexShader = "#version 330 core\n" "\n" "layout(location = 0) in vec4 position;\n" "\n" "void main()\n" "{\n" " gl_Position = position;\n" "}\n"; std::string fragmentShader = "#version 330 core\n" "\n" "layout(location = 0) out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(1.0, 0.0, 0.0, 1.0);\n" "}\n"; unsigned int program = CreateShader(vertexShader,fragmentShader); /* 绑定着色器 */ glUseProgram(program);
/* 绘制三角形 : glDrawArrays() 没有索引缓冲区时用的方法 glDrawElements():有索引缓冲区时用的方法 */ glDrawArrays(GL_TRIANGLES,0,3); //0-first偏移 3-数量 //GL_POINTS、GL_TRIANGLES、GL_LINE_STRIP。
文件的方式实现着色器 (也可以写在两个文件)
#shader vertex #version 330 core layout(location = 0) in vec4 position; void main() { gl_Position = position; }; #shader fragment #version 330 core layout(location = 0) out vec4 color; void main() { color = vec4(1.0, 0.0, 0.0, 1.0); };
#include <fstream> #include <string> #include <sstream> struct ShaderPogramSource { std::string VertexSource; std::string FragmentSource; }; /* 读取着色器 */ static ShaderPogramSource ParseShader(const std::string& filepath) { std::ifstream stream(filepath); enum class ShaderType { NONE = -1, VERTEXT = 0, FRAGMENT = 1 }; std::string line; std::stringstream ss[2]; ShaderType type = ShaderType::NONE; while (getline(stream, line)) { if (line.find("#shader") != std::string::npos) { if (line.find("vertex") != std::string::npos) { type = ShaderType::VERTEXT; } else if(line.find("fragment") != std::string::npos) { type = ShaderType::FRAGMENT; } } else { ss[(int)type] << line << '\n'; } } return { ss[0].str(),ss[1].str() }; }
获取shader
ShaderPogramSource source = ParseShader("res/shaders/Basic.shader");
std::cout << "Vertex " << std::endl; std::cout << source.VertexSource << std::endl; std::cout << "Fragment " << std::endl; std::cout << source.FragmentSource << std::endl; unsigned int program = CreateShader(source.VertexSource, source.FragmentSource);
使用索引缓冲区
索引缓冲区 : 任何缓冲区,都必须由无符号的整数组成
float positions[8] = { -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, };
glBufferData(GL_ARRAY_BUFFER, sizeof(positions) * sizeof(float), positions, GL_STATIC_DRAW);
/* 索引缓冲区 */ unsigned int indices[] = { 0,1,2, 2,3,0 }; unsigned int ibo; glGenBuffers(1, &ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);//数组,缓冲区 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices) * sizeof(int), indices, GL_STATIC_DRAW);
/* 绘制矩形 : glDrawArrays() 没有索引缓冲区时用的方法 glDrawElements():有索引缓冲区时用的方法 */ glDrawElements(GL_TRIANGLES, sizeof(indices), GL_UNSIGNED_INT,nullptr);//已经在glBindBuffer设置,这里给nullptr //glDrawArrays(GL_TRIANGLES,0, sizeof(positions) / 2); //0-first偏移 6-数量
Uniforms
Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。
如果shader里的uniform没有使用,会被剥离,就会返回-1
glfwSwapInterval()---设置刷新帧率
#shader fragment #version 330 core layout(location = 0) out vec4 color; uniform vec4 u_Color; void main() { color = u_Color; };
改变颜色
int location = glGetUniformLocation(program, "u_Color"); glUniform4f(location,0.8f,0.3f,0.8f,1.0f); float r = 0.0f; float increment = 0.05f; glDrawElements(GL_TRIANGLES, sizeof(indices), GL_UNSIGNED_INT,nullptr); /* 动态编颜色 太快,设置glfwSwapInterval() */ if (r > 1.0f) increment = -0.05f; else if (r < 0.0f) increment = 0.05f; r += increment;
改变频率
/* Make the window's context current */ glfwMakeContextCurrent(window); /* 闪屏、刷新频率 */ glfwSwapInterval(1);
每个输入变量也叫顶点属性(Vertex Attribute)。我们能声明的顶点属性是有上限的,它一般由硬件来决定。OpenGL确保至少有16个包含4分量的顶点属性可用,但是有些硬件或许允许更多的顶点属性,你可以查询GL_MAX_VERTEX_ATTRIBS来获取具体的上限
GLSL中包含C等其它语言大部分的默认基础数据类型:int
、float
、double
、uint
和bool
。GLSL也有两种容器类型,它们会在这个教程中使用很多,分别是向量(Vector)和矩阵(Matrix)
本文来自博客园,作者:西瓜皮不甜,转载请注明原文链接:https://www.cnblogs.com/Jieth/p/17197443.html