« 全屏反锯齿 - 多重采样ⅠQuickSort 快速排序的实现 »

全屏反锯齿 - 多重采样Ⅱ

实现无关shader编写的全屏反走样的扩展,全名叫WGL_ARB_multisample。关键字1,ARB,说明它真是扩展(别打~);关键字2,WGL,说明它并非一般的扩展。锯齿,或者,早已把此扩展忘记了吧。本为下篇,有意者可先看上篇,请点:[全屏反锯齿 - 多重采样Ⅰ] 。——ZwqXin.com

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/opengl/antialiasing-multi-sample-2.html

如果你的程序INCLUDE目录里面只有glew.h或者glext.h,你是用不起这个扩展的。它属于ARB委员会的闲置物,因此才会被安排在wglew.h或者wglext.h里的吧。是不是呢?请好心人路过告知。总之,要使用这个扩展,两道。1.程序包含wglew.h,并拥有glew32.lb库文件(与glew.h不同,wglew.h不必要置于gl.h之前,本扩展也没看到需要Initglew()之类的初始化函数);2.程序包含wglext.h......恩,貌似只需要wglext.h就可。

另外,当时发现本扩展存在EXT版本,惊喜之余专门用了一下,发现是无法用的。用opengl extension viewer看了看,本显卡支持的EXT扩展里没有这个的EXT版本,而且貌似其他显卡也没有。只有用WGL_ARB_multisample了。(ARB与EXT是哪门子区别?可求助GOOGLE大神,通俗点说是一个不能直接用,一个可以。见下所述。)

参考NEHE #46,弄了一个类。最初是想封装得好点的,但后来发现这难度大,因为使用该扩展的位置是程序初始化之前——屏幕像素设置,DC,RC设置的地方。好吧,还是先说这个类:

  1. class MultiSample  
  2. {
  3. public:
  4.     MultiSample();
  5.     ~MultiSample();
  6.  
  7.     bool InitMultiSample(HWND hwnd);
  8.     bool ExtentionSurpported(){ return multisampleSupportted;}
  9.  
  10.     int GetMultiSampleFormat(){ return MultiSampleFormat;}
  11.     void SetMultiSample(UINT sample){Multisample = sample; }
  12.  
  13.     UINT GetSupportedFormatNum(){ return numofFormats;}
  14.     int GetSupportedFormats(UINT i){ return i < numofFormats ? pixelformat[i] : 0;}
  15.  
  16. private:
  17.     bool WGLisExtensionSupported(const char *extension);
  18.  
  19.     UINT Multisample;
  20.     bool multisampleSupportted;
  21.     int MultiSampleFormat;
  22.     UINT numofFormats;
  23.     int pixelformat[20];
  24. };

最重要的函数是InitMultiSample(),它完成了“全屏反走样必要的设置”,更准确地说,它给下面列出的私有成员变量赋值了。这些变量中最重要的——也是本扩展所需要的东西和最后给出的东西:输入Multisample,输出MultiSampleFormat。前者就是我们要告诉扩展的采样参数,譬如4代表4X,8代表8X等等(详见[全屏反锯齿 - 多重采样Ⅰ] );后者就是:像素格式。

恩,没错,最后要的就是像素格式。就是我们在初始化OPENGL窗口设置时用来设置渲染窗口像素的模式的那样东西。(详细见[自剖一下自己用的NEHE OpenGL框架(下篇)] 。)作用的话,接下来再探讨,先来看看这个UINT“像素格式”是怎么得到的:

  1. bool MultiSample::InitMultiSample(HWND hwnd)
  2. {
  3.     multisampleSupportted = WGLisExtensionSupported("WGL_ARB_multisample");
  4.  
  5.     if(!multisampleSupportted)return false;
  6.  
  7.     PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
  8.  
  9.     HDC hdc = ::GetDC(hwnd);
  10.  
  11.     float   fAttributes[] = {0,0};
  12.     int iAtributes[] = 
  13.     {
  14.         WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
  15.         WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
  16.         WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
  17.         WGL_COLOR_BITS_ARB, 24,
  18.         WGL_ALPHA_BITS_ARB, 8,
  19.         WGL_DEPTH_BITS_ARB, 16,
  20.         WGL_STENCIL_BITS_ARB, 0,
  21.         WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
  22.         WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
  23.         WGL_SAMPLES_ARB, Multisample,   
  24.         0,0
  25.     };
  26.  
  27.    if(!wglChoosePixelFormatARB(hdc, iAtributes, fAttributes, 20 , pixelformat, &numofFormats))
  28.    {
  29.        multisampleSupportted = false;
  30.        return false;
  31.    }
  32.    if(numofFormats <= 0)return false;
  33.  
  34.    MultiSampleFormat = pixelformat[0];
  35.    return true;
  36. }

本函数接受一个表征窗口的句柄,并接下来从该句柄得出绘图环境DC。这是本扩展第2个需要知道的东西:产生新像素格式必然要先了解绘图环境。multisampleSupportted返回你的显卡是否支持本WGL扩展,WGLisExtensionSupported是直接从NEHE46那里拿来的。然后我们来获取本ARB扩展功能函数:wglChoosePixelFormatARB的“实体”,获取后我们就能用了。用的方法无非是输入-输出。我们输入绘图环境DC,还有一个iAtributes数组指针,一个fAttributes数组指针。fAttributes只是单纯的0数组,我不知道其他值会有什么效果,只知道这里只需要float类型的0数组就够了,或者NULL,我尝试过,也可以的。至于iAtributes,可是戏玉!

它很明显在设置类似PIXELFORMATDESCRIPTOR(像素描述器,见[自剖一下自己用的NEHE OpenGL框架(下篇))的东西,告诉wglChoosePixelFormatARB函数,我们所需要的像素格式的模样,譬如彩色通道位数,是否使用双缓冲等等(注意数组内是:“属性,值”的格式),然后由它去设置新的像素格式。值得注意的是WGL_SAMPLE_BUFFERS_ARB和WGL_SAMPLES_ARB。根据opengl官方给出的的本扩展的文档,前者表明新增一个缓冲(缓存),叫“多重采样缓冲”(如何?),该缓冲启用下,传统5大缓冲中的3个将会失效,只有颜色缓冲和辅助缓冲能继续。是不是因为多重采样缓冲其实包含了这些了呢?文档没兴致看仔细,算了。启用该缓冲,并把WGL_SAMPLES_ARB设置为我们需要的采样参数后,就能用它来描述像素格式了。

像素格式,其实表面上看就是一个简单的UINT,真正的含义只有SetPixelFormat函数才知道。wglChoosePixelFormatARB接收输入后,输出满足要求的像素格式(用UINT表示),我把这些满足的像素格式扔进成员数组pixelformat里了,并规定最多接收20个,numofFormats是实际满足的个数。我做过试验的,其他像素属性按上,1X的不算,2X下共有16种像素格式可以满足(UINT为20,24,28~64);3X~4X,共有12种满足;5X~8X,8种;9X~16X,4种;16X以上,0种。由此一来说明本显卡确实最多支持16X,二来印证上篇所言,只有2的N次方的采样参数有用(中间那些都被默认向大者靠了)。

函数最后让输出的像素格式取第一个有用的(最准确满足的)像素格式。

看MainFrame里的像素格式设置部分(OnCreateClient函数),在两个地方作了代码添加:

  1. BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
  2. {
  3. //.........常规的像素格式描述器设置
  4. //.........
  5.     if(!SecondWindowCreate)
  6.     {
  7.         if ( !( PixelFormat = ChoosePixelFormat ( m_hDC, &pfd ) ) ) {
  8.             KillGLWindow ();    
  9.             MessageBox ( "Can't Find A Suitable PixelFormat.", "ERROR", MB_OK | MB_ICONEXCLAMATION );
  10.             return FALSE;                                               
  11.         }
  12.     }
  13.     else    
  14.         PixelFormat = MultiSampleFormat;
  15.         
  16.         if ( !SetPixelFormat ( m_hDC, PixelFormat, &pfd ) ){
  17.             KillGLWindow ();
  18.             MessageBox ( "Can't Set The PixelFormat.", "ERROR", MB_OK | MB_ICONEXCLAMATION );
  19.             return FALSE;                                               
  20.         }
  21.  
  22. //.........DC,RC关联设置
  23. //..........
  24.     if(SecondWindowCreate)
  25.     {
  26.         if (MessageBox("渲染一堆三角形(YES)  渲染几个一般几何模型(NO)", 
  27.                        "RederTest Selection",MB_YESNO|MB_ICONQUESTION)==IDYES)
  28.             RenderOb=FALSE;     
  29.         else 
  30.                         RenderOb = TRUE;
  31.     }
  32.     else
  33.     {
  34.         MutiSampleSelectDlg MSdLG;
  35.         if(MSdLG.DoModal() == IDOK)
  36.         {
  37.          MultiSample = MSdLG.GetMultiSample();
  38.          Multisam.SetMultiSample(MultiSample);
  39.  
  40.          Multisam.InitMultiSample(m_hWnd);
  41.          PixelFormat = Multisam.GetMultiSampleFormat();
  42.          return bRet;
  43.         }
  44.     }
  45. //.....
  46. //....初始化....
  47. }   

SecondWindowCreate是CMAINFRAME的成员函数,顾名思义,就是表明OnCreateClient函数要执行两次。SecondWindowCreate为FALSE的时候,按照原设置来设置像素格式。然后关联好RC后,调用InitMultiSample,接收当前opengl渲染窗口(原窗口)句柄和从某对话框(我弄的)得来的采样参数,得出的像素格式存入CMAINFRAME另一成员函数PixelFormat里。不必初始化直接返回。

注意,返回后,我会保存生成的PixelFormat,然后把这个刚刚建立的窗口(原窗口)——连显示都不让它显示,直接让它夭折。重新建一个CMAINFRAME对象,生成新窗口,让SecondWindowCreate为TRUE,并把刚才的PixelFormat交给它。好了,第二次来到OnCreateClient,直接用支持多重采样的PixelFormat去SetPixelFormat(),一路下去。

这是因为只有原来的RC建立好后,wglChoosePixelFormatARB才能知道当前有哪些像素格式适合吧。是不方便但也没办法。对了,最后再看一下夭折-再生的处理在我的NEHE框架里怎么改吧,是在APP类的InitInstance函数里(不改动前的,可见[自剖一下自己用的NEHE OpenGL框架(中篇)] ):

  1. BOOL CAntialiasing_MultiSampleApp::InitInstance()
  2. {
  3.     // Standard initialization
  4.  
  5.     // Change the registry key under which our settings are stored.
  6.  
  7.     // TODO: You should modify this string to be something appropriate
  8.     // such as the name of your company or organization.
  9.     SetRegistryKey(_T("Local AppWizard-Generated Applications"));
  10.  
  11.     // To create the main window, this code creates a new frame window
  12.     // object and then sets it as the application's main window object.
  13.  
  14.     m_pMainWnd = NULL;
  15.     CMainFrame* pFrame = new CMainFrame;
  16.     pFrame->CreateWindowSecondTime(false);
  17.  
  18.     if (!pFrame->Create(NULL,"MFC OpenGL"))
  19.         return FALSE;
  20.  
  21.     int MultiSampleFormat = pFrame->GetMultiSampleFormat();
  22.  
  23.     pFrame->DestroyWindow();
  24.     //delete pFrame;
  25.  
  26.     pFrame = new CMainFrame;
  27.     pFrame->CreateWindowSecondTime(true);
  28.     pFrame->SetMultiSampleFormat(MultiSampleFormat);
  29.  
  30.     if (!pFrame->Create(NULL,"MFC OpenGL"))
  31.         return FALSE;
  32.  
  33.     m_pMainWnd = pFrame;
  34.     pFrame->ShowWindow(m_nCmdShow);
  35.     pFrame->UpdateWindow();
  36.  
  37.     return TRUE;
  38. }

在下对MFC不熟,中间那里为什么无法DELETE掉呢?好了,看结果吧:

 全屏反锯齿  ---www.zwqxin.com
(场景1是用了NEHE #46里的旋转矩形场景。设置为1X时,可见基本跟不设置差不多的,边缘锯齿严重)
全屏反锯齿  ---www.zwqxin.com
(设置为4X时,边缘锯齿还是明显的,但稍微不那么严重了吧)
全屏反锯齿  ---www.zwqxin.com
(设置为16X时,边缘锯齿效应基本上消除了哦)
全屏反锯齿  ---www.zwqxin.com
(16X时,再来一张,很好哦。效率呢?这么多次采样平滑,肯定会下降的,但因为场景简单+偶家显卡比较好,所以FPS没有下降)
全屏反锯齿  ---www.zwqxin.com
(自家的场景2,模型边缘,在1X下的严重锯齿)
全屏反锯齿  ---www.zwqxin.com
(自家的场景2,模型边缘,在16X下的锯齿现象消解)


本日志demo放出:AntialiasingMultiSampleDemobYzwqxin.rar
DEMO使用了扩展,需要下载glew库到指定目录,下载见此:OpenGL常用的库

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/opengl/antialiasing-multi-sample-2.html

  • quote 1.croma
  • 太好了 我今天剛好在試做 OpenGL Super Bible 這個章節 可以跟大家討論一下

    >它屬於ARB委員會的閒置物,因此才會被安排在wglew.h或者wglext.h裡的吧。是不是呢?請好心人路過告知

    其實 wglew.h 放的是跟 windows 有關係的 ARB 函數延伸 因為微軟的私心所以這方面的支援很差 畢竟 linux or Mac 上用 glew 是用不到這些啟動函數的所以沒有 linux 與 mac 的對應函數


    >在下對MFC不熟,中間那裡為什麼無法DELETE掉呢?

    @@" 怪了我記得 CMainFrame 是不能 new 的 你把 protected 拿掉了嗎? 我今天用 CWnd 的物件做是可以的 我把發到板主的信箱摟 我們討論一下
  • 2009-10-23 20:27:22 回复该留言
  • quote 2.kigskylgt
  • 哇好文啊。。。。我就对那个为什么要创建2次销毁一次不是很理解 看完以后豁然开朗啊。而且吧其他函数解释的那么好 真是好文好文。牛人啊~~~
  • 2009-11-23 21:34:40 回复该留言
  • quote 3.d3
  • 请问我怎么运行不了你的程序啊?对话框选了倍数后,就结束程序了,该如何?
    谢谢!我的邮箱linghaoduan@yahoo.cn
  • 2010-9-9 14:33:05 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2013 ZwqXin. All Rights Reserved. Theme edited from ipati.