使用VBO扩展 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
原文出处 http://Nehe.gamedev.net lesson 45。
翻译: xheartblue 潘李亮 Stanly Lee 2004-4-18
Homepage: http://gamehunter.3322.net/xpertsoft/
MSN/Email : xheartblue@etangc.om
任何一个3D应用程序的最大的目标之一就是速度,你需要自始至终的限制实际渲染的三角形数目,不管你是采用排序,剔除(Culling),还是层次细节(LOD)。如果其它的方法都无效了,你想简单提高多边形的提交速度的话,通常可以利用OpenGL提供的优化方法,顶点数组是一个比较好的方法。加上最近一个名称为Vertex Buffer Object的扩展,这大大提高了应用的FPS。ARB_Vertex_Buffer的扩展工作的和顶点数组很像,除了一点,那就是ARB_Vertx_Buffer把数据加载到显卡的高性能的显存里。大大降低了渲染时间。当然,这个扩展是依赖较新的硬件的,不是所有的图形卡都支持的,所以我们必须使用一些技术来进行平衡。
在这个教程里,我们将要:
1:从高度图里加载数据。
2: 使用顶点数组更加有效的把网格数据提交给OpenGL。
3: 通过VBO(Vertex_Buffer_Object)把数据加载到高性能的显存里。
现在我们来定义几个应用程序的参数。
#define MESH_RESOLUTION 4.0f // 每个顶点对应几个像素
#define MESH_HEIGHTSCALE 1.0f // Mesh Height Scale
//#define NO_VBOS // 如果定义了,强制的不使用VBO
开头这两个参数是为高度图定义的。第一个定义了高度图里每一个像素的分辨率。后面一个设定了加载了高度图数据后在垂直方向需要进行的缩放比例。第三个常量,如果你定义了的话,将强行不使用VBO。
下一步,我们将定义VBO 扩展的常量,数据类型,和函数指针。
//VBO扩展的定义,从 glext.h摘抄
#define GL_ARRAY_BUFFER_ARB 0x8892
#define GL_STATIC_DRAW_ARB 0x88E4
typedef void (APIENTRY * PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer);
typedef void (APIENTRY * PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers);
typedef void (APIENTRY * PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers);
typedef void (APIENTRY * PFNGLBUFFERDATAARBPROC) (GLenum target, int size, const GLvoid *data, GLenum usage);
// VBO 扩展的函数指针
PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL; // VBO 名字生成函数
PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL; // VBO 绑定函数
PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL; // VBO 数据加载函数
PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL; // VBO 删除函数
我只是包含了这个演示程序所需要的东西次。如果你需要其它更多的扩展函数,我建议你到http://www.opengl.org里去下载最新的glext.h文件并且使用它里面的定义(这样会在一定程度上让你的程序更加美观)。我们将深入那几个我们将会使用到的函数。
现在我们来定义基本数学对象,加上我们自己的网格类,所有这些都是这个演示程序的一个非常简单的设计,我建议大家开发一个自己的数学库。
class CVert // 顶点类
{
public:
float x; // X Component
float y; // Y Component
float z; // Z Component
};
typedef CVert CVec; // 定义CVec 和CVert是一样的。
class CTexCoord // 纹理坐标类
{
public:
float u; // U Component
float v; // V Component
};
class CMesh
{
public:
// 网格数据
int m_nVertexCount; // 顶点数目
CVert* m_pVertices; // 顶点数据
CTexCoord* m_pTexCoords; // 纹理坐标
unsigned int m_nTextureId; // 纹理标ID
// VBO 的名字
unsigned int m_nVBOVertices; // 顶点 VBO 的名字
unsigned int m_nVBOTexCoords; // 纹理坐标 VBO 的名字
// 临时数据
AUX_RGBImageRec* m_pTextureImage; //高度图的数据
public:
CMesh(); // Mesh Constructor
~CMesh(); // Mesh Deconstructor
//高度图加载函数
bool LoadHeightmap( char* szPath, float flHeightScale, float flResolution );
// 得到一个点的高度
float PtHeight( int nX, int nY );
// 绑定VBO对象。
void BuildVBOs();
};
大部分的代码是自注释的。请注意,我把顶点和纹理给分离开存放了。这不是必需的,后面我会给出为什么这么做的解释。
现在我们来定义全局变量。首先是VBO是不是被支持的标志变量,它将在初始化代码里被设置。接下来是我们的网格对象。以及绕Y轴旋转的角度。用来计算FPS的变量。我决定写一个基于FPS的东西来显示这个代码被优化的程度。
bool g_fVBOSupported = false; // ARB_vertex_buffer_object是否被支持?
CMesh* g_pMesh = NULL; //网格数据
float g_flYRot = 0.0f; //旋转
int g_nFPS = 0, g_nFrames = 0; // FPS和FPS 计数器
DWORD g_dwLastFPS = 0; //最后依次计算FPS的时间
让我们直接CMesh函数的定义,从LoadHeightMap开始。对那些没有接触过Heightmap的人来,可以这样理解Heightmap,一个Heightmap就是一个二维的数据组,通常是一个图象,它指定了地形网格在垂直上的高度。有许多方法可以实现一个高度图的,但是几乎没有一个完美的。我的实现方法是从一个3个通道(24bit)的位图里读入数据,利用发光度算法来决定数据所定义的高度,这样无论你使用的彩色的图象还是灰度图,结果都是一样的。所以你可以使用彩色图象来定义高度图。个人建议使用四个通道的图象。如TGA等。我们可以使用它的Alpha通道来表示高度。但是,只为了这个教程,我想一个简单的Bitmap还是最合适的。
首先。我们确定一个高度图是不是存在。如果存在。我们使用GLaux的图象加载例程,这也许对写自己的图象加载例程比较有用,但是这已经超出了本教程的范围了。
bool CMesh :: LoadHeightmap( char* szPath, float flHeightScale, float flResolution )
{
// Error-Checking
FILE* fTest = fopen( szPath, "r" ); // Open The Image
if( !fTest ) // Make Sure It Was Found
return false; // If Not, The File Is Missing
fclose( fTest ); // Done With The Handle
// Load Texture Data
m_pTextureImage = auxDIBImageLoad( szPath ); // Utilize GLaux's Bitmap Load Routine
现在,事情变的有趣起来了。首先,我要指出我的高度图将为每一个三角形产生三个顶点。顶点是不和别的三角形共享的,我将在后面解释我为什么这么做,但是你在代码之前需要知道这点。
我开始计算网格里顶点的总数。算法是这样的: ( ( Terrain Width / Resolution ) * ( Terrain Length / Resolution ) * 3 Vertices in a Triangle * 2 Triangles in a Square )。接着分配数据,然后填充数据。
// Generate Vertex Field
m_nVertexCount = (int) ( m_pTextureImage->sizeX * m_pTextureImage->sizeY * 6 / ( flResolution * flResolution ) );
m_pVertices = new CVec[m_nVertexCount]; // Allocate Vertex Data
m_pTexCoords = new CTexCoord[m_nVertexCount]; // Allocate Tex Coord Data
int nX, nZ, nTri, nIndex=0; // Create Variables
float flX, flZ;
for( nZ = 0; nZ < m_pTextureImage->sizeY; nZ += (int) flResolution )
{
for( nX = 0; nX < m_pTextureImage->sizeX; nX += (int) flResolution )
{
for( nTri = 0; nTri < 6; nTri++ )
{
// Using This Quick Hack, Figure The X,Z Position Of The Point
flX = (float) nX + ( ( nTri == 1 || nTri == 2 || nTri == 5 ) ? flResolution : 0.0f );
flZ = (float) nZ + ( ( nTri == 2 || nTri == 4 || nTri == 5 ) ? flResolution : 0.0f );
// Set The Data, Using PtHeight To Obtain The Y Value
m_pVertices[nIndex].x = flX - ( m_pTextureImage->sizeX / 2 );
m_pVertices[nIndex].y = PtHeight( (int) flX, (int) flZ ) * flHeightScale;
m_pVertices[nIndex].z = flZ - ( m_pTextureImage->sizeY / 2 );
// Stretch The Texture Across The Entire Mesh
m_pTexCoords[nIndex].u = flX / m_pTextureImage->sizeX;
m_pTexCoords[nIndex].v = flZ / m_pTextureImage->sizeY;
// Increment Our Index
nIndex++;
}
}
}
函数的最后将要加载高度图的纹理到OpenGL里来。然后释放我们纹理数据的备份。这和前面的教程很类似的。
// Load The Texture Into OpenGL
glGenTextures( 1, &m_nTextureId ); // Get An Open ID
glBindTexture( GL_TEXTURE_2D, m_nTextureId ); // Bind The Texture
glTexImage2D( GL_TEXTURE_2D, 0, 3,
m_pTextureImage->sizeX, m_pTextureImage->sizeY, 0, GL_RGB,
GL_UNSIGNED_BYTE, m_pTextureImage->data );
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
// Free The Texture Data
if( m_pTextureImage )
{
if( m_pTextureImage->data )
free( m_pTextureImage->data );
free( m_pTextureImage );
}
return true;
}
PtHeight这个函数就比较简单了。它计算了需要查询的索引位置的数据的高度,并进行检测,然后计算高度,亮度值的计算也非常简单。你可以看的到,所以我就不在多讲了。
float CMesh :: PtHeight( int nX, int nY )
{
// Calculate The Position In The Texture, Careful Not To Overflow
int nPos = ( ( nX % m_pTextureImage->sizeX ) + ( ( nY % m_pTextureImage->sizeY ) * m_pTextureImage->sizeX ) ) * 3;
float flR = (float) m_pTextureImage->data[ nPos ]; // Get The Red Component
float flG = (float) m_pTextureImage->data[ nPos + 1 ]; // Get The Green Component
float flB = (float) m_pTextureImage->data[ nPos + 2 ]; // Get The Blue Component
return ( 0.299f * flR + 0.587f * flG + 0.114f * flB ); // Calculate The Height Using The Luminance Algorithm
}
万岁,现在是讲述顶点数组和VBOs的时候了。什么是顶点数组(Vertex Arrays),它是这样一个系统,你可以告诉OpenGL,你的顶点数据在哪里,然后分成子序列来渲染,只需要很少的函数调用就可以了。这样做的结果就是减少了函数的调用(glVertex,等),增加了程序的速度。什么是VBOs呢?Vertex Buffer Object使用高速的显卡内存,而不是普通的系统RAM内存。它不仅仅降低了每帧的内存操作,而且减少了数据在显卡和CPU之间的传输,在我的实验里,VBO大大提高了帧率,而不是提高了一点点。
现在我们来建立一个Vertex Buffer Objects。实际上,有两种方法可以做,其中一种是被称为"Mapping"的方法。我想最简单的方法也是最好的方法。过程如下:首先用glGenBuffersARB来产生一个可用的VBO的“名字”,实质上,一个名字是一个ID数字,OpenGL用这个ID来关联你的数据。因为一个数字并不一定代表一个有效的VBO名字。然后,我们通过glBindBufferARB函数把VBO对象绑定,让其被激活。最后,我们把数据加载到图形卡里,这可以通过glBufferDataARB来实现,把数据的指针和大小传递进去,glBufferDataARB将把你的数据拷贝到显卡内存中,这意味着我们没有必要再维护这些数据,所以我们可以把它删除(内存中的数据)
void CMesh :: BuildVBOs()
{
// Generate And Bind The Vertex Buffer
glGenBuffersARB( 1, &m_nVBOVertices ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices ); // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB,
m_nVertexCount*3*sizeof(float), m_pVertices,
GL_STATIC_DRAW_ARB );
// Generate And Bind The Texture Coordinate Buffer
glGenBuffersARB( 1, &m_nVBOTexCoords ); // Get A Valid Name
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords ); // Bind The Buffer
// Load The Data
glBufferDataARB( GL_ARRAY_BUFFER_ARB,
m_nVertexCount*2*sizeof(float),
m_pTexCoords, GL_STATIC_DRAW_ARB );
// Our Copy Of The Data Is No Longer Necessary, It Is Safe In The Graphics Card
delete [] m_pVertices; m_pVertices = NULL;
delete [] m_pTexCoords; m_pTexCoords = NULL;
}
好了。该是初始化的时候了。我们给Mesh分配内存并加载数据,然后我们测试GL_ARB_vertex_buffer_object是不是支持,如果支持,我们就通过wglGetProcAddress函数得到所有VBO扩展函数的指针,然后建立我们的VBO对象。注意。如果不支持VBO,我们将像通常一样保留数据,同时也请注意前面提到的强制关闭VBOs.
// Load The Mesh Data
g_pMesh = new CMesh(); // Instantiate Our Mesh
if( !g_pMesh->LoadHeightmap( "terrain.bmp", // Load Our Heightmap
MESH_HEIGHTSCALE, MESH_RESOLUTION ) )
{
MessageBox( NULL, "Error Loading Heightmap", "Error", MB_OK );
return false;
}
// Check For VBOs Supported
#ifndef NO_VBOS
g_fVBOSupported = IsExtensionSupported( "GL_ARB_vertex_buffer_object" );
if( g_fVBOSupported )
{
// Get Pointers To The GL Functions
glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");
// Load Vertex Data Into The Graphics Card Memory
g_pMesh->BuildVBOs(); // Build The VBOs
}
#else /* NO_VBOS */
g_fVBOSupported = false;
#endif
IsExtensionSupported是一个可以从OpenGL.org上得到的函数,我函数的变化是:用我粗俗的观点来看是更清晰了。
bool IsExtensionSupported( char* szTargetExtension )
{
const unsigned char *pszExtensions = NULL;
const unsigned char *pszStart;
unsigned char *pszWhere, *pszTerminator;
// Extension names should not have spaces
pszWhere = (unsigned char *) strchr( szTargetExtension, ' ' );
if( pszWhere || *szTargetExtension == '/0' )
return false;
// Get Extensions String
pszExtensions = glGetString( GL_EXTENSIONS );
// Search The Extensions String For An Exact Copy
pszStart = pszExtensions;
for(;;)
{
pszWhere = (unsigned char *) strstr( (const char *) pszStart, szTargetExtension );
if( !pszWhere )
break;
pszTerminator = pszWhere + strlen( szTargetExtension );
if( pszWhere == pszStart || *( pszWhere - 1 ) == ' ' )
if( *pszTerminator == ' ' || *pszTerminator == '/0' )
return true;
pszStart = pszTerminator;
}
return false;
}
这个函数相对来说简单一些了。有些人简单的使用strstr来搜索子字符串,但是显然OpenGL.org并不信任这个函数,我不同意他们的观点。
基本上所有的都做好了。我们剩下来要做的事情就是把数据渲染出来。
void Draw (void)
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity (); // Reset The Modelview Matrix
// Get FPS
if( GetTickCount() - g_dwLastFPS >= 1000 ) // When A Second Has Passed...
{
g_dwLastFPS = GetTickCount(); // Update Our Time Variable
g_nFPS = g_nFrames; // Save The FPS
g_nFrames = 0; // Reset The FPS Counter
char szTitle[256]={0}; // Build The Title String
sprintf( szTitle, "Lesson 45: NeHe & Paul Frazee's VBO Tut - %d Triangles, %d FPS",
g_pMesh->m_nVertexCount / 3, g_nFPS );
if( g_fVBOSupported ) // Include A Notice About VBOs
strcat( szTitle, ", Using VBOs" );
else
strcat( szTitle, ", Not Using VBOs" );
SetWindowText( g_window->hWnd, szTitle ); // Set The Title
}
g_nFrames++; // Increment Our FPS Counter
// Move The Camera
glTranslatef( 0.0f, -220.0f, 0.0f ); // Move Above The Terrain
glRotatef( 10.0f, 1.0f, 0.0f, 0.0f ); // Look Down Slightly
glRotatef( g_flYRot, 0.0f, 1.0f, 0.0f ); // Rotate The Camera
相当的简单,每一秒过去的时候,我们都把帧计数器的值当作FPS的值保存起来。然后把帧计数器清零。接着,我们在Terrain上移动Camera(如果你改变了高度图,也许你需要调整他),和做一些旋转,g_flYRot在每个Update调用是被递增。
要使用顶点数组(和VBOs),你需要告诉OpenGL你想给它提供什么样的数据,所以第一步是打开客户端的GL_VERTEX_ARRAY和GL_TEXTURE_COORD_ARRAY两个状态,然后我将要来设置我们的数据指针,我想,除非你有多个Mesh对象,否则你没有必要在渲染每个帧的时候都做这一步。但是这关系并不大,所以我不认为它是个大问题。
为一个特定的数据类型来设置一个指针,你需要使用对应的函数--glVertexPointer和glTexCoordPointer,在我们的例子里非常简单,把顶点需要变量的总数(一个顶点有3个,一个纹理坐标有2个),数据类型(float),每项数据之间期望间隔(Stride)(如果顶点不是存放在连续的数据结构里的时候会有用),以及数据的指针,你也可以使用glInterleavedArrays,并把所有数据保存在一个大的缓冲区里,但是我选择了把数据分开,这样能更好的演示如何使用多个VBOs。
说到VBOs。实现起来也没有太大不同,唯一个区别就是提供的数据指针,我们绑定了一个VBO后,只要把数据指针设置成NULL就可以了。请看下面:
// Set Pointers To Our Data
if( g_fVBOSupported )
{
glBindBufferARB( GL_ARRAY_BUFFER_ARB, g_pMesh->m_nVBOVertices );
glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL ); // Set The Vertex Pointer To The Vertex Buffer
glBindBufferARB( GL_ARRAY_BUFFER_ARB, g_pMesh->m_nVBOTexCoords );
glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL ); // Set The TexCoord Pointer To The TexCoord Buffer
} else
{
glVertexPointer( 3, GL_FLOAT, 0, g_pMesh->m_pVertices ); // Set The Vertex Pointer To Our Vertex Data
glTexCoordPointer( 2, GL_FLOAT, 0, g_pMesh->m_pTexCoords ); // Set The Vertex Pointer To Our TexCoord Data
}
渲染是非常简单容易的。
// Render
glDrawArrays( GL_TRIANGLES, 0, g_pMesh->m_nVertexCount ); // Draw All Of The Triangles At Once
这里我们使用glDrawArrays来把数据送给OpenGL.glDrawArrays检测哪个客户端状态被激活,然后使用它的指针来渲染。我们告诉OpenGL几何图元的类型,从那个索引开始,以及有多少顶点要渲染。有很多其他的方法可以把数据提交来渲染。如glArrayElement,但是这是最快的方法。你会注意到glDrawArrays没有在glBegin和glEnd之间。因为没有必要。
glDrawArrays是为什么我选择不在三角形间共享顶点的原因。因为共享是不可能的,我所知道的优化内存使用的最佳方法是使用Triangle Strips,但是它已经不是本教程的范围了。也许你还意识到了要为每一个顶点指定一个向量,这意味你要使用向量,每个顶点都要有一个伴随向量,如果你为每个顶点计算出向量能大大提高渲染结果的视觉真实性。
现在我们要做的就是关闭顶点数组,到此,我们就结束了。
// Disable Pointers
glDisableClientState( GL_VERTEX_ARRAY ); // Disable Vertex Arrays
glDisableClientState( GL_TEXTURE_COORD_ARRAY ); // Disable Texture Coord Arrays
}
If you want more information on Vertex Buffer Objects, I recommend reading the documentation in SGI's extension registry - http://oss.sgi.com/projects/ogl-sample/registry. It is a little more tedious to read through than a tutorial, but it will give you much more detailed information. Well that does it for the tutorial. If you find any mistakes or misinformation, or simply have questions, you can contact me at paulfrazee@cox.net. * DOWNLOAD Visual C++ Code For This Lesson. * DOWNLOAD Borland C++ Builder 6 Code For This Lesson. ( Conversion by Le Thanh Cong ) * DOWNLOAD Code Warrior 5.3 Code For This Lesson. ( Conversion by Scott Lupton ) * DOWNLOAD Dev C++ Code For This Lesson. ( Conversion by Gerald Buchgraber ) * DOWNLOAD Linux/SDL Code For This Lesson. ( Conversion by Ilias Maratos ) * DOWNLOAD Visual Studio .NET Code For This Lesson. ( Conversion by Joachim Rohde )
相关推荐
提高了应用的FPS,提高程序的运行速度。把数据加载到显卡的高性能的显存里。大大降低了渲染时间
想学习OpenGL VBO的同学不妨下载该资源,其中提供了使用OpenGL VBO绘制茶壶的实例程序(包含源码)。
一个使用opengl vbo 像素顶点缓存对象的例子
cpp-opengl-vbo-vao着色器 用法: git submodule init git submodule update mkdir build cd build cmake .. make ./triangle 在Linux,MacOS和Windows上进行了测试。
我们通过所谓的 **顶点缓冲对象 (VBO)**,可以在GPU的内存中存储大量的顶点。使用这些缓冲区对象的好处是,我们可以一次将大批数据全部发送到图形卡,如果还有足够的内存,则可以将其保存在此处,而不必一次发送一...
本文将详细介绍如何在32位系统下使用Visual C++ 2008进行OpenGL与VBO环境的配置,并简要提及在64位系统和Visual C++ 2010中可能遇到的问题及解决方案。 #### 二、环境配置步骤 ##### 1. 准备工作 首先,需要下载...
opengl vbo 简单演示 这是一个vbp的小例子。简单的螺旋运动。
### NeHe OpenGL 英文教程知识点总结 #### 一、设置OpenGL在MacOS中的环境 在本章节中,作者详细介绍了如何在MacOS系统中设置OpenGL编程环境。 **所需工具:** - **编译器**:最常用且推荐的是Metrowerks Code...
vbo 使用案例 使用与不使用vbo的区别 正方形 三角形 opengl的vbo实现 7个example 解决vbo使用中的一些问题 如何在opengl里面实现vbo技术的使用
运用定点数组绘制的三维图像,必须包含glew文件,适合Opengl初学者
kotlin-opengl-es-3-basic 现成的Kotlin OpenGL ES 3的简单渲染类。 它很简单,但包含开始使用所需的所有高级内容。 主要特征: 使用纹理阵列将所有纹理图集捆绑到一个纹理对象中。 毫不费力地在着色器中使用...
Android OpenGL顶点缓冲区对象应用演示demo 1、VBO顶点缓冲区对象 详解如下: ...2、开发语言kotlin 3、开发环境Android studio 4、VBO顶点缓冲区概念 VBO(Vertex Buffer Object)顶点缓冲对象,是在显卡存储空间(即,...
opengles之vbo顶点缓冲
1/23/2013-(进入实验阶段)-已实现六边形圆柱逻辑,但仍处于Hibernate状态-TODO:另一种构造三角形数组的方法-TODO:让应用使用这些新坐标-((((不确定是否六边形将作为六边形出现)))) iOS 1/22/2013--大幅...
3.3.2.bin.WIN32 \ glfw-3.3.2.bin.WIN32 \ lib-vc2019 D:\ OPENGL \ glew-2.1.0-win32 \ glew-2.1.0 \ lib \ Release \ Win32具体的な说明はスライドに书いています 1.VAO,VBOよりデータをCPU侧からGPU侧に転送し...
4. **扩展的使用**:在使用扩展之前,开发者需要确保硬件支持该扩展,并通过相应的库(如GLEW)查询和启用该扩展。 #### 四、OpenGL不同版本的特点 随着技术的进步,OpenGL经历了多次版本更新,每一次更新都带来了...
本文将深入探讨在Qt中使用OpenGL进行文字显示、透视投影、纹理贴图以及抗锯齿的技术。 首先,让我们来看看“QtOpenGL文字显示”这个主题。在OpenGL中,显示文本通常需要额外的库支持,如FreeType或GLUT,因为OpenGL...
OPENGL VBO经典例子,初学者必看!
通过本教程的学习,你将了解如何使用OpenGL创建并管理模型资产,并如何通过实例化这些资产来构建复杂场景。 #### 资产(Assets) 在3D图形编程领域,“资产”是指用于渲染3D场景的各种资源。资产可以包括但不限于...
OpenGL Separating Polygons Inside VBO - Stack Overflow 出处: https://stackoverflow.com/questions/26944959/opengl-separating-polygons-inside-vbo You can use primitive restart. The downside of this ...