实现无关shader编写的全屏反走样的扩展,全名叫WGL_ARB_multisample。关键字1,ARB,说明它真是扩展(别打~);关键字2,WGL,说明它并非一般的扩展。锯齿,或者,早已把此扩展忘记了吧。本为下篇,有意者可先看上篇,
如果你的程序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设置的地方。好吧,还是先说这个类:
- class MultiSample
- {
- public:
- MultiSample();
- ~MultiSample();
- bool InitMultiSample(HWND hwnd);
- bool ExtentionSurpported(){ return multisampleSupportted;}
- int GetMultiSampleFormat(){ return MultiSampleFormat;}
- void SetMultiSample(UINT sample){Multisample = sample; }
- UINT GetSupportedFormatNum(){ return numofFormats;}
- int GetSupportedFormats(UINT i){ return i < numofFormats ? pixelformat[i] : 0;}
- private:
- bool WGLisExtensionSupported(const char *extension);
- UINT Multisample;
- bool multisampleSupportted;
- int MultiSampleFormat;
- UINT numofFormats;
- int pixelformat[20];
- };
最重要的函数是InitMultiSample(),它完成了“全屏反走样必要的设置”,更准确地说,它给下面列出的私有成员变量赋值了。这些变量中最重要的——也是本扩展所需要的东西和最后给出的东西:输入Multisample,输出MultiSampleFormat。前者就是我们要告诉扩展的采样参数,譬如4代表4X,8代表8X等等(详见[全屏反锯齿 - 多重采样Ⅰ] );后者就是:像素格式。
恩,没错,最后要的就是像素格式。就是我们在初始化OPENGL窗口设置时用来设置渲染窗口像素的模式的那样东西。(详细见[自剖一下自己用的NEHE OpenGL框架(下篇)] 。)作用的话,接下来再探讨,先来看看这个UINT“像素格式”是怎么得到的:
- bool MultiSample::InitMultiSample(HWND hwnd)
- {
- multisampleSupportted = WGLisExtensionSupported("WGL_ARB_multisample");
- if(!multisampleSupportted)return false;
- PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
- HDC hdc = ::GetDC(hwnd);
- float fAttributes[] = {0,0};
- int iAtributes[] =
- {
- 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, Multisample,
- 0,0
- };
- if(!wglChoosePixelFormatARB(hdc, iAtributes, fAttributes, 20 , pixelformat, &numofFormats))
- {
- multisampleSupportted = false;
- return false;
- }
- if(numofFormats <= 0)return false;
- MultiSampleFormat = pixelformat[0];
- return true;
- }
本函数接受一个表征窗口的句柄,并接下来从该句柄得出绘图环境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函数),在两个地方作了代码添加:
- BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
- {
- //.........常规的像素格式描述器设置
- //.........
- if(!SecondWindowCreate)
- {
- if ( !( PixelFormat = ChoosePixelFormat ( m_hDC, &pfd ) ) ) {
- KillGLWindow ();
- MessageBox ( "Can't Find A Suitable PixelFormat.", "ERROR", MB_OK | MB_ICONEXCLAMATION );
- return FALSE;
- }
- }
- else
- PixelFormat = MultiSampleFormat;
- if ( !SetPixelFormat ( m_hDC, PixelFormat, &pfd ) ){
- KillGLWindow ();
- MessageBox ( "Can't Set The PixelFormat.", "ERROR", MB_OK | MB_ICONEXCLAMATION );
- return FALSE;
- }
- //.........DC,RC关联设置
- //..........
- if(SecondWindowCreate)
- {
- if (MessageBox("渲染一堆三角形(YES) 渲染几个一般几何模型(NO)",
- "RederTest Selection",MB_YESNO|MB_ICONQUESTION)==IDYES)
- RenderOb=FALSE;
- else
- RenderOb = TRUE;
- }
- else
- {
- MutiSampleSelectDlg MSdLG;
- if(MSdLG.DoModal() == IDOK)
- {
- MultiSample = MSdLG.GetMultiSample();
- Multisam.SetMultiSample(MultiSample);
- Multisam.InitMultiSample(m_hWnd);
- PixelFormat = Multisam.GetMultiSampleFormat();
- return bRet;
- }
- }
- //.....
- //....初始化....
- }
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框架(中篇)] ):
- BOOL CAntialiasing_MultiSampleApp::InitInstance()
- {
- // Standard initialization
- // Change the registry key under which our settings are stored.
- // TODO: You should modify this string to be something appropriate
- // such as the name of your company or organization.
- SetRegistryKey(_T("Local AppWizard-Generated Applications"));
- // To create the main window, this code creates a new frame window
- // object and then sets it as the application's main window object.
- m_pMainWnd = NULL;
- CMainFrame* pFrame = new CMainFrame;
- pFrame->CreateWindowSecondTime(false);
- if (!pFrame->Create(NULL,"MFC OpenGL"))
- return FALSE;
- int MultiSampleFormat = pFrame->GetMultiSampleFormat();
- pFrame->DestroyWindow();
- //delete pFrame;
- pFrame = new CMainFrame;
- pFrame->CreateWindowSecondTime(true);
- pFrame->SetMultiSampleFormat(MultiSampleFormat);
- if (!pFrame->Create(NULL,"MFC OpenGL"))
- return FALSE;
- m_pMainWnd = pFrame;
- pFrame->ShowWindow(m_nCmdShow);
- pFrame->UpdateWindow();
- return TRUE;
- }
在下对MFC不熟,中间那里为什么无法DELETE掉呢?好了,看结果吧:
(场景1是用了NEHE #46里的旋转矩形场景。设置为1X时,可见基本跟不设置差不多的,边缘锯齿严重)
(设置为4X时,边缘锯齿还是明显的,但稍微不那么严重了吧)
(设置为16X时,边缘锯齿效应基本上消除了哦)
(16X时,再来一张,很好哦。效率呢?这么多次采样平滑,肯定会下降的,但因为场景简单 偶家显卡比较好,所以FPS没有下降)
(自家的场景2,模型边缘,在1X下的严重锯齿)
(自家的场景2,模型边缘,在16X下的锯齿现象消解)