在用VS ide新建win32工程的选项中,一个叫“Win32控制台应用程序”,另一个叫“Win32应用程序”,其区别的细节这里跳过不讲,最显著的就是win32控制台没有GUI窗口,只弹出一个命令行黑框;而win32应用程序有GUI窗口,但没黑框。
但是!如以下demo,vtk example中统统都有命令行黑窗口,又有GUI显示窗口。都是win32控制台工程,它的GUI显示窗口是从哪儿来的呢?
int main(int , char *[]) { //↓↓↓↓↓↓↓↓↓↓ double p0[3] = {0.0, 0.0, 0.0}; double p1[3] = {1.0, 0.0, 0.0}; double p2[3] = {1.0, 1.0, 0.0}; double p3[3] = {0.0, 1.0, 0.0}; // Add the points to a vtkPoints object vtkPoints *points = vtkPoints::New(); ... ... ... ... //↑↑↑↑↑以上都是数据结构,未见OPENGL相关绘制内容↑↑↑↑ // Setup actor and mapper // 反射机制 在win32下自动反射到vtkOpenGLPolyDataMapper,这里有传统的OPENGL代码 vtkPolyDataMapper*mapper = vtkPolyDataMapper::New(); mapper->SetInput(polydata); vtkActor*actor = vtkActor::New(); actor->SetMapper(mapper); // Setup render window, renderer, and interactor vtkRenderer*renderer = vtkRenderer::New(); vtkRenderWindow*renderWindow = vtkRenderWindow::New(); renderWindow->SetWindowName("Quad"); renderWindow->AddRenderer(renderer); vtkRenderWindowInteractor*renderWindowInteractor = vtkRenderWindowInteractor::New(); renderWindowInteractor->SetRenderWindow(renderWindow); renderer->AddActor(actor); renderWindow->Render(); renderWindowInteractor->Start(); return EXIT_SUCCESS; }
最初,我一直觉得理所当然,因为VTK的底层是OpenGL嘛,当然是OpenGL的窗口啦,那丑不拉几的样子,一看就是OpenGL的窗口,网上所有的OpenGL教程都长这个样子。随着慢慢对OpenGL的了解,发现我又一次被表象蒙蔽了,事实并不是看到的这样,只是长的像而已。。。凡是用到OpenGL显示的程序,都会绑定另外一个包含UI的框架,类似GLUT,Qt,MFC,FLTK,OpenGL自己是不能显示出来的!!!必须绑定一个UI!!!必须绑定一个UI!!!必须绑定一个UI!!!NND,鄙人愚笨,被忽悠这么多年,一直以为OpenGL自己就能绘制窗口。
OpenGL只负责绘制场景,并不会负责窗口,窗口是另外一个框架的。想要在桌面上看到OpenGL场景,就必须与一个绘制UI的框架绑定!
下面看看vtk example为什么能在Win32控制台应用程序下弹出窗口+命令行:
找到类vtkWin32OpenGLRenderWindow,根据名字就可以知道这个类的功能:vtk + Win32 + OpenGL + RenderWindow,可以理解为vtk在win32下将OpenGL与RenderWindow绑定!
// Initialize the window for rendering. void vtkWin32OpenGLRenderWindow::WindowInitialize (void) { int x, y, width, height; GLenum type; static int count = 1; char *windowName; x = ((this->Position[0] >= 0) ? this->Position[0] : 5); y = ((this->Position[1] >= 0) ? this->Position[1] : 5); width = ((this->Size[0] > 0) ? this->Size[0] : 300); height = ((this->Size[1] > 0) ? this->Size[1] : 300); // create our own window if not already set if (!this->WindowId) { WNDCLASS wndClass; int len = strlen( "Visualization Toolkit - Win32OpenGL #") + (int)ceil( (double) log10( (double)(count+1) ) ) + 1; windowName = new char [ len ]; sprintf(windowName,"Visualization Toolkit - Win32OpenGL #%i",count++); this->SetWindowName(windowName); delete [] windowName; // has the class been registered ? if (!GetClassInfo(this->ApplicationInstance,"vtkOpenGL",&wndClass)) { wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = vtkWin32OpenGLRenderWindow::WndProc; wndClass.cbClsExtra = 0; wndClass.hInstance = this->ApplicationInstance; wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); wndClass.lpszMenuName = NULL; wndClass.lpszClassName = "vtkOpenGL"; // vtk doesn't use these extra 4 bytes, but app writers // may want them, so we provide them. wndClass.cbWndExtra = 4; RegisterClass(&wndClass); } // use real mutex vtkWin32OpenGLRenderWindow::WindowMutex->Lock(); if (vtkWin32OpenGLRenderWindow::TempPointerToThis) { vtkErrorMacro("Two windows being created at the same time"); } vtkWin32OpenGLRenderWindow::TempPointerToThis = this; /* create window */ if (this->ParentId) { this->WindowId = CreateWindow( "vtkOpenGL", this->WindowName, WS_CHILD | WS_CLIPCHILDREN /*| WS_CLIPSIBLINGS*/, x, y, width, height, this->ParentId, NULL, this->ApplicationInstance, NULL); } else { this->WindowId = CreateWindow( "vtkOpenGL", this->WindowName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN /*| WS_CLIPSIBLINGS*/, x,y, width+2*GetSystemMetrics(SM_CXFRAME), height+2*GetSystemMetrics(SM_CYFRAME) +GetSystemMetrics(SM_CYCAPTION), NULL, NULL, this->ApplicationInstance, NULL); } vtkWin32OpenGLRenderWindow::TempPointerToThis = NULL; vtkWin32OpenGLRenderWindow::WindowMutex->Unlock(); if (!this->WindowId) { vtkErrorMacro("Could not create window, error: " << GetLastError()); return; } // extract the create info /* display window */ ShowWindow(this->WindowId, SW_SHOW); //UpdateWindow(this->WindowId); this->OwnWindow = 1; } else { SetWindowLong(this->WindowId,GWL_USERDATA,(LONG)this); this->DeviceContext = GetDC(this->WindowId); if (this->StereoCapableWindow) { this->SetupPixelFormat(this->DeviceContext, PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER | PFD_STEREO, this->GetDebug(), 32, 32); } else { this->SetupPixelFormat(this->DeviceContext, PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER, this->GetDebug(), 32, 32); } this->SetupPalette(this->DeviceContext); this->ContextId = wglCreateContext(this->DeviceContext); wglMakeCurrent(this->DeviceContext, this->ContextId); this->OpenGLInit(); } this->Mapped = 1; } else { wglMakeCurrent(this->DeviceContext, this->ContextId); // hsr this->OpenGLInit(); } // set the DPI this->SetDPI(GetDeviceCaps(this->DeviceContext, LOGPIXELSY)); }
上面这段代码就是创建win32窗口的方法,能清晰的看到
1、WNDCLASS,CreateWindow,SetupPixelFormat,
窗口win32应用窗口的接口函数,也是常见的标准方法。
2、wglCreateContext,wglMakeCurrent。
调用windows系统中的gl接口。
3、OpenGLInit初始化函数
void vtkWin32OpenGLRenderWindow::OpenGLInit() { glMatrixMode( GL_MODELVIEW ); glDepthFunc( GL_LEQUAL ); glEnable( GL_DEPTH_TEST ); glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); ....... }
解释到这里,应该算比较清楚了,vtk在“Win32控制台应用程序”中,调用win32应用程序窗口窗口的接口,自己画了一个UI,然后把OpenGL绑上去。
貌似有一段时间没写blog了~
看破游戏规则,回归核心价值。
“如果给我1个小时解答一道决定我生死的问题,我会花55分钟来弄清楚这道题到底是在问什么。”
---爱因斯坦?