当前位置:网站首页>OpenGL - Draw Triangle
OpenGL - Draw Triangle
2022-06-22 06:32:00 【农场主er】
Basic concept
Primitive
指定坐标和颜色的渲染类型,包括GL_POINTS, GL_TRIANGLES and GL_LINE_STRIP
fragments
构成单个像素所需的数据
OpenGL发生在三维空间,但是我们的屏幕是二维的,坐标系的转换依赖graphics pipeline,各个阶段如下所示:
每一步的输出都将作为下一步的输入。
Vertex Shader:对输入的顶点数据进行变换Primitive Assembly:将变换过的顶点数据装配成Primitive指定的形状Geometry Shader:可以产生新的顶点数据构造出其它PrimitiveRasterization Stage:将Primitive映射成屏幕上的像素,生成fragments,并且会丢弃掉屏幕外的fragmentsFragment Shader:产生像素最终绘制的颜色,包括光照、阴影等因素Blending:进行深度测试,并且会考虑透明度
Vertex Buffer Objects(VBO)
输入的顶点坐标要求用Normalized Device Coordinates (NDC),即限制范围为[-1.0, 1.0]:
viewport transform会利用glViewport提供的数据将NDC坐标转化为screen-space coordinates,最终作为fragment shader的输入。
通过VBO管理GPU内存上的顶点数据,优势在于可以一次性将数据送到显卡,毕竟从CPU传数据是比较耗时的。
float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};
unsigned int VBO; // buffer id
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO); // 绑定buffer到特定类型,如果buffer为0将重置当前buffer的边界为空状态?
// 绑定数据到buffer区域,usage常用的有三种
// GL_STREAM_DRAW: the data is set only once and used by the GPU at most a few times.
// GL_STATIC_DRAW: the data is set only once and used many times.
// GL_DYNAMIC_DRAW: the data is changed a lot and used many times.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
Vertex shader
顶点着色器语法如下:
#version 330 core
// 下面会用到location,被in标记的为输入量,称为vertex attributes
// 至少有16个可用的输入,通过int nrAttributes; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes); 可以获取可用数量
layout (location = 0) in vec3 aPos;
void main()
{
// 预定义的输出坐标
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
编译顶点着色器的方式如下:
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
unsigned int vertexShader; // reference id
vertexShader = glCreateShader(GL_VERTEX_SHADER);
// 指定编译源码,第二个参数代表str的数量
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
// 可以通过glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);判断编译结果
glCompileShader(vertexShader);
Fragment shader
片段着色器定义了输出像素的颜色:
#version 330 core
out vec4 FragColor;
void main()
{
// 如果输出颜色失败,最终渲染出来会是黑色或者白色
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
编译的方式与顶点着色器一致:
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
如果想让顶点着色器的输出作为片段着色器的一个输入,变量的类型和名称应该一致。
Shader program
需要将上述编译过的着色器link到Shader program对象上:
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// 使用shader program
glUseProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
Linking Vertex Attributes
我们需要指定解释顶点数据的方式,比如步长:
// 1、layout (location = 0) 定义的位置,即vertex attributes,对应顶点着色器的输入参数
// 2、vertex attribute,即 vec3
// 3、数据类型
// 4、是否需要将坐标 NDC 化
// 5、两个顶点数据间的步长
// 6、从 buffer 中开始读取的位置
// 读取的 buffer 就是之前 VBO 绑定的
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
// 使顶点着色器对应的输入可用,与上一步的参数1相对应
glEnableVertexAttribArray(0);
Vertex Array Object(VAO)
VAO存储了VBO和vertex attributes的对应关系,优势在于只需绑定VAO就可以绘制顶点数据,并且方便切换。
工作流如下:
// ..:: Initialization code (done once (unless your object frequently changes)) :: ..
unsigned int VAO;
glGenVertexArrays(1, &VAO);
// 1. bind Vertex Array Object
// 调用之后,随后的VBO、VEO、 第3步的内容都会绑定到 VAO
glBindVertexArray(VAO);
// 2. copy our vertices array in a buffer for OpenGL to use
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. then set our vertex attributes pointers
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
[...]
// ..:: Drawing code (in render loop) :: ..
// 4. draw the object
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
// 1、想要绘制的类型
// 2、开始的顶点数据下标
// 3、绘制的顶点数量
glDrawArrays(GL_TRIANGLES, 0, 3);
Element Buffer Objects(EBO)
如果我们想绘制一个矩形,也就是两个三角形,六个顶点,但是会有两个顶点是重复的。那么可不可以预先定义好不重复的所有顶点,然后通过一个索引数组去指定我们想要绘制的顶点呢?
如此就引入了EBO的概念:
float vertices[] = {
0.5f, 0.5f, 0.0f, // top right
0.5f, -0.5f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f // top left
};
unsigned int indices[] = {
// note that we start from 0!
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
// 可见EBO只是索引数组的buffer
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// ..:: Initialization code :: ..
// 1. bind Vertex Array Object
glBindVertexArray(VAO);
// 2. copy our vertices array in a vertex buffer for OpenGL to use
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. copy our index array in a element buffer for OpenGL to use
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 4. then set the vertex attributes pointers
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
[...]
// ..:: Drawing code (in render loop) :: ..
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
// 2、指定绘制的顶点数目
// 3、索引元素数据类型
// 4、EBO 的偏移
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0)
glBindVertexArray(0);
现在VAO存储的信息又增加了EBO:
Shader
除了通过上述的方式像着色器中传输数据,还可以用uniform关键词从CPU传输数据到GPU,这种方法是全局性的,在shader program的任何阶段都可以获取到,一旦设置会一直有效,知道重置或更新。如果声明但未赋值,会在编译阶段移除。
#version 330 core
out vec4 FragColor;
uniform vec4 ourColor; // we set this variable in the OpenGL code.
void main()
{
FragColor = ourColor;
}
可以通过下面的方法进行赋值:
float timeValue = glfwGetTime(); // 返回GLFW初始化以来的秒数
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUseProgram(shaderProgram); // 赋值前先 use
// glUniform 有以下几种后缀:
// f: the function expects a float as its value.
// i: the function expects an int as its value.
// ui: the function expects an unsigned int as its value.
// 3f: the function expects 3 floats as its value.
// fv: the function expects a float vector/array as its value.
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
一般设置每一帧都需要变化的值可以这样做。
封装 Shader Class
以后可以单独把shader写到一个文件里,使用的时候也很方便:
// 本地测试相对路径不太好使,可以先用绝对路径代替
Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.fs");
[...]
while(...)
{
ourShader.use();
ourShader.setFloat("someUniform", 1.0f);
DrawStuff();
}
边栏推荐
- Breakthrough in rich device platform: dayu200 based on rk3568 enters the openharmony 3.1 release trunk
- SQL injection vulnerability (XIII) Base64 injection
- [5g NR] NAS connection management - cm status
- Pyg tutorial (7): dissecting neighborhood aggregation
- 四大函数式接口(必需掌握)
- Surfer grid file clipping
- IO intensive and CPU intensive
- e.hash & oldCap == 0 详细解读
- 关于solidity的delegatecall的坑
- KV260的PMOD接口介绍
猜你喜欢

Record of problems caused by WPS document directory update

Callable

【OpenAirInterface5g】高层模块接口及itti实体线程创建

SQL injection vulnerability (XIII) Base64 injection
![[5g NR] ng setup of ngap protocol](/img/aa/bbe4b345374d2cf2ab3bfde5e94d7c.png)
[5g NR] ng setup of ngap protocol

5g-guti detailed explanation
![[php]tp6 cli mode to create tp6 and multi application configurations and common problems](/img/19/0a3319b04fe6449c90ade6f27fca4a.png)
[php]tp6 cli mode to create tp6 and multi application configurations and common problems

MySQL Niuke brush questions
![tab[i = (n - 1) & hash] 的详细解读](/img/be/3e84b3e8406833c2a235494f1a035f.png)
tab[i = (n - 1) & hash] 的详细解读

You are using PIP version 19.0.3, however version 22.1.2 is available
随机推荐
安装boost
什么是JUC
Vertical maximum and minimum and horizontal maximum and minimum greatest(), least(), max(), min()
Armadillo installation
[5g NR] RRC connection reconstruction analysis
Producer and consumer issues
CGIC file upload - rookie notes
Detailed interpretation of tab[i = (n - 1) & hash]
New GDI functions and functions introduced in MiniGUl version 1.1.0 (II)
Dynamically create object execution methods
Pyg tutorial (7): dissecting neighborhood aggregation
Upload file prompt 413 request entity too large error
keil C关于switch语句问题导致芯片不能正常运行
The difference between drop, truncate and delete
Little bear school bearpi HM micro officially integrated into openharmony trunk
Huiding technology gr551x series development board supports openharmony
linux 链接sqlserver,离线安装
SQL 注入漏洞(十三)base64注入
[5g NR] mobile phone ID number IMEI and imeisv
[openairinterface5g] high level module interface and ITTI entity thread creation