[翻译] 现代OpenGL教程 01 – 入门指南

modern-opengl

以为手机游戏《烈火遮天》抄袭《热血传奇》,盛大公司以侵权和不正当竞争将奇天乐地企业、苹果公司、卓易讯畅公司和Nokia公司诉至法院,需求为止侵权及不正当竞争行为,赔礼道歉并赔偿经济损失1000万元。日前,巴黎市海淀区人民法院受理了本案。

译序

早前学OpenGL的时候照旧1.x版本,用的都是glVertexglNormal等原则性管线API。后来工作需求接触DirectX9,shader也只是可挑选而已,跟固定管线同步混用着。现在干活内容是手机游戏,又转到OpenGL
ES,发现OpenGL的社会风气曾经完全两样了,OpenGL ES
2.0版本初阶就不再帮忙固定管线,只支持可编程管线。

pipe2.0

国内广大材料教程良莠不齐,旧式接口满天飞。在知乎探望这一多级教程,觉着挺好,就想着一边学顺便翻译下。毕竟手游市场的空子和竞争压力都在可比猛涨,多通晓OpenGL
ES肯定没有害处。浮躁功利的条件下更亟待怀着一颗宁静致远的心去增强自己基础,长路遥远,与君共勉。

欢迎大家,那是现代OpenGL教程种类的第一篇。所有代码都是开源的,你可以在GitHub上下载:https://github.com/tomdalling/opengl-series

透过这篇教程,你将会学到怎样在Windows下用Visual Studio
2013或Mac下用Xcode搭建OpenGL 3.2工程。该使用包括一个极端着色器(vertex
shader),一个有些着色器(fragment
shader)和使用VAO和VBO来绘制的三角形。该工程运用GLEW来访问OpenGL
API,用GLFW来拍卖窗口创设和输入,还有使用GLM展开矩阵/矢量相关的数学运算。

这听上去有点粗俗,但搭建那样的工程的确挺麻烦的,尤其对于初学者。只要解决完那难题,大家就可以伊始玩些有趣的东西了。

[TOC]

庄重集团诉称,盛大公司是推进中国互为游戏产业升高的领军企业,是炎黄网络游戏产业的领头羊,其商标“盛大”和“SHANDA”被认同为驰名商标。《热血传奇》,业内简称“传奇”,是一款由南韩公司Wemade
Entertainment制作、Actoz
soft负责海外发行的超级网络游戏,盛大公司得到该游戏在中华新大陆地域的独家代理权,并于2001年七月始发正式运转。作为一款国内运营时间最久的网游,《热血传奇》成立了诸多的家产神话,得到最佳科幻、最佳画面等多少个奖项,并付出了种类产品,是社会风气上用户规模最大、收益额位居前列的互联网游戏。在许三人心中中,《热血传奇》不再单纯是一款游戏,而是一个伴随自己14年的知心朋友,一种习惯成自然的生活方法。《热血传奇》注册商标已经拥有极高的分明性、有名度和美誉度,其角色设置、场景画面、设计风格等情节已被群众知道和认同,具有极高的社会知名度。

收获代码

具有例子代码的zip打包可以从此间获得:https://github.com/tomdalling/opengl-series/archive/master.zip

这一与日俱增文章中所使用的代码都存放在:https://github.com/tomdalling/opengl-series。你能够在页面中下载zip,参预你会git的话,也足以复制该仓库。

本文代码你可以在<code>source/01_project_skeleton</code>目录里找到。使用OSX系统的,可以打开根目录里的opengl-series.xcodeproj,选取本文工程。使用Windows系统的,可以在Visual
Studio 2013里打开opengl-series.sln,选取相应工程。

工程里已盈盈所有着重,所以您不必要再设置或者配置额外的事物。假使有别的编译或运行上的标题,请联系我。

二〇一四年1月,盛大公司发现奇天乐地企业开发了一款与《热血传奇》极其类似的手机游戏《烈火遮天》,经进一步取证、鉴定,发现《烈火遮天》完全抄袭了《热血传奇》,在作品的角色形象名称、地图场景、技能、动画特效等多个方面与《热血传奇》构成实质性相似,使得广大玩家对《烈火遮天》与《热血传奇》及改编自《热血传奇》的一而再串手机游戏发生混淆,更令人对游乐的根源也暴发严重混淆。其余,奇天乐地公司在推广《烈火遮天》的历程中,假冒盛大公司的显赫商品、服务名称,盗用盛大公司负有小说权的闻名互连网游戏形象图片,并将得体公司具备商标权的“热血传奇”、“沙城”、“传奇归来”等以拆除、组合、出色等各个款式利用在宣传中,刻意以歧义性语言等引人误解的措施对《烈火遮天》举办宣传,使得众多网友对《烈火遮天》及其所在网站与《热血传奇》等多元游戏暴发混淆。

有关包容性的唤醒

正文使用OpenGL 3.2,但我会尝试保持如下包容:

  • 向后包容OpenGL 2.1
  • 迈进包容OpenGL 3.X和4.X
  • 兼容Android和iOS的OpenGL ES 2.0

因为OpenGL和GLSL存在不少不比版本,本文代码不必然能做到100%上述包容。我梦想能匹配99%,并且不一样版本之间一旦轻微修改即可。

想要精通OpenGL和GLSL不一致版本间的界别,那里很好得罗列了匹配列表

苹果公司是网络苹果应用商店(App
Store)的纳税人、管理者、所有者以及Ipad等苹果设备产品的劳动者、销售者,其经过互连网苹果应用商店对第三方上传的应用程序加以筛选,并通过提供下载服务获得可观的分为经济便宜。近年来,盛大集团察觉互联网苹果应用商店为社会公众提供《烈火遮天》的下载服务,公众得以向来通过Iphone、Ipad、Itouch等制品进入网络苹果应用商店,将《烈火遮天》下载到苹果设备中运作并得到经济利益。卓易讯畅公司和One plus集团均在其网站上向民众提供《烈火遮天》的下载服务,侵凌了尊严企业的作品权。

Visual Studio下安装

代码在Windows 7 32位系统,Visual Studio Express
2013
(免费)下开创和测试。你应该可以打开解决方案并打响编译所有工程。假若有标题请联系我,或者将补丁发我,我会更新工程。

庄重公司认为,各被告借助《热血传奇》的盛名度,擅自入侵自己的文章权并进行不正当竞争,已经对社会公众发生严重误导,严重分流了庄重集团的用户,抢占了严穆公司的市场份额并赚取了巨大不合法利润,给盛大集团造成了巨大损失。

Xcode下安装

Xcode工程实在OSX 10.10连串,Xcode
6.1下成立并测试的。打开Xcode工程应该可以成功编译所有目的。加入你不可以得逞编译请联系我。

当前,此案正在尤其审理中。

Linux下安装

Linux是基于SpartanJ。我在Ubuntu
12.04下不难测试通过。

  • 安装GLM,GLFW和GLEW:
    sudo aptitude install libglm-dev libglew-dev libglfw-dev
  • 跻身工程目录:cd platforms/linux/01_project_skeleto
  • 运行makefile:make
  • 运转可执行文件:bin/01_project_skeleton-debug

发源:中国法院网

GLEW, GLFW和GLM介绍

近年来您有了工程,就让大家初阶介绍下工程所用到的开源库和怎么需求这么些。

The OpenGL Extension Wrangler
(GLEW)
是用来做客OpenGL
3.2
API函数的。不幸的是您无法简单的行使#include <GL/gl.h>来访问OpenGL接口,除非你想用旧版本的OpenGL。在现世OpenGL中,API函数是在运行时(run
time)确定的,而非编译期(compile time)。GLEW可以在运转时加载OpenGL
API。

GLFW允许大家跨平台成立窗口,接受鼠标键盘音讯。OpenGL不处理这一个窗口成立和输入,所以就须要大家团结入手。我接纳GLFW是因为它很小,并且简单通晓。

OpenGL Mathematics
(GLM)
是一个数学库,用来处理矢量和矩阵等大约任何具有东西。旧版本OpenGL提供了近乎glRotate,
glTranslateglScale等函数,在现世OpenGL中,这个函数已经不存在了,大家需求协调处理所有的数学运算。GLM能在三番五次教程里提供许多矢量和矩阵运算上扶助。

在这一而再串的拥有科目中,大家还编制了一个小型库tdogl用来重用C++代码。那篇教程会蕴藏tdogl::Shadertdogl::Program用来加载,编译和链接shaders。

什么是Shaders?

Shaders在现代OpenGL中是个很重点的概念。应用程序离不开它,除非你了然了,否则这么些代码也没有其余意义。

Shaders是一段GLSL小程序,运行在GPU上而非CPU。它们利用OpenGL Shading
Language
(GLSL)
言语编写,看上去像C或C++,但却是其余一种区其他言语。使用shader就好像您写个普通程序一样:写代码,编译,最终链接在一块儿才转移最终的程序。

Shaders并不是个很好的名字,因为它不但只做着色。只要记得它们是个用不一样的语言写的,运行在显卡上的小程序就行。

在旧版本的OpenGL中,shaders是可选的。在现代OpenGL中,为了能在屏幕上出示出物体,shaders是必须的。

为可能中距离了然shaders和图表渲染管线,我推荐Durian
Software的相关小说The Graphics Pipeline
chapter

主程序 Shader程序
语言 C++ GLSL
主函数 int main(int, char**); void main();
运行于 CPU GPU
需要编译?
需要链接?

那shaders实际上干了啥?那有赖于是哪类shader。

Vertex Shaders

Vertex
shader首要用以将点(x,y,z坐标)变换成不一样的点。
终点只是几何样子中的一个点,一个点叫vectex,五个点叫vertices(发音为ver-tuh-seez)。在本教程中,我们的三角须求四个极端(vertices)组成。

Vertex Shader的GLSL代码如下:

#version 150

in vec3 vert;

void main() {
    // does not alter the vertices at all
    gl_Position = vec4(vert, 1);
}

第一行#version 150告诉OpenGL这个shader使用GLSL版本1.50.

第二行in vec3 vert;告诉shader须要这几个极限作为输入,放入变量vert

其三行定义函数main,这是shader运行入口。那看起来像C,但GLSL中main不要求带任何参数,并且毫不回到void。

第四行gl_Position = vec4(vert, 1);将输入的终端直接出口,变量gl_Position是OpenGL定义的全局变量,用来储存vertex
shader的输出。所有vertex shaders都急需对gl_Position开展赋值。

gl_Position是4D坐标(vec4),但vert是3D坐标(vec3),所以大家须要将vert转换为4D坐标vec4(vert, 1)。第一个的参数1是赋值给第四维坐标。我们会在一而再教程中学到越多关于4D坐标的东西。但昨日,大家即使了解第四维坐标是1即可,i可以忽略它就把它作为3D坐标来比较。

Vertex
Shader在本文中没有做其余事,后续大家会修改它来处理动画,视频机和其余东西。

Fragment Shaders

Fragment shader的首要意义是持筹握算每个要求绘制的像素点的颜色。

一个”fragment”基本上就是一个像素,所以您可以认为部分着色器(fragment
shader)就是像素着色器(pixel
shader)。在本文中各样片段都是一像素,但那并不总是如此的。你可以转移某个OpenGL设置,以便赢得比像素更小的一些,之后的篇章我们会讲到那几个。

正文所运用的fragment shader代码如下:

#version 150

out vec4 finalColor;

void main() {
    //set every drawn pixel to white
    finalColor = vec4(1.0, 1.0, 1.0, 1.0);
}

再次,第一行#version 150报告OpenGL那一个shader使用的是GLSL 1.50。

第二行finalColor = vec4(1.0, 1.0, 1.0, 1.0);将出口变量设为白色。vec4(1.0, 1.0, 1.0, 1.0)是创办一个RGBA颜色,并且红绿蓝和alpha都设为最大值,即白色。

现今,就能用shader在OpenGL中绘制出了纯白色。在随后的篇章中,我们还会投入差别颜色和贴图。贴图就是你3D模型上的图像。

编译和链接Shaders

在C++中,你要求对你的.cpp文件举行编译,然后链接到一起构成最后的次序。OpenGL的shaders也是这么回事。

在那篇小说中用到了八个可复用的类,是用来拍卖shaders的编译和链接:tdogl::Shadertdogl::Program。那三个类代码不多,并且有详细的笺注,我提出您读书源码并且去链接OpenGL是何等工作的。

什么是VBO和VAO?

当shaders运行在GPU,其余代码运行在CPU时,你须求有种方式将数据从CPU传给GPU。在本文中,大家传递了一个三角的七个极点数据,但在更大的工程中3D模型会有不少个顶峰,颜色,贴图坐标和此外东西。

那就是大家为啥必要Vertex Buffer Objects (VBOs)和Vertex Array Objects
(VAOs)。VBO和VAO用来将C++程序的数目传给shaders来渲染。

在旧版本的OpenGL中,是透过glVertexglTexCoordglNormal函数把每帧数据发送给GPU的。在现代OpenGL中,所有数据必须经过VBO在渲染以前发送给显卡。当您必要渲染某些数据时,通过安装VAO来描述该得到哪些VBO数据推送给shader变量。

Vertex Buffer Objects (VBOs)

第一步大家需求从内存里上传三角形的多个顶峰到显存中。这就是VBO该干的事。**VBO其实就是显存的“缓冲区(buffers)”

一串包括各类二进制数据的字节区域。**你能上传3D坐标,颜色,甚至是您喜爱的音乐和诗篇。VBO不关切那些数量是吗,因为它只是对内存举办复制。

Vertex Array Objects (VAOs)

其次步大家要用VBO的多少在shaders中渲染三角形。请记住VBO只是一块数据,它不知情这么些多少的花色。而报告OpenGL那缓冲区里是吗类型数据,这事就归VAO管。

VAO对VBO和shader变量进行了连接。它讲述了VBO所包罗的数据类型,还有该传递数据给哪些shader变量。在OpenGL所有不规范的技巧名词中,“Vertex
Array Object”是最烂的一个,因为它根本没有解释VAO该干的事。

您回头看下本文的vertex
shader(在小说的前头),你就能发现我们只有一个输入变量vert。在本文中,大家用VAO来表达“hi,OpenGL,那里的VBO有3D顶点,我想要你在vertex
shader时,发八个终端数据给vert变量。”

在持续的篇章中,大家会用VAO来说“hi,OpenGL,那里的VBO有3D顶点,颜色,贴图坐标,我想要你在shader时,发顶点数据给vert变量,发颜色数据给vertColor变量,发贴图坐标给vertTexCoord变量。”

给采纳上个OpenGL版本的用户的唤醒

如果你在旧版本的OpenGL中选用了VBO但没有用到VAO,你恐怕会不认可VAO的叙述。你会争辩说“顶点属性”可以用glVertexAttribPointer将VBO和shaders连接起来,而不是用VAO。那有赖于你是不是觉得顶点属性应该是VAO“内置(inside)”的(我是那样觉得的),或者说它们是还是不是是VAO外置的一个大局状态。3.2基础和我用的AIT驱动中,VAO不是可采用

  • 没有VAO的封装glEnableVertexAttribArray,
    glVertexAttribPointerglDrawArrays都会造成GL_INVALID_OPERATION错误。那就是干什么我以为顶点属性应该放权于VAO,而非全局状态的由来。3.2内核手册也说VAO是必须的,但本身只听说ATI驱动会抛错误。上边描述引用自OpenGL
    3.2内核手册

怀有与终端处理有关的数量定义都应该封装在VAO里。
诚如VAO边界包罗所有改变vertex
array状态的授命,比如VertexAttribPointer和EnableVertexAttribArray;所有应用vertex
array举行绘图的吩咐,比如DrawArrays和DrawElements;所有对vertex
array状态举行查询的下令(见第6章)。

不管如何,我也领略为什么会有人觉得顶点属性应该放在VAO外部。glVertexAttribPointer并发早于VAO,在那段时间里顶点属性一向被认为是大局状态。你应有能看得出VAO是一种改变全局状态的灵光格局。我更倾向于认为是那样:如若你从未开创VAO,那OpenGL通过了一个默许的全局VAO。所以当您使用glVertexAttribPointer时,你依旧是在VAO内修改顶点属性,只但是现在从默许的VAO变成你协调创设的VAO。

那边有越来越多的议论:http://www.opengl.org/discussion\_boards/showthread.php/174577-Questions-on-VAOs

代码解释

到底!理论已经说完了,大家初阶编码。OpenGL对于初学者而言不是专程温馨,但只要您精通了事先所介绍的定义(shaders,VBO,VAO)那你就没啥难点。

打开main.cpp,我们从main()函数伊始。

先是,大家开首化GLFW:

glfwSetErrorCallback(OnError);
if(!glfwInit())
    throw std::runtime_error("glfwInit failed");

glfwSetErrorCallback(OnError)这一行告诉GLFW当错误发生时调用OnError函数。OnError函数会抛一个含有错误信息的那个,我们能从中发现哪儿出错了。

然后大家用GLFW创设一个窗口。

glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
gWindow = glfwCreateWindow((int)SCREEN_SIZE.x, (int)SCREEN_SIZE.y, "OpenGL Tutorial", NULL, NULL);
if(!gWindow)
    throw std::runtime_error("glfwCreateWindow failed. Can your hardware handle OpenGL 3.2?");

该窗口包蕴一个前进包容的OpenGL
3.2内核上下文。要是glfwCreateWindow败北了,你应当降落OpenGL版本。

创立窗口最终一步,我们理应安装一个“当前”OpenGL上下文给刚成立的窗口:

glfwMakeContextCurrent(gWindow);

随便大家调用哪个OpenGL函数,都会影响到“当前上下文”。大家只会用到一个上下文,所以设置完后,就别管它了。理论上来说,大家得以有五个窗口,且每个窗口都得以有协调的上下文。

现行大家窗口有了OpenGL上下文变量,大家必要起首化GLEW以便访问OpenGL接口。

glewExperimental = GL_TRUE; //stops glew crashing on OSX :-/
if(glewInit() != GLEW_OK)
    throw std::runtime_error("glewInit failed");

那里的GLEW与OpenGL内核有点小难题,设置glewExperimental就足以修复,但愿意再未来永远不要爆发。

大家也足以用GLEW再度肯定3.2本子是或不是留存:

if(!GLEW_VERSION_3_2)
    throw std::runtime_error("OpenGL 3.2 API is not available.");

LoadShaders函数中,大家选择本学科提供的tdogl::Shadertdogl::Program多少个类编译和链接了vertex
shader和fragment shader。

std::vector<tdogl::Shader> shaders;
shaders.push_back(tdogl::Shader::shaderFromFile(ResourcePath("vertex-shader.txt"), GL_VERTEX_SHADER));
shaders.push_back(tdogl::Shader::shaderFromFile(ResourcePath("fragment-shader.txt"), GL_FRAGMENT_SHADER));
gProgram = new tdogl::Program(shaders);

LoadTriangle函数中,大家创立了一个VAO和VBO。那是首先步,成立和绑定新的VAO:

glGenVertexArrays(1, &gVAO);
glBindVertexArray(gVAO);

下一场大家创设和绑定新的VBO:

glGenBuffers(1, &gVBO);
glBindBuffer(GL_ARRAY_BUFFER, gVBO);

继之,大家上传一些多少到VBO中。这一个多少就是三个极点,每个终端包涵三个GLfloat

GLfloat vertexData[] = {
    //  X     Y     Z
     0.0f, 0.8f, 0.0f,
    -0.8f,-0.8f, 0.0f,
     0.8f,-0.8f, 0.0f,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);

明日缓冲区包含了三角形的多个极点,是时候初始安装VAO了。首先,大家理应启用shader程序中的vert变量。那个变量能被拉开或关闭,默许情形下是倒闭的,所以大家必要敞开它。vert变量是一个“属性变量(attribute
variable)”,这也是为什么OpenGL函数名称中有带“Attrib”。大家可以在一而再的篇章中看看愈来愈多花色。

glEnableVertexAttribArray(gProgram->attrib("vert"));

VAO设置最复杂的有的就是下个函数:glVertexAttribPointer。让大家先调用该函数,等会解释。

glVertexAttribPointer(gProgram->attrib("vert"), 3, GL_FLOAT, GL_FALSE, 0, NULL);

第三个参数,gProgram->attrib("vert"),那就是足够须求上传数据的shder变量。在那么些事例中,大家须要发多少给vertshader变量。

第四个参数,3表明各样终端要求七个数字。

其三个参数,GL_FLOAT声明多个数字是GLfloat类型。这非常紧要,因为GLdouble品种的多寡大小跟它是例外的。

第八个参数,GL_FALSE讲明大家不要求对浮点数举办“归一化”,假若大家运用了归一化,那这几个值会被界定为最小0,最大1。我们不要求对大家的终端举办限定,所以这么些参数为false。

第一个参数,0,该参数可以在终端之间有距离时行使,设置参数为0,表示数据里面平昔不距离。

第八个参数,NULL,假如大家的多寡不是从缓冲区底部开首以来,可以安装那个参数来指定。设置该参数为NULL,表示我们的数目从VBO的率先个字节开头。

现行VBO和VAO都安装完毕,大家需求对它们举行解绑定,幸免一不小心被哪个地方给更改了。

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

到此,shader,VBO和VAO都准备好了。大家得以起来在Render函数里绘制了。

先是,大家先清空下显示屏,让它成为纯灰色:

glClearColor(0, 0, 0, 1); // black
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

然后告诉OpenGL大家要从头采取VAO和shader了:

glUseProgram(gProgram->object());
glBindVertexArray(gVAO);

说到底,我们绘制出三角形:

glDrawArrays(GL_TRIANGLES, 0, 3);

调用glDrawArrays函数表达大家须要绘制三角形,从第0个极点初步,有3个极点被发送到shader。OpenGL会在此时此刻VAO范围内确定该从哪儿得到极限。

极端将会从VBO中取出并发送到vertex
shader。然后三角形内的每个像素会发送给fragment shader。接着fragment
shader将种种像素变成白色。欢呼!

最近绘制截至了,为了安全起见,大家需求将shader和VAO举行解绑定:

glBindVertexArray(0);
glUseProgram(0);

最后一件事,在我们看看三角形此前须要切换帧缓冲:

glfwSwapBuffers(gWindow);

在帧缓冲被换成前,大家会绘制到一个不可知的离屏(off-screen)帧缓冲区。当我们调用glfwSwapBuffers时,离屏缓冲会变成显示屏缓冲,所以大家就能在窗口上看见内容了。

越是读书

在三番五次小说中,大家会对三角形举行贴图。之后,你会学到一点矩阵变换知识,就可以利用vertex
shader来已毕3D立方体旋转。

在那事后,咱们初始创办3D场景并提交几个物体。

越多现代OpenGL资料

不幸的是,我只能跳过很多情节,幸免本学科的字数过长。前边还有好多好的当代OpenGL资料能满意你的求知欲:

翻译越来越多内容请移步huangwei.pro

发表评论

电子邮件地址不会被公开。 必填项已用*标注