初级着色器

来源:互联网 发布:京东店铺销量数据 编辑:程序博客网 时间:2024/05/21 05:17
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 <GL/glew.h>// GLFW#include <GLFW/glfw3.h>#include <iostream>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);//绑定VAOglBindVertexArray(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);//解绑VAOglBindVertexArray(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);//绑定VAOglBindVertexArray(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);



原创粉丝点击