本文章是根据教程http://bullteacher.com/5-hello-triangle.html  实现的,在我本地已经正常运行。源码http://download.csdn.net/detail/qq_28637193/9618496 里面的main.cpp是绘制一个窗口。Triangle.cpp是绘制三角形(索引绘制是绘制的正方形)

如果不使用索引缓冲对象画出来的是个三角形,不适用索引缓冲对象时删除

同时在最后制图时

 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//绑定glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
换为glDrawArrays(GL_TRIANGLES, 0, 3);

下面是全部的代码

/*
在opengl中任何事物都在3D空间中,但是屏幕和窗口是一个2D像素阵列,所以oepngl的大部分
作都是关于如何把3D坐标转换为适应你的屏幕的2D像素。3D坐标转为2D坐标的处理过程是由OpenGL的图形输送管道管理的
,图形输送管道可以被划分为两个主要的部分:第一个是把你的3D坐标转换为2D坐标,第二部分是把2D坐标转换为实际的有颜色
的像素。这样的教程里,我们会简单地讨论一下图形输送管道,以及如何使用它创建一些像素,着对我们来说是有好处的
*/
/*
图形输送管道接受一组3D坐标,然后把他们转换为你屏幕上的有色的2D像素,图像输送管道可以别划分为
几个阶段,每个阶段需要把前一个阶段的输出作为输入。所有这些阶段都是高度专门化的(它们有一个特定的函数)
它们能简单地并行执行。由于它们的并行执行的特征,当今大多数显卡都有成千上万的小处理核心,在一个GPU上为每一个(输送管道)
阶段运行各自的小程序,从而在图形输送管道中快速处理你的数据。这些小程序叫做着色器。
有些着色器允许开发者自己设置,用我们自己写的着色器替换默认存在的,这样我们就可以更加细致地控制输送管道的特定功能的部分了
因为他们运行在GPu上,他们也会节约宝贵的cpu的时间。着色器是用opengl着色器语言(opengl shading Language)GLSL写成的,
我们在下节会花更多的时间去研究使用它
*/
/*输是送管道的第一部分是顶点着色器(vertex shader),它把一单独的顶点作为输入。顶点着色器主要的目的是把3D
坐标转换为另一种3D(后面解释),同时顶点着色器允许我们对顶点属性进行一些基本的处理基本图形组装(primitive assembly)阶段把顶点着色器的表示为基本图形的所有顶点作为输入
(如果选择的是GL_POINTS,那么就是一个单独的顶点),把所有点组装为特定的基本图形的形状
本节例子是个三角形基本图形组装阶段的输出会传递给几何着色器(geometry shader)。几何着色器把基本图形形式一系列顶点的集合作为输入
它可以通过产生新顶点构造出新的(或者其他的)基本图形来生成其他形状。例子中它生成另一个三角形细分着色器(tessellation shaders)拥有把给定基本图形细分为更多小基本图形能力。
这样我们就能在物体更接近玩家的时候通过创建更多的三角形的方式创建出更加平滑的效果
细分着色器的输出会进入像素化(resterization也译为光栅化)阶段,这里它会把基本图形映射到屏幕上
相应的像素,生成供像素着色器(fragment也译为片段着色器)使用的fragment(片段),在像素着色器运行之前,
会执行裁切。裁切会丢弃超出你的视图以外的那些像素像素着色器主要的目的是计算一个像素的最终的颜色,一个opengl程序至少
包含顶点着色器和像素着色器(片段着色)*/
#define GLEW_STATIC
#include<GL/glew.h>
#include<iostream>#define GLFW_INCLUDE_GLU
#include<GLFW/glfw3.h>
using namespace std;void key_callback(GLFWwindow *window,int key,int sccncode,int action,int mode);
// Window dimensions
const GLuint WIDTH = 800, HEIGHT = 600;//顶点着色器
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";//version 330 core声明一个版本,对应的是opengl3.3 GLSL420对应opengl4.2  同样显示会调用core profile功能
/*
我们在顶点着色器中声明所有输入顶点输入属性,使用in关键字,现在我们只关心位置数据(position)
,所以我们只需一个顶点属性(attribute).GLSL有一个向量的数据类型,它包含1到4个float元素,包含的数量可以从它的
后缀看出来。由于每个顶点都有一个3D坐标,我们就创建一个vec3输入变量来表示当前位置(position).同样也指定输入变量的location
,这里是用layout(location=0)来完成(location在红宝书中有详细的解释)向量(vector)在图形编程中我们经常会使用向量这个数学概念,因为它简明地表达了任意空间中位置和方向,二者是有用的数学属性。在GLSL中一个向量有最多4个元素,
每个元素值都可以从各自代表一个空间坐标的vec.x、vec.y、vec.z和vec.w来获取到。注意vec.w元素不是用作表达空间中的位置的(我们处理的是3D不是4D)
而是用在所谓透视除法(perspective division)上。我们会在后面的教程中更详细地讨论向量。
*//*为了设置顶点着色器的输出,我们必须把位置数据赋值给预定义的gl_Position变量
,这个位置数据是一个vec4类型。在main函数的最后,无论我们给gl_Position设置成什么,
它都会成为着色器的输出,由于我们设置的是3个元素的向量,所以我们转化为4个
*/
int main()
{glfwInit();//初始化// Set all the required options for GLFW 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);// Create a GLFWwindow object that we can use for GLFW's functions  创建一个窗口GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);glfwMakeContextCurrent(window);//设置为当前窗口// Set the required callback functions 设置回调函数glfwSetKeyCallback(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();int width, height;
glfwGetFramebufferSize(window, &width, &height);  glViewport(0, 0, width, height);//设置窗口的位置和大小//为了让opengl能够使用GLSL编写的顶点着色器代码,我们必须在运行时动态编译它的源码所以我们首先做的是创建一个着色器对象
//再次引用它的ID,所以我们存储这个顶点着色器的GLuint然后用glCreateShader创建着色器
GLuint vertexShader;
vertexShader=glCreateShader(GL_VERTEX_SHADER);
//下一步是这个着色器源码附加到着色器对象然后编译它
glShaderSource(vertexShader,1,&vertexShaderSource,NULL);
glCompileShader(vertexShader);
/*glShaderSource函数把着色器对象作为第一个参数来编译它。第二参数指定了源码中有多少个字符串,这里只有一个。第三个参数是顶点着色器真正的源码,
我们可以把第四个参数设置为NULL。*/
//检查是否编译成功GLint success;GLchar infoLog[512];glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);/*首先我们定义一个整型来表示是否成功编译,还需要一个储存错误消息的容器(如果有的话)。然后我们用glGetShaderiv检查是否编译成功了。如果编译失败,我们应该用glGetShaderInfoLog获取错误消息,然后打印它*/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);
//编译完两个着色器后就是把两个着色器对象连接到一个着色器程序中
//(shader program)它是用来渲染的// Check for compile time errors  检查是否创建成功glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;}//编译完两个着色器后就是把两个着色器对象连接到一个着色器程序中
//(shader program)它是用来渲染的/*
着色器程序对象(shader program object)是用多个着色器最后的连接版本
如果要使用刚才编译的着色器我们必须把他们连接为一个着色器程序对象,然后当渲染物体
的时候激活这个着色器程序,激活了的着色器程序的着色器,在调用渲染函数的时候才可以调用
把着色器连接称为一个程序就等于把每个着色器的输出链接到下一个着色器的输入。如果你的输出和输入不匹配的时候
就是一个错误的连接
*///创建一个程序对象
GLuint shaderProgram;
shaderProgram=glCreateProgram();
//glCreateProgram函数创建一个程序,返回新创建的程序对象的ID引用。
//现在我们需要把前面编译的着色器附加到程序对象上,然后用glLinkProgram链接它们:
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;}
//设置完上面后我们可以调用glUseProgram函数,用新创建的程序对象作为参数,这样就能激活程序对象
//把着色器对象链接到程序对象后,不要忘记删除着色器对象
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);//我们希望绘制一个三角行,所以我们指定所有的3个顶点的3D位置,不适用索引画图时用这个
/*GLfloat vertices[]={-0.5f,-0.5f,0.0f,0.5f,-0.5f,0.0f,0.0f,0.5f,0.0f
};*/
//下面是使用索引画图
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!0, 1, 3, // 第一个三角形1, 2, 3  // 第二个三角形
};
/*
有了这样的顶点数据,我们会把它最为输入发送给图形输送管道的以第一个阶段
:顶点着色器。它会在GPU上创建存储空间用于存储我们的数据,还要配置opengl如何解释这些内存
并且制定如何发送给显卡。顶点着色器接着会处理我们告诉他要处理的内存顶点的数量
我们通过顶点缓冲对象(Vertex buffer object,VBO)管理这个内存,它会在GPU内存储大批的顶点
使用这些缓冲对象的好处是我们可以一次性的发送大批数据到显卡上,而不是每个顶点发送一次。
从cpu把数据发送到GPU相对缓慢,所以无论如何处我们都要尝试尽量一次性发送尽可能多的数据
当数据到了显卡内存中的时候,顶点着色器几乎立即就能获得顶点
*/
//VBO(顶点缓冲对象)这个缓冲有一个独一无二的ID,所以我们可以使用glGenBuffer函数生成一个缓冲ID
GLuint VBO;
glGenBuffers(1,&VBO);
/*
opengl有很多缓冲对象的类型,GL_ARRAY_BUFFER是其中一个顶点缓冲对象的缓冲类型。
opengl允许我们同时绑定多个缓冲,只要它们是不同的类型。我们可以使用glBinBuffer函数把创建的缓冲帮点到
GL_ARRAY_BUFFER上
*/
glBindBuffer(GL_ARRAY_BUFFER,VBO);
// 然后我们可以调用glBUffweData函数它会把之前定义的顶点数据复制到缓冲的内存中
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
/*
glBufferData是一个用来把用户定义数据复制到当前缓冲的函数。它的第一个参数是我们希望
把数据复制到上面的缓冲类型:顶点缓冲对象当前绑定到GL_ARRAY_BUFFER目标上,第二个参数
指定我们希望传递给缓冲的数据的大小(字节);用一个简单的sizeof计算出顶点顶点数据就行,第三个参数
我们希望发送的是真是的数据,第四个参数是指定我们希望显卡如何管理给定的数据。有三种形式:
GL_STATIC_DRAW:数据不会或几乎不会改变。
GL_DYNAMIC_DRAW:数据会被改变很多。
GL_STREAM_DRAW:数据每次绘制时都会改变。
*/
//生成一个VAO(Vertex Array Object 顶点数组对象)
/*
一个顶点数组对象储存下面的内容:调用glEnableVertexAttribArray和glDisableVertexAttribArray的。
使用glVertexAttribPointer的顶点属性配置。
使用glVertexAttribPointer进行的顶点缓冲对象与顶点属性链接。*/
//建立EBO(索引缓冲对象)
GLuint EBO;
glGenBuffers(1, &EBO);
/*
与VBO相似,我们绑定EBO然后用glBufferData把索引复制到缓冲里。同样,和VBO相似,我们会把这些函数调用放在绑定和解绑函数调用之间,
这次我们把缓冲的类型定义为GL_ELEMENT_ARRAY_BUFFER。
*/
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices,GL_STATIC_DRAW);
//建立VAO(顶点数组对象)
GLuint VAO;
glGenVertexArrays(1, &VAO);
/*
使用VAO要做的全部就是使用glBindVertexArray绑定VAO。自此我们就应该绑定/配置相应的VBO和属性指针,然后解绑VAO以备后用。当我们打算绘制一个物体的时候,
我们只要在绘制物体前简单地把VAO绑定到希望用到的配置就行了
*/glBindVertexArray(VAO);/*
现在,我们把输入的顶点数据发送给GPU,指示GPU如何在顶点和像素着色器中处理它,还没结束。
opengGl还不知道如何解释内存中的顶点数据,以及怎样把顶点数据链接到顶点着色器的属性上
我们会告诉opengl怎么做顶点着色器允许我们以任何我们想要的形式作为顶点属性的输入,
同样它也具有很强的灵活性,这意味着我们必须手动指定我们的输入数据的哪一个
部分对应顶点着色器的哪一个顶点属性。这意味着我们必须在渲染前指定OpenGL如何解释顶点数据。
*///有了这些知识我们就可以告诉OpenGL如何解释顶点数据(每一个顶点的属性),我们使用
//glVertexAttripoint这个函数设置顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid * )0);
glEnableVertexAttribArray(0);//开启
/*glVertexAttribPointer函数很多参数,所以我们小心的去了解他们:
第一个参数指定我们要配置那一个顶点属性。记住,我们在顶点着色器中使用layout(location=0)
定义了顶点属性-位置(position)的location。这样要把顶点属性的location设置为0,因为我们希望把数据传到这个顶点属性中
所以我们在这里填0。第二个参数是顶点属性的大小。顶点属性是vec3类型,它由3个数值组成
第三个参数指定数据的类型,这里是GL_FLOAT(GLSL中的vec*是浮点数(float))
第四个参数定义我们是否希望数据被标准化。如果我们设置为GL_TRUE,所有的数据都会被映射到0
(对于有符号型signed数据是-1)到1之间。我们设置为GL_FALSE
第五个参数叫做步长(stride),它告诉我们在连续的顶点属性之间间隔多少。由于下个位置数据
在3个GLfloat后面的位置,我们把步长设置为3*sizeof(float)。要注意的是由于我们这知道这个数组是紧密排列的
(两个顶点属性之间没有间隔)我们可以设置为0来让opengl决定具体的补偿是多少(只有当数值是紧密排列时才可用)。每当我们有更多的顶点属性,我们就必须小心地定义每个顶点属性之间的空间,
我们在后面会看到更多的例子
最后一个参数有古怪的GLvoid*的强制类型转换。它我们的位置数据在缓冲中起始位置的偏移量。由于位置数据是数组的开始,所以这里是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 unbind(解绑)glBindVertexArray(0); // Unbind VAO (it's always a good thing to unbind any buffer/array to prevent strange bugs) 解绑VAOwhile (!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 triangle 绘制物体glUseProgram(shaderProgram);//调用渲染程序glBindVertexArray(VAO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//绑定glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);/*第一个参数指定了我们绘制的模式,这个和glDrawArrays的一样。第二个参数是我们打算绘制顶点的次数。我们填6,说明我们总共想绘制6个顶点。第三个参数是索引的类型,这里是GL_UNSIGNED_INT。最后一个参数里我们可以指定EBO中的偏移量(或者传递一个索引数组,但是这只是当你不是在使用索引缓冲对象的时候),但是我们只打算在这里填写0。*///下面注释带掉的glDrawArrays(GL_TRIANGLES, 0, 3);是在不使用索引缓冲对象的时候使用// glDrawArrays(GL_TRIANGLES, 0, 3);/*glDrawArrays函数第一个参数是我们打算绘制的OpenGL基本图形的类型。由于我们在一开始时说过,我们希望绘制三角形,我们传递GL_TRIANGLES给它。第二个参数定义了我们打算绘制的那个顶点数组的起始位置的索引;我们这里填0。最后一个参数指定我们打算绘制多少个顶点,这里是3(我们只从我们的数据渲染一个三角形,它只有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);
}

用glew,glfw实现的opengl 学习笔记2画一个四方形相关推荐

  1. OpenGL学习笔记(2) 画一个正方形

    画一个正方形 其实,画正方形就是画两个三角形,用四个顶点以及使用索引来实现 完整代码在Square项目的Application.cpp里 先贴上窗口初始化代码 void BaseInit() {glf ...

  2. OpenGL学习笔记:画点、直线和多边形(第二讲)

    1.关于点  点的大小默认为1个像素,但也可以改变之. 改变的命令为glPointSize,其函数原型如下:  void glPointSize(GLfloat size);  size必须大于0.0 ...

  3. OpenGL学习笔记:画点、直线和多边形(第一讲)

    我的运行环境: CentOS7 g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16) OpenGL实现厂商的名字:VMware, Inc. 渲染器标识符:Galliu ...

  4. OpenGL学习笔记:第一个OpenGL程序完全注释

    运行环境: CentOS7 g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16) OpenGL实现厂商的名字:VMware, Inc. 渲染器标识符:Gallium ...

  5. OpenGL 学习笔记 II:初始化 API,第一个黑窗,游戏循环和帧率,OpenGL 默认垂直同步,glfw 帧率

    前情提要: 上一篇: OpenGL 学习笔记 I:OpenGL glew glad glfw glut 的关系,OpenGL 状态机,现代操作系统的窗口管理器,OpenGL 窗口和上下文 OpenGL ...

  6. 【OpenGL学习笔记⑧】——键盘控制正方体+光源【冯氏光照模型 光照原理 环境光照+漫反射光照+镜面光照】

    ✅ 重点参考了 LearnOpenGL CN 的内容,但大部分知识内容,小编已作改写,以方便读者理解. 文章目录 零. 成果预览图 一. 光照原理与投光物的配置 1.1 光照原理 1.2 投光物 二. ...

  7. 【OpenGL学习笔记⑥】——3D变换【旋转的正方体 实现地月系统 旋转+平移+缩放】

    ✈️ 文章目录 零. 成果预览图 一.3D立方体的顶点数组 二.纹理旋转 三.纹理缩放 四.画n个3D图形 五.轨道的数学公式 六.深度缓冲(Z 缓冲) 七.完整代码 八.参考附录: 神器的正方体 ☁ ...

  8. 【OpenGL学习笔记】地月系

    OpenGL学习笔记2-地月系 文章目录 OpenGL学习笔记2-地月系 前言 运行结果 纹理图片 一.TexturePool 1.**TexturePool.h** 2.**TexturePool. ...

  9. OpenGL学习笔记(八):进一步理解VAO、VBO和SHADER,并使用VAO、VBO和SHADER绘制一个三角形

    原博主博客地址:http://blog.csdn.net/qq21497936 本文章博客地址:http://blog.csdn.net/qq21497936/article/details/7888 ...

  10. OpenGL学习笔记:矩阵变换

    文章目录 缩放 glm矩阵表示 glm缩放矩阵实现 位移 齐次坐标 glm位移矩阵实现 旋转 沿x轴旋转 沿y轴旋转 沿z轴旋转 沿任意轴旋转 glm旋转矩阵实现 矩阵的组合 glm矩阵组合使用 接上 ...

最新文章

  1. matlab 注意事项
  2. 《研磨设计模式》读后感一
  3. c memcpy 与 strcpy 区别
  4. 13、MySQL索引的设计原则
  5. python去重且顺序不变_Python实现嵌套列表去重方法示例
  6. PHPExcel导出excel 复制代码
  7. 第十三章——表和索引分区(2)——使用拆分删除和加载大数据
  8. 使用Spring Boot CLI的Spring Boot Initilizr
  9. 利用数据绑定(DataBinding)简化多线程数据展示
  10. 深入浅出Python闭包
  11. PHP实现微信公众平台开发---提升篇(access_token存session)
  12. linux运行mentohust,Ubuntu下使用MentoHUST代替锐捷认证上网
  13. ubuntu root账户下添加和删除用户
  14. org.apache.hadoop.hbase.ipc.ServerNotRunningYetException: Server is not runn Hbase shell 无法执行命令
  15. ubuntu18.04下安装微信不能发图片和文件
  16. Android学习 书籍
  17. 数理统计之 置信区间(置信度)
  18. 微信公众号开发者接入
  19. caffe学习笔记31-理解全连接层
  20. 异常检测 | 用于无监督异常检测的自监督学习适应性记忆网络

热门文章

  1. 两个时间相减(vb.net)
  2. 【转】ASP.NET AJAX入门系列(8):使用ScriptManager控件
  3. 用Java写一个浪费cpu的程序_Java程序是如何浪费内存的
  4. iphone 内部函数使用 (函数可能无法响应的部分解决方案)
  5. 自增ID有什么坏处?什么样的场景下不使用自增ID?
  6. 数百亿的新疆安防市场,集成巨头告诉你如何才能从中分杯羹
  7. MySQL 主外键关系
  8. SQL的bit列名转换成access是/否数据类型,True无效的问题
  9. 邬建国教授受聘为浙江大学光彪教授
  10. Day38 python基础--并发编程基础-IO模型