核心概念速覽
- DC (Device Context):GDI 繪圖裝置環境,含繪圖屬性、筆刷、字型等。
- RC (Rendering Context):OpenGL 狀態機容器,綁定特定 DC,所有 GL 命令透過 RC 發送。
- 像素格式 (Pixel Format):描述色緩衝、深度緩衡、模板、雙緩衝、立體視等屬性,由
PIXELFORMATDESCRIPTOR 結構定義。
- 雙緩衝:前緩衝 (顯示) + 後緩衝 (繪製),
SwapBuffers 交換,消除閃爍、撕裂。
- 反鋸齒:多重採樣 (MSAA) 或混合模式邊緣平滑。
框架類別設計
class TOpenGLView : public TGraphicControl // 無視窗句柄,輕量
{
private:
HDC FDC; // 視窗 DC
HGLRC FRC; // OpenGL RC
int FPixelFormat;
bool FInitialized;
void SetupPixelFormat();
void CreateRenderContext();
void DestroyRenderContext();
protected:
virtual void __fastcall Paint(); // 繪製入口
virtual void __fastcall CreateParams(TCreateParams& Params);
virtual void __fastcall WndProc(TMessage& Message);
public:
__fastcall TOpenGLView(TComponent* Owner);
__fastcall ~TOpenGLView();
// 使用者覆寫
virtual void __fastcall InitializeGL(); // 一次性初始化 (材質、顯示列表、著色器)
virtual void __fastcall ResizeGL(int W, int H); // 視口/投影矩陣調整
virtual void __fastcall RenderGL(); // 每幀繪製邏輯
};
關鍵實作細節
1. 建立視窗參數
void __fastcall TOpenGLView::CreateParams(TCreateParams& Params) {
TGraphicControl::CreateParams(Params);
Params.Style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS; // 防止重繪閃爍
Params.WindowClass.style |= CS_OWNDC; // 每視窗獨立 DC
}
2. 像素格式選擇
void __fastcall TOpenGLView::SetupPixelFormat() {
PIXELFORMATDESCRIPTOR pfd = {0};
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 24;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
FDC = GetDC(Handle);
FPixelFormat = ChoosePixelFormat(FDC, &pfd);
SetPixelFormat(FDC, FPixelFormat, &pfd);
}
3. RC 建立/銷毀
void __fastcall TOpenGLView::CreateRenderContext() {
FRC = wglCreateContext(FDC);
wglMakeCurrent(FDC, FRC);
// 這裡可載入擴展函數指標 (wglGetProcAddress)
InitializeGL(); // 使用者初始化
FInitialized = true;
}
void __fastcall TOpenGLView::DestroyRenderContext() {
if (FRC) {
wglMakeCurrent(NULL, NULL);
wglDeleteContext(FRC);
FRC = NULL;
}
if (FDC) { ReleaseDC(Handle, FDC); FDC = NULL; }
}
4. 繪製迴圈
void __fastcall TOpenGLView::Paint() {
if (!FInitialized) { SetupPixelFormat(); CreateRenderContext(); }
wglMakeCurrent(FDC, FRC);
RenderGL(); // 使用者繪製
SwapBuffers(FDC); // 雙緩衝交換
}
5. 尺寸變更處理
void __fastcall TOpenGLView::WndProc(TMessage& Message) {
if (Message.Msg == WM_SIZE) {
if (FInitialized) {
ResizeGL(LOWORD(Message.LParam), HIWORD(Message.LParam));
}
}
TGraphicControl::WndProc(Message);
}
使用者衍生類範例:繪製旋轉三角形
class TMyGLView : public TOpenGLView {
float angle;
protected:
virtual void __fastcall InitializeGL() {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glEnable(GL_DEPTH_TEST);
glEnable(GL_MULTISAMPLE); // 多重採樣反鋸齒 (需像素格式支援)
angle = 0.0f;
}
virtual void __fastcall ResizeGL(int W, int H) {
glViewport(0, 0, W, H);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (double)W/H, 0.1, 100.0);
glMatrixMode(GL_MODELVIEW);
}
virtual void __fastcall RenderGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -5.0f);
glRotatef(angle, 0.0f, 1.0f, 0.0f);
glBegin(GL_TRIANGLES);
glColor3f(1,0,0); glVertex3f(0, 1, 0);
glColor3f(0,1,0); glVertex3f(-1,-1, 0);
glColor3f(0,0,1); glVertex3f(1,-1, 0);
glEnd();
angle += 0.5f;
}
};
在表單中使用
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
TMyGLView* glView = new TMyGLView(this);
glView->Parent = this;
glView->Align = alClient;
// 可加 Timer 驅動重繪
Timer1->Interval = 16; // ~60 FPS
Timer1->OnTimer = [](TObject*) { glView->Repaint(); };
}
現代化擴展
- 著色器管線:
glCreateProgram, glCreateShader,棄用固定管線。
- VBO/VAO:
glGenBuffers, glGenVertexArrays,取代 glBegin/End。
- GLEW/GLAD:自動載入 OpenGL 3.0+ 核心/擴展函數指標。
- 高 DPI:處理
WM_DPICHANGED,調整視口像素尺寸。