888集团浏览器app,排名优化怎么做,wordpress电商平台搭建,国内开源cms本人刚学OpenGL不久且自学#xff0c;文中定有代码、术语等错误#xff0c;欢迎指正 我写的项目地址#xff1a;https://github.com/liujianjie/LearnOpenGLProject
LearnOpenGL中文官网#xff1a;https://learnopengl-cn.github.io/ 文章目录图形渲染管线基本介绍着色器… 本人刚学OpenGL不久且自学文中定有代码、术语等错误欢迎指正 我写的项目地址https://github.com/liujianjie/LearnOpenGLProject
LearnOpenGL中文官网https://learnopengl-cn.github.io/
文章目录图形渲染管线基本介绍着色器阶段顶点输入着色器代码流程链接顶点属性顶点数组对象VAO绘制三角形元素索引缓冲对象EBO小结草稿图重复重要的流程着色器流程绘制流程图形渲染管线
基本介绍 功能 将3D坐标变为2D坐标 将2D坐标转换为实际的有颜色的像素 图形渲染管线与着色器 图形渲染管线分为多个阶段多个阶段对应多个自己特定的函数小程序在各自特定的函数可并行执行调用显卡的成千上万的核心这些小程序被称为着色器
着色器阶段 顶点数据 以数组的形式传递3个3D坐标作为图形渲染管线的输入用来表示一个三角形这个数组叫做顶点数据(Vertex Data) 顶点着色器 它把一个单独的顶点作为输入主要的目的是把3D坐标转为另一种3D坐标 形状图元装配 将顶点着色器输出的所有顶点作为输入如果是GL_POINTS那么就是一个顶点并所有的点装配成指定图元的形状 几何着色器 把图元形式的一系列顶点的集合作为输入它可以通过产生新顶点构造出新的或是其它的图元来生成其他形状 光栅化 把图元映射为最终屏幕上相应的像素生成供片段着色器(Fragment Shader)使用的片段(Fragment)在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出你的视图以外的所有像素用来提升执行效率。 片段着色器 片段着色器的主要目的是计算一个像素的最终颜色 Alpha测试和混合 检测片段的对应的深度和模板(Stencil)值后面会讲用它们来判断这个像素是其它物体的前面还是后面决定是否应该丢弃 检查alpha值alpha值定义了一个物体的透明度并对物体进行混合(Blend)可以认为改变片段的颜色
在现代OpenGL中我们必须定义至少一个顶点着色器和一个片段着色器因为GPU中没有默认的顶点/片段着色器。
顶点输入 标准化设备坐标 在顶点着色器中处理过它们就应该是标准化设备坐标x、y和z值在-1.0到1.0的一小段空间 图示 顶点数据 float vertices[] {-0.5f, -0.5f, 0.0f,0.5f, -0.5f, 0.0f,0.0f, 0.5f, 0.0f
};一个在CPU内存的数组 顶点缓冲对象VBO 由于CPU内存的顶点数据需要传入GPU内存中就需要在GPU内存中存储同样大小的顶点数据而顶点缓冲对象管理这个在GPU上的内存有点模糊这个概念 unsigned int VBO;
// 1.在GPU上生成一个缓冲返回ID
glGenBuffers(1, VBO);
// 2.绑定缓冲
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 3.CPU内存的顶点数据复制到GPU内存中
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);着色器代码流程 创建一个着色器对象 用glCreateShader创建这个着色器 unsigned int vertexShader;
vertexShader glCreateShader(GL_VERTEX_SHADER);着色器源码附加到着色器对象 glShaderSource(vertexShader, 1, vertexShaderSource, NULL);编译着色器 glCompileShader(vertexShader);编译完顶点着色器后片段着色器同样这样编译 两个着色器对象链接到一个用来渲染的着色器程序 创建一个着色器程序对象 unsigned int shaderProgram;
shaderProgram glCreateProgram();编译的着色器附加到着色器程序对象 glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);glLinkProgram链接着色器程序对象 glLinkProgram(shaderProgram);使用着色器程序 glUseProgram(shaderProgram);着色器对象链接到着色器程序对象以后删除着色器对象 glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);另外 在编译着色器对象和链接时可以看是否成功 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;
}链接顶点属性 我们必须手动指定顶点输入数据的哪一个部分对应顶点着色器的哪一个顶点属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
// 若第一个参数为0对应顶点着色器的layout(location 0) in vec3 a_Position;
// 若第一个参数为1对应顶点着色器的layout(location 1) in vec4 a_Color;
glEnableVertexAttribArray(0);// 代表启用顶点着色器location0的输入设置好OpenGL如何解释顶点数据但是设置的顶点数据来源于上一次将顶点缓冲对象绑定的那个VBO。 glVertexAttribPointer参数 1要配置的顶点属性 layout(location 0) 2顶点属性的大小 3数据的类型 4是否希望数据被标准化 GL_TRUE所有数据都会被映射到0对于有符号型signed数据是-1到1之间 5步长 6偏移量 由此绘制的代码 // 省略创建缓冲
// 0. CPU内存的顶点数据复制到GPU内存中
glBindBuffer(GL_ARRAY_BUFFER, VBO1);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 1. 设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 2. 当我们渲染一个物体时要使用着色器程序
glUseProgram(shaderProgram);
// 3. 绘制物体
someOpenGLFunctionThatDrawsOurTriangle();若有第二个不同的物体不同的顶点数据需要渲染 又要写一遍这个代码
// 0. CPU内存的顶点数据复制到GPU内存中
glBindBuffer(GL_ARRAY_BUFFER, VBO2);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 1. 设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 2. 当我们渲染一个物体时要使用着色器程序
glUseProgram(shaderProgram);
// 3. 绘制物体
someOpenGLFunctionThatDrawsOurTriangle();缺点 由上可看出有多少个物体就得重复写绑定的顶点缓冲区、顶点属性指针属实麻烦则应该使用顶点数组对象VAO
顶点数组对象VAO 使用这个有什么用 原话 当配置顶点属性指针时你只需要将那些调用执行一次之后再绘制物体的时候只需要绑定相应的VAO就行了。 这使在不同顶点数据和属性配置之间切换变得非常简单只需要绑定不同的VAO就行了。刚刚设置的所有状态都将存储在VAO中 我认为 顶点数组对象VAO与顶点缓冲对象VBO一对多一个VAO的顶点属性指针可以来源于多个不同的顶点缓冲对象在初始化时VAO设置好顶点属性指针后绘制的时候绑定对应的VAO就行不需要写绑定顶点缓冲与设置顶点属性指针的代码了可以在绘制时无关初始化设置状态的代码。 代码 // ..:: 初始化代码只运行一次 (除非你的物体频繁改变) :: ..
unsigned int VAO;
glGenVertexArrays(1, VAO);
// 1. 绑定VAO
glBindVertexArray(VAO);
// 2. 把顶点数组复制到缓冲中供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);[...]// ..:: 绘制代码渲染循环中 :: ..
// 4. 绘制物体
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
someOpenGLFunctionThatDrawsOurTriangle();若有第二个物体要绘制跟上一样在初始化部分绑定相应顶点缓冲对象设置顶点属性后在渲染绘制代码只要切换VAO就行 // 4. 绘制物体
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
someOpenGLFunctionThatDrawsOurTriangle();glUseProgram(shaderProgram2);
glBindVertexArray(VAO2);
someOpenGLFunctionThatDrawsOurTriangle();图示 如图VAO与VBO一一对应但实际上VAO的顶点属性指针可以来源于多个不同的顶点缓冲VBO一般是一一对应
绘制三角形 代码 glsl version 330 core
layout (location 0) in vec3 aPos;
void main()
{gl_Position vec4(aPos.x, aPos.y, aPos.z, 1.0);\n
}#version 330 core
out vec4 FragColor;
void main()
{FragColor vec4(1.0f, 0.5f, 0.2f, 1.0f);
}cpp // 0.顶点数据
float vertices[] {-0.5f, -0.5f, 0.0f, // left 0.5f, -0.5f, 0.0f, // right 0.0f, 0.5f, 0.0f // top
};
unsigned int VBO, VAO;
glGenVertexArrays(1, VAO);
glGenBuffers(1, VBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
// 1. 绑定顶点数组对象
glBindVertexArray(VAO);
// 2. 把我们的CPU的顶点数据复制到GPU顶点缓冲中供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 设定顶点属性指针来解释顶点缓冲中的顶点属性布局
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
while (!glfwWindowShouldClose(window))
{.....// 4.使用着色器程序对象glUseProgram(shaderProgram);// 5.绑定顶点数组对象并绘制glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3);// 这里.....
}效果 元素索引缓冲对象EBO 简介 绘制矩形有重复的顶点正确使用索引顺序绘制图形可以重复利用顶点从而减少顶点数据。 使用 和VBO同样的生成使用方法生成EBO缓冲区返回ID、绑定ID、设置索引数据、绑定在VAO上 绘制不同 用glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);代替glDrawArrays(GL_TRIANGLES, 0, 3); 图示 由图可见VAO索引缓冲区的指针只有一个且在最后 代码 glsl不变 version 330 core
layout (location 0) in vec3 aPos;
void main()
{gl_Position vec4(aPos.x, aPos.y, aPos.z, 1.0);\n
}#version 330 core
out vec4 FragColor;
void main()
{FragColor vec4(1.0f, 0.5f, 0.2f, 1.0f);
}cpp // 0.1顶点数据
float vertices[] {0.5f, 0.5f, 0.0f, // top right0.5f, -0.5f, 0.0f, // bottom right-0.5f, -0.5f, 0.0f, // bottom left-0.5f, 0.5f, 0.0f // top left
};
// 0.2索引数据
unsigned int indices[] { // note that we start from 0!0, 1, 3, // first Triangle1, 2, 3 // second Triangle
};unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, VAO);
glGenBuffers(1, VBO);
glGenBuffers(1, EBO);
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
// 1. 绑定顶点数组对象
glBindVertexArray(VAO);
// 2. 把我们的CPU的顶点数据复制到GPU顶点缓冲中供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 复制我们的CPU的索引数组到GPU索引缓冲中供OpenGL使用
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 4. 设定顶点属性指针来解释顶点缓冲中的顶点属性布局
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
while (!glfwWindowShouldClose(window))
{.....// 5.使用着色器程序对象glUseProgram(shaderProgram);// 6.绑定顶点数组对象并绘制glBindVertexArray(VAO); //glDrawArrays(GL_TRIANGLES, 0, 3);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);// 这里不同// 由于只有一个顶点数组对象不需要解绑.....
}效果
小结
草稿图 重复重要的流程
着色器流程 顶点着色器 创建顶点着色器对象 附加源码给顶点着色器对象 编译顶点着色器对象 可以打印是否编译成功 片段着色器同上 着色器程序 创建着色器程序对象 附加着色器对象给着色器程序对象 链接着色器程序对象 可以检查是否成功 删除着色器对象
const char* vShaderCode vertexCode.c_str();
const char* fShaderCode fragmentCode.c_str();
unsigned int vertex, fragment;
// 1.1创建顶点着色器对象
vertex glCreateShader(GL_VERTEX_SHADER);
// 1.2附加顶点着色器源码给顶点着色器对象
glShaderSource(vertex, 1, vShaderCode, NULL);
// 1.3编译顶点着色器对象
glCompileShader(vertex);
// 1.4检测是否编译成功
checkCompileErrors(vertex, VERTEX);
// 2.1创建片段着色器对象
fragment glCreateShader(GL_FRAGMENT_SHADER);
// 2.2附加片段着色器源码给片段着色器对象
glShaderSource(fragment, 1, fShaderCode, NULL);
// 2.3编译片段着色器对象
glCompileShader(fragment);
// 2.4检测是否编译成功
checkCompileErrors(fragment, FRAGMENT);// 3.1创建着色器程序对象
ID glCreateProgram();
// 3.2附加着色器对象给着色器程序对象
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
// 3.3链接着色器程序对象
glLinkProgram(ID);
checkCompileErrors(ID, PROGRAM);// 可以检查是否成功
// 4.删除着色器对象
glDeleteShader(vertex);
glDeleteShader(fragment);void checkCompileErrors(unsigned int shader, std::string type)
{int success;char infoLog[1024];if (type ! PROGRAM){glGetShaderiv(shader, GL_COMPILE_STATUS, success);if (!success){glGetShaderInfoLog(shader, 1024, NULL, infoLog);std::cout ERROR::SHADER_COMPILATION_ERROR of type: type \n infoLog \n -- --------------------------------------------------- -- std::endl;}}else{glGetProgramiv(shader, GL_LINK_STATUS, success);if (!success){glGetProgramInfoLog(shader, 1024, NULL, infoLog);std::cout ERROR::PROGRAM_LINKING_ERROR of type: type \n infoLog \n -- --------------------------------------------------- -- std::endl;}}
}绘制流程
顶点数组对象 创建顶点数组对象绑定顶点数组对象 顶点缓冲对象 创建顶点缓冲对象绑定顶点缓冲对象将顶点数据从CPU拷贝到GPU的顶点缓冲对象中设置顶点数组里的顶点属性指针解释此顶点缓冲区的布局 索引缓冲对象 创建索引缓冲对象绑定索引缓冲对象当前绑定顶点数组对象的索引缓冲对象指针会指向当前索引缓冲对象自己的语言将索引数据从CPU拷贝到GPU的索引缓冲对象中 绘制代码 使用着色器程序对象绑定顶点数组对象绘制元素解绑顶点数组对象
// ..:: 初始化代码 :: ..
// 1. 绑定顶点数组对象
glBindVertexArray(VAO);
// 2. 把我们的CPU的顶点数据复制到GPU顶点缓冲中供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 复制我们的CPU的索引数组到GPU索引缓冲中供OpenGL使用
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 4. 设定顶点属性指针来解释顶点缓冲中的顶点属性布局
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);[...]// ..:: 绘制代码渲染循环中 :: ..
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);