Hello Triangle

Basic concept

Primitive
指定坐标和颜色的渲染类型,包括GL_POINTS, GL_TRIANGLES and GL_LINE_STRIP

fragments
构成单个像素所需的数据

OpenGL发生在三维空间,但是我们的屏幕是二维的,坐标系的转换依赖graphics pipeline,各个阶段如下所示:

每一步的输出都将作为下一步的输入。

  1. Vertex Shader:对输入的顶点数据进行变换
  2. Primitive Assembly:将变换过的顶点数据装配成Primitive指定的形状
  3. Geometry Shader:可以产生新的顶点数据构造出其它Primitive
  4. Rasterization Stage:将Primitive映射成屏幕上的像素,生成fragments,并且会丢弃掉屏幕外的fragments
  5. Fragment Shader:产生像素最终绘制的颜色,包括光照、阴影等因素
  6. 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); 可以获取可用数量
// 顶点属性最大允许的数据大小等于一个vec4。因为一个mat4本质上是4个vec4,我们需要为这个矩阵预留4个顶点属性。
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

需要将上述编译过的着色器linkShader 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存储了VBOvertex 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 right0.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 triangle1, 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();
}

OpenGL - Draw Triangle相关推荐

  1. OpenGL Draw TransformFeedback 绘制变换反馈的实例

    OpenGL Draw TransformFeedback 绘制变换反馈 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include "vapp.h ...

  2. OpenGL Tessellated Triangle镶嵌三角形的实例

    OpenGL Tessellated Triangle镶嵌三角形 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <sb7.h> cl ...

  3. OpenGL绘制Triangle三角形

    OpenGL绘制Triangle三角形 前期知识准备 顶点输入 顶点着色器 编译着色器 片段着色器 着色器程序 链接顶点属性 顶点数组对象 我们一直期待的三角形 索引缓冲对象 前期知识准备 在Open ...

  4. WhyGL:一套学习OpenGL的框架,及翻写Nehe的OpenGL教程

    最近在重学OpenGL,之所以说重学是因为上次接触OpenGL还是在学校里,工作之后就一直在搞D3D,一转眼已经毕业6年了.OpenGL这门手艺早就完全荒废了,现在只能是重学.学习程序最有效的办法是动 ...

  5. All about OpenGL ES 2.x – (part 2/3)(转载)

    来源: http://db-in.com/blog/2011/02/all-about-opengl-es-2-x-part-23/ Very welcome back, my friends! No ...

  6. OpenGL编程指南6:顶点数组

    1.前言 前面的例子中,我们可以看到,OpenGL需要进行大量的函数调用才能完成对几何图元的渲染.绘制一个20条边的多边形至少需要22个函数调用.首先调用一个glBegin(),然后为每个顶点调用一次 ...

  7. Android官方开发文档Training系列课程中文版:OpenGL绘图之图形绘制

    原文地址:http://android.xsoftlab.net/training/graphics/opengl/draw.html 如果你还不清楚如何定义图形及坐标系统,请移步:Android官方 ...

  8. OpenGL ES渲染管线与着色器

    转自:http://blog.csdn.net/kesalin/article/details/8223649 [OpenGL ES 02]OpenGL ES渲染管线与着色器 罗朝辉 (http:// ...

  9. [OpenGL ES 02]OpenGL ES渲染管线与着色器

    http://blog.csdn.net/kesalin/article/details/8223649 罗朝辉 (http://blog.csdn.net/kesalin) 本文遵循"署名 ...

最新文章

  1. cnc加工中心保养表_CNC数控加工中心,硬轨的好还是线轨的好?
  2. 2018年『web』开发者不得不知的技术趋势
  3. [Android学习笔记]理解焦点处理原理的相关记录
  4. Centos系统更改yum源为163
  5. 前端dashboard框架_微前端在网易七鱼的实践
  6. 使用shell脚本监控共享池内存碎片
  7. python类和对象的定义_python类与对象基本语法
  8. 获取linux命令硬盘信息,Linux下如何获取磁盘信息
  9. 几何画板是哪方面的计算机应用,几何画板在几何中的奇妙应用
  10. 在线考试系统架构设计
  11. DB2错误码sqlcode对应表
  12. keil4找不到c语言头文件路径,keil4中头文件路径设置的方法汇总
  13. 图像表格实线和虚线检测
  14. 办理物联网卡不实名会有什么后果呢
  15. 【企业微信怎么使用】如何快速做好企业微信客户数量增长?
  16. module ‘sklearn.utils._openmp_helpers‘ has no attribute ‘__pyx_capi__‘
  17. xeon e5-2400 系列处理器能做四路服务器吗?,英特尔Xeon E5系列双路处理器大阅兵
  18. DEDE织梦网站首页仿制实战操作
  19. apollo自动驾驶进阶学习之:ST与ST迭代过程
  20. 怎么吃才能促进孩子长个子?

热门文章

  1. java 实现 内存池_从连接池到内存池
  2. 输入框限制输入为两位小数
  3. 专业英文文档阅读神器
  4. 毕业差不多一年,跳槽华为od岗,已入职
  5. 菜单点击/关注/取消关注
  6. vue存储数据的几种方法(Vuex与本地存储)
  7. 拓扑排序 Codeforces Round #748 (Div. 3)E. Gardener and Tree
  8. Rigetti完成英国量子计算机的搭建;拓扑量子计算遭遇严重的挫折 | 全球量子科技与工业快讯第三十九期
  9. 我的书出版了,本周限时优惠,免费送书30本!
  10. QT+OpenGL光照