在用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分钟来弄清楚这道题到底是在问什么。”
---爱因斯坦?