1.环境配置

1.1 OpenGL与Windows操作系统有什么关系?

OpenGL是一种应用程序编程接口(API),也是一种可以对图形硬件设备特性进行访问的软件库。因而事实上,OpenGL其实与显卡的关系更密切一些,而对于支持OpenGL的显卡才能使用OpenGL库。
由于OpenGL与显卡(硬件)有关,因而也与操作系统有关,所以Windows是自带OpenGL的,因此Windows会包含OpenGL的标准库,而在OpenGL官网是找不到下载的。
当我们安装了编译器(例如VS2010)后,编译器会把OpenGL的相关头文件和类库复制入编译器的相关文件夹中。例如头文件放在C:\ProgramFiles (x86)\Microsoft SDKs\Windows\v7.0A\Include\gl。库文件放在C:\Program Files(x86)\Microsoft SDKs\Windows\v7.0A\Lib。

1.2 Windows中OpenGL的版本过低怎么办?

这个不用担心,用Windows自带的OpenGL已经足够了。OpenGL版本的限制其实是在于显卡支持的OpenGL版本而不是你本身的OpenGL库的版本
那怎么查看显卡支持的OpenGL版本呢?一种是通过编程的方式,但是我们刚接触不会编程,那怎么办呢?可以采用第二种方式,下载一个OpenGLExtension Viewer,如下图所示:
我用的电脑是实验室的电脑,2009年买的,显卡也已经支持OpenGL4.4了。
那怎么让系统使用比较新的OpenGL库进行编程呢?我们还需要第三方软件库GLUT和GLEW。所谓第三方软件库是指,其代码不是OpenGL官方写的。但是这两个库都受到广泛的使用,也受到官方的认可。
GLUT库下载地址:http://www.transmissionzero.co.uk/software/freeglut-devel/
GLEW库下载地址:http://glew.sourceforge.net/
选择合适的版本进行下载,可参考http://blog.sina.com.cn/s/blog_7cfb366d01012icr.html
如果选用了红宝书,最好还是利用红宝书附带的源代码文件进行配置

1.3 环境配置

http://blog.csdn.net/candycat1992/article/details/39676669
此博客中的三个代码分别制作成三个文件。
然后在VS2010中新建一个项目:
然后把之前的三个文件triangles.cpp、triangles.vert、triangles.frag,以及D:\opengl\oglpg-8th-edition中的LoadShaders.h、vgl.h、LoadShaders.cpp(不在第一级目录中,可自己搜索一下)复制到D:\opengl\triangles\triangles中,如图所示:
注意到vgl.h中,无法加载源文件”GL/glew.h”,这是因为我们没有设置附加包含文件,而且我们注意到,头文件中指定了所需的静态库“glew_static_vs2010.lib”,从而静态库的名字也指定,后面还有"freeglut_static_vs2010.lib",这样的名称是红宝书源代码特有的,通常从官网下载下来的名称是“glew32.lib”和“freeglut.lib”,而且还是动态库,我们也不妨可以像这样一样先查看一下头文件。
选择triangles解决方案>>右键>>属性>>C/C++>>常规,修改附加包含目录,如图所示:
选择triangles解决方案>>右键>>属性>>链接器>>常规,修改附加包含目录,如图所示:
然后编译,如图所示:
由此推测,这些错误都是由默认库“libcmtd.lib”与其他库冲突造成的,因此我们必须要忽略这一个库。选择triangles解决方案>>右键>>属性>>链接器>>输入,忽略特定默认库,填入LIBCMT;LIBCMTD,这样在debug或release编译模式下都能把这个库忽略掉。
选择确定后,我们再进行编译,最后编译成功了:

1.4 glGenVertexArrays 崩溃的处理方式

然后键盘输入CTRL+F5,开始执行(但不调试),程序竟然崩溃:
于是我对程序运行调试(F5),程序中断在一条语句上:
从而是这条语句引起了崩溃,只要在glewInit()前加入glewExperimental= GL_TRUE;即可,如图所示:
重新编译,运行,成功:

2.第一个程序剖析

2.1 candycat怎么说

OpenGL能做的事情太多了!很多程序也看起来很复杂。很多人感觉OpenGL晦涩难懂,原因大多是被OpenGL里面各种语句搞得头大,一会gen一下,一会bind一下,一会又active一下。搞到最后都不知道自己在干嘛,更有可能某步顺序错误导致最后渲染出错,总感觉记下这些操作的顺序是非常烦人的一件事。
我们先来解释一下OpenGL为什么会涉及这么多操作顺序。这是因为,和我们现在使用的C++、C#这种面向对象的语言不同,OpenGL中的大多数函数使用了一种基于状态的方法,大多数OpenGL对象都需要在使用前把该对象绑定到context上。这里有两个新名词——OpenGL对象和Context。
  • Context
    Context是一个非常抽象的概念,我们姑且把它理解成一个包含了所有OpenGL状态的对象。如果我们把一个Context销毁了,那么OpenGL也不复存在。
  • OpenGL对象
    我们可以把OpenGL对象理解成一个状态的集合,它负责管理它下属的所有状态。当然,除了状态,OpenGL对象还会存储其他数据。注意。这些状态和上述context中的状态并不重合,只有在把一个OpenGL对象绑定到context上时,OpenGL对象的各种状态才会映射到context的状态。因此,这时如果我们改变了context的状态,那么也会影响这个对象,而相反地,依赖这些context状态的函数也会使用存储在这个对象上的数据。
因此,OpenGL对象的绑定既可能是为了修改该对象的状态(大多数对象需要绑定到context上才可以改变它的状态),也可能是为了让context渲染时使用它的状态。
画了一个图,仅供理解。图中灰色的方块代表各种状态,箭头表示当把一个OpenGL对象绑定到context上后,对应状态的映射。

前面提到过,OpenGL就是一个“状态机”。那些各种各样的API调用会改变这些状态,或者根据这些状态进行操作。但我们要注意的是,这只是说明了OpenGL是怎样被定义的,但硬件是否是按状态机实现的就是另一回事了。不过,这不是我们需要担心的地方。

2.2 OpenGL对象及其相关的重要函数

前面提到过,OpenGL就是一个“状态机”。那些各种各样的API调用会改变这些状态,或者根据这些状态进行操作。但我们要注意的是,这只是说明了OpenGL是怎样被定义的,但硬件是否是按状态机实现的就是另一回事了。不过,这不是我们需要担心的地方。
OpenGL对象包含了下面一些类型:Buffer Objects,Vertex Array Objects,Textures,Framebuffer Objects等等。我们下面会讲到Vertex Array Objects这个对象。
  • void glGen*(GLsizei n​, GLuint *objects​);负责生成一个对象的name。而name就是这个对象的引用。
  • void glDelete*(GLsizei n​, const GLuint *objects​);负责销毁一个对象。
  • void glBind*(GLenum target​, GLuint object​);将对象绑定到context上。

2.3 OpenGL中的图形名词

  • 渲染(Rendering):计算机从模型到创建一张图像的过程。OpenGL仅仅是其中一个渲染系统。它是一个基于光栅化的系统,其他的系统还有光线追踪(但有时也会用到OpenGL)等。
  • 模型(Models)或者对象(Objects):这里两者的含义是一样的。指从几何图元——点、线、三角形中创建的东西,由顶点指定。
  • Shaders:这是一类特殊的函数,是在图形硬件上执行的。我们可以理解成,Shader是一些为图形处理单元(GPU)编译的小程序。OpenGL包含了编译工具来把我们编写的Shader源代码编译成可以在GPU上运行的代码。在OpenGL中,我们可以使用四种shader阶段。最常见的就是vertex shaders——它们可以处理顶点数据;以及fragment shaders,它们处理光栅化后生成的fragments。vertex shaders和fragment shaders是每个OpenGL程序必不可少的部分。
  • 像素(pixel):像素是我们显示器上的最小可见元素。我们系统中的像素被存储在一个帧缓存(framebuffer)中。帧缓存是一块由图形硬件管理的内存空间,用于供给给我们的显示设备。

2.4 第一个程序的代码

#include <iostream>
using namespace std;
#include "vgl.h"
#include "LoadShaders.h"
//announce Global variable and other struct
enum VAO_IDs { Triangles, NumVAOs };
enum Buffer_IDs { ArrayBuffer, NumBuffers };
enum Attrib_IDs { vPosition = 0 };  GLuint  VAOs[NumVAOs];
GLuint  Buffers[NumBuffers];
const GLuint NumVertices = 6;  // init()uesed to setdata  used later,eg. vertex, texture
void init(void) {  glGenVertexArrays(NumVAOs, VAOs);// important!glBindVertexArray(VAOs[Triangles]);  //firstly ensure the triangles we wanna to render position   GLfloat  vertices[NumVertices][2] = {  { -0.90, -0.90 },  // Triangle 1  {  0.85, -0.90 },  { -0.90,  0.85 },  {  0.90, -0.85 },  // Triangle 2  {  0.90,  0.90 },  { -0.85,  0.90 }  };  glGenBuffers(NumBuffers, Buffers);  glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);  glBufferData(GL_ARRAY_BUFFER, sizeof(vertices),  vertices, GL_STATIC_DRAW);  // Secondly,refer vertex & fragment shaders  ShaderInfo  shaders[] = {  { GL_VERTEX_SHADER, "triangles.vert" },  { GL_FRAGMENT_SHADER, "triangles.frag" },  { GL_NONE, NULL }  };  // LoadShaders() is definited by users // simplify the process for GPU preparing shaders GLuint program = LoadShaders(shaders);  glUseProgram(program);  // lastly is shader plumbing (the pipeline of shader)  // connect the data and variables of shaderglVertexAttribPointer(vPosition, 2, GL_FLOAT,  GL_FALSE, 0, BUFFER_OFFSET(0));  glEnableVertexAttribArray(vPosition);
}
// we do render here, it's favored by OpenGL
// nearly all display() will executive following 3 steps
void display(void) {  // 1.glClear():clear window glClear(GL_COLOR_BUFFER_BIT);  // 2.render our objectglBindVertexArray(VAOs[Triangles]);  glDrawArrays(GL_TRIANGLES, 0, NumVertices);  // 3.Requests the image drawing to the windowglFlush();
}
// main():used to create window,Call init(),last,goto event loop.
// here we also can see some function which header is 'gl'.
// all these functions are from Third-party libraries,
// So, we can use OpenGL on any OS.
// here,we use GLUT&GLEW.
int main(int argc, char** argv) {  glutInit(&argc, argv);  glutInitDisplayMode(GLUT_RGBA);  glutInitWindowSize(512, 512);  glutInitContextVersion(4, 3);  glutInitContextProfile(GLUT_CORE_PROFILE);  glutCreateWindow(argv[0]);  glewExperimental= GL_TRUE;if (glewInit()) {  cerr << "Unable to initialize GLEW ... exiting" << endl; exit(EXIT_FAILURE);  }  init();  glutDisplayFunc(display);  glutMainLoop();
}  

剖析1—如何传递顶点数据

那么,现在的问题是,我们怎么把顶点和它相关的信息,例如纹理坐标、法线等,传递给GLSL呢?一般人都会想到多维数组。我们下面把它称为顶点流(Vertex Stream)。
我们负责创建这个顶点流,然后只需要告诉OpenGL怎样解读它就可以了。
为了渲染一个对象,我们必须使用一个shader program。而这个program会定义一系列顶点属性,例如上述Vertex Shader中的vPosition一行。这些属性决定了我们需要传递哪些顶点数据。每一个属性对应了一个数组,并且这些数据的维度都必须相等,即是一一对应的关系
比如我们想要渲染3个顶点,我们会定义下面的数据:
{ {1, 1, 1}, {0, 0, 0}, {0, 0, 1} }  ;
这些顶点的顺序是非常重要的,OpenGL将会根据这些顺序渲染网格。我们可以直接使用上述这种数据来直接渲染,也可以使用索引(indices)来指定顺序,这样可以重复使用同一个顶点。
例如,我们使用下面的索引列表:
{2, 1, 0, 2, 1, 2};
那么OpenGL将会渲染6个顶点:
{ {0, 0, 1}, {0, 0, 0}, {1, 1, 1}, {0, 0, 1}, {0, 0, 0}, {0, 0, 1} };
现在,我们还想传递一个新的顶点属性,即每个顶点的纹理坐标,那么新的纹理数组可能长这样:
{ {0, 0}, {0.5, 0}, {0, 1} };
那么,合并后的顶点属性列表就是:
[{0, 0, 1}, {0, 1}], [{0, 0, 0}, {0.5, 0}], [{1, 1, 1}, {0, 0}], [{0, 0, 1}, {0, 1}], [{0, 0, 0}, {0.5, 0}], [{0, 0, 1}, {0, 1} }];  
剖析2—VAO/VBO
VAO(Vertex Array Object):我们这里遇到了第一种OpenGL对象——VAO(Vertex Array Object)。前面说到OpenGL对象是状态的集合,那么VAO就是所有顶点数据的状态集合。它存储了顶点数据的格式以及顶点数据数据所需的缓存对象的引用。前面提过,OpenGL对象都有三个非常重要的函数,而VAO对应的就是glGenVertexArrays​、glDeleteVertexArrays和glBindVertexArray​。
AO负责管理顶点属性,而这些顶点属性从0到GL_MAX_VERTEX_ATTRIBS​ - 1被编号。这些属性在Vertex Shader里的表现就是类似下面的语句:
layout(location = 0) in vec4 vPosition; 
上述顶点属性vPosition被编号为0。
每个属性可以被enable或者disable,被disable的属性是不会传递给shader的,即便在shader里定义了这些属性,它们读出的值也会是一个常量,而非真正的数据。一个新建的VAO的所有属性访问都是disable的。而开启一个属性是通过下面的函数:
void glEnableVertexAttribArray​(GLuint index​); 
与其对应的是glDisableVertexAttribArray​ 函数。
而为了使用上述函数来改变VAO的状态,我们首先需要把VAO绑定到当前的context上
VBO(Vertex Buffer Object)
VBO是一种Buffer Object,即它也是一个OpenGl对象。VBO是顶点数组数据真正所在的地方
为了指定一个属性数据的格式和来源,我们需要告诉OpenGL,编号为0的属性使用哪个VBO,编号为1的属性使用哪个VBO等等。为了实现它,我们可以这么做。
首先,我们要知道,任何VBO都需要先绑定到GL_ARRAY_BUFFER​才可以对它进行操作。绑定后,我们可以调用下面的函数之一:
void glVertexAttribPointer​( GLuint index​, GLint size​, GLenum type​,  GLboolean normalized​, GLsizei stride​, const void *offset​);  void glVertexAttribIPointer​( GLuint index​, GLint size​, GLenum type​,  GLsizei stride​, const void *offset​ );  void glVertexAttribLPointer​( GLuint index​, GLint size​, GLenum type​,  GLsizei stride​, const void *offset​ );  

它们的作用大同小异,就是告诉OpenGl,编号为index的属性使用当前绑定在GL_ARRAY_BUFFER​的VBO。为了更好理解,我们举例:

glBindBuffer(GL_ARRAY_BUFFER, buf1);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);  

上面第一行代码将buf1绑定到了GL_ARRAY_BUFFER​上。第二行意味着,编号为0的属性将使用buf1的数据,因为当前绑定到GL_ARRAY_BUFFER​上的是buf1。第三行将缓存对象0绑定到了GL_ARRAY_BUFFER​上,这不会对顶点属性有任何影响,只有glVertexAttribPointer​函数可以影响它们!

这个过程就像一个中介人的作用,而中介人就是GL_ARRAY_BUFFER​。我们可以这么想,glBindBuffer​ 设置了一个全局变量,然后glVertexAttribPointer读取了这个全局变量并把它存储在VAO中,这个全局变量就是GL_ARRAY_BUFFER。当调用完glVertexAttribPointer后,顶点属性已经知道了数据来源就是buf1,它们之间就会直接联系,而不需要在通过GL_ARRAY_BUFFER。

3.参看资料

[1].http://blog.csdn.net/outtt/article/details/50771057
[2].http://blog.csdn.net/candycat1992/article/details/39676669
[3].OpenGL Programming Guide 8th Edition 

OpenGL编程指南2:环境搭配与第一个实例剖析相关推荐

  1. OpenGL编程指南(第八版)第一个渲染三角形案例代码在win8双显卡电脑VS2015中运行方法总结

    弄了好几天才把第一个程序运行出来,用该博客记录一下参考的博客资料及问题: 写得很好的博客参考资料为: http://blog.csdn.net/IceTeaSet/article/details/50 ...

  2. OpenGL编程指南3:GLUT:OpenGL使用工具库

    1.前言 我们知道的,OpenGL包含了很多的渲染函数,这些函数的设计目的就是独立与任何窗口系统或操作系统.因此,他并没有包含打开窗口或者从键盘或鼠标读取事件的函数.遗憾的是,如果连最基本的打开窗口的 ...

  3. 《OpenGL编程指南(原书第8版)》——计算着色器

    原文  http://www.csdn.net/article/2014-11-21/2822754 主题 OpenGL 数学 概述 由于图形处理器每秒能够进行数以亿计次的计算,它已成为一种性能十分惊 ...

  4. OpenGL编程指南4:双缓冲实现运行

    1.前言 绘制一个旋转的方块,主要为了说明glutSwapBuffers()函数的用法. 同时也为了学习如何使用GLUT控制输入设备,并打开或关闭空闲处理函数. 2.OpenGL编程及程序剖析 /** ...

  5. 《OpenGL编程指南》一3.2 OpenGL缓存数据

    本节书摘来自华章出版社<OpenGL编程指南>一书中的第3章,第3.2节,作者 Bill Licea-Kane ,更多章节内容可以访问云栖社区"华章计算机"公众号查看 ...

  6. OpenGL深入探索——《OpenGL编程指南(原书第8版)》——计算着色器

    转载自 <OpenGL编程指南(原书第8版)>--计算着色器 概述 由于图形处理器每秒能够进行数以亿计次的计算,它已成为一种性能十分惊人的器件.过去,这种处理器主要被设计用于承担实时图形渲 ...

  7. OpenGl编程指南例2.4大白话分析

    OpenGl编程指南例2.4分析 即上一篇文章搭建了opengl的环境后,继续学习Opengl,被第二章的各种gen,bind,buffer搞得晕头转向,在还没有消化完全的时候,又被一计重击打到--为 ...

  8. 《OpenGL编程指南》一第2章 着色器基础

    本节书摘来自华章出版社<OpenGL编程指南>一书中的第2章,作者 Bill Licea-Kane ,更多章节内容可以访问云栖社区"华章计算机"公众号查看 第2章 着色 ...

  9. OpenGL编程指南7:视图-

    1.前言 计算机图形学的要点就是创建三维物体的二维图像(图像必须是二维的,因为他是在平面的屏幕上显示的).但是,当我们决定怎样在屏幕上绘图时,必须使用三维坐标的方式考虑. 为了把一个物体的三维坐标变成 ...

最新文章

  1. ar编码matlab仿真_matlab-ofdm通信链路仿真
  2. 数据库高可用和分区解决方案-MySQL 篇
  3. Windows下用cmd命令安装及卸载服务[转]
  4. iframe打印excel bold_搭载君正X1000E芯片 中盈SP7080激光打印机现已开售!
  5. 2022年版955不加班公司名单!
  6. DotNetCore跨平台~Dockerfile的解释
  7. javafx动画_JavaFX动画工具
  8. 【转】其实Unix很简单
  9. redhat6.3下安装ORACLE11.2.3RAC
  10. mysql统计某一个数据库中有几张表
  11. LOL自制皮肤1-解析WAD文件的一些总结
  12. matlab读取scv文件,matlab如何读取csv文件
  13. BP反向传播算法原理及公式推导
  14. Landsat数据之介绍
  15. 【cocos2dx】记录解决csb创建font字体造成的内存泄漏问题
  16. 南京计算机类事业单位,南京市属事业单位公开招聘579人 3月25日起报名
  17. 阿里巴巴编码规范技能认证考试心得与试题
  18. UTON NFT的到来将为摄影师带来全新的未来!
  19. 【机器学习算法】感知机模型
  20. VMware Workstation虚拟机设置联网(Linux)

热门文章

  1. C语言实现猜拳小游戏
  2. iOS 蓝牙开发(二)iOS 连接外设的代码实现
  3. 《Metasploit渗透测试魔鬼训练营》学习笔记
  4. 基于web的电子图书管理系统
  5. 终于拿到蚂蚁金服Offer!!!分享一下全程面试题和面试经验!
  6. C语言程序设计 程序设计与C语言
  7. 数据库入门-----SQL语言概述
  8. 逸仙电商在美上市:高瓴资本、真格基金持股,被指疫情期非法裁员
  9. Layuimini一个适合懒人的开源代码-相关使用
  10. 触心创业路 - 记一家VR游戏创业团队的经历