在刚学 OpenGL 的时候千万不要沉迷细节,后面你会慢慢懂,又或者说:放弃

前置:OpenGL基础1:最简单的OpenGL例子

一、最简单的着色器

前面讲过,如果我们打算做渲染的话必需要配置顶点和片段着色器,因此这里不得不先提前了解一下着色器

最简单的顶点着色器:

#version 330 core
layout (location = 0) in vec3 position;
void main()
{gl_Position = vec4(position.x, position.y, position.z, 1.0);
}

这段很像 C 语言的代码即是 GLSL(OpenGL Shading Language)着色器语言,即开发人员写的短小的自定义程序,它们是在图形卡的 GPU 上执行的,代替了固定的渲染管线的一部分

  • #version 330 core:版本号:对应着 3.3 版本
  • In 关键字:声明所有的输入顶点属性,目前我们只需要关心位置数据
  • layout (location = 0):设定输入变量的位置属性为 0

你会发现预定义的 gl_Position 变量是 vec4 类型的,但其实第 4 维并不表示位置,我们先默认设为 1.0,并且暂时不关心这个

最简单的片段着色器:

#version 330 core
out vec4 color;
void main()
{color = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

相对于顶点着色器,片段着色器貌似更加简洁一点

  • out 限定符:该着色器会将color所对应的数值输出,这也是片段所对应的颜色值

二、着色器编译与链接

为了能够让OpenGL使用对应的着色器,我们必须在运行时动态编译它

GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// Check for compile time errors
GLint success;
GLchar 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;
}
  • vertexShader = glCreateShader(GL_VERTEX_SHADER):创建着色器,其中参数 GL_VERTEX_SHADER 即顶点着色器
  • glShaderSource(vertexShader, 1, &vertexShaderSource, NULL):第一个参数为编译的着色器对象,第二个参数指定了需要传递的源码字符串的数量,第三个参数就是上面的着色器源码了,第四个参数先不管,目前只需要设置为 null 即可

下半部分的代码,主要是用于检测是否编译成功,如果编译失败获取异常

片段着色器和期类似,具体可以参考完整代码

着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本,如果想要使用刚才编译的着色器,我们必须把它们链接为一个着色器程序对象,然后在渲染对象的时候激活这个着色器程序,已激活的着色器程序将在我们发送渲染调用的时候被使用

当链接着色器至一个程序的时候,它会把每个着色器的输出链接到下个着色器的输入,当输出和输入不匹配的时候,会得到一个连接错误

GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// Check for linking errors
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);

着色器链接代码如上,下半部分的代码,仍然是用于检测是否链接成功,如果失败获取异常

  • glCreateProgram:创建一个程序,并返回新创建程序对象的ID引用
  • glLinkProgram:将之前编译的着色器附加到程序对象上后,用此方法链接
  • glDeleteShader:别忘了用完删除

三、VAO 与 VBO

前面已经介绍过了 glVertexAttribPointer() 这个函数,现在我们可以以现在的理解再来看一次

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0):

  • 第一个参数指定我们要配置的顶点属性,现在我们考虑了着色器,还记得我们在顶点着色器中使用 layout(location = 0) 定义了 position 顶点属性的位置值(Location)吗?它把顶点属性的位置值设置为了 0 。因为我们希望把数据传递到这一个顶点属性中,所以这里我们也传入 0
  • 第二三四个参数就不用多说了,前面有讲
  • 第五个参数:步长(Stride),也就是我们相邻两个顶点属性的间隔(这个属性第二次出现的地方到整个数组第一次出现的地方之间有多少字节),如下图,我们绘制三角形,自然是需要 3 个顶点,并且每个顶点都是个 vec3 ,由 3 个浮点数组成,又因为我们数据是紧挨着的,所以我们相邻两个顶点属性的间隔自然是 3 * sizeof(GLfloat),既然是紧挨着的,我们也可以通过传 0 来让 OpenGL 决定具体步长是多少,一旦我们有更多的顶点属性,我们就必须更小心地定义每个顶点属性之间的间隔
  • 最后一个参数:偏移位置,也就是第一个数据所在的位置。这里是0

那上图中的数据是存储在哪里的呢?正是 VBO

程序中可以有多个 VBO,获取则是通过在调用 glVetexAttribPointer 时绑定到 GL_ARRAY_BUFFER 的 VBO 决定的,由于在调用 glVetexAttribPointer 之前绑定的是先前定义的 VBO 对象,顶点属性 0 现在会链接到它的顶点数据

每当我们绘制一个物体的时候都必须重复这一过程(复制顶点数组到缓冲中供OpenGL使用 → 设置顶点属性指针 → 确定使用着色器程序 → 绘制物体),假设属性特别的多,又或者说是物体特别的多,这样就显得特别的麻烦,这个时候就需要VAO了

VAO(Vertex Array Object,顶点数组对象),看这个名字应该就可以明白了,它可以用于存储顶点属性调用,这样的话你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了,不但如此,不同顶点数据和属性配置之间切换也变得变得非常简单,绑定不同的VAO即可

一个顶点数组对象会储存以下这些内容:

  • glEnableVertexAttribArray 和 glDisableVertexAttribArray 的调用。
  • 通过 glVertexAttribPointer 设置的顶点属性配置。
  • 通过 glVertexAttribPointer 调用进行的顶点缓冲对象与顶点属性链接。

glGenVertexArrays(1, &VAO):绑定 VAO,传 0 解绑

四、实例代码

到这里,终于掌握了绘制三角形的所有知识点,前戏比较长,现在可以进入正题了!

#pragma comment(lib,"glew32.lib")
#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include<opengl/freeglut.h>// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);// Window dimensions
const GLuint WIDTH = 800, HEIGHT = 600;// Shaders
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"
"}\n\0";// The MAIN function, from here we start the application and run the game loop
int main()
{// Init GLFWglfwInit();// Set all the required options for GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);// Create a GLFWwindow object that we can use for GLFW's functionsGLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);glfwMakeContextCurrent(window);// Set the required callback functionsglfwSetKeyCallback(window, key_callback);// Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensionsglewExperimental = GL_TRUE;// Initialize GLEW to setup the OpenGL Function pointersglewInit();// Define the viewport dimensionsint width, height;glfwGetFramebufferSize(window, &width, &height);glViewport(0, 0, width , height);// Build and compile our shader program// Vertex shaderGLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);glCompileShader(vertexShader);// Check for compile time errorsGLint success;GLchar 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;}// Fragment shaderGLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);glCompileShader(fragmentShader);// Check for compile time errorsglGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;}// Link shadersGLuint shaderProgram = glCreateProgram();glAttachShader(shaderProgram, vertexShader);glAttachShader(shaderProgram, fragmentShader);glLinkProgram(shaderProgram);// Check for linking errorsglGetProgramiv(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);// Set up vertex data (and buffer(s)) and attribute pointersGLfloat vertices[] = {-0.5f, -0.5f, 0.0f, // Left  0.5f, -0.5f, 0.0f, // Right 0.0f,  0.5f, 0.0f  // Top   };GLuint VBO, VAO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);// Bind the Vertex Array Object first, then bind and set vertex buffer(s) and attribute pointer(s).glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(0);glBindBuffer(GL_ARRAY_BUFFER, 0); // Note that this is allowed, the call to glVertexAttribPointer registered VBO as the currently bound vertex buffer object so afterwards we can safely unbindglBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs)// Game loopwhile (!glfwWindowShouldClose(window)){// Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functionsglfwPollEvents();// Render// Clear the colorbufferglClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// Draw our first triangleglUseProgram(shaderProgram);glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 3);glBindVertexArray(0);// Swap the screen buffersglfwSwapBuffers(window);}// Properly de-allocate all resources once they've outlived their purposeglDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);// Terminate GLFW, clearing any resources allocated by GLFW.glfwTerminate();return 0;
}// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);
}

效果如下:

参考文献:https://learnopengl.com/#!Getting-started/OpenGL

OpenGL基础4:最基础的单元 —— 三角形相关推荐

  1. 计算机网络基础学测,《计算机网络技术基础》第二1章单元学习测习题-20210622072616.docx-原创力文档...

    <计算机网络技术基础>第二1章单元学习测习题 <计算机网络技术基础>第二1章单元学习测习题 PAGE PAGE3 <计算机网络技术基础>第二1章单元学习测习题 PA ...

  2. OpenGL开发之旅基础知识介绍

    最近由于手机项目中需要用到OpenGL ES的知识,所以这段时间正在研究OpenGL的相关知识.因为OpenGL ES是OpenGL的剪裁版本,所以我直接从OpenGL入手,然后再去看OpenGL E ...

  3. 现代OpenGL教程(一):绘制三角形(imgui+OpenGL3.3)

    前言:imgui 是一个开源的GUI框架,自带的例子里面直接集成了glfw+gl3w环境,本例使用的版本是imgui v1.61,下载地址:https://github.com/ocornut/img ...

  4. 【OpenGL ES】入门及绘制一个三角形

    本文首发于个人博客:Lam's Blog - [OpenGL ES]入门及绘制一个三角形,文章由MarkDown语法编写,可能不同平台渲染效果不一,如果有存在排版错误图片无法显示等问题,烦请移至个人博 ...

  5. 使用微型计算机的基础知识,计算机应用基础考试大纲基本要求1具有使用微型计算机的基础知识.DOC...

    计算机应用基础考试大纲基本要求1具有使用微型计算机的基础知识 计算机应用基础考试大纲 基本要求 ???????????????????????????????????????????????????? ...

  6. 公共基础知识计算机,公共基础知识计算机基础知识试题

    计算机基础知识是公共基础知识考试的组成成分之一,以下是由学习啦小编整理关于共基础知识计算机基础知识试题的内容,希望大家喜欢! 公共基础知识计算机基础知识试题 1.CPU的主要功能是进行( ). A.算 ...

  7. 计算机硬件价钱分配,电脑基础知识计算机硬件基础课件.ppt

    电脑基础知识计算机硬件基础课件 操作系统的概念 指直接控制和管理计算机的硬件和软件资源以便于有效的使用这些资源的程序. 操作系统分类 :单用户操作系统.批处理操作系统.实时操作系统.分时操作系统.网络 ...

  8. 基础计算机构,基础计算与设计

    第七章 基础计算与设计 录入系统建模完成后,选择[生成计算数据]菜单,弹出对话框,选择[生成基础CAD数据],当进入[基础CAD]后才能显示平面图形.进入[基础CAD]后,选择[读取墙柱底力]菜单,弹 ...

  9. 计算机应用基础难点,计算机应用基础(本科)重、难点

    计算机应用基础 <计算机应用基础>(本)课程重点.难点 (2005年9月) <计算机应用基础>是中央广播电视大学本科各专业学生必修的公共基础课,它是为培养应用型人才掌握使用计算 ...

  10. HTML基础介绍和基础骨架

    HTML基础介绍和基础骨架 HTML的概念 HTML 全称为 HyperText Markup Language,译为超文本标记语言. HTML 不是一种编程语言,是一种描述性的标记语言. 作用:HT ...

最新文章

  1. Microsoft公司的匈牙利法命名规则
  2. Lucene系列:(9)搜索结果排序
  3. java类与对象明星,明星档案的
  4. 单片机方波幅度调节c语言,为什么我用单片机做的频率可调的方波输出会有尖刺,而且会断...
  5. JavaScriptWindow使用对象
  6. Redis -- Hash(哈希) [3]
  7. java词汇速查手册_java 词汇表速查手册
  8. OpenCV Mat主要用法(2)_MatExpr
  9. 分别求两个整数的最大公约数和最小公倍数。_看不懂辗转相除法求最小公约数?以身相许那种哦!...
  10. 没有服务器配置信息是怎么回事,isp服务器未配置怎么回事
  11. 有关linux下find和xargs的使用
  12. C语言计算表达式咋写,C语言如何计算表达式(x++)+(++x)+(x++)
  13. 【问底】徐汉彬:PHP7和HHVM的性能之争
  14. 第三季-第5课-Linux编程规范
  15. vscode调试时几个常用参数的含义和设置总结
  16. 网页html5游戏修改器,正版H5游戏无限物品
  17. 最全面的Nginx工作原理讲解,Nginx学习笔记——来自动力节点
  18. apple苹果IOS内购申请教程协议、税务和银行业务配置
  19. 获得代理ippython_Python3.x:免费代理ip的批量获取并入库
  20. MySQL:创建数据库并插入数据

热门文章

  1. python编程入门教程下载-《Python编程从入门到精通》PDF高清完整版-PDF下载
  2. python基础教程百度云-python从入门到精通视频百度云盘下载
  3. java try catch陷阱_java异常捕捉陷阱(内存泄漏,finally块,catch块,继承得到的异常)...
  4. vuex的计算属性_vue中vuex的五个属性和基本用法
  5. dfs时间复杂度_Python实现图的经典DFS、BFS、Dijkstra、Floyd、Prim、Kruskal算法
  6. 修改html本地样式,html-如何通过Javascript更改CSS类样式?
  7. c++语言取整为什么要加0.5_蛋鸡饲料要加石粉,那你知道为什么加?要加什么石粉?我来告诉你...
  8. vue router-view 匹配路由后,第一次可以点击,再次点击同一个路由无响应,如何处理?
  9. 【java笔记】TCP通信程序
  10. php 请求java_怎么php发送get请求给Java,然后返回想要的具体参数