出于我自己的娱乐,我一直在用Win32用C ++创建一个3D图形程序,该程序不使用任何浮点数。由于某些原因,有时在更新窗口时窗口会闪烁。我认为这是由窗口更新导致的,该更新是在删除了先前在窗口中显示的内容之后以及在绘制接下来要绘制的内容之前进行的。我发现了几个网页和问题,这些问题的答案似乎是同一问题,但解决方案不起作用。
一些示例{
请勿使用CS_HREDRAW | CS_VREDRAW。 (无明显效果)
返回窗口消息“ WM_ERASEBKGND”的1。 (我需要删除以前绘制的内容。这通过防止擦除来停止闪烁。)
代替擦除,在WM_PAINT的情况下绘制矩形。 (这使情况更糟)
}
我实际上如何防止窗口闪烁?
// compile with: /D_UNICODE /DUNICODE /DWIN32 /D_WINDOWS /c
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
boolean Draw;
//Trigonometry tables in increments of 1/256 of a rotation and multiplied by 65536
const int Total=3,Sine[256]={0,1608,3216,4821,6424,8022,9616,11204,12785,14359,15924,17479,19024,20557,22078,23586,25080,26558,28020,29466,30893,32303,33692,35062,36410,37736,39040,40320,41576,42806,44011,45190,46341,47464,48559,49624,50660,51665,52639,53581,54491,55368,56212,57022,57798,58538,59244,59914,60547,61145,61705,62228,62714,63162,63572,63944,64277,64571,64827,65043,65220,65358,65457,65516,65536,65516,65457,65358,65220,65043,64827,64571,64277,63944,63572,63132,62714,62228,61705,61145,60547,59914,59244,58538,57798,57022,56212,55368,54491,53581,52639,51665,50660,49624,48559,47464,46341,45190,44011,42806,41576,40320,39040,37736,36410,35062,33692,32303,30893,29466,28020,26558,25080,23586,22078,20557,19024,17479,15924,14359,12785,11204,9616,8022,6424,4821,3216,1608,0,-1608,-3216,-4821,-6424,-8022,-9616,-11204,-12785,-14359,-15924,-17479,-19024,-20557,-22078,-23586,-25080,-26558,-28020,-29466,-30893,-32303,-33692,-35062,-36410,-37736,-39040,-40320,-41576,-42806,-44011,-45190,-46341,-47464,-48559,-49624,-50660,-51665,-52639,-53581,-54491,-55368,-56212,-57022,-57798,-58538,-59244,-59914,-60547,-61145,-61705,-62228,-62714,-63162,-63572,-63944,-64277,-64571,-64827,-65043,-65220,-65358,-65457,-65516,-65536,-65516,-65457,-65358,-65220,-65043,-64827,-64571,-64277,-63944,-63572,-63162,-62714,-62228,-61705,-61145,-60547,-59914,-59244,-58538,-57798,-57022,-56212,-55368,-54491,-53581,-52639,-51665,-50660,-49624,-48559,-47464,-46341,-45190,-44011,-42806,-41576,-40320,-39040,-37736,-36410,-35062,-33692,-32303,-30893,-29466,-28020,-26558,-25080,-23586,-22078,-20557,-19024,-17479,-15924,-14359,-12785,-11204,-9616,-8022,-6424,-4821,-3216,-1608},Cosine[256]={65536,65516,65457,65358,65220,65043,64827,64571,64277,63944,63572,63132,62714,62228,61705,61145,60547,59914,59244,58538,57798,57022,56212,55368,54491,53581,52639,51665,50660,49624,48559,47464,46341,45190,44011,42806,41576,40320,39040,37736,36410,35062,33692,32303,30893,29466,28020,26558,25080,23586,22078,20557,19024,17479,15924,14359,12785,11204,9616,8022,6424,4821,3216,1608,0,-1608,-3216,-4821,-6424,-8022,-9616,-11204,-12785,-14359,-15924,-17479,-19024,-20557,-22078,-23586,-25080,-26558,-28020,-29466,-30893,-32303,-33692,-35062,-36410,-37736,-39040,-40320,-41576,-42806,-44011,-45190,-46341,-47464,-48559,-49624,-50660,-51665,-52639,-53581,-54491,-55368,-56212,-57022,-57798,-58538,-59244,-59914,-60547,-61145,-61705,-62228,-62714,-63162,-63572,-63944,-64277,-64571,-64827,-65043,-65220,-65358,-65457,-65516,-65536,-65516,-65457,-65358,-65220,-65043,-64827,-64571,-64277,-63944,-63572,-63162,-62714,-62228,-61705,-61145,-60547,-59914,-59244,-58538,-57798,-57022,-56212,-55368,-54491,-53581,-52639,-51665,-50660,-49624,-48559,-47464,-46341,-45190,-44011,-42806,-41576,-40320,-39040,-37736,-36410,-35062,-33692,-32303,-30893,-29466,-28020,-26558,-25080,-23586,-22078,-20557,-19024,-17479,-15924,-14359,-12785,-11204,-9616,-8022,-6424,-4821,-3216,-1608,0,1608,3216,4821,6424,8022,9616,11204,12785,14359,15924,17479,19024,20557,22078,23586,25080,26558,28020,29466,30893,32303,33692,35062,36410,37736,39040,40320,41576,42806,44011,45190,46341,47464,48559,49624,50660,51665,52639,53581,54491,55368,56212,57022,57798,58538,59244,59914,60547,61145,61705,62228,62714,63162,63572,63944,64277,64571,64827,65043,65220,65358,65457,65516};
int HorizontalAxis,VerticalAxis,SemiHorizontalAxis,SemiVerticalAxis,Speed=1,Phi=64,Theta,VelX,VelY,VelZ,SinTheta,CosTheta=65536,SinPhi,CosPhi=-65536,RegX,RegY,RegZ,Quadrilaterals[3][6]={{0,1,2,3,1,0},{0,3,4,5,0,1},{6,7,8,9,1,2}};
LONG64 AxAv,X,Y,Z,XCoordinates[10]={-5,-5,5,5,5,-5,60,60,120,120},YCoordinates[10]={-5,5,5,-5,-5,-5,80,140,140,80},ZCoordinates[10]={10,10,10,10,30,30,100,100,200,200},q$,w$,e$,r$,t$,y$,u$,i$;
POINT Points[3][4]={{{0,0},{0,0},{0,0},{0,0}}};
RECT WindowRect;
const HPEN Pens[2]={CreatePen(PS_NULL,0,0),CreatePen(PS_SOLID,1,RGB(0,0,0))};
const HBRUSH Brushes[3]={CreateSolidBrush(RGB(0,0,0)),CreateSolidBrush(RGB(128,128,128)),CreateSolidBrush(RGB(255,255,255))};
HINSTANCE hInst;
HWND hWnd;
PAINTSTRUCT ps;
HDC hdc;
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam){
switch(message){
case WM_KEYDOWN:
if(GetAsyncKeyState(16)){Speed=1+3*Speed/2;}
if(GetAsyncKeyState(17)){
Speed=2*Speed/3-1;
if(Speed<1){Speed=1;}
}if(GetAsyncKeyState(82)){
X=0;Y=0;Z=0;Theta=0;Phi=0;RegX=0;RegY=0;RegZ=0;
SinTheta=0;CosTheta=65536;SinPhi=0;CosPhi=65536;
}return 0;
case WM_SIZE:
GetWindowRect(hWnd,&WindowRect);
HorizontalAxis=WindowRect.right-WindowRect.left;
SemiHorizontalAxis=HorizontalAxis/2;
VerticalAxis=WindowRect.bottom-WindowRect.top-100;
SemiVerticalAxis=VerticalAxis/2;
AxAv=(HorizontalAxis+VerticalAxis)/2;
return 0;
case WM_PAINT:
hdc=BeginPaint(hWnd,&ps);
if(GetAsyncKeyState(83)){
if(Phi>0){
Phi--;
SinPhi=Cosine[Phi];CosPhi=-Sine[Phi];
}
}if(GetAsyncKeyState(87)){
if(Phi<128){
Phi++;
SinPhi=Cosine[Phi];CosPhi=-Sine[Phi];
}
}if(GetAsyncKeyState(65)){
Theta--;
if(Theta<0){Theta=255;}
SinTheta=Sine[Theta];CosTheta=Cosine[Theta];
}if(GetAsyncKeyState(68)){
Theta++;
if(Theta>255){Theta=0;}
SinTheta=Sine[Theta];CosTheta=Cosine[Theta];
}if(GetAsyncKeyState(39)){VelZ=-SinTheta*Speed;VelX=-CosTheta*Speed;}
if(GetAsyncKeyState(38)){VelZ+=CosTheta*Speed;VelX-=SinTheta*Speed;}
if(GetAsyncKeyState(37)){VelZ+=SinTheta*Speed;VelX+=CosTheta*Speed;}
if(GetAsyncKeyState(40)){VelZ-=CosTheta*Speed;VelX+=SinTheta*Speed;}
if(GetAsyncKeyState(81)){VelY=65536*Speed;}
if(GetAsyncKeyState(69)){VelY=-65536*Speed;}
RegX+=VelX;RegY+=VelY;RegZ+=VelZ;
X+=RegX/65536;Y+=RegY/65536;Z+=RegZ/65536;
RegX%=65536;RegY%=65536;RegZ%=65536;
VelX=0;VelY=0;VelZ=0;
q$=0;
while(q$<Total){
Draw=false;
w$=Quadrilaterals[q$][0];
e$=65536*(XCoordinates[w$]-X);
r$=65536*(YCoordinates[w$]-Y);
t$=65536*(ZCoordinates[w$]-Z);
y$=(CosTheta*e$+SinTheta*t$)/65536;
e$=(CosTheta*t$-SinTheta*e$)/65536;
u$=(CosPhi*r$+SinPhi*e$)/65536;
i$=(CosPhi*e$-SinPhi*r$)/65536;
if(i$<0){
Draw=true;
Points[q$][0].x=SemiHorizontalAxis+AxAv*y$/i$;
Points[q$][0].y=SemiVerticalAxis+AxAv*u$/i$;
}w$=Quadrilaterals[q$][1];
e$=65536*(XCoordinates[w$]-X);
r$=65536*(YCoordinates[w$]-Y);
t$=65536*(ZCoordinates[w$]-Z);
y$=(CosTheta*e$+SinTheta*t$)/65536;
e$=(CosTheta*t$-SinTheta*e$)/65536;
u$=(CosPhi*r$+SinPhi*e$)/65536;
i$=(CosPhi*e$-SinPhi*r$)/65536;
if(i$<0){
Draw=true;
Points[q$][1].x=SemiHorizontalAxis+AxAv*y$/i$;
Points[q$][1].y=SemiVerticalAxis+AxAv*u$/i$;
}w$=Quadrilaterals[q$][2];
e$=65536*(XCoordinates[w$]-X);
r$=65536*(YCoordinates[w$]-Y);
t$=65536*(ZCoordinates[w$]-Z);
y$=(CosTheta*e$+SinTheta*t$)/65536;
e$=(CosTheta*t$-SinTheta*e$)/65536;
u$=(CosPhi*r$+SinPhi*e$)/65536;
i$=(CosPhi*e$-SinPhi*r$)/65536;
if(i$<0){
Draw=true;
Points[q$][2].x=SemiHorizontalAxis+AxAv*y$/i$;
Points[q$][2].y=SemiVerticalAxis+AxAv*u$/i$;
}w$=Quadrilaterals[q$][3];
e$=65536*(XCoordinates[w$]-X);
r$=65536*(YCoordinates[w$]-Y);
t$=65536*(ZCoordinates[w$]-Z);
y$=(CosTheta*e$+SinTheta*t$)/65536;
e$=(CosTheta*t$-SinTheta*e$)/65536;
u$=(CosPhi*r$+SinPhi*e$)/65536;
i$=(CosPhi*e$-SinPhi*r$)/65536;
if(i$<0){
Draw=true;
Points[q$][3].x=SemiHorizontalAxis+AxAv*y$/i$;
Points[q$][3].y=SemiVerticalAxis+AxAv*u$/i$;
}if(Draw){
SelectPen(hdc,Pens[Quadrilaterals[q$][4]]);
SelectBrush(hdc,Brushes[Quadrilaterals[q$][5]]);
Polygon(hdc,Points[q$],4);
}q$++;
}EndPaint(hWnd,&ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:return DefWindowProc(hWnd,message,wParam,lParam);
}
}
int CALLBACK WinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nCmdShow){
GetWindowRect(GetDesktopWindow(),&WindowRect);
HorizontalAxis=WindowRect.right;
SemiHorizontalAxis=HorizontalAxis/2;
VerticalAxis=WindowRect.bottom-100;
SemiVerticalAxis=VerticalAxis/2;
AxAv=(HorizontalAxis+VerticalAxis)/2;
WNDCLASSEX wcex;
wcex.cbSize=sizeof(WNDCLASSEX);
wcex.style=CS_HREDRAW|CS_VREDRAW;
wcex.lpfnWndProc=WndProc;
wcex.cbClsExtra=0;
wcex.cbWndExtra=0;
wcex.hInstance=hInstance;
wcex.hIcon=LoadIcon(hInstance,IDI_APPLICATION);
wcex.hCursor=LoadCursor(NULL,IDC_ARROW);
wcex.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName=NULL;
wcex.lpszClassName=_T("DesktopApp");
wcex.hIconSm=LoadIcon(wcex.hInstance,IDI_APPLICATION);
if(!RegisterClassEx(&wcex)){
MessageBox(NULL,_T("RegisterClassEx failed!"),_T("Something"),NULL);
return 1;
}hInst=hInstance;
hWnd=CreateWindow(_T("DesktopApp"),_T("Something Application"),WS_OVERLAPPEDWINDOW,0,0,HorizontalAxis,VerticalAxis,NULL,NULL,hInstance,NULL);
if(!hWnd){
MessageBox(NULL,_T("CreateWindow failed!"),_T("Something"),NULL);
return 1;
}ShowWindow(hWnd,nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while(GetMessage(&msg,NULL,0,0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
Sleep(10);
InvalidateRect(hWnd,NULL,true);
}return(int)msg.wParam;
}
这是大多数(但不是全部)明显的闪烁的核心:
while(GetMessage(&msg,NULL,0,0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
Sleep(10);
InvalidateRect(hWnd,NULL,true);
}return(int)msg.wParam;
不仅在每个WM_PAINT调用之间,而且在每个Windows消息之后,都放置了Sleep
语句和InvalidateRect
调用。那会引起各种各样的问题。此外,您的InvalidateRect
正在传递true
作为擦除标志。这是一个改进:
[在WM_CREATE或在开始发送消息之前创建一个计时器。
SetTimer(hWnd, 999, 33, NULL); // 999 is just a random timer id, "33" is the milliseconds to wait between WM_TIMER messages
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}return(int)msg.wParam;
然后在WndProc中按以下方式处理计时器。
case WM_TIMER:
{
InvalidateRect(hWnd, NULL, FALSE);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:return DefWindowProc(hWnd, message, wParam, lParam);
}
注意,我将重绘间隔从10毫秒更改为33毫秒。我有一台不能超过60帧/秒的4k显示器。 10毫秒的时间间隔会将其推向100帧/秒。那是闪烁的另一个元素。所以我将其降低到30fps。经过其他一些修复后,我们可以尝试将其备份。
并且由于我们已经购买了InvalidateRect而不清除整个屏幕,所以我们可以继续将其添加到WndProc中,以便所有其他重绘事件也不会将其擦除。
case WM_ERASEBKGND:
{
return 1;
}
至此,到目前为止,我已经进行了所有更改,只剩下了很少的闪烁,尤其是当我将窗口的大小从全屏缩小到4k屏幕的四分之一时。另外,当我为Release而不是Debug构建代码时,它甚至进一步减少了。几乎可以证明,BeginPaint / Endpaint之间的所有数学代码花费的时间太长,并且没有在1帧刻度内绘制该帧。
我有时使用的另一个次要优化,但不能确定地说是否有帮助,是不使用BeginPaint调用提供的HDC,它可能与之关联一个裁剪区域。有时,获取未剪切的HDC并用它重绘有时会更快。您可以尝试将此模块设置为WM_PAINT。 YMMV:
case WM_PAINT:
// validate that HWND
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
// fetch the unclipped DC so that subsequent drawing operations don't get boundary checked
hdc = GetDC(hWnd);
...
}if (Draw) {
SelectPen(hdc, Pens[Quadrilaterals[q$][4]]);
SelectBrush(hdc, Brushes[Quadrilaterals[q$][5]]);
Polygon(hdc, Points[q$], 4);
}q$++;
}
ReleaseDC(hWnd, hdc);
hdc = NULL;
return 0;
如上所述,大多数(但不是全部)闪烁已消除。现在,要摆脱其余部分,请遵循in the link provided by Alan Birtles说明。也就是说,在WM_PAINT处理程序中,创建一个屏幕外内存DC并对其进行绘制。然后从该屏幕外DC变为真实窗口DC。修改后的WM_PAINT应该将整个内存DC(包括背景)都绘制出来。当您这样做时,可能会提高帧速率。