[{"data":1,"prerenderedAt":1066},["ShallowReactive",2],{"page-\u002Fcpp\u002Fopengel\u002F绘制一个窗口":3},{"id":4,"title":5,"body":6,"description":15,"extension":1060,"meta":1061,"navigation":44,"path":1062,"seo":1063,"stem":1064,"__hash__":1065},"content\u002Fcpp\u002Fopengel\u002F绘制一个窗口.md","绘制一个窗口",{"type":7,"value":8,"toc":1045},"minimark",[9,312,331,343,348,356,368,398,406,426,441,457,463,466,471,499,515,519,545,557,570,573,627,638,687,691,705,712,723,727,744,749,760,764,770,873,877,884,921,925,934,938,947,972,976,981,1041],[10,11,16],"pre",{"className":12,"code":13,"language":14,"meta":15,"style":15},"language-c++ shiki shiki-themes github-light github-dark","#include \u003Cglad\u002Fglad.h>\n#include \u003CGLFW\u002Fglfw3.h>\n#include \u003Ciostream>\n\n\u002F\u002F 窗口大小改变时的回调函数\nvoid framebuffer_size_callback(GLFWwindow* window, int width, int height) {\n    glViewport(0, 0, width, height);\n}\n\nint main() {\n    \u002F\u002F 初始化GLFW\n    glfwInit();\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);\n    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);\n    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);\n\n    \u002F\u002F 创建窗口\n    GLFWwindow* window = glfwCreateWindow(800, 600, \"OpenGL Window\", NULL, NULL);\n    if (window == NULL) {\n        std::cout \u003C\u003C \"Failed to create GLFW window\" \u003C\u003C std::endl;\n        glfwTerminate();\n        return -1;\n    }\n    \n    glfwMakeContextCurrent(window);\n    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);\n\n    \u002F\u002F 初始化GLAD\n    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {\n        std::cout \u003C\u003C \"Failed to initialize GLAD\" \u003C\u003C std::endl;\n        return -1;\n    }\n\n    \u002F\u002F 渲染循环\n    while (!glfwWindowShouldClose(window)) {\n        \u002F\u002F 处理输入\n        glfwPollEvents();\n        \n        \u002F\u002F 渲染指令\n        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);\n        glClear(GL_COLOR_BUFFER_BIT);\n        \n        \u002F\u002F 交换缓冲区和检查IO事件\n        glfwSwapBuffers(window);\n    }\n\n    \u002F\u002F 清理资源\n    glfwTerminate();\n    return 0;\n}\n","c++","",[17,18,19,27,33,39,46,52,58,64,70,75,81,87,93,99,105,111,116,122,128,134,140,146,152,158,164,170,176,181,187,193,199,204,209,214,220,226,232,238,244,250,256,262,267,273,279,284,289,295,301,307],"code",{"__ignoreMap":15},[20,21,24],"span",{"class":22,"line":23},"line",1,[20,25,26],{},"#include \u003Cglad\u002Fglad.h>\n",[20,28,30],{"class":22,"line":29},2,[20,31,32],{},"#include \u003CGLFW\u002Fglfw3.h>\n",[20,34,36],{"class":22,"line":35},3,[20,37,38],{},"#include \u003Ciostream>\n",[20,40,42],{"class":22,"line":41},4,[20,43,45],{"emptyLinePlaceholder":44},true,"\n",[20,47,49],{"class":22,"line":48},5,[20,50,51],{},"\u002F\u002F 窗口大小改变时的回调函数\n",[20,53,55],{"class":22,"line":54},6,[20,56,57],{},"void framebuffer_size_callback(GLFWwindow* window, int width, int height) {\n",[20,59,61],{"class":22,"line":60},7,[20,62,63],{},"    glViewport(0, 0, width, height);\n",[20,65,67],{"class":22,"line":66},8,[20,68,69],{},"}\n",[20,71,73],{"class":22,"line":72},9,[20,74,45],{"emptyLinePlaceholder":44},[20,76,78],{"class":22,"line":77},10,[20,79,80],{},"int main() {\n",[20,82,84],{"class":22,"line":83},11,[20,85,86],{},"    \u002F\u002F 初始化GLFW\n",[20,88,90],{"class":22,"line":89},12,[20,91,92],{},"    glfwInit();\n",[20,94,96],{"class":22,"line":95},13,[20,97,98],{},"    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);\n",[20,100,102],{"class":22,"line":101},14,[20,103,104],{},"    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);\n",[20,106,108],{"class":22,"line":107},15,[20,109,110],{},"    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);\n",[20,112,114],{"class":22,"line":113},16,[20,115,45],{"emptyLinePlaceholder":44},[20,117,119],{"class":22,"line":118},17,[20,120,121],{},"    \u002F\u002F 创建窗口\n",[20,123,125],{"class":22,"line":124},18,[20,126,127],{},"    GLFWwindow* window = glfwCreateWindow(800, 600, \"OpenGL Window\", NULL, NULL);\n",[20,129,131],{"class":22,"line":130},19,[20,132,133],{},"    if (window == NULL) {\n",[20,135,137],{"class":22,"line":136},20,[20,138,139],{},"        std::cout \u003C\u003C \"Failed to create GLFW window\" \u003C\u003C std::endl;\n",[20,141,143],{"class":22,"line":142},21,[20,144,145],{},"        glfwTerminate();\n",[20,147,149],{"class":22,"line":148},22,[20,150,151],{},"        return -1;\n",[20,153,155],{"class":22,"line":154},23,[20,156,157],{},"    }\n",[20,159,161],{"class":22,"line":160},24,[20,162,163],{},"    \n",[20,165,167],{"class":22,"line":166},25,[20,168,169],{},"    glfwMakeContextCurrent(window);\n",[20,171,173],{"class":22,"line":172},26,[20,174,175],{},"    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);\n",[20,177,179],{"class":22,"line":178},27,[20,180,45],{"emptyLinePlaceholder":44},[20,182,184],{"class":22,"line":183},28,[20,185,186],{},"    \u002F\u002F 初始化GLAD\n",[20,188,190],{"class":22,"line":189},29,[20,191,192],{},"    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {\n",[20,194,196],{"class":22,"line":195},30,[20,197,198],{},"        std::cout \u003C\u003C \"Failed to initialize GLAD\" \u003C\u003C std::endl;\n",[20,200,202],{"class":22,"line":201},31,[20,203,151],{},[20,205,207],{"class":22,"line":206},32,[20,208,157],{},[20,210,212],{"class":22,"line":211},33,[20,213,45],{"emptyLinePlaceholder":44},[20,215,217],{"class":22,"line":216},34,[20,218,219],{},"    \u002F\u002F 渲染循环\n",[20,221,223],{"class":22,"line":222},35,[20,224,225],{},"    while (!glfwWindowShouldClose(window)) {\n",[20,227,229],{"class":22,"line":228},36,[20,230,231],{},"        \u002F\u002F 处理输入\n",[20,233,235],{"class":22,"line":234},37,[20,236,237],{},"        glfwPollEvents();\n",[20,239,241],{"class":22,"line":240},38,[20,242,243],{},"        \n",[20,245,247],{"class":22,"line":246},39,[20,248,249],{},"        \u002F\u002F 渲染指令\n",[20,251,253],{"class":22,"line":252},40,[20,254,255],{},"        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);\n",[20,257,259],{"class":22,"line":258},41,[20,260,261],{},"        glClear(GL_COLOR_BUFFER_BIT);\n",[20,263,265],{"class":22,"line":264},42,[20,266,243],{},[20,268,270],{"class":22,"line":269},43,[20,271,272],{},"        \u002F\u002F 交换缓冲区和检查IO事件\n",[20,274,276],{"class":22,"line":275},44,[20,277,278],{},"        glfwSwapBuffers(window);\n",[20,280,282],{"class":22,"line":281},45,[20,283,157],{},[20,285,287],{"class":22,"line":286},46,[20,288,45],{"emptyLinePlaceholder":44},[20,290,292],{"class":22,"line":291},47,[20,293,294],{},"    \u002F\u002F 清理资源\n",[20,296,298],{"class":22,"line":297},48,[20,299,300],{},"    glfwTerminate();\n",[20,302,304],{"class":22,"line":303},49,[20,305,306],{},"    return 0;\n",[20,308,310],{"class":22,"line":309},50,[20,311,69],{},[313,314,315,322,323,326,327,330],"p",{},[316,317,318,321],"strong",{},[17,319,320],{},"framebuffer_size_callback","："," 这是一个回调函数，当",[316,324,325],{},"用户改变窗口大小时","，",[316,328,329],{},"GLFW 会自动调用它","。",[332,333,334],"ul",{},[335,336,337,342],"li",{},[316,338,339],{},[17,340,341],{},"glViewport(0, 0, width, height)","：这是一个关键的 OpenGL 函数。它告诉 OpenGL 渲染窗口（Framebuffer）中哪个区域应该被渲染。在这里，它将渲染区域设置为与新的窗口尺寸完全匹配，确保内容不会被拉伸或裁剪。",[344,345,347],"h3",{"id":346},"初始化-glfw-和创建窗口","初始化 GLFW 和创建窗口",[313,349,350,355],{},[316,351,352,321],{},[17,353,354],{},"glfwInit()"," 初始化 GLFW 库。在使用任何 GLFW 函数之前必须调用它。",[313,357,358,363,364,367],{},[316,359,360,321],{},[17,361,362],{},"glfwWindowHint(...)"," 这是设置 ",[316,365,366],{},"OpenGL 上下文版本和模式"," 的重要函数：",[332,369,370,384],{},[335,371,372,375,376,379,380,383],{},[17,373,374],{},"GLFW_CONTEXT_VERSION_MAJOR, 3"," 和 ",[17,377,378],{},"GLFW_CONTEXT_VERSION_MINOR, 3","：要求 GLFW 创建一个 ",[316,381,382],{},"OpenGL 3.3"," 版本的上下文。",[335,385,386,389,390,393,394,397],{},[17,387,388],{},"GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE","：要求使用 ",[316,391,392],{},"核心模式 (Core Profile)","，这意味着你将无法使用旧版（已弃用）的固定功能管线函数，强制使用现代的 ",[316,395,396],{},"Shader"," 编程。",[313,399,400,405],{},[316,401,402,321],{},[17,403,404],{},"glfwCreateWindow(...)"," 创建一个 800x600 像素、标题为 \"OpenGL Window\" 的窗口对象。",[313,407,408,413,414,417,418,421,422,425],{},[316,409,410,321],{},[17,411,412],{},"glfwMakeContextCurrent(window)"," 告诉 GLFW\u002FOpenGL，接下来的",[316,415,416],{},"所有 OpenGL 调用都将作用于这个窗口上下文","。它执行的是 ",[316,419,420],{},"激活"," 动作，而不是 ",[316,423,424],{},"绑定指针"," 动作。",[313,427,428,429,432,433,436,437,440],{},"​\t\t\t在 OpenGL 中，所有的状态（比如设置的颜色、启用的特性、创建的缓冲区 ID 等）都存储在一个被称为 ",[316,430,431],{},"渲染上下文（Rendering Context）"," 的巨大状态机中。",[316,434,435],{},"创建上下文："," 当你调用 ",[17,438,439],{},"glfwCreateWindow()"," 时，GLFW 不仅创建了操作系统窗口，它还秘密地完成了两个关键步骤：",[442,443,444,450],"ol",{},[335,445,446,447,330],{},"调用操作系统底层 API（在 Windows 上是 WGL）创建一个 ",[316,448,449],{},"OpenGL 渲染上下文",[335,451,452,453,456],{},"将这个上下文与新创建的 ",[316,454,455],{},"窗口对象"," 关联起来。",[313,458,459,462],{},[17,460,461],{},"glfwMakeContextCurrent()"," 的实际工作",[313,464,465],{},"在一个应用程序中，你可能同时创建多个窗口，每个窗口都有自己的渲染上下文。",[313,467,468,470],{},[17,469,412],{}," 的作用就是：",[442,472,473,493],{},[335,474,475,478,479,482,483,486,487,490,491,330],{},[316,476,477],{},"激活："," 它告诉当前的 CPU 线程，从现在开始，任何 OpenGL 调用（如 ",[17,480,481],{},"glClearColor","、",[17,484,485],{},"glDrawArrays"," 等）都必须作用于与传入的 ",[17,488,489],{},"window"," 对象关联的那个 ",[316,492,449],{},[335,494,495,498],{},[316,496,497],{},"切换："," 如果你之前激活了另一个窗口的上下文，这个函数会将其替换为新的上下文。",[313,500,501,506,507,510,511,514],{},[316,502,503,321],{},[17,504,505],{},"glfwSetFramebufferSizeCallback(...)"," 注册前面定义的回调函数，以处理窗口大小改变事件。            这个函数的作用是作为 ",[316,508,509],{},"回调（Callback）"," 被调用。当用户拖动窗口边框、最大化或最小化窗口时，窗口的",[316,512,513],{},"帧缓冲（Framebuffer）"," 尺寸会发生变化。每当这个尺寸发生变化时，GLFW 就会自动调用你注册的这个函数。",[344,516,518],{"id":517},"初始化-glad","初始化 GLAD",[10,520,522],{"className":12,"code":521,"language":14,"meta":15,"style":15},"\u002F\u002F 初始化GLAD\n    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {\n        std::cout \u003C\u003C \"Failed to initialize GLAD\" \u003C\u003C std::endl;\n        return -1;\n    }\n",[17,523,524,529,533,537,541],{"__ignoreMap":15},[20,525,526],{"class":22,"line":23},[20,527,528],{},"\u002F\u002F 初始化GLAD\n",[20,530,531],{"class":22,"line":29},[20,532,192],{},[20,534,535],{"class":22,"line":35},[20,536,198],{},[20,538,539],{"class":22,"line":41},[20,540,151],{},[20,542,543],{"class":22,"line":48},[20,544,157],{},[313,546,547,552,553,556],{},[316,548,549,321],{},[17,550,551],{},"gladLoadGLLoader(...)"," 这是 ",[316,554,555],{},"GLAD 的核心功能","。它负责加载所有现代 OpenGL 3.3 函数的指针。",[332,558,559,562],{},[335,560,561],{},"它需要一个函数指针作为参数，用于查找函数地址。",[335,563,564,569],{},[316,565,566],{},[17,567,568],{},"glfwGetProcAddress"," 是 GLFW 提供的一个函数，它能够正确地在当前操作系统中查询到 OpenGL 函数的地址。GLAD 使用这个函数来完成它的加载任务。",[344,571,572],{"id":572},"渲染循环",[10,574,576],{"className":12,"code":575,"language":14,"meta":15,"style":15},"\u002F\u002F 渲染循环\n    while (!glfwWindowShouldClose(window)) {\n        \u002F\u002F 处理输入\n        glfwPollEvents();\n        \n        \u002F\u002F 渲染指令\n        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);\n        glClear(GL_COLOR_BUFFER_BIT);\n        \n        \u002F\u002F 交换缓冲区和检查IO事件\n        glfwSwapBuffers(window);\n    }\n",[17,577,578,583,587,591,595,599,603,607,611,615,619,623],{"__ignoreMap":15},[20,579,580],{"class":22,"line":23},[20,581,582],{},"\u002F\u002F 渲染循环\n",[20,584,585],{"class":22,"line":29},[20,586,225],{},[20,588,589],{"class":22,"line":35},[20,590,231],{},[20,592,593],{"class":22,"line":41},[20,594,237],{},[20,596,597],{"class":22,"line":48},[20,598,243],{},[20,600,601],{"class":22,"line":54},[20,602,249],{},[20,604,605],{"class":22,"line":60},[20,606,255],{},[20,608,609],{"class":22,"line":66},[20,610,261],{},[20,612,613],{"class":22,"line":72},[20,614,243],{},[20,616,617],{"class":22,"line":77},[20,618,272],{},[20,620,621],{"class":22,"line":83},[20,622,278],{},[20,624,625],{"class":22,"line":89},[20,626,157],{},[313,628,629,630,633,634,637],{},"这是一个典型的游戏\u002F渲染循环，它会一直运行，直到用户关闭窗口 (",[17,631,632],{},"glfwWindowShouldClose(window)"," 返回 ",[17,635,636],{},"true",")。",[332,639,640,651,663,675],{},[335,641,642,647,648,650],{},[316,643,644,321],{},[17,645,646],{},"glfwPollEvents()"," 检查是否有触发的事件（如键盘输入、鼠标移动、窗口关闭），并调用相应的回调函数（如 ",[17,649,320],{},"）。",[335,652,653,658,659,662],{},[316,654,655,321],{},[17,656,657],{},"glClearColor(R, G, B, A)"," 设置",[316,660,661],{},"清除颜色","。这里设置为深蓝色\u002F蓝绿色 (RGB: 0.2, 0.3, 0.3)。",[335,664,665,670,671,674],{},[316,666,667,321],{},[17,668,669],{},"glClear(GL_COLOR_BUFFER_BIT)"," 使用上面设置的颜色来",[316,672,673],{},"清空颜色缓冲区","（即清除屏幕）。这是每帧绘制的起点。",[335,676,677,682,683,686],{},[316,678,679,321],{},[17,680,681],{},"glfwSwapBuffers(window)"," ",[316,684,685],{},"交换前后缓冲区。"," 大多数渲染系统使用双缓冲机制，在后台缓冲区进行所有绘制操作，然后与显示在屏幕上的前台缓冲区进行交换，以避免画面闪烁。",[344,688,690],{"id":689},"glfw-的默认行为","GLFW 的默认行为",[313,692,693,694,697,698,700,701,704],{},"​\t当你在 ",[17,695,696],{},"main()"," 函数中调用 ",[17,699,404],{}," 时，GLFW 会调用操作系统的底层 API（在 Windows 上是 Win32 API）来创建窗口。默认情况下，它请求操作系统创建一个 ",[316,702,703],{},"带有标准边框和标题栏"," 的窗口。",[313,706,707,708,711],{},"​\t这个标题栏（你所说的“菜单栏”）不是由 GLFW 或 OpenGL 绘制的，而是由 ",[316,709,710],{},"操作系统（Windows 的桌面窗口管理器 DWM）"," 绘制和管理的。它负责处理：",[332,713,714,717,720],{},[335,715,716],{},"窗口的拖动和移动。",[335,718,719],{},"窗口的最小化、最大化和关闭按钮。",[335,721,722],{},"窗口的边框调整大小。",[344,724,726],{"id":725},"opengl-仅渲染客户区-client-area","OpenGL 仅渲染客户区 (Client Area)",[313,728,729,730,375,732,735,736,739,740,743],{},"​\t你的所有 OpenGL 渲染指令（包括 ",[17,731,481],{},[17,733,734],{},"glClear","）都只作用于窗口的 ",[316,737,738],{},"客户区（Client Area）","，也就是标题栏和边框内部的区域。标题栏和边框是",[316,741,742],{},"非客户区（Non-Client Area）","，由操作系统控制。",[745,746,748],"h2",{"id":747},"win32-api-创建窗口的原理","Win32 API 创建窗口的原理",[313,750,751,752,755,756,759],{},"​\t调用 Win32 API 创建窗口的原理，",[316,753,754],{},"不是","简单地利用 Windows API 的画笔（GDI）来绘制，而是一个涉及",[316,757,758],{},"对象注册、资源分配和消息循环","的复杂过程。",[344,761,763],{"id":762},"核心机制窗口类window-class与对象句柄hwnd","核心机制：窗口类（Window Class）与对象句柄（HWND）",[313,765,766,767,330],{},"​\t创建窗口的第一步是",[316,768,769],{},"定义窗口的行为和外观",[771,772,773,789],"table",{},[774,775,776],"thead",{},[777,778,779,783,786],"tr",{},[780,781,782],"th",{},"步骤",[780,784,785],{},"Win32 API 调用",[780,787,788],{},"作用",[790,791,792,820,852],"tbody",{},[777,793,794,800,805],{},[795,796,797],"td",{},[316,798,799],{},"A. 注册",[795,801,802],{},[17,803,804],{},"RegisterClassEx()",[795,806,807,808,811,812,815,816,819],{},"你的程序必须先定义一个",[316,809,810],{},"窗口类（Window Class）","。这个类是窗口的模板，定义了窗口的默认",[316,813,814],{},"行为","（如",[316,817,818],{},"窗口过程函数地址","、图标、背景色、鼠标样式等）。这个类在 OS 内部注册，所有基于此模板创建的窗口都共享这些属性。",[777,821,822,827,832],{},[795,823,824],{},[316,825,826],{},"B. 创建",[795,828,829],{},[17,830,831],{},"CreateWindowEx()",[795,833,834,835,838,839,842,843,330,849,851],{},"OS 根据你注册的",[316,836,837],{},"窗口类","创建一个具体的",[316,840,841],{},"窗口实例","。这个函数返回一个 ",[316,844,845,848],{},[17,846,847],{},"HWND"," (Window Handle，窗口句柄)",[17,850,847],{}," 是一个不透明的指针，是 OS 内部用来唯一标识和管理这个窗口对象的 ID。",[777,853,854,859,864],{},[795,855,856],{},[316,857,858],{},"C. 显示",[795,860,861],{},[17,862,863],{},"ShowWindow()",[795,865,866,869,870,872],{},[316,867,868],{},"窗口对象创建后并不会立刻显示","，你必须调用 ",[17,871,863],{}," 来让 OS 调整其状态，使其可见。",[344,874,876],{"id":875},"窗口行为的核心窗口过程window-procedure和消息循环","窗口行为的核心：窗口过程（Window Procedure）和消息循环",[313,878,879,880,883],{},"​\t窗口本身不包含任何绘制或逻辑代码，它的行为完全由其关联的 ",[316,881,882],{},"窗口过程函数（Window Proc\u002FWndProc）"," 决定。",[332,885,886,896],{},[335,887,888,891,892,895],{},[316,889,890],{},"消息机制："," 你的程序通过一个**消息循环（Message Loop）**不断从 OS 接收消息。任何用户或系统事件（鼠标点击、按键、窗口移动、窗口需要重绘）都被 OS 封装成一个 ",[316,893,894],{},"消息（Message）"," 并放入你的程序的消息队列中。",[335,897,898,901,902,905,906,909,910,913,914,916,917,920],{},[316,899,900],{},"窗口过程："," 当收到消息时，OS 调用窗口的 ",[17,903,904],{},"WndProc"," 函数，并将消息（例如 ",[17,907,908],{},"WM_LBUTTONDOWN"," 或 ",[17,911,912],{},"WM_PAINT","）作为参数传递进去。你的代码在 ",[17,915,904],{}," 中通过巨大的 ",[17,918,919],{},"switch"," 语句来响应这些消息。",[745,922,924],{"id":923},"窗口绘制gdidirectxopengl-与-dwm","窗口绘制：GDI、DirectX\u002FOpenGL 与 DWM",[313,926,927,928,375,931,330],{},"关于“画笔绘制”的问题，我们需要区分 ",[316,929,930],{},"非客户区",[316,932,933],{},"客户区",[344,935,937],{"id":936},"a-非客户区绘制title-bar-border","A. 非客户区绘制（Title Bar & Border）",[313,939,940,941,944,945,330],{},"你之前在 GLFW 窗口中看到的 ",[316,942,943],{},"标题栏、边框、控制按钮"," 属于",[316,946,742],{},[332,948,949,963],{},[335,950,951,954,955,958,959,962],{},[316,952,953],{},"绘制者："," 这个区域主要由 ",[316,956,957],{},"Windows 桌面窗口管理器 (DWM, Desktop Window Manager)"," 负责绘制和管理。DWM 使用 ",[316,960,961],{},"DirectX"," 来进行硬件加速渲染，以确保标题栏的 Aero 特效、动画和透明度等效果流畅。",[335,964,965,968,969,637],{},[316,966,967],{},"你的程序无法直接控制"," 除非你指示 OS 移除这些装饰 (",[17,970,971],{},"GLFW_DECORATED, GLFW_FALSE",[344,973,975],{"id":974},"b-客户区绘制client-area","B. 客户区绘制（Client Area）",[313,977,978,979,330],{},"​\t窗口的内部区域（即你运行 OpenGL 或 GDI 绘制图形的区域）被称为",[316,980,738],{},[771,982,983,993],{},[774,984,985],{},[777,986,987,990],{},[780,988,989],{},"渲染技术",[780,991,992],{},"描述",[790,994,995,1015],{},[777,996,997,1002],{},[795,998,999],{},[316,1000,1001],{},"GDI (画笔\u002F画刷)",[795,1003,1004,1005,1007,1008,375,1011,1014],{},"GDI（Graphics Device Interface）是传统的 Windows 2D 绘图 API。如果你收到 ",[17,1006,912],{}," 消息，OS 会要求你重绘窗口的客户区。在这个函数中，你通常使用 GDI 的 ",[316,1009,1010],{},"画笔（HPEN）",[316,1012,1013],{},"画刷（HBRUSH）"," 来绘制 2D 图形和文本。",[777,1016,1017,1022],{},[795,1018,1019],{},[316,1020,1021],{},"OpenGL\u002FDirectX",[795,1023,1024,1025,1027,1028,909,1031,1034,1035,909,1038,1040],{},"对于高性能 3D\u002F2D 渲染，你通常会忽略 ",[17,1026,912],{}," 消息，而是直接使用 ",[316,1029,1030],{},"WGL（Windows Graphics Library）",[316,1032,1033],{},"DXGI"," 创建一个硬件加速的渲染上下文，并使用 ",[316,1036,1037],{},"OpenGL",[316,1039,961],{}," 在客户区上进行渲染。这些现代 API 绕过了 GDI，直接将指令发送给 GPU。",[1042,1043,1044],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":15,"searchDepth":29,"depth":29,"links":1046},[1047,1048,1049,1050,1051,1052,1056],{"id":346,"depth":35,"text":347},{"id":517,"depth":35,"text":518},{"id":572,"depth":35,"text":572},{"id":689,"depth":35,"text":690},{"id":725,"depth":35,"text":726},{"id":747,"depth":29,"text":748,"children":1053},[1054,1055],{"id":762,"depth":35,"text":763},{"id":875,"depth":35,"text":876},{"id":923,"depth":29,"text":924,"children":1057},[1058,1059],{"id":936,"depth":35,"text":937},{"id":974,"depth":35,"text":975},"md",{},"\u002Fcpp\u002Fopengel\u002F绘制一个窗口",{"description":15},"cpp\u002Fopengel\u002F绘制一个窗口","plzV8efHhPTerONRAMztcD7KGJ-9-JRjw7UpPN90w3k",1791042463771]