以文本方式查看主题

-  计算机科学论坛  (http://bbs.xml.org.cn/index.asp)
--  『 C/C++编程思想 』  (http://bbs.xml.org.cn/list.asp?boardid=61)
----  [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 45-lesson 46  (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=54729)


--  作者:一分之千
--  发布时间:11/1/2007 11:42:00 AM

--  [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 45-lesson 46

第四十五 四十六课 源码    


第四十五课

按此在新窗口浏览图片顶点缓存

你想更快地绘制么?直接操作显卡吧,这可是当前的图形技术,不要犹豫,我带你入门。接下来,你自己向前走吧。

  
   
   
速度是3D程序中最重要的指标,你必须限制绘制的多边形的个数,或者提高显卡绘制多边形的效率。显卡最近增加了一个新的扩展,叫做顶点缓存VS,它直接把顶点放置在显卡中的高速缓存中,极大的增加了绘制速度。
在这个教程里,我们会加载一个高度图,使用顶点数组高效的把网格数据发送到OpenGL里,并使用VBO扩展把顶点数据放入高效的显存里。
现在让我们开始吧,我们先来定义一些程序参数。

  
   

#define MESH_RESOLUTION 4.0f       // 每个顶点使用的像素#define MESH_HEIGHTSCALE 1.0f       // 高度的缩放比例//#define NO_VBOS        // 如果定义将不使用VBO扩展
// 定义VBO扩展它们在glext.h头文件中被定义#define GL_ARRAY_BUFFER_ARB 0x8892#define GL_STATIC_DRAW_ARB 0x88E4typedef 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; // 创建缓存名称
PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL; // 绑定缓存
PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL; // 绑定缓存数据
PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL; // 删除缓存


   
现在我们来定义自己的网格类:
  
   

class CVert              // 顶点类
{
public:
 float x;             
 float y;             
 float z;             
};
typedef CVert CVec;            

class CTexCoord             // 纹理坐标类
{
public:
 float u;             
 float v;             
};

//网格类
class CMesh
{
public:
 // 网格数据
 int    m_nVertexCount;        // 顶点个数
 CVert*   m_pVertices;        // 顶点数据的指针
 CTexCoord*  m_pTexCoords;        // 顶点的纹理坐标
 unsigned int m_nTextureId;        // 纹理的ID

 unsigned int m_nVBOVertices;        // 顶点缓存对象的名称
 unsigned int m_nVBOTexCoords;       // 顶点纹理缓存对象的名称

 AUX_RGBImageRec* m_pTextureImage;       // 高度数据

public:
 CMesh();             // 构造函数
 ~CMesh();             // 析构函数

 // 载入高度图
 bool LoadHeightmap( char* szPath, float flHeightScale, float flResolution );
 // 返回单个点的高度
 float PtHeight( int nX, int nY );
 // 创建顶点缓存对象
 void BuildVBOs();
};

   
大部分代码都很简单,这里不多加解释。

下面我们来定义一些全局变量:
  
   

bool  g_fVBOSupported = false;       // 是否支持顶点缓存对象
CMesh*  g_pMesh = NULL;          // 网格数据
float  g_flYRot = 0.0f;         // 旋转角度
int   g_nFPS = 0, g_nFrames = 0;       // 帧率计数器
DWORD  g_dwLastFPS = 0;         // 上一帧的计数 

   
下面的代码加载高度图,它和34课的内容差不多,在这里不多加解释了:   
   

//加载高度图
bool CMesh :: LoadHeightmap( char* szPath, float flHeightScale, float flResolution )
{

 FILE* fTest = fopen( szPath, "r" );       
 if( !fTest )            
  return false;           
 fclose( fTest );           

 // 加载图像文件
 m_pTextureImage = auxDIBImageLoad( szPath );    

 // 读取顶点数据
 m_nVertexCount = (int) ( m_pTextureImage->sizeX * m_pTextureImage->sizeY * 6 / ( flResolution * flResolution ) );
 m_pVertices = new CVec[m_nVertexCount];      
 m_pTexCoords = new CTexCoord[m_nVertexCount];    
 int nX, nZ, nTri, nIndex=0;         
 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++ )
   {
    flX = (float) nX + ( ( nTri == 1 || nTri == 2 || nTri == 5 ) ? flResolution : 0.0f );
    flZ = (float) nZ + ( ( nTri == 2 || nTri == 4 || nTri == 5 ) ? flResolution : 0.0f );

    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 );

    m_pTexCoords[nIndex].u = flX / m_pTextureImage->sizeX;
    m_pTexCoords[nIndex].v = flZ / m_pTextureImage->sizeY;

    nIndex++;
   }
  }
 }

 // 载入纹理,它和高度图是同一副图像
 glGenTextures( 1, &m_nTextureId );       
 glBindTexture( GL_TEXTURE_2D, m_nTextureId );    
 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);

 // 释放纹理数据
 if( m_pTextureImage )
 {
  if( m_pTextureImage->data )
   free( m_pTextureImage->data );
  free( m_pTextureImage );
 }
 return true;
}

   
下面的代码用来计算(x,y)处的亮度  
   

//计算(x,y)处的亮度
float CMesh :: PtHeight( int nX, int nY )
{
 int nPos = ( ( nX % m_pTextureImage->sizeX )  + ( ( nY % m_pTextureImage->sizeY ) * m_pTextureImage->sizeX ) ) * 3;
 float flR = (float) m_pTextureImage->data[ nPos ];   // 返回红色分量
 float flG = (float) m_pTextureImage->data[ nPos + 1 ];  // 返回绿色分量
 float flB = (float) m_pTextureImage->data[ nPos + 2 ];  // 返回蓝色分量
 return ( 0.299f * flR + 0.587f * flG + 0.114f * flB );  // 计算亮度
}


   
下面的代码把顶点数据绑定到顶点缓存,即把内存中的数据发送到显存   
   

void CMesh :: BuildVBOs(){ glGenBuffersARB( 1, &m_nVBOVertices );       // 创建一个顶点缓存,并把顶点数据绑定到缓存 glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices );    glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*3*sizeof(float), m_pVertices, GL_STATIC_DRAW_ARB );
glGenBuffersARB( 1, &m_nVBOTexCoords ); // 创建一个纹理缓存,并把纹理数据绑定到缓存
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_nVertexCount*2*sizeof(float), m_pTexCoords, GL_STATIC_DRAW_ARB );

// 删除分配的内存
delete [] m_pVertices; m_pVertices = NULL;
delete [] m_pTexCoords; m_pTexCoords = NULL

  }

   
好了,现在到了初始化的地方了。首先我将分配并载入纹理数据。接着检测是否支持VBO扩展。如果支持我们将把函数指针和它对应的函数关联起来,如果不支持将只返回数据。  
   

//初始化
BOOL Initialize (GL_Window* window, Keys* keys)     
{
 g_window = window;
 g_keys  = keys;

 // 载入纹理数据
 g_pMesh = new CMesh();          
 if( !g_pMesh->LoadHeightmap( "terrain.bmp",     
        MESH_HEIGHTSCALE,
        MESH_RESOLUTION ) )
 {
  MessageBox( NULL, "Error Loading Heightmap", "Error", MB_OK );
  return false;
 }

 // 检测是否支持VBO扩展
#ifndef NO_VBOS
 g_fVBOSupported = IsExtensionSupported( "GL_ARB_vertex_buffer_object" );
 if( g_fVBOSupported )
 {
  // 获得函数的指针
  glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB");
  glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB");
  glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB");
  glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");
  // 创建VBO对象
  g_pMesh->BuildVBOs();         
 }
#else
 g_fVBOSupported = false;
#endif
 //设置OpenGL状态
 glClearColor (0.0f, 0.0f, 0.0f, 0.5f);      
 glClearDepth (1.0f);          
 glDepthFunc (GL_LEQUAL);         
 glEnable (GL_DEPTH_TEST);         
 glShadeModel (GL_SMOOTH);         
 glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);   
 glEnable( GL_TEXTURE_2D );         
 glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );      

 return TRUE;            
}

   
下面的函数用来检测是否包含特定的扩展名称  
   

// 返回是否支持指定的扩展
bool IsExtensionSupported( char* szTargetExtension )
{
 const unsigned char *pszExtensions = NULL;
 const unsigned char *pszStart;
 unsigned char *pszWhere, *pszTerminator;

 pszWhere = (unsigned char *) strchr( szTargetExtension, ' ' );
 if( pszWhere || *szTargetExtension == '\0' )
  return false;

 // 返回扩展字符串
 pszExtensions = glGetString( GL_EXTENSIONS );

 // 在扩展字符串中搜索
 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' )
    //如果存在返回True
    return true;
  pszStart = pszTerminator;
 }
 return false;
}

   
好了,几乎结束了,我们下面来看看我们的渲染代码.
  
   

void Draw (void){ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   glLoadIdentity ();           
// 显示当前的帧率
if( GetTickCount() - g_dwLastFPS >= 1000 )
{
g_dwLastFPS = GetTickCount();
g_nFPS = g_nFrames;
g_nFrames = 0;

char szTitle[256]={0};
sprintf( szTitle, "Lesson 45: NeHe & Paul Frazee's VBO Tut - %d Triangles, %d FPS", g_pMesh->m_nVertexCount / 3, g_nFPS );
if( g_fVBOSupported ) // 是否支持VBO
strcat( szTitle, ", Using VBOs" );
else
strcat( szTitle, ", Not Using VBOs" );
SetWindowText( g_window->hWnd, szTitle ); // 设置窗口标题
}
g_nFrames++;

// 设置视口
glTranslatef( 0.0f, -220.0f, 0.0f );
glRotatef( 10.0f, 1.0f, 0.0f, 0.0f );
glRotatef( g_flYRot, 0.0f, 1.0f, 0.0f );

// 使用顶点,纹理坐标数组
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );


   
为了使用VBO,你必须告诉OpenGL内存中的那部分需要加载到VBO中。所以第一步我们要起用顶点数组和纹理坐标数组。接着我们必须告诉OpenGL去把数据的指针设置到特定的地方,glVertexPointer函数可以完成这个功能。
我们分为启用和不启用VBO两个路径来渲染,他们都差不多,唯一的区别是当你需要把指针指向VBO缓存时,记得把数据指针设置NULL。

  
   

 // 如果支持VBO扩展 if( g_fVBOSupported ) {  glBindBufferARB( GL_ARRAY_BUFFER_ARB, g_pMesh->m_nVBOVertices );  glVertexPointer( 3, GL_FLOAT, 0, (char *) NULL );  // 设置顶点数组的指针为顶点缓存  glBindBufferARB( GL_ARRAY_BUFFER_ARB, g_pMesh->m_nVBOTexCoords );  glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL );  // 设置顶点数组的指针为纹理坐标缓存 }  // 不支持VBO扩展 else {  glVertexPointer( 3, GL_FLOAT, 0, g_pMesh->m_pVertices );   glTexCoordPointer( 2, GL_FLOAT, 0, g_pMesh->m_pTexCoords );  }
   
好了,渲染所有的三角形吧  
   

// 渲染 glDrawArrays( GL_TRIANGLES, 0, g_pMesh->m_nVertexCount );  
   
最后,别忘了恢复到默认的OpenGL状态.   
   

 glDisableClientState( GL_VERTEX_ARRAY );     glDisableClientState( GL_TEXTURE_COORD_ARRAY );   }
   
如果你想更多的了解VBO对象,我建议你读一下SGI的扩展说明:
http://oss.sgi.com/projects/ogl-sample/registry
它会给你更多的信息

好了,那就是这次的课程,如果你发现任何问题,请联系我.



--  作者:一分之千
--  发布时间:11/1/2007 11:43:00 AM

--  
Lesson: 45
   
One of the largest goals of any 3d application is speed. You should always limit the amount of polygons actually rendered, whether by sorting, culling, or level-of-detail algorithms. However, when all else fails and you simply need raw polygon-pushing power, you can always utilize the optimizations provided by OpenGL. Vertex Arrays are one good way to do that, plus a recent extension to graphics cards named Vertex Buffer Objects adds the FPS boost everybody dreams of. The extension, ARB_vertex_buffer_object, works just like vertex arrays, except that it loads the data into the graphics card's high-performance memory, significantly lowering rendering time. Of course, the extension being relatively new, not all cards will support it, so we will have to write in some technology scaling.
In this tutorial, we will
Load data from a heightmap
Use Vertex Arrays to send mesh data to OpenGL more efficiently
Load data into high-performance memory via the VBO extension
So let's get started! First we are going to define a few application parameters.
  
   

#define MESH_RESOLUTION 4.0f       // Pixels Per Vertex#define MESH_HEIGHTSCALE 1.0f       // Mesh Height Scale//#define NO_VBOS        // If Defined, VBOs Will Be Forced Off

   
The first two constants are standard heightmap fare - the former sets the resolution at which the heightmap will be generated per pixel, and the latter sets the vertical scaling of the data retrieved from the heightmap. The third constant, when defined, will force VBOs off - a provision I added so that those with bleeding-edge cards can easily see the difference.
Next we have the VBO extension constant, data type, and function pointer definitions.
  
   

// VBO Extension Definitions, From glext.h#define GL_ARRAY_BUFFER_ARB 0x8892#define GL_STATIC_DRAW_ARB 0x88E4typedef 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 Extension Function Pointers
PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL; // VBO Name Generation Procedure
PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL; // VBO Bind Procedure
PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL; // VBO Data Loading Procedure
PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL; // VBO Deletion Procedure


   
I have only included what will be necessary for the demo. If you need any more of the functionality, I recommend downloading the latest glext.h from http://www.opengl.org and using the definitions there (it will be much cleaner for your code, anyway). We will get into the specifics of those functions as we use them.
Now we find the standard mathematical definitions, plus our mesh class. All of them are very bare-bones, designed specifically for the demo. As always, I recommend developing your own math library.
  
   

class CVert         // Vertex Class{public: float x;        // X Component float y;        // Y Component float z;        // Z Component};typedef CVert CVec;        // The Definitions Are Synonymous
class CTexCoord // Texture Coordinate Class
{
public:
float u; // U Component
float v; // V Component
};

class CMesh
{
public:
// Mesh Data
int m_nVertexCount; // Vertex Count
CVert* m_pVertices; // Vertex Data
CTexCoord* m_pTexCoords; // Texture Coordinates
unsigned int m_nTextureId; // Texture ID

// Vertex Buffer Object Names
unsigned int m_nVBOVertices; // Vertex VBO Name
unsigned int m_nVBOTexCoords; // Texture Coordinate VBO Name

// Temporary Data
AUX_RGBImageRec* m_pTextureImage; // Heightmap Data

public:
CMesh(); // Mesh Constructor
~CMesh(); // Mesh Deconstructor

// Heightmap Loader
bool LoadHeightmap( char* szPath, float flHeightScale, float flResolution );
// Single Point Height
float PtHeight( int nX, int nY );
// VBO Build Function
void BuildVBOs();
};


   
Most of that code is relatively self-explanatory. Note that while I do keep the Vertex and Texture Coordinate data seperate, that is not wholly necessary, as will be indicated later.
Here we have our global variables. First we have a VBO extension validity flag, which will be set in the initialization code. Then we have our mesh, followed by our Y rotation counter. Leading up the rear are the FPS monitoring variables. I decided to write in a FPS guage to help display the optimization provided by this code.
  
   

bool  g_fVBOSupported = false;     // ARB_vertex_buffer_object supported?CMesh*  g_pMesh = NULL;       // Mesh Datafloat  g_flYRot = 0.0f;      // Rotationint  g_nFPS = 0, g_nFrames = 0;     // FPS and FPS CounterDWORD  g_dwLastFPS = 0;      // Last FPS Check Time
   
Let's skip over to the CMesh function definitions, starting with LoadHeightmap. For those of you who live under a rock, a heightmap is a two-dimensional dataset, commonly an image, which specifies the terrain mesh's vertical data. There are many ways to implement a heightmap, and certainly no one right way. My implementation reads a three channel bitmap and uses the luminosity algorithm to determine the height from the data. The resulting data would be exactly the same if the image was in color or in grayscale, which allows the heightmap to be in color. Personally, I recommend using a four channel image, such as a targa, and using the alpha channel for the heights. However, for the purpose of this tutorial, I decided that a simple bitmap would be best.
First, we make sure that the heightmap exists, and if so, we load it using GLaux's bitmap loader. Yes yes, it probably is better to write your own image loading routines, but that is not in the scope of this tutorial.
  
   

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

   
Now things start getting a little more interesting. First of all, I would like to point out that my heightmap generates three vertices for every triangle - vertices are not shared. I will explain why I chose to do that later, but I figured you should know before looking at this code.
I start by calculating the amount of vertices in the mesh. The algorithm is essentially ( ( Terrain Width / Resolution ) * ( Terrain Length / Resolution ) * 3 Vertices in a Triangle * 2 Triangles in a Square ). Then I allocate my data, and start working my way through the vertex field, setting data.
  
   

// 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++;
}
}
}


   
I finish off the function by loading the heightmap texture into OpenGL, and freeing our copy of the data. This should be fairly familiar from past tutorials.   
   

 // 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 is relatively simple. It calculates the index of the data in question, wrapping any overflows to avoid error, and calculates the height. The luminance formula is very simple, as you can see, so don't sweat it too much.   
   

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}

   
Hurray, time to get dirty with Vertex Arrays and VBOs. So what are Vertex Arrays? Essentially, it is a system by which you can point OpenGL to your geometric data, and then subsequently render data in relatively few calls. The resulting cut down on function calls (glVertex, etc) adds a significant boost in speed. What are VBOs? Well, Vertex Buffer Objects use high-performance graphics card memory instead of your standard, ram-allocated memory. Not only does that lower the memory operations every frame, but it shortens the bus distance for your data to travel. On my specs, VBOs actually triple my framerate, which is something not to be taken lightly.
So now we are going to build the Vertex Buffer Objects. There are really a couple of ways to go about this, one of which is called "mapping" the memory. I think the simplist way is best here. The process is as follows: first, use glGenBuffersARB to get a valid VBO "name". Essentially, a name is an ID number which OpenGL will associate with your data. We want to generate a name because the same ones won't always be available. Next, we make that VBO the active one by binding it with glBindBufferARB. Finally, we load the data into our gfx card's data with a call to glBufferDataARB, passing the size and the pointer to the data. glBufferDataARB will copy that data into your gfx card memory, which means that we will not have any reason to maintain it anymore, so we can delete it.
  
   

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;
}


   
Ok, time to initialize. First we will allocate and load our mesh data. Then we will check for GL_ARB_vertex_buffer_object support. If we have it, we will grab the function pointers with wglGetProcAddress, and build our VBOs. Note that if VBOs aren't supported, we will retain the data as usual. Also note the provision for forced no 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


   
NIsExtensionSupported is a function you can get from OpenGL.org. My variation is, in my humble opinion, a little cleaner.  
   

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;
}


   
It is relatively simple. Some people simply use a sub-string search with strstr, but apparently OpenGL.org doesn't trust the consistancy of the extension string enough to accept that as proof. And hey, I am not about to argue with those guys.
Almost finished now! All we gotta do is render the data.
  
   

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


   
Pretty simple - every second, save the frame counter as the FPS and reset the frame counter. I decided to throw in poly count for impact. Then we move the camera above the terrain (you may need to adjust that if you change the heightmap), and do a few rotations. g_flYRot is incremented in the Update function.
To use Vertex Arrays (and VBOs), you need to tell OpenGL what data you are going to be specifying with your memory. So the first step is to enable the client states GL_VERTEX_ARRAY and GL_TEXTURE_COORD_ARRAY. Then we are going to want to set our pointers. I doubt you have to do this every frame unless you have multiple meshes, but it doesn't hurt us cycle-wise, so I don't see a problem.

To set a pointer for a certain data type, you have to use the appropriate function - glVertexPointer and glTexCoordPointer, in our case. The usage is pretty easy - pass the amount of variables in a point (three for a vertex, two for a texcoord), the data cast (float), the stride between the desired data (in the event that the vertices are not stored alone in their structure), and the pointer to the data. You can actually use glInterleavedArrays and store all of your data in one big memory buffer, but I chose to keep it seperate to show you how to use multiple VBOs.

Speaking of VBOs, implementing them isn't much different. The only real change is that instead of providing a pointer to the data, we bind the VBO we want and set the pointer to zero. Take a look.
  
   

// 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 }
   
Guess what? Rendering is even easier.  
   

// Render glDrawArrays( GL_TRIANGLES, 0, g_pMesh->m_nVertexCount );  // Draw All Of The Triangles At Once
   
Here we use glDrawArrays to send our data to OpenGL. glDrawArrays checks which client states are enabled, and then uses their pointers to render. We tell it the geometric type, the index we want to start from, and how many vertices to render. There are many other ways we can send the data for rendering, such as glArrayElement, but this is the fastest way to do it. You will notice that glDrawArrays is not within glBegin / glEnd statements. That isn't necessary here.
glDrawArrays is why I chose not to share my vertex data between triangles - it isn't possible. As far as I know, the best way to optimize memory usage is to use triangle strips, which is, again, out of this tutorial's scope. Also you should be aware that normals operate "one for one" with vertices, meaning that if you are using normals, each vertex should have an accompanying normal. Consider that an opportunity to calculate your normals per-vertex, which will greatly increase visual accuracy.

Now all we have left is to disable vertex arrays, and we are finished.
  
   

// 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.



--  作者:一分之千
--  发布时间:11/1/2007 11:44:00 AM

--  

第四十六课   


按此在新窗口浏览图片全屏反走样

当今显卡的强大功能,你几乎什么都不用做,只需要在创建窗口的时候该一个数据。看看吧,驱动程序为你做完了一切。

  
   
   
在图形的绘制中,直线的走样是非常影响美观的,我们可以使用反走样解决这个问题。在众多的解决方案里,多重采样是一种易于硬件实现的方法,也是一种快速的方法。
全凭多重采样可以使你的图形看起来更美观,我们可以使用ARB_MULTISAMPLE扩展完成这个功能,但它会降低你的程序的速度。


  
   

Vid_mem = sizeof(Front_buffer) + sizeof(Back_buffer) + num_samples  * (sizeof(Front_buffer) +sizeof(ZS_buffer))
   
如果你想知道更多的关于多重采样的信息,请访问下面的链接:
GDC2002 -- OpenGL Multisample
OpenGL Pixel Formats and Multisample Antialiasing

下面我们来介绍如何使用多重采样,不向其他的扩展,我们在使用多重采样时,必须在窗口创建时告诉它使用多重采样,典型的步骤如下:
1、创建一个窗口
2、查询是否支持多重采样
3、如果支持删除当前的窗口,使用支持多重采样的格式创建窗口
4、如果我们想使用多重采样,仅仅启用它既可。

了解了上面,我们从头说明如何使用多重采样,并介绍ARB_Multisample的实现方法:
  
   

#include <windows.h>#include <gl.h>#include <glu.h>#include "arb_multisample.h"

   
下面两行定义我需要使用的像素格式  
   

// 声明我们将要使用#define WGL_SAMPLE_BUFFERS_ARB 0x2041#define WGL_SAMPLES_ARB  0x2042
bool arbMultisampleSupported = false;
int arbMultisampleFormat = 0;

   
下面这个函数在扩展名的字符串中查找,如果包含则返回true

  
   

// 判断是否支持这个扩展bool WGLisExtensionSupported(const char *extension){ const size_t extlen = strlen(extension); const char *supported = NULL;
// 返回在WGL的扩展中查找是否支持特定的扩展
PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");

if (wglGetExtString)
supported = ((char*(__stdcall*)(HDC))wglGetExtString)(wglGetCurrentDC());

//在OpenGL的扩展中查找是否支持特定的扩展
if (supported == NULL)
supported = (char*)glGetString(GL_EXTENSIONS);

// 如果都不支持,则返回失败
if (supported == NULL)
return false;

// 查找是否包含需要的扩展名
for (const char* p = supported; ; p++)
{
p = strstr(p, extension);

if (p == NULL)
return false;

if ((p==supported || p[-1]==' ') && (p[extlen]=='\0' || p[extlen]==' '))
return true;
}
}

   
下面这个函数在扩展名的字符串中查找,如果包含则返回true  
   

// 初始化多重渲染bool InitMultisample(HINSTANCE hInstance,HWND hWnd,PIXELFORMATDESCRIPTOR pfd){    // 检测是否支持多重渲染 if (!WGLisExtensionSupported("WGL_ARB_multisample")) {  arbMultisampleSupported=false;  return false; }
// 返回wglChoosePixelFormatARB函数的入口
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
if (!wglChoosePixelFormatARB)
{
arbMultisampleSupported=false;
return false;
}

HDC hDC = GetDC(hWnd);

int pixelFormat;
int valid;
UINT numFormats;
float fAttributes[] = {0,0};

//下面的代码设置多重采样的像素格式
int iAttributes[] =
{
WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
WGL_COLOR_BITS_ARB,24,
WGL_ALPHA_BITS_ARB,8,
WGL_DEPTH_BITS_ARB,16,
WGL_STENCIL_BITS_ARB,0,
WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
WGL_SAMPLES_ARB,4,
0,0
};

// 首先我们测试是否支持4个采样点的多重采样
valid = wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
// 如果返回true并且numformats大于1,则表示成功,那么起用多重采样
if (valid && numFormats >= 1)
{
arbMultisampleSupported = true;
arbMultisampleFormat = pixelFormat;
return arbMultisampleSupported;
}

// 接着我们测试是否支持2个采样点的多重采样
iAttributes[19] = 2;
// 如果返回true并且numformats大于1,则表示成功,那么起用多重采样
valid = wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
if (valid && numFormats >= 1)
{
arbMultisampleSupported = true;
arbMultisampleFormat = pixelFormat;
return arbMultisampleSupported;
}

// 返回支持多重采样
return arbMultisampleSupported;
}

   
下面到了我们的主程序部分了,和前面一样还是按照常规包含一些头文件  
   

#include <windows.h>        #include <gl/gl.h>        #include <gl/glu.h>       #include "NeHeGL.h" 
  #include "ARB_MULTISAMPLE.h"
BOOL DestroyWindowGL (GL_Window* window);
BOOL CreateWindowGL (GL_Window* window);

   
下面我们需要在CreateWindowGL函数中添加下面的代码,首先我们先创建一个不需要支持多重采样的窗口,它在不启用多重采样的情况下起作用。  
   

 //如果不启用多重采样 if(!arbMultisampleSupported) {  PixelFormat = ChoosePixelFormat (window->hDC, &pfd);    // 选择一种相容的像素格式  if (PixelFormat == 0)            // 是否获得相容的像素格式  {                                      ReleaseDC (window->hWnd, window->hDC);       // 释放设备描述表   window->hDC = 0;            // 设置窗口设备描述表为0   DestroyWindow (window->hWnd);         // 删除窗口   window->hWnd = 0;            // 设置窗口句柄为0   return FALSE;             // 返回错误  }
}
//如果起用多重采样
else
{
PixelFormat = arbMultisampleFormat; //设置采样格式为多重采样格式
}


   
接着测试是否支持多重采样,如果支持,初始化多重采样,并重新创建窗口  
   

 //检测是否支持多重采样 if(!arbMultisampleSupported && CHECK_FOR_MULTISAMPLE) {  //如果是,初始化多重采样  if(InitMultisample(window->init.application->hInstance,window->hWnd,pfd))  {   //消耗当前窗口   DestroyWindowGL (window);   //创建一个支持多重采样的窗口   return CreateWindowGL(window);  } }

   
好了创建好多重采样后,只需要起用它。其他的一切绘制都和平常一样。  
   

glEnable(GL_MULTISAMPLE_ARB);
// 渲染场景

glDisable(GL_MULTISAMPLE_ARB);

   
好了,那就是全部,希望你能喜欢:)



--  作者:一分之千
--  发布时间:11/1/2007 11:45:00 AM

--  
Lesson: 46
   
Howdy all, the Friendly Neighborhood Roach here with an interesting tutorial that will help you get your apps top notch. In the realm of getting your OpenGL programs to look better, a big problem we all run into is aliasing. That is, the square edged "jaggies" that exist on diagonal lines in relation to the square pixels that exist on your screen. Ie, Bad Mojo. The answer, termed Anti-Aliasing, is used to smudge those "jaggies" in order to create a smoother edge for objects. One process used to achieve anti-aliasing is called "Multisampling." The idea is that for each pixel, we sample the pixels around it to determine if this edge needs to be anti-aliased, basically, we discard the jaggies by "smudging" the pixel itself.
Fullscreen AntiAliasing is something that non-realtime rendering programs have always had an advantage in. However, with current hardware, we're able to pull off the same effect real time. The ARB_MULTISAMPLE extension allows us to do this. Essentially, each pixel is sampled by it's neighbors to find out the optimal antialias to perform. This comes at a cost however, and can slow down performance.


  
   

Vid_mem = sizeof(Front_buffer) + sizeof(Back_buffer) + num_samples  * (sizeof(Front_buffer) +sizeof(ZS_buffer))
   
For more specific information on anti-aliasing, as well as the information I'm about to present, please check out the following links:
GDC2002 -- OpenGL Multisample
OpenGL Pixel Formats and Multisample Antialiasing

With that being said, a brief overview of how our process is going to work. Unlike other extensions, which relate to OpenGL rendering, the ARB_MULTISAMPLE extension must be dealt with during the creation of your rendering window. Our process shall go as follows:
Create our Window as normal
Query the possible Multisample pixel values (InitMultisample)
If Multisampling is available, destroy this Window and recreate it with our NEW pixelFormat
For parts we want to antialias, simply call glEnable(GL_ARB_MULTISAMPLE);
Let's start off from the top, and talk about our source file arbMultiSample.cpp. We start off with the standard includes for gl.h & glu.h, as well as windows.h, we'll talk about arb_multisample.h later.
  
   

#include <windows.h>#include <gl/gl.h>#include <gl/glu.h>#include "arb_multisample.h"

   
The two lines below define our access points into the WGL string listing. We'll use these in accessing the pixel format attributes to test for our sample format. The other two variables are used other places in the program to access our data.   
   

// Declarations We'll Use#define WGL_SAMPLE_BUFFERS_ARB 0x2041#define WGL_SAMPLES_ARB  0x2042
bool arbMultisampleSupported = false;
int arbMultisampleFormat = 0;

   
The next function we're going to talk about is WGLisExtensionSupported, which will be used to query the WGL extensions listing to verify if a given format is supported on the system. We'll provide the description of the code as we walk through it, because it's easier to do that then all the html back and forth jumping....
NOTE: The code below was rewritten by Henry Goffin. His update adds: Better parsing of the supported GL Extensions and fixes a problem with the fallback code if the first check was to fail.
  
   

bool WGLisExtensionSupported(const char *extension){ const size_t extlen = strlen(extension); const char *supported = NULL;
// Try To Use wglGetExtensionStringARB On Current DC, If Possible
PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");

if (wglGetExtString)
supported = ((char*(__stdcall*)(HDC))wglGetExtString)(wglGetCurrentDC());

// If That Failed, Try Standard Opengl Extensions String
if (supported == NULL)
supported = (char*)glGetString(GL_EXTENSIONS);

// If That Failed Too, Must Be No Extensions Supported
if (supported == NULL)
return false;

// Begin Examination At Start Of String, Increment By 1 On False Match
for (const char* p = supported; ; p++)
{
// Advance p Up To The Next Possible Match
p = strstr(p, extension);

if (p == NULL)
return false; // No Match

// Make Sure That Match Is At The Start Of The String Or That
// The Previous Char Is A Space, Or Else We Could Accidentally
// Match "wglFunkywglExtension" With "wglExtension"

// Also, Make Sure That The Following Character Is Space Or NULL
// Or Else "wglExtensionTwo" Might Match "wglExtension"
if ((p==supported || p[-1]==' ') && (p[extlen]=='\0' || p[extlen]==' '))
return true; // Match
}
}


   
The next function is the gist of the program itself. Basically, we're going to query if our arb extention is supported on the system. From there, we'll move on to querying our device context to find out the scope of our multisample. Again... let's just jump into the code.   
   

bool InitMultisample(HINSTANCE hInstance,HWND hWnd,PIXELFORMATDESCRIPTOR pfd){   // See If The String Exists In WGL! if (!WGLisExtensionSupported("WGL_ARB_multisample")) {  arbMultisampleSupported=false;  return false; }
// Get Our Pixel Format
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB =
(PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");

if (!wglChoosePixelFormatARB)
{
// We Didn't Find Support For Multisampling, Set Our Flag And Exit Out.
arbMultisampleSupported=false;
return false;
}

// Get Our Current Device Context. We Need This In Order To Ask The OpenGL Window What Attributes We Have
HDC hDC = GetDC(hWnd);

int pixelFormat;
bool valid;
UINT numFormats;
float fAttributes[] = {0,0};

// These Attributes Are The Bits We Want To Test For In Our Sample
// Everything Is Pretty Standard, The Only One We Want To
// Really Focus On Is The SAMPLE BUFFERS ARB And WGL SAMPLES
// These Two Are Going To Do The Main Testing For Whether Or Not
// We Support Multisampling On This Hardware
int iAttributes[] = { WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
WGL_COLOR_BITS_ARB,24,
WGL_ALPHA_BITS_ARB,8,
WGL_DEPTH_BITS_ARB,16,
WGL_STENCIL_BITS_ARB,0,
WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
WGL_SAMPLES_ARB, 4 , // Check For 4x Multisampling
0,0};

// First We Check To See If We Can Get A Pixel Format For 4 Samples
valid = wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats);

// if We Returned True, And Our Format Count Is Greater Than 1
if (valid && numFormats >= 1)
{
arbMultisampleSupported = true;
arbMultisampleFormat = pixelFormat;
return arbMultisampleSupported;
}

// Our Pixel Format With 4 Samples Failed, Test For 2 Samples
iAttributes[19] = 2;
valid = wglChoosePixelFormatARB(hDC,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
if (valid && numFormats >= 1)
{
arbMultisampleSupported = true;
arbMultisampleFormat = pixelFormat;
return arbMultisampleSupported;
}

// Return The Valid Format
return arbMultisampleSupported;
}


   
Now that we've gotten our query code done, we have to modify how our window creation works. First, we have to include our arb_multisample.h file, along with moving the destroywindow and createwindow prototypes up to the top of the file.   
   

#include <windows.h>        // Header File For The Windows Library#include <gl/gl.h>        // Header File For The OpenGL32 Library#include <gl/glu.h>        // Header File For The GLu32 Library#include "NeHeGL.h" // Header File For The NeHeGL Basecode
// ROACH
#include "ARB_MULTISAMPLE.h"

BOOL DestroyWindowGL (GL_Window* window);
BOOL CreateWindowGL (GL_Window* window);
// ENDROACH


   
This next bit needs to be added inside the CreateWindowGL function, the original code is left, with where you need to make modifications. Basically, we're doing a bit of overkill to get the job done. We can't request the pixel format (to query the multisampling) until we've created a window. But we can't create a FSAA screen, unless we know the pixel format will support it. Kinda the chicken and the egg thing. So, what I've done, is a little 2 pass system; We create the window, query the pixelFormat, then destroy/remake the window if multisampling is supported. Kinda nifty...   
   

 window->hDC = GetDC (window->hWnd);     // Grab A Device Context For This Window if (window->hDC == 0)       // Did We Get A Device Context? {  // Failed  DestroyWindow (window->hWnd);     // Destroy The Window  window->hWnd = 0;      // Zero The Window Handle  return FALSE;       // Return False }
// ROACH
// Our First Pass, Multisampling Hasn't Been Created Yet, So We Create A Window Normally
// If It Is Supported, Then We're On Our Second Pass
// That Means We Want To Use Our Pixel Format For Sampling
// So Set PixelFormat To arbMultiSampleformat Instead
if(!arbMultisampleSupported)
{
PixelFormat = ChoosePixelFormat (window->hDC, &pfd); // Find A Compatible Pixel Format
if (PixelFormat == 0) // Did We Find A Compatible Format?
{
// Failed
ReleaseDC (window->hWnd, window->hDC); // Release Our Device Context
window->hDC = 0; // Zero The Device Context
DestroyWindow (window->hWnd); // Destroy The Window
window->hWnd = 0; // Zero The Window Handle
return FALSE; // Return False
}
}
else
{
PixelFormat = arbMultisampleFormat;
}
//ENDROACH

if (SetPixelFormat (window->hDC, PixelFormat, &pfd) == FALSE) // Try To Set The Pixel Format
{
// Failed
ReleaseDC (window->hWnd, window->hDC); // Release Our Device Context
window->hDC = 0; // Zero The Device Context
DestroyWindow (window->hWnd); // Destroy The Window
window->hWnd = 0; // Zero The Window Handle
return FALSE; // Return False
}


   
Now, the window has been created, so we have a valid handle to query for Multisample support. If we find it's supported, we destroy this window, and call CreateWindowGL again, but with the new pixel format.   
   

// Make The Rendering Context Our Current Rendering Context if (wglMakeCurrent (window->hDC, window->hRC) == FALSE) {  // Failed  wglDeleteContext (window->hRC);     // Delete The Rendering Context  window->hRC = 0;      // Zero The Rendering Context  ReleaseDC (window->hWnd, window->hDC);    // Release Our Device Context  window->hDC = 0;      // Zero The Device Context  DestroyWindow (window->hWnd);     // Destroy The Window  window->hWnd = 0;      // Zero The Window Handle  return FALSE;       // Return False }

// ROACH
// Now That Our Window Is Created, We Want To Query What Samples Are Available
// We Call Our InitMultiSample Window
// if We Return A Valid Context, We Want To Destroy Our Current Window
// And Create A New One Using The Multisample Interface.
if(!arbMultisampleSupported && CHECK_FOR_MULTISAMPLE)
{
if(InitMultisample(window->init.application->hInstance,window->hWnd,pfd))
{
DestroyWindowGL (window);
return CreateWindowGL(window);
}
}
// ENDROACH

ShowWindow (window->hWnd, SW_NORMAL); // Make The Window Visible
window->isVisible = TRUE;


   
OK, so setup is now complete! We've setup our screen to allow proper depth for multi sampling. Now comes the fun part, actually doing it! Luckily, ARB decided to make multisampling dynamic, so that allows us to turn it on and off with a glEnable / glDisable call.   
   

glEnable(GL_MULTISAMPLE_ARB);
// Render The Scene

glDisable(GL_MULTISAMPLE_ARB);

   
And that's it! As far as the other code for the demo goes, it's a simple rotating effect to show off how nifty this method works. ENJOY!
Colt "MainRoach" McAnlis



--  作者:yugg_03
--  发布时间:7/29/2008 4:36:00 PM

--  
thank you
W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
281.250ms