OpenGL快问快答
OpenGL快问快答
本文内容主要来自对(http://www.opengl.org/wiki/FAQ)的翻译,随机加入了本人的观点。与原文相比,章节未必完整,含义未必雷同,顺序未必一致。仅供参考。
名词术语
渲染:等于"画",等于"draw"。
OpenGL是什么?
OpenGL是Open Graphics Library(开源图形库)的缩写。它是一本说明书,是一个PDF文件。它写的是渲染三维图形所使用的API(Application Programming Interface)。OpenGL实现是实现了说明书里定义的那些API的函数库。
简单理解:OpenGL是一个关于C语言函数声明的*.h文件。
普通显卡里都有一个OpenGL实现。犯二显卡里可能没有。所以你才能通过include一个"gl.h"之类的头文件就能编写运行OpenGL程序。OpenGL说明书是平台无关的,所以你写的OpenGL程序能够在多种显卡上运行,并且可能支持以后出现的新型显卡。
OpenGL不是什么?
OpenGL的API只处理渲染什么图形的问题。OpenGL实现处理如何渲染图形的问题。作为OpenGL使用者,我们不学如何渲染图形的问题,只学渲染什么图形的问题。OpenGL里没有处理动画、计时、文件读写、图片格式、GUI这些功能的函数。OpenGL只关心渲染。
GLUT不是OpenGL。GLUT也不是OpenGL的一部分。它是一个库,可以用来创建OpenGL窗口。
OpenGL说明书是谁写的?
OpenGL说明书是OpenGL Architectural Review Board(简称ARB)编写和维护的。
OpenGL是开源的吗?
不。因为OpenGL根本就没有代码,它只是一本说明书。说明书里讲的是程序员可以使用哪些函数,这些函数应该做什么。准确来说,OpenGL是一套理论,一些函数声明,一本开源的说明书,人人都可以免费下载。ISO的标准和说明书就不是免费下载的。
有一个开源的OpenGL实现(估计这是提问者想要的东西),名字是Mesa3D。它自称实现了 OpenGL 3.0 和 GLSL 1.30。反正我没研究过。
在哪儿下载OpenGL?
如"开源"之问,OpenGL不是一个软件产品,它是一本说明书。在苹果电脑上,自动集成了OpenGL的实现。
在Windows系统上,nVidia和AMD/ATI等公司根据OpenGL说明书实现了各自的OpenGL功能。所以说OpenGL功能包含在他们提供的显卡驱动里。
如果你的OpenGL版本不够新,更新显卡驱动就可以了。
程序员编程时需要获取OpenGL函数指针(手动加载 或 自动加载),这可以参考openGL入门。
OpenGL有SDK吗?
没有。
不过文档、教程之类的还是有的,比如这里。
NVIDIA 和ATI关于OpenGL有各自的SDK,且富含示例代码。
哪些平台支持OpenGL?
Windows:95及其以上。
苹果:所有版本。
Linux:由开源驱动、Mesa库或专有Nvidia驱动提供。
嵌入式系统中通常支持OpenGL ES。但是OpenGL ES是一套不同于传统的OpenGL的API。
OpenGL上下文是什么?
(详情参见 OpenGL context)
OpenGL能画很大很复杂很绚丽的三维世界,它肯定要用一些变量记录一些状态、编号,把这些状态、编号集合起来,作为一个整体,就是OpenGL上下文。OpenGL看到这些状态、编号,才知道自己应该画什么,所以叫做上下文。
你必须先创建一个OpenGL上下文,之后才能调用OpenGL函数。像下面这样直接调用是不行的:
1 int main(int argc, char **argv) 2 { 3 char *GL_version=(char *)glGetString(GL_VERSION); 4 char *GL_vendor=(char *)glGetString(GL_VENDOR); 5 char *GL_renderer=(char *)glGetString(GL_RENDERER); 6 return 0; 7 }
这段代码中,程序员只是想获取OpenGL版本信息,但是失败了。因为这段代码还没有与OpenGL驱动器开始对话。
假如你想问一个人的名字,至少得先找个人在你面前。上面这段代码就等于直接对空气问"what's your name?"。
如果是我,我会想让上帝给我找一个大学生,性别女,爱好男,学习成绩好,性格温和,相貌端庄,无不良嗜好……然后问"你喜欢吃青椒吗?"。
类似地,我想用OpenGL时,我会想让OpenGL驱动器给我分配一块显卡上的内存,指定像素格式、色彩缓存格式、深度缓存、模版缓存、累积缓存、是否启用光照、雾等,然后才能使用上面那段代码。
如何实现离屏渲染?
有人想做离屏渲染:渲染的时候不显示窗口。唯一的方法是:像平常一样创建窗口,然后把窗口隐藏起来。你指定像素格式,创建OpenGL上下文,把它置为当前上下文,然后调用OpenGL函数进行渲染。建议你创建一个FBO并渲染到FBO上,否则可能会失败。这方面的详情请参考:
http://www.opengl.org/wiki/Common_Mistakes#The_Pixel_Ownership_Problem
http://www.opengl.org/wiki/Common_Mistakes#The_Object_Oriented_Language_Problem
http://www.opengl.org/wiki/Platform_specifics
OpenGL程序在Windows上是如何实现的?
当你编译一个App时,会链接opengl32.dll 。
当你运行此App,opengl32.dll会被加载并检测是否存在OpenGL驱动器。如果存在,就加载驱动器。例如,ATI的OpenGL驱动器是atioglxx.dll而NVIDIA的是nvoglv32.dll。(这两个文件名可能随版本不同而有所差异。)
opengl32.dll仅直接包含OpenGL 1.1的函数。想使用更高版本里的函数的话,你必须使用 wglGetProcAddress 来手动获取其函数指针。详情在此。有一些辅助库帮你做这件事,被称为 Extension Loading Libraries。
重要的是要知道opengl32.dll属于Microsoft。所以不要改动它,不要替换它,也不要在发布你的App时把它加安装包。也不要把nvoglv32.dll(或者其它系统文件)加入安装包。
安装OpenGL驱动是用户的事,不管他们是从哪里(Dell、HP、nVidia、ATI/AMD……)下载。你可以提醒他们,这很正常。
我的OpenGL版本是多少?
使用 glGetString,传入参数 GL_VERSION。它会返回一个字符串(可能很长)。
在OpenGL 3.0以上,也可以用 glGetIntegerv(GL_MAJOR_VERSION, *) 和 glGetIntegerv(GL_MINOR_VERSION, *) 。
想获取你的GPU支持的OpenGL最高版本?更新你的显卡驱动即可,OpenGL就在里面。
假如你当前的OpenGL版本是2.0,而你的GPU却不支持更高版本,那么显卡厂商可能就不给你做新的驱动了。
这怎么破?两个办法:买个新显卡,或者使用Mesa3D(纯软件实现的OpenGL,在http://www.mesa3d.org)。不过截止到本文编写时(2015年5月11日),Mesa3D只支持到OpenGL 3.3,而最新的OpenGL说明书已经出到OpenGL 4.5了。
为什么我的OpenGL版本只有1.4或者更低?
可能原因有三:
1. 在Windows上,如果你创建OpenGL上下文时使用了未加速的(我也不知道什么叫"加速")像素格式,你就只能得到OpenGL1.1版本。
解决办法是小心选择像素格式,详情参考Platform_specifics:_Windows或自行百度。
2. 另一个原因是你的显卡及其驱动厂商没有提供新版的OpenGL实现。好多显卡厂商都已经关门大吉了,哪有什么售后。
不过,在那些尚未死掉的厂商里,Intel集成显卡最容易出这个问题。对此我们无计可施。NVIDIA 和ATI就很好地支持他们的集成显卡更新。
3. 最后,检查一下你有没有安装显卡驱动吧。
用glGetString检测OpenGL版本正常时,你就万事俱备了。
glTranslate/glRotate/glScale 有硬件加速吗?
没有。
没有GPU直接支持这三个家伙。他们已经在OpenGL 3.0被标记为弃用了。你应该用你自己的数学库,自己构造矩阵,把矩阵上传到shader。这里有几个库你可以试试。
现代GPU还支持固定功能管道吗?
(详情参见 Legacy OpenGL)
现在GPU不再为管道中的某步计算提供专门的硬件。所有的计算都由shader完成。为了保持兼容性,OpenGL驱动器生成一个特别的shader,这个shader模仿了固定功能管道。
例如渲染一个图元,固定功能管道的方式是在glBegin() 和glEnd() 之间用glVertex3f( , , ),提交各个顶点的位置属性。使用shader的方式时,你必须先在内存中用数组保存所有顶点的属性,然后创建缓存对象,最后用glBufferData、glBufferSubData、glMapBuffer或者glMapBufferRange把数组上传到显卡内存中。这样shader就可以用上传的数据进行渲染了。
如何在像素空间渲染?(在屏幕固定位置渲染一个模型)
指定这样一个投影矩阵:
1 glMatrixMode(GL_PROJECTION); 2 glLoadIdentity(); 3 glOrtho(0.0, WindowWidth, 0.0, WindowHeight, -1.0, 1.0); 4 //Setup modelview to identity if you don't need GL to move around objects for you 5 glMatrixMode(GL_MODELVIEW); 6 glLoadIdentity();
注意,在glOrtho中Y轴是从下到上的。当然你可以通过交换bottom和top参数来翻转Y轴。你得保证你用正确的顺序(顺时针or逆时针)渲染多边形,不然OpenGL可能会剔除它。保证不了就禁用剔除: glDisable(GL_CULL_FACE); 。
怎么画全屏四边形?
经常有人问,怎么画一个四边形,让它覆盖全屏?这需要什么样的投影矩阵?
投影矩阵用单位矩阵就可以了。在老版OpenGL,你可以用
1 glMatrixMode(GL_PROJECTION); 2 glLoadIdentity(); 3 glMatrixMode(GL_MODELVIEW); 4 glLoadIdentity();
在使用shader的OpenGL里,GLSL代码甚至不需要矩阵。这样就行:
#version 110 void main() { gl_Position = gl_Vertex; //Just output the incoming vertex }
当然,这要求四边形的顶点得是:{-1.0, -1.0, 0.0}, {1.0, -1.0, 0.0}, {1.0, 1.0, 0.0}, {-1.0, 1.0, 0.0}。
多索引渲染?
多索引渲染的意思是,每种顶点属性(位置、法向量等)都有自己的索引数组。OpenGL和Direct3D都不支持多索引渲染。
程序员必须自己调整数据格式,使得各种顶点属性的数组长度相同。这样才能使用单一的索引。
使用OBJ文件格式的同学经常问这个问题。
1 v 1.52284 39.3701 1.01523 2 v 36.7365 17.6068 1.01523 3 v 12.4045 17.6068 -32.475 4 and so on ... 5 n 0.137265 0.985501 -0.0997287 6 n 0.894427 0.447214 -8.16501e-08 7 n 0.276393 0.447214 -0.850651 8 and so on ... 9 t 0.6 1 10 t 0.5 0.647584 11 t 0.7 0.647584 12 and so on ... 13 f 102/102/102 84/84/84 158/158/158 14 f 158/158/158 84/84/84 83/83/83 15 f 158/158/158 83/83/83 159/159/159 16 and so on ...
以f开头的行是面(face)。每个顶点都有三个索引,分别是位置、法向量和贴图坐标(v、n和t)。上面的例子里,幸运的是每个{位置, 法向量, 贴图坐标}里的三个分量都是相等的。你得处理它们不相等的情况,比如:
1 f 1/1/1 2/2/2 3/2/2 2 f 5/5/5 6/6/6 3/4/5
这里的3/2/2和3/4/5就是两个不同的顶点。
所以加载OBJ文件后你得做一些后处理。OBJ文件里的顶点位置数、法向量数、贴图坐标数……都要相同。例如,你有10各顶点的位置,那么你必须有10个法向量与之对应。你不能有10个顶点的位置却只有7个法向量。
你有三个选项:
1. 为顶点的每个属性分别分配一个数组。
2. 创建一个数组,交错填入各个属性值。
3. 创建一个数组,依次填入各个属性。(例如,先把所有位置属性放到数组开头,然后放法向量,最后放贴图坐标)。
详情可参考Vertex Specification
画立方体
很多人都需要画立方体,所以这个问题值得一提。
从上一个问题我们知道,OpenGL只支持单一索引。
由于立方体的面是平的,我们可以用平面着色来渲染各个面。这意味着每个面都只有1个法向量。但是OpenGL只知道顶点的法向量,面的法向量是通过顶点的法向量计算出来的。那么办法来了:把画立方体的工作看做画6个互不相干的正方形的工作。这就是说要把一个顶点复制2次,变为3个。自己体会。
还有个办法,如下代码所示:
1 //The following code is for GL 2.0 2 3 //Render face 1 4 glNormal3fv(mynormal1); //Set your face normal 5 glBindBuffer(GL_ARRAY_BUFFER, VertexVBOID); 6 glEnableClientState(GL_VERTEX_ARRAY); 7 glVertexPointer(3, GL_FLOAT, sizeof(MyVertex), BUFFER_OFFSET(0)); //The starting point of the VBO, for the vertices 8 glClientActiveTexture(GL_TEXTURE0); 9 glEnableClientState(GL_TEXTURE_COORD_ARRAY); 10 glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), BUFFER_OFFSET(12)); //The starting point of texcoords, 12 bytes away 11 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexVBOID); //Bind the IBO 12 glDrawElements(GL_QUADS, 4, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0)); 13 14 //Render face 2 15 glNormal3fv(mynormal2); //Set your face normal 16 //We can just call glDrawElements since the rest of the setup is already done above 17 glDrawElements(GL_QUADS, 4, GL_UNSIGNED_SHORT, BUFFER_OFFSET(16)); 18 19 //.... and render face 3 and 4 and 5 and 6
上述代码中,缺点是渲染1个立方体就需要6次调用 glDrawElements,且使用了被鄙视的 glNormal3fv这个函数。 glNormal3fv不使用VBO里的数据,很low。
但是也有一点好处,绘制1个立方体可以节省3个法向量 * 3个浮点数 * 4字节 * 6 面= 216 字节。
glClear和 glScissor
glScissor 是为数不多的几个影响glClear的函数之一。如果你只想清除画面上某一块区域,就调用 glScissor 和glEnable(GL_SCISSOR_TEST)。
如果你忘记用glDisable(GL_SCISSOR_TEST);关闭剪切测试,glClear就可能会出奇奇怪怪的问题。
屏蔽(Masking)
小心使用 glColorMask , glStencilMask 和 glDepthMask 。如果你用 glDepthMask(FALSE); 禁掉了深度写入,那么所有对 glClear 的调用都不会清除深度缓存。
glGetError (如何检查OpenGL错误?)
(详情参见GL Error Codes)
OpenGL保存最近一次的各种类型的错误,每调用一次glGetError() 就返回一个类型的错误代码并清除其记录。如果一个错误记录都没有,就返回 GL_NO_ERROR 。
这个辅助函数可用于查询最近所有命中的错误:
1 int CheckGLErrors() 2 { 3 int errCount = 0; 4 for(GLenum currError = glGetError(); currError != GL_NO_ERROR; currError = glGetError()) 5 { 6 //Do something with `currError`. 7 ++errCount; 8 } 9 10 return errCount; 11 }
这个函数主动轮询错误信息。这会降低性能。所以最好只在debug版的程序里使用。
有个OpenGL扩展(ARB_debug_output)提供另一种机制来进行错误处理,且不需要轮询。开销也很明显,且只在拥有 CONTEXT_DEBUG_BIT_ARB 标记的上下文里才能用。它在OpenGL 4.3里成为了核心特性(KHR_debug),你随时可以用,但是在非debug上下文里不会有实际有效的log信息。
我应该用什么3D文件格式?
新手往往对使用哪种3D文件格式感到没底。
OpenGL不加载文件。所以,你可以用任何网格格式。这也意味着你得自己编写格式解析代码,OpenGL不管。
现有的文件格式各具特色。下述格式都可以用Open Asset Import 库解析。
此格式较简单。每个.obj文件描述一个单独的网格。Obj文件可以引用外部的材质文件(存储为.mtl格式,比较少见)。Obj里的网格只能包含位置、法向量和贴图坐标(可有可无)。
这是二进制的网格格式。包含材质信息,能存储多个命名的网格。
也是二进制网格格式。不含材质信息,只存储一个网格。支持关键帧动画,就是说一个单独的网格文件可能含有所有的关键帧及动画数据。
这是基于XML的网格文件格式。什么玩意都能存。它适合用在不同的3D文件格式之间互相转换上。
OpenGL有内存泄漏吗?
没有。
常常有人认为OpenGL驱动器发生了内存泄漏。他们写下如下的代码:
1 glClear(...); 2 SwapBuffers(...);
然后他们观察到:每次渲染都会使内存占用量增长。
这其实很正常。驱动器可能在另一个线程上进行优化相关的运算,或者在准备缓存数据。具体干什么无法得知,但是内存泄漏是没有的。
也有人使用 glDeleteTextures 或者 glDeleteLists 或者其它的delete函数,然后他们注意到内存占用量没有下降。这是你我无能为力的。内存管理是驱动器自己负责的,它可能不在你调用delete函数时就立即执行释放内存的操作。所以这也不是什么内存泄漏的证据。
谁管理内存?OpenGL如何管理内存?
显卡的内存也是有限的。如果你想分配太多的缓存对象或者贴图或者其它OpenGL资源,OpenGL驱动器就会把他们存到系统内存里。当你使用他们的时候,驱动器会视情况做数据交换。这显然会降低渲染速度。驱动器本身持有的内存量也是有限的,所以你调用 glGetError() 时可能会得到一个 GL_OUT_OF_MEMORY 。就算还有一些内存可用,如果你申请一个超大的缓存对象,OpenGL驱动器也会给你一个 GL_OUT_OF_MEMORY。
内存不足的问题在OpenGL说明书里没有写。因为OpenGL是与系统无关的,它才不管系统设计,不在乎系统有没有显卡,用的是集成显卡还是独立显卡。
我应该用显示列表、顶点数组还是顶点缓存对象?
详情参见(Vertex Specification)
显示列表和顶点数组从最初的OpenGL开始就有。顶点缓存对象在OpenGL 1.5引入。他们都可以渲染图元,但是有很大的区别。
显示列表里的数据提交到显卡内存后是不能改变的,但渲染速度较快。
多个顶点信息保存在数组里,通过一次API调用就可以完成传送,但是始终保存在App内存中,每次渲染时都要传送到显卡内存。
顶点缓存对象把顶点信息存储在显卡内存里。这避免了每次渲染都传送顶点数据。且数据是可以更新的。最后,需要调用的API次数也很少。
显示列表和顶点数组都是OpenGL最古老的特性。现代OpenGL一般都用顶点缓存对象和顶点数组对象描述顶点信息。建议你克制使用那些古老的特性。
当且仅当需要维护已有的古老代码且不可能重写渲染系统时,再用显示列表描述静态的顶点信息;如果顶点信息需要更新,就用顶点数组。如果有立即模式(比如glBegin() ),包含的顶点有几百几千,那么用显示列表或顶点数组都可以提升性能。
Unresolved External Symbol是什么意思?
新手编译OpenGL程序时可能会遇到这样的错误:
error LNK2001: unresolved external symbol _glBegin
你需要告诉编译器它应该搜索哪个库文件。
使用VC++2010时,你可以右击项目,选择'属性'。在属性对话框里,在左侧,打开'配置属性',打开'链接器',点击'输入'。在右侧,有一项'附加依赖'。输入库的名字,用分号隔开。如果是OpenGL,就输入'opengl32.lib',如果是GLU,就输入'glu32.lib'。
你也可以在cpp文件里添加下面两行来达到同一效果。
1 #pragma comment(lib, "opengl32.lib") 2 #pragma comment(lib, "glu32.lib")
不过有的编译器可能不支持#pragma。
我就不一一列举各种IDE的做法了,请自行百度。
如果你使用GCC,在这样编译时:
1 gcc -lGL -lglut myprogram.c -o myprogram
编译器告诉你:
/tmp/ccCQkTKm.o:myprogram.c:function display: error: undefined reference to 'gluLookAt'
这是因为你使用了GLU但是没有链接GLU库,你应该这么写:
1 gcc -lGL -lGLU -lglut myprogram.c -o myprogram
Not Declared In This Scope是什么意思?
有时候你会得到这样的编译错误:
GL_TEXTURE_3D was not declared in this scope
这是因为编译器找不到 GL_TEXTURE_3D 的声明。
你可能没有include合适的OpenGL头文件。gl.h可能不包含所有的东西。你应该用OpenGL Loading Library;;不然就得用the OpenGL Registry里的glext.h,而且还得手动加载OpenGL函数。
OpenGL最多渲染8个光源吗?
以前,OpenGL固定功能管道有个最大光源数的概念。现在,shader根本没有光源的概念,你可以用shader渲染任意数量的光源。
经常有人问为什么管道功能管道最多渲染8个光源。其实并非如此。OpenGL要求的是最少支持8个光源。你的驱动器/GPU可以支持超过8个光源,但是他们一般都限制为8个。原因是有3、4个光源时,再增加光源就对一个照射到一个平面上的光没多少区别了。所以,实际上8是一个足够大的数了。
例如,你设计了一个城市,路上有路灯,那需要的光源肯定海了去了。怎么办?办法是把你的街道划分成小段,使得只有3个光源能影响到这段路里的平面。
例如,你在做一个粒子系统,每个粒子都是一个光源,所以你可能需要1000个光源。对于旧硬件(比如仅支持到OpenGL 1.5的),这太疯狂了。你可以用一个覆盖整个粒子系统范围的光源代替那1000个光源。
如果你实在想要超过8个光源,你可以采用多遍渲染的方式。先开启8个光源,渲染一遍,然后开启混合并使用glBlendFunc(GL_ONE, GL_ONE),然后再渲染一遍。你可能需要把深度测试设置为 GL_LEQUAL 。
现在换个角度看问题。那些古老的游戏使用光源了吗?实际上,没有。很多古老的游戏对静态表面使用灯光贴图。对于运动的物体,程序员自己计算光照或者预计算(即light volume)。
能否预编译shader?
OpenGL 4.1增加了预编译shader的功能。你需要为你的显卡/GPU下载专门的shader编译器。这玩意换个GPU可能就不行了;显卡驱动更新后可能也就不行了。这个特性的目的是一次编译后留作以后运行。这避免了每次运行都需要编译、链接的时间。但是,驱动器更新时可能更新shader版本,这会强制你再次编译shader。
如果你发布的App里使用了预编译的shader,你还是得把shader源码也放在安装包里。因为一旦预编译shader无法加载,你只能重新用源码编译一次。
注意,实现这一特性的扩展(ARB_get_program_binary)在4.1之前的硬件就广泛存在了。所以4.x级的硬件不是必须的。
如何组织纹理?
这项技术也称为纹理地图集。
有的程序员认为,把多个小型2D纹理放到一个大型2D纹理上(1024 x 1024什么的),可以少调用一次 glBindTexture,也许能提升性能。这是个办法,但是你必须小心处理你的模型的纹理坐标。你也要仔细处理纹理滤波,因为线性滤波会导致纹素渗透(texel bleed),即一个模型使用了其它模型的纹理(因为纹素相邻了)
如果所有的2D纹理的大小都相同,你还可以把它们全放到一个3D纹理中。OpenGL 1.3开始支持 GL_TEXTURE_3D。不过在线性滤波时还是会有纹素渗透的问题。在S、T方向上不会有事,有事的是纹理层之间。
另一个办法是使用2D纹理数组。OpenGL 3.0开始支持 GL_TEXTURE_2D_ARRAY。这个方法没有纹素渗透的问题,不过也要求所有的2D纹理大小都相同。
字体渲染和文字渲染
OpenGL是一个底层库,它不处理渲染文字的事。它只渲染点、线、三角形这些。想渲染文字,要么使用第三方库,要么自己做个库。
一种最简单的方法是把字符做成纹理。需要渲染文字的时候就渲染四边形,并把纹理贴上去。
当然,你也可以做一个整句的纹理。
你也可以读取操作系统的字体信息,然后生成纹理。使用方法同上。
Windows提供了一些渲染文字的方法,不过很老了,不建议使用。详情参见(wglUseFontBitmaps)和(wglUseFontOutlines)。
更多内容可参考 http://www.opengl.org/archives/resources/faq/technical/#indx0170
GLU是什么?
GLU是OpenGL utility library 的缩写。它基于老版OpenGL特性,提供一些实用的功能,最著名的例子就是gluLooktAt()和gluPerspective(),它们简化了设置视图和投影矩阵的工作。但是,这两个函数都使用老版的特性(矩阵栈等)。这在现代OpenGL程序中不提倡。
如果你使用现代OpenGL(3.0及其以上),建议你要么使用非基于OpenGL的第三方库要么自己写这种库,要么就使用OpenGL 3.0以上的核心特性。这里有一些可用的第三方库。
如果你仍然打算使用GLU,你要知道Windows提供glu32.dll的是1.2版的GLU。你的编译器需要glu32.lib或glu32.a。最新的GLU是1.3版。你可以下载Mesa3D(http://www.mesa3d.org)提供的完整包。
这个包里有GLU1.3的源码,你得自己编译它。记住,你不能替换Microsoft的glu32.dll,它是系统文件,永远都不要覆盖它。
微信扫码,自愿捐赠。天涯同道,共谱新篇。
微信捐赠不显示捐赠者个人信息,如需要,请注明联系方式。 |