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