初级着色器
3D坐标转2D坐标,由OpenGL图形渲染管线管理。
Graphics Pipeline:管线:实际上指的是一堆原始图形数据途经一个输送管道,期间经过各种变化处理最终出现在屏幕的过程
2D坐标与像素不同:
2D坐标精确表示一个点在2D空间中的位置,而2D像素是这个点的近似值,2D像素受到你的屏幕/窗口分辨率的限制。
OpenGl着色器:OpenGL Shading Language, GLSL
图元(Primitive):OpenGL需要你去指定这些数据所表示的渲染类型,例子:GL_POINTS、GL_TRIANGLES、GL_LINE_STRIP。
图形渲染管线的几个阶段?
一、
顶点着色器(Vertex Shader):顶点着色器主要的目的是把3D坐标转为另一种3D坐标(后面会解释),同时顶点着色器允许我们对顶点属性进行一些基本处理。
二、
图元装配(Primitive Assembly)阶段:把着色器的顶点作为输入。装配成指定的图元形状。
三、
几何着色器(Geometry Shader):把图元形式的顶点集作为输入,并通过构造新顶点产生形状。本例是三角形。
四、
光栅化阶段(Rasterization Stage):把图元映射为屏幕上的像素,生成(Fragment)片段(供片段着色器使用Fragment Shader)
五、
片段之前会裁切(Clipping),裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。
***OpenGL中的一个片段是OpenGL渲染一个像素所需的所有数据。***
六、
片段着色器的主要目的是计算一个像素的最终颜色。
七、
Alpha测试和混合(Blending)阶段,这个阶段检测片段的对应的深度(和模板(Stencil))值(后面会讲),用它们来判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。
标准化设备坐标(Normalized Device Coordinates, NDC)
:标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间
我们通过顶点缓冲对象(Vertex Buffer Objects, VBO)管理这个内存:好处:对象比点发送的速度要快。
绑定缓冲:OpenGL允许同时绑定多个缓冲,只要是不同类型的缓冲,使用glBindBuffer函数吧新创建的缓冲绑定到GL_ARRAY_BUFFER上。
GLuint VBO;
glGenBuffers(1, &VBO);
生成VBO对象。
OpenGl有很多 缓冲对象类型, 但对于《《顶点缓冲对象》》的类型是GL_ARRAY_BUFFER.
然后呢?
glBindBuffer(GL_ARRAY_BUFFER, VBO);
这是吧VBO绑定到GL_ARRAY_BUFFER目标上,
所以,之后的任何缓冲调用都会用来配置当前绑定的缓冲(VBO)。
glBufferDate(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
把之前定义的顶点数据复制到缓冲内存中。glBufferDate是专门把用户定义的数据复制到当前绑定缓冲的函数。
四个参数:1,目标缓冲的类型,绑定到谁身上。
2,传输数据的大小(字节) 3,发送的数据 4,显卡管理形式:
(1) GL_STATIC_DRAW :数据不会或几乎不会改变。
(2) GL_DYNAMIC_DRAW:数据会被改变很多。
(3) GL_STREAM_DRAW :数据每次绘制时都会改变。
片段着色器:控制颜色输出:RGBA:红色、绿色、蓝色和alpha(透明度)分量
OpenGl和GLSL定义的颜色,分量强度设置在0.0到1.0之间。
(Fragment Shader:
下步骤:
将数据与着色器属性链接。
索引缓冲对象(Element Buffer Object,EBO,也叫Index Buffer Object,IBO):
作用:专门储存索引
创建索引缓冲对象:
GLuint EBO;
glGenBuffers(1, &EBO);
然后是绑定:绑定的缓冲类型定义为:GL_ELEMENT_ARRAY_BUFFER
之后把索引复制到缓冲里。
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
最后一件要做的事是用glDrawElements来替换glDrawArrays函数,来指明我们从索引缓冲渲染
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);:
四个参数:绘制模式、顶点数、索引类型、偏移量。
下面是基础图形代码:
// GLEW#define GLEW_STATIC#include // GLFW#include #include void key_callback(GLFWwindow* windows, int key, int scancode, int action, int mode);void key_callback2(GLFWwindow* windows, int key, int scancode, int action, int mode);//顶点着色器源码:const GLchar* vertexShaderSource = "#version 330 core\n""layout(location = 0) in vec3 position;\n""void main()\n""{\n"" gl_Position = vec4(position.x, position.y, position.z, 1.0);\n""}\0";const GLchar* fragmentShaderSource = "#version 330 core\n""out vec4 color;\n""void main()\n""{\n""color = vec4(1.0f, 0.5f, 0.2f, 1.0f);\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. vertexShader = glCreateShader(GL_VERTEX_SHADER); //下一步把着色器的源码附加到着色器对象上,然后编译它 glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); //下面代码是检查是否编译成功 GLint success; GLchar infoLog[512]; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); //用glGetShaderiv检查是否便宜成功,如果失败,获取错误消息,然后打印 if (!success) { glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; } //片段着色器同样的道理 GLuint 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; } //下面是把多个着色器链接为一个着色器程序对象 GLuint shaderProgram; shaderProgram = glCreateProgram(); //glCreateProgram函数创建一个程序,并返回新创建程序对象的ID引用。 //后面是附加,把之前的着色器附加到程序对象上,然后glLinkProgram链接他们 glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); //检查编译错误 glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; } //链接完厚删除着色器对象 glDeleteShader(vertexShader); glDeleteShader(fragmentShader); GLfloat vertices[] = { 0.5f, 0.5f, 0.0f, // 右上角 0.5f, -0.5f, 0.0f, // 右下角 -0.5f, -0.5f, 0.0f, // 左下角 -0.5f, 0.5f, 0.0f, // 左上角 }; GLuint indices[] = { 0,1,3, 1,2,3, 0,2,3, }; GLuint VBO, VAO,EBO; //使用glGenBuffers函数和一个缓冲ID生成一个VBO对象?? glGenBuffers(1, &VBO); glGenVertexArrays(1, &VAO); glGenBuffers(1, &EBO); //绑定VAO glBindVertexArray(VAO); //复制顶点数组到缓冲中,glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数 glBindBuffer(GL_ARRAY_BUFFER, VBO);//glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //设置定点属性指针 //在这里加上索引缓冲,复制我们的索引数组到一个索引缓冲中,供OpenGL使用 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(0); //安全? glBindBuffer(GL_ARRAY_BUFFER, 0); //解绑VAO glBindVertexArray(0); //防止退出的循环 while (!glfwWindowShouldClose(window)) { glfwPollEvents(); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); //glClearColor是一个状态设置函数,glClear是一个状态应用函数 //绘图 glUseProgram(shaderProgram); glBindVertexArray(VAO); //glDrawArrays(GL_TRIANGLES, 0, 3);//绘制图形函数,0-3是数组索引 //用glDrawElements来替换glDrawArrays函数,来指明我们从索引缓冲渲染 //glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); //线框模式 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDrawElements(GL_TRIANGLES, 9, GL_UNSIGNED_INT, 0); 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);}void key_callback2(GLFWwindow* window, int key, int scancode, int action, int mode){ std::cout << key << std::endl; //用户按下ESC键,我们设置window窗口WindowShouldClose属性为true //关闭应用程序 if (key == 65) std::cout << "成功回调A键" << std::endl;}//问题:对ABO和VAO的并不怎么理解?他们的区别在这个例子中也看不出来。
简单的几种图形和线框模式很容易做出来了。
下面是几个练习:
1.Try to draw 2 triangles next to each other using glDrawArrays by adding more vertices to your data:
增加定点后,添加一个绘制三角形函数即可。glDrawArrays:
GLfloat vertices[] = { 0.0f,0.0f,0.0f, 0.5f,0.5f,0.0f, -0.5f,0.5f,0.0f, 0.5f,-0.5f,0.0f, -0.5f,-0.5f,0.0f };
glDrawArrays(GL_TRIANGLES, 0, 3);//绘制图形函数,0-3是数组索引glDrawArrays(GL_TRIANGLES, 3, 6);
或者也可以增加到6个点,之后:glDrawArrays(GL_TRIANGLES, 0, 6);
2.Now create the same 2 triangles using two different VAOs and VBOs for their data:
该练习告诉你的是VBO,VAO也可以是数组,可以多个。
建立两个顶点数组:
GLfloat firstTriangle[] = { -0.9f,-0.5f,0.0f, -0.0f,-0.5f,0.0f, -0.45f,0.5f,0.0f }; GLfloat secondTriangle[] = { 0.9f,-0.5f,0.0f, 0.0f,-0.5f,0.0f, 0.45f,0.5f,0.0f };
绑定2次:
GLuint VBOs[2], VAOs[2],EBO; //使用glGenBuffers函数和一个缓冲ID生成一个VBO对象?? glGenBuffers(2, VBOs); glGenVertexArrays(2, VAOs); glGenBuffers(1, &EBO); //绑定VAO glBindVertexArray(VAOs[0]); //复制顶点数组到缓冲中,glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的函数 glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);//glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上 glBufferData(GL_ARRAY_BUFFER, sizeof(firstTriangle), firstTriangle, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); //设置顶点属性指针 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(0); glBindVertexArray(0); //安全? glBindBuffer(GL_ARRAY_BUFFER, 0);
绘图两次即可:
glUseProgram(shaderProgram); glBindVertexArray(VAOs[0]); glDrawArrays(GL_TRIANGLES, 0, 3);//绘制图形函数,0-3是数组索引 glBindVertexArray(VAOs[1]); glDrawArrays(GL_TRIANGLES, 0, 3); glBindVertexArray(0);
3.Create two shader programs where the second program uses a different fragment shader that outputs the color yellow; draw both triangles again where one outputs the color yellow:
我自己是写的数组,写两个片段着色器两套源码。也可以写两个名字两个片段着色器。
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); GLuint fragmentShaderOrange = glCreateShader(GL_FRAGMENT_SHADER); GLuint fragmentShaderYellow = glCreateShader(GL_FRAGMENT_SHADER); GLuint shaderProgramOrange = glCreateProgram(); GLuint shaderProgramYellow = glCreateProgram(); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); glShaderSource(fragmentShaderOrange, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShaderOrange); glShaderSource(fragmentShaderYellow, 1, &fragmentShaderSource2, NULL); glCompileShader(fragmentShaderYellow); glAttachShader(shaderProgramOrange, vertexShader); glAttachShader(shaderProgramOrange, fragmentShaderOrange); glLinkProgram(shaderProgramOrange); glAttachShader(shaderProgramYellow, vertexShader); glAttachShader(shaderProgramYellow, fragmentShaderYellow); glLinkProgram(shaderProgramYellow);
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
暂时没有评论,来抢沙发吧~