[{"data":1,"prerenderedAt":6468},["ShallowReactive",2],{"page-\u002Fgraphy\u002Fopengel\u002F光照":3},{"id":4,"title":5,"body":6,"description":6461,"extension":6462,"meta":6463,"navigation":487,"path":6464,"seo":6465,"stem":6466,"__hash__":6467},"content\u002Fgraphy\u002Fopengel\u002F光照.md","光照",{"type":7,"value":8,"toc":6457},"minimark",[9,18,76,83,89,92,103,122,214,225,308,311,396,403,406,410,417,432,444,451,569,576,587,702,709,782,789,875,882,889,944,963,969,979,1017,1020,1090,1093,1145,1148,1153,1168,1173,1193,1203,1205,1227,1242,1245,1252,1257,1267,1282,1299,1302,1305,1313,1317,1342,1352,1395,1398,5572,6022,6025,6040,6062,6068,6073,6076,6088,6091,6104,6132,6149,6156,6159,6162,6165,6170,6181,6191,6208,6215,6239,6250,6269,6280,6288,6325,6328,6383,6390,6395,6398,6441,6448,6453],[10,11,12,13,17],"p",{},"​\t现实世界中有无数种颜色，",[14,15,16],"strong",{},"每一个物体都有它们自己的颜色。我们需要使用（有限的）数值来模拟真实世界中（无限）的颜色，所以并不是所有现实世界中的颜色都可以用数值来表示的","。然而我们仍能通过数值来表现出非常多的颜色，甚至你可能都不会注意到与现实的颜色有任何的差异。颜色可以数字化的由红色(Red)、绿色(Green)和蓝色(Blue)三个分量组成，它们通常被缩写为RGB。仅仅用这三个值就可以组合出任意一种颜色。例如，要获取一个**珊瑚红(Coral)**色的话，我们可以定义这样的一个颜色向量：",[19,20,25],"pre",{"className":21,"code":22,"language":23,"meta":24,"style":24},"language-c++ shiki shiki-themes github-light github-dark","glm::vec3 coral(1.0f, 0.5f, 0.31f);\n","c++","",[26,27,28],"code",{"__ignoreMap":24},[29,30,33,37,41,44,47,50,54,58,61,64,66,68,71,73],"span",{"class":31,"line":32},"line",1,[29,34,36],{"class":35},"sScJk","glm",[29,38,40],{"class":39},"sVt8B","::",[29,42,43],{"class":35},"vec3",[29,45,46],{"class":35}," coral",[29,48,49],{"class":39},"(",[29,51,53],{"class":52},"sj4cs","1.0",[29,55,57],{"class":56},"szBVR","f",[29,59,60],{"class":39},", ",[29,62,63],{"class":52},"0.5",[29,65,57],{"class":56},[29,67,60],{"class":39},[29,69,70],{"class":52},"0.31",[29,72,57],{"class":56},[29,74,75],{"class":39},");\n",[10,77,78,79,82],{},"​\t",[14,80,81],{},"我们在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色，而是它所反射的(Reflected)颜色。换句话说，那些不能被物体所吸收(Absorb)的颜色（被拒绝的颜色）就是我们能够感知到的物体的颜色","。例如，太阳光能被看见的白光其实是由许多不同的颜色组合而成的（如下图所示）。如果我们将白光照在一个蓝色的玩具上，这个蓝色的玩具会吸收白光中除了蓝色以外的所有子颜色，不被吸收的蓝色光被反射到我们的眼中，让这个玩具看起来是蓝色的。下图显示的是一个珊瑚红的玩具，它以不同强度反射了多个颜色。",[10,84,85],{},[86,87],"img",{"alt":86,"src":88},".\u002Fassets\u002Flight_reflection.png",[10,90,91],{},"你可以看到，白色的阳光实际上是所有可见颜色的集合，物体吸收了其中的大部分颜色。它仅反射了代表物体颜色的部分，被反射颜色的组合就是我们所感知到的颜色（此例中为珊瑚红）。",[93,94,95,100],"blockquote",{},[10,96,97],{},[29,98,99],{},"!NOTE",[10,101,102],{},"从技术上来说，情况会更复杂一些，但我们将在PBR章节中进一步探讨。",[10,104,105,106,109,110,113,114,117,118,121],{},"​\t这些颜色反射的定律",[14,107,108],{},"被直接地运用在图形领域。当我们在OpenGL中创建一个光源时，我们希望给光源一个颜色。在上一段中我们有一个白色的太阳，所以我们也将光源设置为白色","。当我们",[14,111,112],{},"把光源的颜色与物体的颜色值相乘，所得到的就是这个物体所反射的颜色（也就是我们所感知到的颜色）","。让我们再次审视我们的玩具（这一次它还是珊瑚红），",[14,115,116],{},"看看如何在图形学中计算出它的反射颜色","。我们将这",[14,119,120],{},"两个颜色向量作分量相乘","，结果就是最终的颜色向量了：",[19,123,125],{"className":21,"code":124,"language":23,"meta":24,"style":24},"glm::vec3 lightColor(1.0f, 1.0f, 1.0f);\nglm::vec3 toyColor(1.0f, 0.5f, 0.31f);\nglm::vec3 result = lightColor * toyColor; \u002F\u002F = (1.0f, 0.5f, 0.31f);\n",[26,126,127,158,190],{"__ignoreMap":24},[29,128,129,131,133,135,138,140,142,144,146,148,150,152,154,156],{"class":31,"line":32},[29,130,36],{"class":35},[29,132,40],{"class":39},[29,134,43],{"class":35},[29,136,137],{"class":35}," lightColor",[29,139,49],{"class":39},[29,141,53],{"class":52},[29,143,57],{"class":56},[29,145,60],{"class":39},[29,147,53],{"class":52},[29,149,57],{"class":56},[29,151,60],{"class":39},[29,153,53],{"class":52},[29,155,57],{"class":56},[29,157,75],{"class":39},[29,159,161,163,165,167,170,172,174,176,178,180,182,184,186,188],{"class":31,"line":160},2,[29,162,36],{"class":35},[29,164,40],{"class":39},[29,166,43],{"class":35},[29,168,169],{"class":35}," toyColor",[29,171,49],{"class":39},[29,173,53],{"class":52},[29,175,57],{"class":56},[29,177,60],{"class":39},[29,179,63],{"class":52},[29,181,57],{"class":56},[29,183,60],{"class":39},[29,185,70],{"class":52},[29,187,57],{"class":56},[29,189,75],{"class":39},[29,191,193,195,198,201,204,207,210],{"class":31,"line":192},3,[29,194,36],{"class":35},[29,196,197],{"class":39},"::vec3 result ",[29,199,200],{"class":56},"=",[29,202,203],{"class":39}," lightColor ",[29,205,206],{"class":56},"*",[29,208,209],{"class":39}," toyColor;",[29,211,213],{"class":212},"sJ8bj"," \u002F\u002F = (1.0f, 0.5f, 0.31f);\n",[10,215,216,217,220,221,224],{},"​\t我们可以看到玩具的颜色",[14,218,219],{},"吸收","了白色光源中很大一部分的颜色，但它根据自身的颜色值对红、绿、蓝三个分量都做出了一定的反射。这也表现了现实中颜色的工作原理。由此，我们可以定义物体的颜色为",[14,222,223],{},"物体从一个光源反射各个颜色分量的大小","。现在，如果我们使用绿色的光源又会发生什么呢？",[19,226,228],{"className":21,"code":227,"language":23,"meta":24,"style":24},"glm::vec3 lightColor(0.0f, 1.0f, 0.0f);\nglm::vec3 toyColor(1.0f, 0.5f, 0.31f);\nglm::vec3 result = lightColor * toyColor; \u002F\u002F = (0.0f, 0.5f, 0.0f);\n",[26,229,230,261,291],{"__ignoreMap":24},[29,231,232,234,236,238,240,242,245,247,249,251,253,255,257,259],{"class":31,"line":32},[29,233,36],{"class":35},[29,235,40],{"class":39},[29,237,43],{"class":35},[29,239,137],{"class":35},[29,241,49],{"class":39},[29,243,244],{"class":52},"0.0",[29,246,57],{"class":56},[29,248,60],{"class":39},[29,250,53],{"class":52},[29,252,57],{"class":56},[29,254,60],{"class":39},[29,256,244],{"class":52},[29,258,57],{"class":56},[29,260,75],{"class":39},[29,262,263,265,267,269,271,273,275,277,279,281,283,285,287,289],{"class":31,"line":160},[29,264,36],{"class":35},[29,266,40],{"class":39},[29,268,43],{"class":35},[29,270,169],{"class":35},[29,272,49],{"class":39},[29,274,53],{"class":52},[29,276,57],{"class":56},[29,278,60],{"class":39},[29,280,63],{"class":52},[29,282,57],{"class":56},[29,284,60],{"class":39},[29,286,70],{"class":52},[29,288,57],{"class":56},[29,290,75],{"class":39},[29,292,293,295,297,299,301,303,305],{"class":31,"line":192},[29,294,36],{"class":35},[29,296,197],{"class":39},[29,298,200],{"class":56},[29,300,203],{"class":39},[29,302,206],{"class":56},[29,304,209],{"class":39},[29,306,307],{"class":212}," \u002F\u002F = (0.0f, 0.5f, 0.0f);\n",[10,309,310],{},"​\t可以看到，并没有红色和蓝色的光让我们的玩具来吸收或反射。这个玩具吸收了光线中一半的绿色值，但仍然也反射了一半的绿色值。玩具现在看上去是深绿色(Dark-greenish)的。我们可以看到，如果我们用绿色光源来照射玩具，那么只有绿色分量能被反射和感知到，红色和蓝色都不能被我们所感知到。这样做的结果是，一个珊瑚红的玩具突然变成了深绿色物体。现在我们来看另一个例子，使用深橄榄绿色(Dark olive-green)的光源：",[19,312,314],{"className":21,"code":313,"language":23,"meta":24,"style":24},"glm::vec3 lightColor(0.33f, 0.42f, 0.18f);\nglm::vec3 toyColor(1.0f, 0.5f, 0.31f);\nglm::vec3 result = lightColor * toyColor; \u002F\u002F = (0.33f, 0.21f, 0.06f);\n",[26,315,316,349,379],{"__ignoreMap":24},[29,317,318,320,322,324,326,328,331,333,335,338,340,342,345,347],{"class":31,"line":32},[29,319,36],{"class":35},[29,321,40],{"class":39},[29,323,43],{"class":35},[29,325,137],{"class":35},[29,327,49],{"class":39},[29,329,330],{"class":52},"0.33",[29,332,57],{"class":56},[29,334,60],{"class":39},[29,336,337],{"class":52},"0.42",[29,339,57],{"class":56},[29,341,60],{"class":39},[29,343,344],{"class":52},"0.18",[29,346,57],{"class":56},[29,348,75],{"class":39},[29,350,351,353,355,357,359,361,363,365,367,369,371,373,375,377],{"class":31,"line":160},[29,352,36],{"class":35},[29,354,40],{"class":39},[29,356,43],{"class":35},[29,358,169],{"class":35},[29,360,49],{"class":39},[29,362,53],{"class":52},[29,364,57],{"class":56},[29,366,60],{"class":39},[29,368,63],{"class":52},[29,370,57],{"class":56},[29,372,60],{"class":39},[29,374,70],{"class":52},[29,376,57],{"class":56},[29,378,75],{"class":39},[29,380,381,383,385,387,389,391,393],{"class":31,"line":192},[29,382,36],{"class":35},[29,384,197],{"class":39},[29,386,200],{"class":56},[29,388,203],{"class":39},[29,390,206],{"class":56},[29,392,209],{"class":39},[29,394,395],{"class":212}," \u002F\u002F = (0.33f, 0.21f, 0.06f);\n",[10,397,398,399,402],{},"​\t可以看到，我们可以",[14,400,401],{},"使用不同的光源颜色来让物体显现出意想不到的颜色","。有创意地利用颜色其实并不难。",[10,404,405],{},"这些颜色的理论已经足够了，下面我们来构造一个实验用的场景吧。",[407,408,409],"h1",{"id":409},"创建一个光照场景",[10,411,412,413,416],{},"​\t在接下来的教程中，我们将会广泛地",[14,414,415],{},"使用颜色来模拟现实世界中的光照效果","，创造出一些有趣的视觉效果。由于我们现在将会使用光源了，我们希望将它们显示为可见的物体，并在场景中至少加入一个物体来测试模拟光照的效果。",[10,418,419,420,423,424,431],{},"​\t首先我们",[14,421,422],{},"需要一个物体来作为被投光(Cast the light)的对象，我们将使用前面教程中的那个著名的立方体箱子","。我们还需要一个物体来代表光源在3D场景中的位置。简单起见，我们依然使用一个立方体来代表光源（我们已拥有立方体的",[425,426,430],"a",{"href":427,"rel":428},"https:\u002F\u002Flearnopengl.com\u002Fcode_viewer.php?code=getting-started\u002Fcube_vertices",[429],"nofollow","顶点数据","是吧？）。",[10,433,434,435,438,439,443],{},"​\t填一个",[14,436,437],{},"顶点缓冲对象(VBO)","，设定一下顶点属性指针和其它一些乱七八糟的东西现在对你来说应该很容易了，所以我们就不再赘述那些步骤了。如果你仍然觉得这很困难，我建议你复习[之前的教程](",[425,440,441],{"href":441,"rel":442},"https:\u002F\u002Flearnopengl-cn.github.io\u002F01",[429]," Getting started\u002F04 Hello Triangle\u002F)，并且在继续学习之前先把练习过一遍。",[10,445,446,447,450],{},"​\t我们首先",[14,448,449],{},"需要一个顶点着色器来绘制箱子","。与之前的顶点着色器相比，容器的顶点位置是保持不变的（虽然这一次我们不需要纹理坐标了），因此顶点着色器中没有新的代码。我们将会使用之前教程顶点着色器的精简版：",[19,452,454],{"className":21,"code":453,"language":23,"meta":24,"style":24},"#version 330 core\nlayout (location = 0) in vec3 aPos;\n\nuniform mat4 model;\nuniform mat4 view;\nuniform mat4 projection;\n\nvoid main()\n{\n    gl_Position = projection * view * model * vec4(aPos, 1.0);\n}\n",[26,455,456,467,483,489,495,501,507,512,524,530,563],{"__ignoreMap":24},[29,457,458,461,464],{"class":31,"line":32},[29,459,460],{"class":39},"#version ",[29,462,463],{"class":52},"330",[29,465,466],{"class":39}," core\n",[29,468,469,472,475,477,480],{"class":31,"line":160},[29,470,471],{"class":35},"layout",[29,473,474],{"class":39}," (location ",[29,476,200],{"class":56},[29,478,479],{"class":52}," 0",[29,481,482],{"class":39},") in vec3 aPos;\n",[29,484,485],{"class":31,"line":192},[29,486,488],{"emptyLinePlaceholder":487},true,"\n",[29,490,492],{"class":31,"line":491},4,[29,493,494],{"class":39},"uniform mat4 model;\n",[29,496,498],{"class":31,"line":497},5,[29,499,500],{"class":39},"uniform mat4 view;\n",[29,502,504],{"class":31,"line":503},6,[29,505,506],{"class":39},"uniform mat4 projection;\n",[29,508,510],{"class":31,"line":509},7,[29,511,488],{"emptyLinePlaceholder":487},[29,513,515,518,521],{"class":31,"line":514},8,[29,516,517],{"class":56},"void",[29,519,520],{"class":35}," main",[29,522,523],{"class":39},"()\n",[29,525,527],{"class":31,"line":526},9,[29,528,529],{"class":39},"{\n",[29,531,533,536,538,541,543,546,548,551,553,556,559,561],{"class":31,"line":532},10,[29,534,535],{"class":39},"    gl_Position ",[29,537,200],{"class":56},[29,539,540],{"class":39}," projection ",[29,542,206],{"class":56},[29,544,545],{"class":39}," view ",[29,547,206],{"class":56},[29,549,550],{"class":39}," model ",[29,552,206],{"class":56},[29,554,555],{"class":35}," vec4",[29,557,558],{"class":39},"(aPos, ",[29,560,53],{"class":52},[29,562,75],{"class":39},[29,564,566],{"class":31,"line":565},11,[29,567,568],{"class":39},"}\n",[10,570,571,572,575],{},"​\t记得",[14,573,574],{},"更新你的顶点数据和属性指针使其与新的顶点着色器保持一致","（当然你可以继续留着纹理数据和属性指针。在这一节中我们将不会用到它们，但有一个全新的开始也不是什么坏主意）。",[10,577,578,579,582,583,586],{},"​\t因为我们还要",[14,580,581],{},"创建一个表示灯（光源）的立方体","，所以我们还要",[14,584,585],{},"为这个灯创建一个专门的VAO。当然我们也可以让这个灯和其它物体使用同一个VAO，简单地对它的model（模型）矩阵做一些变换就好了","，然而接下来的教程中我们会频繁地对顶点数据和属性指针做出修改，我们并不想让这些修改影响到灯（我们只关心灯的顶点位置），因此我们有必要为灯创建一个新的VAO。",[19,588,590],{"className":21,"code":589,"language":23,"meta":24,"style":24},"unsigned int lightVAO;\nglGenVertexArrays(1, &lightVAO);\nglBindVertexArray(lightVAO);\n\u002F\u002F 只需要绑定VBO不用再次设置VBO的数据，因为箱子的VBO数据中已经包含了正确的立方体顶点数据\nglBindBuffer(GL_ARRAY_BUFFER, VBO);\n\u002F\u002F 设置灯立方体的顶点属性（对我们的灯来说仅仅只有位置数据）\nglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);\nglEnableVertexAttribArray(0);\n",[26,591,592,603,621,629,634,642,647,691],{"__ignoreMap":24},[29,593,594,597,600],{"class":31,"line":32},[29,595,596],{"class":56},"unsigned",[29,598,599],{"class":56}," int",[29,601,602],{"class":39}," lightVAO;\n",[29,604,605,608,610,613,615,618],{"class":31,"line":160},[29,606,607],{"class":35},"glGenVertexArrays",[29,609,49],{"class":39},[29,611,612],{"class":52},"1",[29,614,60],{"class":39},[29,616,617],{"class":56},"&",[29,619,620],{"class":39},"lightVAO);\n",[29,622,623,626],{"class":31,"line":192},[29,624,625],{"class":35},"glBindVertexArray",[29,627,628],{"class":39},"(lightVAO);\n",[29,630,631],{"class":31,"line":491},[29,632,633],{"class":212},"\u002F\u002F 只需要绑定VBO不用再次设置VBO的数据，因为箱子的VBO数据中已经包含了正确的立方体顶点数据\n",[29,635,636,639],{"class":31,"line":497},[29,637,638],{"class":35},"glBindBuffer",[29,640,641],{"class":39},"(GL_ARRAY_BUFFER, VBO);\n",[29,643,644],{"class":31,"line":503},[29,645,646],{"class":212},"\u002F\u002F 设置灯立方体的顶点属性（对我们的灯来说仅仅只有位置数据）\n",[29,648,649,652,654,657,659,662,665,667,670,673,675,678,681,684,687,689],{"class":31,"line":509},[29,650,651],{"class":35},"glVertexAttribPointer",[29,653,49],{"class":39},[29,655,656],{"class":52},"0",[29,658,60],{"class":39},[29,660,661],{"class":52},"3",[29,663,664],{"class":39},", GL_FLOAT, GL_FALSE, ",[29,666,661],{"class":52},[29,668,669],{"class":56}," *",[29,671,672],{"class":56}," sizeof",[29,674,49],{"class":39},[29,676,677],{"class":56},"float",[29,679,680],{"class":39},"), (",[29,682,683],{"class":56},"void*",[29,685,686],{"class":39},")",[29,688,656],{"class":52},[29,690,75],{"class":39},[29,692,693,696,698,700],{"class":31,"line":514},[29,694,695],{"class":35},"glEnableVertexAttribArray",[29,697,49],{"class":39},[29,699,656],{"class":52},[29,701,75],{"class":39},[10,703,704,705,708],{},"​\t这段代码对你来说应该非常直观。现在我们已经创建了表示灯和被照物体箱子，我们只需要再定义一个",[14,706,707],{},"片段着色器","就行了：",[19,710,712],{"className":21,"code":711,"language":23,"meta":24,"style":24},"#version 330 core\nout vec4 FragColor;\n\nuniform vec3 objectColor;\nuniform vec3 lightColor;\n\nvoid main()\n{\n    FragColor = vec4(lightColor * objectColor, 1.0);\n}\n",[26,713,714,722,727,731,736,741,745,753,757,778],{"__ignoreMap":24},[29,715,716,718,720],{"class":31,"line":32},[29,717,460],{"class":39},[29,719,463],{"class":52},[29,721,466],{"class":39},[29,723,724],{"class":31,"line":160},[29,725,726],{"class":39},"out vec4 FragColor;\n",[29,728,729],{"class":31,"line":192},[29,730,488],{"emptyLinePlaceholder":487},[29,732,733],{"class":31,"line":491},[29,734,735],{"class":39},"uniform vec3 objectColor;\n",[29,737,738],{"class":31,"line":497},[29,739,740],{"class":39},"uniform vec3 lightColor;\n",[29,742,743],{"class":31,"line":503},[29,744,488],{"emptyLinePlaceholder":487},[29,746,747,749,751],{"class":31,"line":509},[29,748,517],{"class":56},[29,750,520],{"class":35},[29,752,523],{"class":39},[29,754,755],{"class":31,"line":514},[29,756,529],{"class":39},[29,758,759,762,764,766,769,771,774,776],{"class":31,"line":526},[29,760,761],{"class":39},"    FragColor ",[29,763,200],{"class":56},[29,765,555],{"class":35},[29,767,768],{"class":39},"(lightColor ",[29,770,206],{"class":56},[29,772,773],{"class":39}," objectColor, ",[29,775,53],{"class":52},[29,777,75],{"class":39},[29,779,780],{"class":31,"line":532},[29,781,568],{"class":39},[10,783,784,785,788],{},"​\t这个",[14,786,787],{},"片段着色器从uniform变量中接受物体的颜色和光源的颜色","。正如本节一开始所讨论的那样，我们将光源的颜色和物体（反射的）颜色相乘。这个着色器理解起来应该很容易。我们把物体的颜色设置为之前提到的珊瑚红色，并把光源设置为白色。",[19,790,792],{"className":21,"code":791,"language":23,"meta":24,"style":24},"\u002F\u002F 在此之前不要忘记首先 use 对应的着色器程序（来设定uniform）\nlightingShader.use();\nlightingShader.setVec3(\"objectColor\", 1.0f, 0.5f, 0.31f);\nlightingShader.setVec3(\"lightColor\",  1.0f, 1.0f, 1.0f);\n",[26,793,794,799,810,843],{"__ignoreMap":24},[29,795,796],{"class":31,"line":32},[29,797,798],{"class":212},"\u002F\u002F 在此之前不要忘记首先 use 对应的着色器程序（来设定uniform）\n",[29,800,801,804,807],{"class":31,"line":160},[29,802,803],{"class":39},"lightingShader.",[29,805,806],{"class":35},"use",[29,808,809],{"class":39},"();\n",[29,811,812,814,817,819,823,825,827,829,831,833,835,837,839,841],{"class":31,"line":192},[29,813,803],{"class":39},[29,815,816],{"class":35},"setVec3",[29,818,49],{"class":39},[29,820,822],{"class":821},"sZZnC","\"objectColor\"",[29,824,60],{"class":39},[29,826,53],{"class":52},[29,828,57],{"class":56},[29,830,60],{"class":39},[29,832,63],{"class":52},[29,834,57],{"class":56},[29,836,60],{"class":39},[29,838,70],{"class":52},[29,840,57],{"class":56},[29,842,75],{"class":39},[29,844,845,847,849,851,854,857,859,861,863,865,867,869,871,873],{"class":31,"line":491},[29,846,803],{"class":39},[29,848,816],{"class":35},[29,850,49],{"class":39},[29,852,853],{"class":821},"\"lightColor\"",[29,855,856],{"class":39},",  ",[29,858,53],{"class":52},[29,860,57],{"class":56},[29,862,60],{"class":39},[29,864,53],{"class":52},[29,866,57],{"class":56},[29,868,60],{"class":39},[29,870,53],{"class":52},[29,872,57],{"class":56},[29,874,75],{"class":39},[10,876,877,878,881],{},"​\t要注意的是，",[14,879,880],{},"当我们修改顶点或者片段着色器后，灯的位置或颜色也会随之改变，这并不是我们想要的效果。我们不希望灯的颜色在接下来的教程中因光照计算的结果而受到影响，而是希望它能够与其它的计算分离","。我们希望灯一直保持明亮，不受其它颜色变化的影响（这样它才更像是一个真实的光源）。",[10,883,884,885,888],{},"​\t为了实现这个目标，",[14,886,887],{},"我们需要为灯的绘制创建另外的一套着色器，从而能保证它能够在其它光照着色器发生改变的时候不受影响","。顶点着色器与我们当前的顶点着色器是一样的，所以你可以直接把现在的顶点着色器用在灯上。灯的片段着色器给灯定义了一个不变的常量白色，保证了灯的颜色一直是亮的：",[19,890,892],{"className":21,"code":891,"language":23,"meta":24,"style":24},"#version 330 core\nout vec4 FragColor;\n\nvoid main()\n{\n    FragColor = vec4(1.0); \u002F\u002F 将向量的四个分量全部设置为1.0\n}\n",[26,893,894,902,906,910,918,922,940],{"__ignoreMap":24},[29,895,896,898,900],{"class":31,"line":32},[29,897,460],{"class":39},[29,899,463],{"class":52},[29,901,466],{"class":39},[29,903,904],{"class":31,"line":160},[29,905,726],{"class":39},[29,907,908],{"class":31,"line":192},[29,909,488],{"emptyLinePlaceholder":487},[29,911,912,914,916],{"class":31,"line":491},[29,913,517],{"class":56},[29,915,520],{"class":35},[29,917,523],{"class":39},[29,919,920],{"class":31,"line":497},[29,921,529],{"class":39},[29,923,924,926,928,930,932,934,937],{"class":31,"line":503},[29,925,761],{"class":39},[29,927,200],{"class":56},[29,929,555],{"class":35},[29,931,49],{"class":39},[29,933,53],{"class":52},[29,935,936],{"class":39},");",[29,938,939],{"class":212}," \u002F\u002F 将向量的四个分量全部设置为1.0\n",[29,941,942],{"class":31,"line":509},[29,943,568],{"class":39},[10,945,946,947,950,951,954,955,958,959,962],{},"​\t当我们",[14,948,949],{},"想要绘制我们的物体的时候","，我们需要",[14,952,953],{},"使用刚刚定义的光照着色器来绘制箱子","（或者可能是其它的物体）。当我们想要绘制灯的时候，我们会使用",[14,956,957],{},"灯的着色器","。在之后的教程里我们会",[14,960,961],{},"逐步更新这个光照着色器","，从而能够慢慢地实现更真实的效果。",[10,964,78,965,968],{},[14,966,967],{},"使用这个灯立方体的主要目的是为了让我们知道光源在场景中的具体位置","。我们通常在场景中定义一个光源的位置，但这只是一个位置，它并没有视觉意义。为了显示真正的灯，我们将表示光源的立方体绘制在与光源相同的位置。我们将使用我们为它新建的片段着色器来绘制它，让它一直处于白色的状态，不受场景中的光照影响。",[10,970,971,972,974,975,978],{},"​\t我们声明一个全局",[26,973,43],{},"变量来表示",[14,976,977],{},"光源在场景的世界空间坐标中的位置","：",[19,980,982],{"className":21,"code":981,"language":23,"meta":24,"style":24},"glm::vec3 lightPos(1.2f, 1.0f, 2.0f);\n",[26,983,984],{"__ignoreMap":24},[29,985,986,988,990,992,995,997,1000,1002,1004,1006,1008,1010,1013,1015],{"class":31,"line":32},[29,987,36],{"class":35},[29,989,40],{"class":39},[29,991,43],{"class":35},[29,993,994],{"class":35}," lightPos",[29,996,49],{"class":39},[29,998,999],{"class":52},"1.2",[29,1001,57],{"class":56},[29,1003,60],{"class":39},[29,1005,53],{"class":52},[29,1007,57],{"class":56},[29,1009,60],{"class":39},[29,1011,1012],{"class":52},"2.0",[29,1014,57],{"class":56},[29,1016,75],{"class":39},[10,1018,1019],{},"​\t然后我们把灯位移到这里，然后将它缩小一点，让它不那么明显：",[19,1021,1023],{"className":21,"code":1022,"language":23,"meta":24,"style":24},"model = glm::mat4();\nmodel = glm::translate(model, lightPos);\nmodel = glm::scale(model, glm::vec3(0.2f));\n",[26,1024,1025,1042,1058],{"__ignoreMap":24},[29,1026,1027,1030,1032,1035,1037,1040],{"class":31,"line":32},[29,1028,1029],{"class":39},"model ",[29,1031,200],{"class":56},[29,1033,1034],{"class":35}," glm",[29,1036,40],{"class":39},[29,1038,1039],{"class":35},"mat4",[29,1041,809],{"class":39},[29,1043,1044,1046,1048,1050,1052,1055],{"class":31,"line":160},[29,1045,1029],{"class":39},[29,1047,200],{"class":56},[29,1049,1034],{"class":35},[29,1051,40],{"class":39},[29,1053,1054],{"class":35},"translate",[29,1056,1057],{"class":39},"(model, lightPos);\n",[29,1059,1060,1062,1064,1066,1068,1071,1074,1076,1078,1080,1082,1085,1087],{"class":31,"line":192},[29,1061,1029],{"class":39},[29,1063,200],{"class":56},[29,1065,1034],{"class":35},[29,1067,40],{"class":39},[29,1069,1070],{"class":35},"scale",[29,1072,1073],{"class":39},"(model, ",[29,1075,36],{"class":35},[29,1077,40],{"class":39},[29,1079,43],{"class":35},[29,1081,49],{"class":39},[29,1083,1084],{"class":52},"0.2",[29,1086,57],{"class":56},[29,1088,1089],{"class":39},"));\n",[10,1091,1092],{},"​\t绘制灯立方体的代码应该与下面的类似：",[19,1094,1096],{"className":21,"code":1095,"language":23,"meta":24,"style":24},"lampShader.use();\n\u002F\u002F 设置模型、视图和投影矩阵uniform\n...\n\u002F\u002F 绘制灯立方体对象\nglBindVertexArray(lightVAO);\nglDrawArrays(GL_TRIANGLES, 0, 36);\n",[26,1097,1098,1107,1112,1117,1122,1128],{"__ignoreMap":24},[29,1099,1100,1103,1105],{"class":31,"line":32},[29,1101,1102],{"class":39},"lampShader.",[29,1104,806],{"class":35},[29,1106,809],{"class":39},[29,1108,1109],{"class":31,"line":160},[29,1110,1111],{"class":212},"\u002F\u002F 设置模型、视图和投影矩阵uniform\n",[29,1113,1114],{"class":31,"line":192},[29,1115,1116],{"class":39},"...\n",[29,1118,1119],{"class":31,"line":491},[29,1120,1121],{"class":212},"\u002F\u002F 绘制灯立方体对象\n",[29,1123,1124,1126],{"class":31,"line":497},[29,1125,625],{"class":35},[29,1127,628],{"class":39},[29,1129,1130,1133,1136,1138,1140,1143],{"class":31,"line":503},[29,1131,1132],{"class":35},"glDrawArrays",[29,1134,1135],{"class":39},"(GL_TRIANGLES, ",[29,1137,656],{"class":52},[29,1139,60],{"class":39},[29,1141,1142],{"class":52},"36",[29,1144,75],{"class":39},[10,1146,1147],{},"​\t请把上述的所有代码片段放在你程序中合适的位置，这样我们就能有一个干净的光照实验场地了。如果一切顺利，运行效果将会如下图所示：",[10,1149,1150],{},[86,1151],{"alt":86,"src":1152},".\u002Fassets\u002Fcolors_scene.png",[10,1154,1155,1156,1159,1160,1163,1164,1167],{},"​\t现实世界的光照是极其复杂的，而且会受到诸多因素的影响，这是我们有限的计算能力所无法模拟的。因此",[14,1157,1158],{},"OpenGL的光照使用的是简化的模型，对现实的情况进行近似，这样处理起来会更容易一些，而且看起来也差不多一样","。这些光照模型都是基于我们对光的物理特性的理解。",[14,1161,1162],{},"其中一个模型被称为风氏光照模型(Phong Lighting Model)","。风氏光照模型的主要结构由",[14,1165,1166],{},"3个分量组成：环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照","。下面这张图展示了这些光照分量看起来的样子：",[10,1169,1170],{},[86,1171],{"alt":86,"src":1172},".\u002Fassets\u002Fbasic_lighting_phong.png",[1174,1175,1176,1180,1187],"ul",{},[1177,1178,1179],"li",{},"环境光照(Ambient Lighting)：即使在黑暗的情况下，世界上通常也仍然有一些光亮（月亮、远处的光），所以物体几乎永远不会是完全黑暗的。为了模拟这个，我们会使用一个环境光照常量，它永远会给物体一些颜色。",[1177,1181,1182,1183,1186],{},"漫反射光照(Diffuse Lighting)：模拟光源对物体的方向性影响(Directional Impact)。它是风氏光照模型中视觉上最显著的分量。",[14,1184,1185],{},"物体的某一部分越是正对着光源，它就会越亮","。",[1177,1188,1189,1190,1186],{},"镜面光照(Specular Lighting)：模拟有光泽物体上面出现的亮点。",[14,1191,1192],{},"镜面光照的颜色相比于物体的颜色会更倾向于光的颜色",[10,1194,1195,1196,1199,1200,1186],{},"​\t为了创建有趣的视觉场景，我们希望",[14,1197,1198],{},"模拟至少这三种光照分量","。我们将以最简单的一个开始：",[14,1201,1202],{},"环境光照",[407,1204,1202],{"id":1202},[10,1206,78,1207,1210,1211,1214,1215,1218,1219,1222,1223,1226],{},[14,1208,1209],{},"光通常都不是来自于同一个光源，而是来自于我们周围分散的很多光源","，即使它们可能并不是那么显而易见。光的一个属性是，",[14,1212,1213],{},"它可以向很多方向发散并反弹","，从而",[14,1216,1217],{},"能够到达不是非常直接临近的点","。所以，光能够在其它的表面上",[14,1220,1221],{},"反射","，对一个物体产生间接的影响。",[14,1224,1225],{},"考虑到这种情况的算法叫做全局照明(Global Illumination)算法","，但是这种算法既开销高昂又极其复杂。",[10,1228,1229,1230,1233,1234,1237,1238,1241],{},"​\t由于我们现在对那种又复杂又开销高昂的算法不是很感兴趣，",[14,1231,1232],{},"所以我们将会先使用一个简化的全局照明模型","，即环境光照。正如你在上一节所学到的，",[14,1235,1236],{},"我们使用一个很小的常量（光照）颜色","，",[14,1239,1240],{},"添加到物体片段的最终颜色中","，这样子的话即便场景中没有直接的光源也能看起来存在有一些发散的光。",[407,1243,1244],{"id":1244},"漫反射光照",[10,1246,1247,1248,1251],{},"​\t环境光照本身不能提供最有趣的结果，但是",[14,1249,1250],{},"漫反射光照就能开始对物体产生显著的视觉影响了。漫反射光照使物体上与光线方向越接近的片段能从光源处获得更多的亮度","。为了能够更好的理解漫反射光照，请看下图：",[10,1253,1254],{},[86,1255],{"alt":86,"src":1256},".\u002Fassets\u002Fdiffuse_light.png",[10,1258,1259,1260,1263,1264,1186],{},"​\t图左上方有一个光源，它所发出的",[14,1261,1262],{},"光线落在物体的一个片段上。我们需要测量这个光线是以什么角度接触到这个片段的。如果光线垂直于物体表面，这束光对物体的影响会最大化（译注：更亮）","。为了测量光线和片段的角度，我们使用一个叫做法向量(Normal Vector)的东西，",[14,1265,1266],{},"它是垂直于片段表面的一个向量（这里以黄色箭头表示），我们在后面再讲这个东西。这两个向量之间的角度很容易就能够通过点乘计算出来",[10,1268,1269,1270,1273,1274,1277,1278,1281],{},"​\t你可能记得在[变换](",[425,1271,441],{"href":441,"rel":1272},[429]," Getting started\u002F07 Transformations\u002F)那一节教程里，我们知道",[14,1275,1276],{},"两个单位向量的夹角越小，它们点乘的结果越倾向于1","。当",[14,1279,1280],{},"两个向量的夹角为90度的时候，点乘会变为0","。这同样适用于θ，θ越大，光对片段颜色的影响就应该越小。",[93,1283,1284,1288],{},[10,1285,1286],{},[29,1287,99],{},[10,1289,1290,1291,1294,1295,1298],{},"注意，",[14,1292,1293],{},"为了（只）得到两个向量夹角的余弦值，我们使用的是单位向量（长度为1的向量）","，所以我们需要确保所有的向量都是标准化的，否则点乘返回的就不仅仅是余弦值了（见[变换](",[425,1296,441],{"href":441,"rel":1297},[429]," Getting started\u002F07 Transformations\u002F)）。",[10,1300,1301],{},"​\t点乘返回一个标量，我们可以用它计算光线对片段颜色的影响。不同片段朝向光源的方向的不同，这些片段被照亮的情况也不同。",[10,1303,1304],{},"所以，计算漫反射光照需要什么？",[1174,1306,1307,1310],{},[1177,1308,1309],{},"法向量：一个垂直于顶点表面的向量。",[1177,1311,1312],{},"定向的光线：作为光源的位置与片段的位置之间向量差的方向向量。为了计算这个光线，我们需要光的位置向量和片段的位置向量。",[1314,1315,1316],"h2",{"id":1316},"法向量",[10,1318,1319,1320,1323,1324,1327,1328,1331,1332,1335,1336,1341],{},"​\t法向量是",[14,1321,1322],{},"一个垂直于顶点表面的（单位）向量","。由于",[14,1325,1326],{},"顶点本身并没有表面（它只是空间中一个独立的点）","，我们利用它",[14,1329,1330],{},"周围的顶点来计算出这个顶点的表面","。我们能够使用一个小技巧，",[14,1333,1334],{},"使用叉乘对立方体所有的顶点计算法向量","，但是由于3D立方体不是一个复杂的形状，所以我们可以简单地把法线数据手工添加到顶点数据中。更新后的顶点数据数组可以在",[425,1337,1340],{"href":1338,"rel":1339},"https:\u002F\u002Flearnopengl.com\u002Fcode_viewer.php?code=lighting\u002Fbasic_lighting_vertex_data",[429],"这里","找到。试着去想象一下，这些法向量真的是垂直于立方体各个平面的表面的（一个立方体由6个平面组成）。",[10,1343,1344,1345,1348,1349,978],{},"​\t由于我们",[14,1346,1347],{},"向顶点数组添加了额外的数据","，所以",[14,1350,1351],{},"我们应该更新光照的顶点着色器",[19,1353,1355],{"className":21,"code":1354,"language":23,"meta":24,"style":24},"#version 330 core\nlayout (location = 0) in vec3 aPos;\nlayout (location = 1) in vec3 aNormal;\n...\n",[26,1356,1357,1365,1377,1391],{"__ignoreMap":24},[29,1358,1359,1361,1363],{"class":31,"line":32},[29,1360,460],{"class":39},[29,1362,463],{"class":52},[29,1364,466],{"class":39},[29,1366,1367,1369,1371,1373,1375],{"class":31,"line":160},[29,1368,471],{"class":35},[29,1370,474],{"class":39},[29,1372,200],{"class":56},[29,1374,479],{"class":52},[29,1376,482],{"class":39},[29,1378,1379,1381,1383,1385,1388],{"class":31,"line":192},[29,1380,471],{"class":35},[29,1382,474],{"class":39},[29,1384,200],{"class":56},[29,1386,1387],{"class":52}," 1",[29,1389,1390],{"class":39},") in vec3 aNormal;\n",[29,1392,1393],{"class":31,"line":491},[29,1394,1116],{"class":39},[10,1396,1397],{},"完整代码：",[19,1399,1401],{"className":21,"code":1400,"language":23,"meta":24,"style":24},"#include \u003Cglad\u002Fglad.h>\n#include \u003CGLFW\u002Fglfw3.h>\n#include \u003Ciostream>\n#include \u003Ccmath>\n#include \u003Cimgui.h>\n#include \u003Cimgui_impl_glfw.h>\n#include \u003Cimgui_impl_opengl3.h>\n#include \"utils\u002Fshader.h\"\n#include \"utils\u002FglmCompute.h\"\n#include \"include\u002Fdpi_stabilizer.h\"\n#include \"include\u002Fimgui_manager.h\"\n\nusing namespace glm;\n\n\u002F\u002F 窗口大小改变时的回调函数\nvoid framebuffer_size_callback(GLFWwindow* window, int width, int height) {\n    glViewport(0, 0, width, height);\n}\n\n\n\u002F\u002F 全局变量\nfloat deltaTime = 0.0f; \u002F\u002F 当前帧与上一帧的时间差\nfloat lastFrame = 0.0f; \u002F\u002F 上一帧的时间\n\n\u002F\u002F DPI 稳定器实例\nDPIStabilizer dpiStabilizer(0.1f, 50.0f, 0.8f);\n\n\u002F\u002F ImGui 管理器实例\nImGuiManager imguiManager;\n\n\u002F\u002F 简化的输入处理函数\nvoid processInput(GLFWwindow *window){\n    \u002F\u002F 使用 DPI 稳定器处理所有输入\n    dpiStabilizer.processKeyboardInput(window);\n\n    if (glfwGetKey(window, GLFW_KEY_F2) == GLFW_PRESS) {\n        imguiManager.toggleWindow(\"控制面板\");\n    }\n    if (glfwGetKey(window, GLFW_KEY_F3) == GLFW_PRESS) {\n        imguiManager.toggleWindow(\"性能监控\");\n    }\n    if (glfwGetKey(window, GLFW_KEY_F4) == GLFW_PRESS) {\n        imguiManager.toggleWindow(\"调试信息\");\n    }\n    \n    \u002F\u002F 显示统计信息 (按 T 键)\n    if (glfwGetKey(window, GLFW_KEY_T) == GLFW_PRESS) {\n        std::cout \u003C\u003C dpiStabilizer.getStats() \u003C\u003C std::endl;\n    }\n}\n\nint main() {\n\n    \u002F\u002F 模型矩阵\n    mat4 model = mat4(1.0f);\n    model = rotate(model, radians(-55.0f), vec3(1.0f, 0.0f, 0.0f));\n\n    \u002F\u002F 投影矩阵\n    mat4 projection = perspective(radians(45.0f), (float)800 \u002F (float)600, 0.1f, 100.0f);\n\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 设置 DPI 感知提示\n    DPIStabilizer::setDPIHint();\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 初始化 DPI 稳定器\n    dpiStabilizer.initialize(window);\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 初始化 ImGui 管理器\n    imguiManager.initialize(window);\n    \n    \u002F\u002F 注册窗口\n    imguiManager.registerWindow(\"Hello ImGui!\", []() {\n        ImGui::Text(\"DPI 稳定器状态: %s\", dpiStabilizer.isMouseCaptured() ? \"已捕获\" : \"已释放\");\n        ImGui::Text(\"鼠标敏感度: %.3f\", dpiStabilizer.getSensitivity());\n        \n        if (ImGui::Button(\"重置统计\")) {\n            dpiStabilizer.resetStats();\n        }\n        \n        float sensitivity = dpiStabilizer.getSensitivity();\n        if (ImGui::SliderFloat(\"鼠标敏感度\", &sensitivity, 0.01f, 0.5f)) {\n            dpiStabilizer.setSensitivity(sensitivity);\n        }\n    });\n    \n    imguiManager.registerWindow(\"控制面板\", []() {\n        ImGuiIO& io = ImGui::GetIO();\n        ImGui::Text(\"FPS: %.1f\", io.Framerate);\n        ImGui::Text(\"帧时间: %.3f ms\", 1000.0f \u002F io.Framerate);\n    });\n    \n\n    \u002F\u002F 启用深度测试\n    glEnable(GL_DEPTH_TEST);\n\n    float vertices[] = {\n    \u002F\u002F 立方体顶点数据（位置 + 法线）\n    \u002F\u002F 位置(3) + 法线(3) = 6个浮点数\n    \u002F\u002F 前面\n    -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,\n     0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,\n     0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,\n     0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,\n    -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,\n    -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,\n    \n    \u002F\u002F 后面\n    -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,\n     0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,\n     0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,\n     0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,\n    -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,\n    -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,\n    \n    \u002F\u002F 左面\n    -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,\n    -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,\n    -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,\n    -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,\n    -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,\n    -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,\n    \n    \u002F\u002F 右面\n     0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,\n     0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,\n     0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,\n     0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,\n     0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,\n     0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,\n    \n    \u002F\u002F 下面\n    -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,\n     0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,\n     0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,\n     0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,\n    -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,\n    -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,\n    \n    \u002F\u002F 上面\n    -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,\n     0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,\n     0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,\n     0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,\n    -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,\n    -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f\n};\n    \n    \u002F\u002F 创建VAO\n    unsigned int VAO;\n    glGenVertexArrays(1, &VAO);\n    glBindVertexArray(VAO); \u002F\u002F 绑定VAO\n\n    \u002F\u002F 创建VBO\n    unsigned int VBO;\n    glGenBuffers(1, &VBO);\n    glBindBuffer(GL_ARRAY_BUFFER, VBO); \u002F\u002F 绑定VBO\n    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);\n\n    int nrAttributes;\n    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);\n    std::cout \u003C\u003C \"Maximum nr of vertex attributes supported: \" \u003C\u003C nrAttributes \u003C\u003C std::endl;\n\n    \u002F\u002F 位置属性\n    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);\n    glEnableVertexAttribArray(0); \u002F\u002F 启用顶点属性\n    \n    \u002F\u002F 法线属性\n    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));\n    glEnableVertexAttribArray(1); \u002F\u002F 启用法线属性\n\n    \u002F\u002F 构建并编译着色器程序\n    Shader lightingShader(\"shader\u002Flighting.vert\", \"shader\u002Flighting.frag\");\n    Shader lampShader(\"shader\u002Flamp.vert\", \"shader\u002Flamp.frag\");\n\n    \u002F\u002F 创建光源的 VAO\n    unsigned int lightVAO;\n    glGenVertexArrays(1, &lightVAO);\n    glBindVertexArray(lightVAO);\n    glBindBuffer(GL_ARRAY_BUFFER, VBO);\n\n    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);\n    glEnableVertexAttribArray(0);\n\n    \u002F\u002F 光源位置\n    vec3 lightPos(1.2f, 1.0f, 2.0f);\n\n    \u002F\u002F 渲染循环\n    while (!glfwWindowShouldClose(window)) {\n        \u002F\u002F 处理输入\n        processInput(window);\n        glfwPollEvents();\n\n        \u002F\u002F 从 DPI 稳定器获取 view 矩阵\n        mat4 view = dpiStabilizer.getViewMatrix();\n        \n        float currentFrame = glfwGetTime();\n        deltaTime = currentFrame - lastFrame;\n        lastFrame = currentFrame;\n\n        \u002F\u002F 渲染指令\n        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);\n        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n        \n        \u002F\u002F 使用光照着色器绘制物体\n        lightingShader.use();\n        lightingShader.setMat4(\"projection\", projection);\n        lightingShader.setMat4(\"view\", view);\n        lightingShader.setMat4(\"model\", model);\n        \n        \u002F\u002F 设置物体颜色和光源颜色\n        lightingShader.setVec3(\"objectColor\", 1.0f, 0.5f, 0.31f); \u002F\u002F 珊瑚红\n        lightingShader.setVec3(\"lightColor\", 1.0f, 1.0f, 1.0f);   \u002F\u002F 白色光源\n        lightingShader.setVec3(\"lightPos\", lightPos.x, lightPos.y, lightPos.z);\n        \n        glBindVertexArray(VAO);\n        glDrawArrays(GL_TRIANGLES, 0, 36);\n        \n        \u002F\u002F 绘制光源立方体\n        lampShader.use();\n        lampShader.setMat4(\"projection\", projection);\n        lampShader.setMat4(\"view\", view);\n        \n        \u002F\u002F 光源立方体的模型矩阵\n        mat4 lampModel = mat4(1.0f);\n        lampModel = translate(lampModel, lightPos);\n        lampModel = scale(lampModel, vec3(0.2f)); \u002F\u002F 缩小光源立方体\n        lampShader.setMat4(\"model\", lampModel);\n        \n        glBindVertexArray(lightVAO);\n        glDrawArrays(GL_TRIANGLES, 0, 36);\n        \n        \u002F\u002F 使用 ImGui 管理器渲染所有窗口\n        imguiManager.render();\n\n        \u002F\u002F 交换缓冲区和检查IO事件\n        glfwSwapBuffers(window);\n    }\n\n    \u002F\u002F 清理 ImGui 管理器\n    imguiManager.shutdown();\n\n    \u002F\u002F 清理资源\n    glDeleteVertexArrays(1, &VAO);\n    glDeleteVertexArrays(1, &lightVAO);\n    glDeleteBuffers(1, &VBO);\n    glfwTerminate();\n    return 0;\n}\n\n",[26,1402,1403,1411,1418,1425,1432,1439,1446,1453,1460,1467,1474,1481,1486,1500,1505,1511,1548,1565,1570,1575,1580,1586,1607,1626,1631,1637,1669,1674,1680,1686,1691,1697,1717,1723,1735,1740,1761,1777,1783,1799,1813,1818,1834,1848,1853,1859,1865,1881,1910,1915,1920,1925,1935,1940,1946,1967,2018,2023,2029,2088,2093,2099,2107,2120,2132,2140,2145,2151,2164,2169,2175,2215,2230,2249,2257,2270,2275,2280,2288,2297,2302,2308,2318,2323,2329,2345,2363,2374,2379,2384,2390,2400,2405,2411,2427,2471,2498,2504,2528,2539,2545,2550,2567,2606,2617,2622,2628,2633,2646,2669,2691,2722,2727,2732,2737,2743,2752,2757,2771,2777,2783,2789,2834,2876,2915,2954,2995,3038,3043,3049,3096,3141,3184,3227,3272,3319,3324,3330,3373,3418,3465,3512,3557,3600,3605,3611,3650,3691,3734,3777,3818,3857,3862,3868,3915,3960,4003,4046,4091,4138,4143,4149,4192,4233,4272,4311,4352,4394,4400,4405,4411,4422,4439,4451,4456,4462,4472,4489,4501,4516,4521,4530,4544,4568,4573,4579,4616,4631,4636,4642,4687,4701,4706,4712,4733,4753,4758,4764,4773,4788,4795,4802,4807,4842,4853,4858,4864,4892,4897,4903,4919,4925,4933,4941,4946,4952,4969,4974,4989,5004,5015,5020,5026,5059,5074,5079,5085,5095,5111,5126,5141,5146,5152,5186,5220,5235,5240,5249,5265,5270,5276,5286,5299,5312,5317,5323,5343,5357,5384,5398,5403,5410,5425,5430,5436,5446,5451,5457,5465,5470,5475,5481,5491,5496,5502,5518,5533,5549,5557,5567],{"__ignoreMap":24},[29,1404,1405,1408],{"class":31,"line":32},[29,1406,1407],{"class":56},"#include",[29,1409,1410],{"class":821}," \u003Cglad\u002Fglad.h>\n",[29,1412,1413,1415],{"class":31,"line":160},[29,1414,1407],{"class":56},[29,1416,1417],{"class":821}," \u003CGLFW\u002Fglfw3.h>\n",[29,1419,1420,1422],{"class":31,"line":192},[29,1421,1407],{"class":56},[29,1423,1424],{"class":821}," \u003Ciostream>\n",[29,1426,1427,1429],{"class":31,"line":491},[29,1428,1407],{"class":56},[29,1430,1431],{"class":821}," \u003Ccmath>\n",[29,1433,1434,1436],{"class":31,"line":497},[29,1435,1407],{"class":56},[29,1437,1438],{"class":821}," \u003Cimgui.h>\n",[29,1440,1441,1443],{"class":31,"line":503},[29,1442,1407],{"class":56},[29,1444,1445],{"class":821}," \u003Cimgui_impl_glfw.h>\n",[29,1447,1448,1450],{"class":31,"line":509},[29,1449,1407],{"class":56},[29,1451,1452],{"class":821}," \u003Cimgui_impl_opengl3.h>\n",[29,1454,1455,1457],{"class":31,"line":514},[29,1456,1407],{"class":56},[29,1458,1459],{"class":821}," \"utils\u002Fshader.h\"\n",[29,1461,1462,1464],{"class":31,"line":526},[29,1463,1407],{"class":56},[29,1465,1466],{"class":821}," \"utils\u002FglmCompute.h\"\n",[29,1468,1469,1471],{"class":31,"line":532},[29,1470,1407],{"class":56},[29,1472,1473],{"class":821}," \"include\u002Fdpi_stabilizer.h\"\n",[29,1475,1476,1478],{"class":31,"line":565},[29,1477,1407],{"class":56},[29,1479,1480],{"class":821}," \"include\u002Fimgui_manager.h\"\n",[29,1482,1484],{"class":31,"line":1483},12,[29,1485,488],{"emptyLinePlaceholder":487},[29,1487,1489,1492,1495,1497],{"class":31,"line":1488},13,[29,1490,1491],{"class":56},"using",[29,1493,1494],{"class":56}," namespace",[29,1496,1034],{"class":35},[29,1498,1499],{"class":39},";\n",[29,1501,1503],{"class":31,"line":1502},14,[29,1504,488],{"emptyLinePlaceholder":487},[29,1506,1508],{"class":31,"line":1507},15,[29,1509,1510],{"class":212},"\u002F\u002F 窗口大小改变时的回调函数\n",[29,1512,1514,1516,1519,1521,1524,1526,1530,1532,1535,1538,1540,1542,1545],{"class":31,"line":1513},16,[29,1515,517],{"class":56},[29,1517,1518],{"class":35}," framebuffer_size_callback",[29,1520,49],{"class":39},[29,1522,1523],{"class":35},"GLFWwindow",[29,1525,206],{"class":56},[29,1527,1529],{"class":1528},"s4XuR"," window",[29,1531,60],{"class":39},[29,1533,1534],{"class":56},"int",[29,1536,1537],{"class":1528}," width",[29,1539,60],{"class":39},[29,1541,1534],{"class":56},[29,1543,1544],{"class":1528}," height",[29,1546,1547],{"class":39},") {\n",[29,1549,1551,1554,1556,1558,1560,1562],{"class":31,"line":1550},17,[29,1552,1553],{"class":35},"    glViewport",[29,1555,49],{"class":39},[29,1557,656],{"class":52},[29,1559,60],{"class":39},[29,1561,656],{"class":52},[29,1563,1564],{"class":39},", width, height);\n",[29,1566,1568],{"class":31,"line":1567},18,[29,1569,568],{"class":39},[29,1571,1573],{"class":31,"line":1572},19,[29,1574,488],{"emptyLinePlaceholder":487},[29,1576,1578],{"class":31,"line":1577},20,[29,1579,488],{"emptyLinePlaceholder":487},[29,1581,1583],{"class":31,"line":1582},21,[29,1584,1585],{"class":212},"\u002F\u002F 全局变量\n",[29,1587,1589,1591,1594,1596,1599,1601,1604],{"class":31,"line":1588},22,[29,1590,677],{"class":56},[29,1592,1593],{"class":39}," deltaTime ",[29,1595,200],{"class":56},[29,1597,1598],{"class":52}," 0.0",[29,1600,57],{"class":56},[29,1602,1603],{"class":39},";",[29,1605,1606],{"class":212}," \u002F\u002F 当前帧与上一帧的时间差\n",[29,1608,1610,1612,1615,1617,1619,1621,1623],{"class":31,"line":1609},23,[29,1611,677],{"class":56},[29,1613,1614],{"class":39}," lastFrame ",[29,1616,200],{"class":56},[29,1618,1598],{"class":52},[29,1620,57],{"class":56},[29,1622,1603],{"class":39},[29,1624,1625],{"class":212}," \u002F\u002F 上一帧的时间\n",[29,1627,1629],{"class":31,"line":1628},24,[29,1630,488],{"emptyLinePlaceholder":487},[29,1632,1634],{"class":31,"line":1633},25,[29,1635,1636],{"class":212},"\u002F\u002F DPI 稳定器实例\n",[29,1638,1640,1643,1646,1648,1651,1653,1655,1658,1660,1662,1665,1667],{"class":31,"line":1639},26,[29,1641,1642],{"class":35},"DPIStabilizer",[29,1644,1645],{"class":35}," dpiStabilizer",[29,1647,49],{"class":39},[29,1649,1650],{"class":52},"0.1",[29,1652,57],{"class":56},[29,1654,60],{"class":39},[29,1656,1657],{"class":52},"50.0",[29,1659,57],{"class":56},[29,1661,60],{"class":39},[29,1663,1664],{"class":52},"0.8",[29,1666,57],{"class":56},[29,1668,75],{"class":39},[29,1670,1672],{"class":31,"line":1671},27,[29,1673,488],{"emptyLinePlaceholder":487},[29,1675,1677],{"class":31,"line":1676},28,[29,1678,1679],{"class":212},"\u002F\u002F ImGui 管理器实例\n",[29,1681,1683],{"class":31,"line":1682},29,[29,1684,1685],{"class":39},"ImGuiManager imguiManager;\n",[29,1687,1689],{"class":31,"line":1688},30,[29,1690,488],{"emptyLinePlaceholder":487},[29,1692,1694],{"class":31,"line":1693},31,[29,1695,1696],{"class":212},"\u002F\u002F 简化的输入处理函数\n",[29,1698,1700,1702,1705,1707,1709,1711,1714],{"class":31,"line":1699},32,[29,1701,517],{"class":56},[29,1703,1704],{"class":35}," processInput",[29,1706,49],{"class":39},[29,1708,1523],{"class":35},[29,1710,669],{"class":56},[29,1712,1713],{"class":1528},"window",[29,1715,1716],{"class":39},"){\n",[29,1718,1720],{"class":31,"line":1719},33,[29,1721,1722],{"class":212},"    \u002F\u002F 使用 DPI 稳定器处理所有输入\n",[29,1724,1726,1729,1732],{"class":31,"line":1725},34,[29,1727,1728],{"class":39},"    dpiStabilizer.",[29,1730,1731],{"class":35},"processKeyboardInput",[29,1733,1734],{"class":39},"(window);\n",[29,1736,1738],{"class":31,"line":1737},35,[29,1739,488],{"emptyLinePlaceholder":487},[29,1741,1743,1746,1749,1752,1755,1758],{"class":31,"line":1742},36,[29,1744,1745],{"class":56},"    if",[29,1747,1748],{"class":39}," (",[29,1750,1751],{"class":35},"glfwGetKey",[29,1753,1754],{"class":39},"(window, GLFW_KEY_F2) ",[29,1756,1757],{"class":56},"==",[29,1759,1760],{"class":39}," GLFW_PRESS) {\n",[29,1762,1764,1767,1770,1772,1775],{"class":31,"line":1763},37,[29,1765,1766],{"class":39},"        imguiManager.",[29,1768,1769],{"class":35},"toggleWindow",[29,1771,49],{"class":39},[29,1773,1774],{"class":821},"\"控制面板\"",[29,1776,75],{"class":39},[29,1778,1780],{"class":31,"line":1779},38,[29,1781,1782],{"class":39},"    }\n",[29,1784,1786,1788,1790,1792,1795,1797],{"class":31,"line":1785},39,[29,1787,1745],{"class":56},[29,1789,1748],{"class":39},[29,1791,1751],{"class":35},[29,1793,1794],{"class":39},"(window, GLFW_KEY_F3) ",[29,1796,1757],{"class":56},[29,1798,1760],{"class":39},[29,1800,1802,1804,1806,1808,1811],{"class":31,"line":1801},40,[29,1803,1766],{"class":39},[29,1805,1769],{"class":35},[29,1807,49],{"class":39},[29,1809,1810],{"class":821},"\"性能监控\"",[29,1812,75],{"class":39},[29,1814,1816],{"class":31,"line":1815},41,[29,1817,1782],{"class":39},[29,1819,1821,1823,1825,1827,1830,1832],{"class":31,"line":1820},42,[29,1822,1745],{"class":56},[29,1824,1748],{"class":39},[29,1826,1751],{"class":35},[29,1828,1829],{"class":39},"(window, GLFW_KEY_F4) ",[29,1831,1757],{"class":56},[29,1833,1760],{"class":39},[29,1835,1837,1839,1841,1843,1846],{"class":31,"line":1836},43,[29,1838,1766],{"class":39},[29,1840,1769],{"class":35},[29,1842,49],{"class":39},[29,1844,1845],{"class":821},"\"调试信息\"",[29,1847,75],{"class":39},[29,1849,1851],{"class":31,"line":1850},44,[29,1852,1782],{"class":39},[29,1854,1856],{"class":31,"line":1855},45,[29,1857,1858],{"class":39},"    \n",[29,1860,1862],{"class":31,"line":1861},46,[29,1863,1864],{"class":212},"    \u002F\u002F 显示统计信息 (按 T 键)\n",[29,1866,1868,1870,1872,1874,1877,1879],{"class":31,"line":1867},47,[29,1869,1745],{"class":56},[29,1871,1748],{"class":39},[29,1873,1751],{"class":35},[29,1875,1876],{"class":39},"(window, GLFW_KEY_T) ",[29,1878,1757],{"class":56},[29,1880,1760],{"class":39},[29,1882,1884,1887,1890,1893,1896,1899,1902,1904,1907],{"class":31,"line":1883},48,[29,1885,1886],{"class":35},"        std",[29,1888,1889],{"class":39},"::cout ",[29,1891,1892],{"class":56},"\u003C\u003C",[29,1894,1895],{"class":39}," dpiStabilizer.",[29,1897,1898],{"class":35},"getStats",[29,1900,1901],{"class":39},"() ",[29,1903,1892],{"class":56},[29,1905,1906],{"class":35}," std",[29,1908,1909],{"class":39},"::endl;\n",[29,1911,1913],{"class":31,"line":1912},49,[29,1914,1782],{"class":39},[29,1916,1918],{"class":31,"line":1917},50,[29,1919,568],{"class":39},[29,1921,1923],{"class":31,"line":1922},51,[29,1924,488],{"emptyLinePlaceholder":487},[29,1926,1928,1930,1932],{"class":31,"line":1927},52,[29,1929,1534],{"class":56},[29,1931,520],{"class":35},[29,1933,1934],{"class":39},"() {\n",[29,1936,1938],{"class":31,"line":1937},53,[29,1939,488],{"emptyLinePlaceholder":487},[29,1941,1943],{"class":31,"line":1942},54,[29,1944,1945],{"class":212},"    \u002F\u002F 模型矩阵\n",[29,1947,1949,1952,1954,1956,1959,1961,1963,1965],{"class":31,"line":1948},55,[29,1950,1951],{"class":35},"    mat4",[29,1953,550],{"class":39},[29,1955,200],{"class":56},[29,1957,1958],{"class":35}," mat4",[29,1960,49],{"class":39},[29,1962,53],{"class":52},[29,1964,57],{"class":56},[29,1966,75],{"class":39},[29,1968,1970,1973,1975,1978,1980,1983,1985,1988,1991,1993,1996,1998,2000,2002,2004,2006,2008,2010,2012,2014,2016],{"class":31,"line":1969},56,[29,1971,1972],{"class":39},"    model ",[29,1974,200],{"class":56},[29,1976,1977],{"class":35}," rotate",[29,1979,1073],{"class":39},[29,1981,1982],{"class":35},"radians",[29,1984,49],{"class":39},[29,1986,1987],{"class":56},"-",[29,1989,1990],{"class":52},"55.0",[29,1992,57],{"class":56},[29,1994,1995],{"class":39},"), ",[29,1997,43],{"class":35},[29,1999,49],{"class":39},[29,2001,53],{"class":52},[29,2003,57],{"class":56},[29,2005,60],{"class":39},[29,2007,244],{"class":52},[29,2009,57],{"class":56},[29,2011,60],{"class":39},[29,2013,244],{"class":52},[29,2015,57],{"class":56},[29,2017,1089],{"class":39},[29,2019,2021],{"class":31,"line":2020},57,[29,2022,488],{"emptyLinePlaceholder":487},[29,2024,2026],{"class":31,"line":2025},58,[29,2027,2028],{"class":212},"    \u002F\u002F 投影矩阵\n",[29,2030,2032,2034,2036,2038,2041,2043,2045,2047,2050,2052,2054,2056,2058,2061,2064,2066,2068,2070,2073,2075,2077,2079,2081,2084,2086],{"class":31,"line":2031},59,[29,2033,1951],{"class":35},[29,2035,540],{"class":39},[29,2037,200],{"class":56},[29,2039,2040],{"class":35}," perspective",[29,2042,49],{"class":39},[29,2044,1982],{"class":35},[29,2046,49],{"class":39},[29,2048,2049],{"class":52},"45.0",[29,2051,57],{"class":56},[29,2053,680],{"class":39},[29,2055,677],{"class":56},[29,2057,686],{"class":39},[29,2059,2060],{"class":52},"800",[29,2062,2063],{"class":56}," \u002F",[29,2065,1748],{"class":39},[29,2067,677],{"class":56},[29,2069,686],{"class":39},[29,2071,2072],{"class":52},"600",[29,2074,60],{"class":39},[29,2076,1650],{"class":52},[29,2078,57],{"class":56},[29,2080,60],{"class":39},[29,2082,2083],{"class":52},"100.0",[29,2085,57],{"class":56},[29,2087,75],{"class":39},[29,2089,2091],{"class":31,"line":2090},60,[29,2092,488],{"emptyLinePlaceholder":487},[29,2094,2096],{"class":31,"line":2095},61,[29,2097,2098],{"class":212},"    \u002F\u002F 初始化GLFW\n",[29,2100,2102,2105],{"class":31,"line":2101},62,[29,2103,2104],{"class":35},"    glfwInit",[29,2106,809],{"class":39},[29,2108,2110,2113,2116,2118],{"class":31,"line":2109},63,[29,2111,2112],{"class":35},"    glfwWindowHint",[29,2114,2115],{"class":39},"(GLFW_CONTEXT_VERSION_MAJOR, ",[29,2117,661],{"class":52},[29,2119,75],{"class":39},[29,2121,2123,2125,2128,2130],{"class":31,"line":2122},64,[29,2124,2112],{"class":35},[29,2126,2127],{"class":39},"(GLFW_CONTEXT_VERSION_MINOR, ",[29,2129,661],{"class":52},[29,2131,75],{"class":39},[29,2133,2135,2137],{"class":31,"line":2134},65,[29,2136,2112],{"class":35},[29,2138,2139],{"class":39},"(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);\n",[29,2141,2143],{"class":31,"line":2142},66,[29,2144,1858],{"class":39},[29,2146,2148],{"class":31,"line":2147},67,[29,2149,2150],{"class":212},"    \u002F\u002F 设置 DPI 感知提示\n",[29,2152,2154,2157,2159,2162],{"class":31,"line":2153},68,[29,2155,2156],{"class":35},"    DPIStabilizer",[29,2158,40],{"class":39},[29,2160,2161],{"class":35},"setDPIHint",[29,2163,809],{"class":39},[29,2165,2167],{"class":31,"line":2166},69,[29,2168,488],{"emptyLinePlaceholder":487},[29,2170,2172],{"class":31,"line":2171},70,[29,2173,2174],{"class":212},"    \u002F\u002F 创建窗口\n",[29,2176,2178,2181,2183,2186,2188,2191,2193,2195,2197,2199,2201,2204,2206,2209,2211,2213],{"class":31,"line":2177},71,[29,2179,2180],{"class":35},"    GLFWwindow",[29,2182,206],{"class":56},[29,2184,2185],{"class":39}," window ",[29,2187,200],{"class":56},[29,2189,2190],{"class":35}," glfwCreateWindow",[29,2192,49],{"class":39},[29,2194,2060],{"class":52},[29,2196,60],{"class":39},[29,2198,2072],{"class":52},[29,2200,60],{"class":39},[29,2202,2203],{"class":821},"\"OpenGL Window\"",[29,2205,60],{"class":39},[29,2207,2208],{"class":52},"NULL",[29,2210,60],{"class":39},[29,2212,2208],{"class":52},[29,2214,75],{"class":39},[29,2216,2218,2220,2223,2225,2228],{"class":31,"line":2217},72,[29,2219,1745],{"class":56},[29,2221,2222],{"class":39}," (window ",[29,2224,1757],{"class":56},[29,2226,2227],{"class":52}," NULL",[29,2229,1547],{"class":39},[29,2231,2233,2235,2237,2239,2242,2245,2247],{"class":31,"line":2232},73,[29,2234,1886],{"class":35},[29,2236,1889],{"class":39},[29,2238,1892],{"class":56},[29,2240,2241],{"class":821}," \"Failed to create GLFW window\"",[29,2243,2244],{"class":56}," \u003C\u003C",[29,2246,1906],{"class":35},[29,2248,1909],{"class":39},[29,2250,2252,2255],{"class":31,"line":2251},74,[29,2253,2254],{"class":35},"        glfwTerminate",[29,2256,809],{"class":39},[29,2258,2260,2263,2266,2268],{"class":31,"line":2259},75,[29,2261,2262],{"class":56},"        return",[29,2264,2265],{"class":56}," -",[29,2267,612],{"class":52},[29,2269,1499],{"class":39},[29,2271,2273],{"class":31,"line":2272},76,[29,2274,1782],{"class":39},[29,2276,2278],{"class":31,"line":2277},77,[29,2279,488],{"emptyLinePlaceholder":487},[29,2281,2283,2286],{"class":31,"line":2282},78,[29,2284,2285],{"class":35},"    glfwMakeContextCurrent",[29,2287,1734],{"class":39},[29,2289,2291,2294],{"class":31,"line":2290},79,[29,2292,2293],{"class":35},"    glfwSetFramebufferSizeCallback",[29,2295,2296],{"class":39},"(window, framebuffer_size_callback);\n",[29,2298,2300],{"class":31,"line":2299},80,[29,2301,1858],{"class":39},[29,2303,2305],{"class":31,"line":2304},81,[29,2306,2307],{"class":212},"    \u002F\u002F 初始化 DPI 稳定器\n",[29,2309,2311,2313,2316],{"class":31,"line":2310},82,[29,2312,1728],{"class":39},[29,2314,2315],{"class":35},"initialize",[29,2317,1734],{"class":39},[29,2319,2321],{"class":31,"line":2320},83,[29,2322,488],{"emptyLinePlaceholder":487},[29,2324,2326],{"class":31,"line":2325},84,[29,2327,2328],{"class":212},"    \u002F\u002F 初始化GLAD\n",[29,2330,2332,2334,2336,2339,2342],{"class":31,"line":2331},85,[29,2333,1745],{"class":56},[29,2335,1748],{"class":39},[29,2337,2338],{"class":56},"!",[29,2340,2341],{"class":35},"gladLoadGLLoader",[29,2343,2344],{"class":39},"((GLADloadproc)glfwGetProcAddress)) {\n",[29,2346,2348,2350,2352,2354,2357,2359,2361],{"class":31,"line":2347},86,[29,2349,1886],{"class":35},[29,2351,1889],{"class":39},[29,2353,1892],{"class":56},[29,2355,2356],{"class":821}," \"Failed to initialize GLAD\"",[29,2358,2244],{"class":56},[29,2360,1906],{"class":35},[29,2362,1909],{"class":39},[29,2364,2366,2368,2370,2372],{"class":31,"line":2365},87,[29,2367,2262],{"class":56},[29,2369,2265],{"class":56},[29,2371,612],{"class":52},[29,2373,1499],{"class":39},[29,2375,2377],{"class":31,"line":2376},88,[29,2378,1782],{"class":39},[29,2380,2382],{"class":31,"line":2381},89,[29,2383,488],{"emptyLinePlaceholder":487},[29,2385,2387],{"class":31,"line":2386},90,[29,2388,2389],{"class":212},"    \u002F\u002F 初始化 ImGui 管理器\n",[29,2391,2393,2396,2398],{"class":31,"line":2392},91,[29,2394,2395],{"class":39},"    imguiManager.",[29,2397,2315],{"class":35},[29,2399,1734],{"class":39},[29,2401,2403],{"class":31,"line":2402},92,[29,2404,1858],{"class":39},[29,2406,2408],{"class":31,"line":2407},93,[29,2409,2410],{"class":212},"    \u002F\u002F 注册窗口\n",[29,2412,2414,2416,2419,2421,2424],{"class":31,"line":2413},94,[29,2415,2395],{"class":39},[29,2417,2418],{"class":35},"registerWindow",[29,2420,49],{"class":39},[29,2422,2423],{"class":821},"\"Hello ImGui!\"",[29,2425,2426],{"class":39},", []() {\n",[29,2428,2430,2433,2435,2438,2440,2443,2446,2449,2452,2455,2457,2460,2463,2466,2469],{"class":31,"line":2429},95,[29,2431,2432],{"class":35},"        ImGui",[29,2434,40],{"class":39},[29,2436,2437],{"class":35},"Text",[29,2439,49],{"class":39},[29,2441,2442],{"class":821},"\"DPI 稳定器状态: ",[29,2444,2445],{"class":52},"%s",[29,2447,2448],{"class":821},"\"",[29,2450,2451],{"class":39},", dpiStabilizer.",[29,2453,2454],{"class":35},"isMouseCaptured",[29,2456,1901],{"class":39},[29,2458,2459],{"class":56},"?",[29,2461,2462],{"class":821}," \"已捕获\"",[29,2464,2465],{"class":56}," :",[29,2467,2468],{"class":821}," \"已释放\"",[29,2470,75],{"class":39},[29,2472,2474,2476,2478,2480,2482,2485,2488,2490,2492,2495],{"class":31,"line":2473},96,[29,2475,2432],{"class":35},[29,2477,40],{"class":39},[29,2479,2437],{"class":35},[29,2481,49],{"class":39},[29,2483,2484],{"class":821},"\"鼠标敏感度: ",[29,2486,2487],{"class":52},"%.3f",[29,2489,2448],{"class":821},[29,2491,2451],{"class":39},[29,2493,2494],{"class":35},"getSensitivity",[29,2496,2497],{"class":39},"());\n",[29,2499,2501],{"class":31,"line":2500},97,[29,2502,2503],{"class":39},"        \n",[29,2505,2507,2510,2512,2515,2517,2520,2522,2525],{"class":31,"line":2506},98,[29,2508,2509],{"class":56},"        if",[29,2511,1748],{"class":39},[29,2513,2514],{"class":35},"ImGui",[29,2516,40],{"class":39},[29,2518,2519],{"class":35},"Button",[29,2521,49],{"class":39},[29,2523,2524],{"class":821},"\"重置统计\"",[29,2526,2527],{"class":39},")) {\n",[29,2529,2531,2534,2537],{"class":31,"line":2530},99,[29,2532,2533],{"class":39},"            dpiStabilizer.",[29,2535,2536],{"class":35},"resetStats",[29,2538,809],{"class":39},[29,2540,2542],{"class":31,"line":2541},100,[29,2543,2544],{"class":39},"        }\n",[29,2546,2548],{"class":31,"line":2547},101,[29,2549,2503],{"class":39},[29,2551,2553,2556,2559,2561,2563,2565],{"class":31,"line":2552},102,[29,2554,2555],{"class":56},"        float",[29,2557,2558],{"class":39}," sensitivity ",[29,2560,200],{"class":56},[29,2562,1895],{"class":39},[29,2564,2494],{"class":35},[29,2566,809],{"class":39},[29,2568,2570,2572,2574,2576,2578,2581,2583,2586,2588,2590,2593,2596,2598,2600,2602,2604],{"class":31,"line":2569},103,[29,2571,2509],{"class":56},[29,2573,1748],{"class":39},[29,2575,2514],{"class":35},[29,2577,40],{"class":39},[29,2579,2580],{"class":35},"SliderFloat",[29,2582,49],{"class":39},[29,2584,2585],{"class":821},"\"鼠标敏感度\"",[29,2587,60],{"class":39},[29,2589,617],{"class":56},[29,2591,2592],{"class":39},"sensitivity, ",[29,2594,2595],{"class":52},"0.01",[29,2597,57],{"class":56},[29,2599,60],{"class":39},[29,2601,63],{"class":52},[29,2603,57],{"class":56},[29,2605,2527],{"class":39},[29,2607,2609,2611,2614],{"class":31,"line":2608},104,[29,2610,2533],{"class":39},[29,2612,2613],{"class":35},"setSensitivity",[29,2615,2616],{"class":39},"(sensitivity);\n",[29,2618,2620],{"class":31,"line":2619},105,[29,2621,2544],{"class":39},[29,2623,2625],{"class":31,"line":2624},106,[29,2626,2627],{"class":39},"    });\n",[29,2629,2631],{"class":31,"line":2630},107,[29,2632,1858],{"class":39},[29,2634,2636,2638,2640,2642,2644],{"class":31,"line":2635},108,[29,2637,2395],{"class":39},[29,2639,2418],{"class":35},[29,2641,49],{"class":39},[29,2643,1774],{"class":821},[29,2645,2426],{"class":39},[29,2647,2649,2652,2654,2657,2659,2662,2664,2667],{"class":31,"line":2648},109,[29,2650,2651],{"class":35},"        ImGuiIO",[29,2653,617],{"class":56},[29,2655,2656],{"class":39}," io ",[29,2658,200],{"class":56},[29,2660,2661],{"class":35}," ImGui",[29,2663,40],{"class":39},[29,2665,2666],{"class":35},"GetIO",[29,2668,809],{"class":39},[29,2670,2672,2674,2676,2678,2680,2683,2686,2688],{"class":31,"line":2671},110,[29,2673,2432],{"class":35},[29,2675,40],{"class":39},[29,2677,2437],{"class":35},[29,2679,49],{"class":39},[29,2681,2682],{"class":821},"\"FPS: ",[29,2684,2685],{"class":52},"%.1f",[29,2687,2448],{"class":821},[29,2689,2690],{"class":39},", io.Framerate);\n",[29,2692,2694,2696,2698,2700,2702,2705,2707,2710,2712,2715,2717,2719],{"class":31,"line":2693},111,[29,2695,2432],{"class":35},[29,2697,40],{"class":39},[29,2699,2437],{"class":35},[29,2701,49],{"class":39},[29,2703,2704],{"class":821},"\"帧时间: ",[29,2706,2487],{"class":52},[29,2708,2709],{"class":821}," ms\"",[29,2711,60],{"class":39},[29,2713,2714],{"class":52},"1000.0",[29,2716,57],{"class":56},[29,2718,2063],{"class":56},[29,2720,2721],{"class":39}," io.Framerate);\n",[29,2723,2725],{"class":31,"line":2724},112,[29,2726,2627],{"class":39},[29,2728,2730],{"class":31,"line":2729},113,[29,2731,1858],{"class":39},[29,2733,2735],{"class":31,"line":2734},114,[29,2736,488],{"emptyLinePlaceholder":487},[29,2738,2740],{"class":31,"line":2739},115,[29,2741,2742],{"class":212},"    \u002F\u002F 启用深度测试\n",[29,2744,2746,2749],{"class":31,"line":2745},116,[29,2747,2748],{"class":35},"    glEnable",[29,2750,2751],{"class":39},"(GL_DEPTH_TEST);\n",[29,2753,2755],{"class":31,"line":2754},117,[29,2756,488],{"emptyLinePlaceholder":487},[29,2758,2760,2763,2766,2768],{"class":31,"line":2759},118,[29,2761,2762],{"class":56},"    float",[29,2764,2765],{"class":39}," vertices[] ",[29,2767,200],{"class":56},[29,2769,2770],{"class":39}," {\n",[29,2772,2774],{"class":31,"line":2773},119,[29,2775,2776],{"class":212},"    \u002F\u002F 立方体顶点数据（位置 + 法线）\n",[29,2778,2780],{"class":31,"line":2779},120,[29,2781,2782],{"class":212},"    \u002F\u002F 位置(3) + 法线(3) = 6个浮点数\n",[29,2784,2786],{"class":31,"line":2785},121,[29,2787,2788],{"class":212},"    \u002F\u002F 前面\n",[29,2790,2792,2795,2797,2799,2801,2803,2805,2807,2809,2811,2813,2815,2817,2819,2821,2823,2825,2827,2829,2831],{"class":31,"line":2791},122,[29,2793,2794],{"class":56},"    -",[29,2796,63],{"class":52},[29,2798,57],{"class":56},[29,2800,60],{"class":39},[29,2802,1987],{"class":56},[29,2804,63],{"class":52},[29,2806,57],{"class":56},[29,2808,856],{"class":39},[29,2810,63],{"class":52},[29,2812,57],{"class":56},[29,2814,856],{"class":39},[29,2816,244],{"class":52},[29,2818,57],{"class":56},[29,2820,856],{"class":39},[29,2822,244],{"class":52},[29,2824,57],{"class":56},[29,2826,856],{"class":39},[29,2828,53],{"class":52},[29,2830,57],{"class":56},[29,2832,2833],{"class":39},",\n",[29,2835,2837,2840,2842,2844,2846,2848,2850,2852,2854,2856,2858,2860,2862,2864,2866,2868,2870,2872,2874],{"class":31,"line":2836},123,[29,2838,2839],{"class":52},"     0.5",[29,2841,57],{"class":56},[29,2843,60],{"class":39},[29,2845,1987],{"class":56},[29,2847,63],{"class":52},[29,2849,57],{"class":56},[29,2851,856],{"class":39},[29,2853,63],{"class":52},[29,2855,57],{"class":56},[29,2857,856],{"class":39},[29,2859,244],{"class":52},[29,2861,57],{"class":56},[29,2863,856],{"class":39},[29,2865,244],{"class":52},[29,2867,57],{"class":56},[29,2869,856],{"class":39},[29,2871,53],{"class":52},[29,2873,57],{"class":56},[29,2875,2833],{"class":39},[29,2877,2879,2881,2883,2885,2887,2889,2891,2893,2895,2897,2899,2901,2903,2905,2907,2909,2911,2913],{"class":31,"line":2878},124,[29,2880,2839],{"class":52},[29,2882,57],{"class":56},[29,2884,856],{"class":39},[29,2886,63],{"class":52},[29,2888,57],{"class":56},[29,2890,856],{"class":39},[29,2892,63],{"class":52},[29,2894,57],{"class":56},[29,2896,856],{"class":39},[29,2898,244],{"class":52},[29,2900,57],{"class":56},[29,2902,856],{"class":39},[29,2904,244],{"class":52},[29,2906,57],{"class":56},[29,2908,856],{"class":39},[29,2910,53],{"class":52},[29,2912,57],{"class":56},[29,2914,2833],{"class":39},[29,2916,2918,2920,2922,2924,2926,2928,2930,2932,2934,2936,2938,2940,2942,2944,2946,2948,2950,2952],{"class":31,"line":2917},125,[29,2919,2839],{"class":52},[29,2921,57],{"class":56},[29,2923,856],{"class":39},[29,2925,63],{"class":52},[29,2927,57],{"class":56},[29,2929,856],{"class":39},[29,2931,63],{"class":52},[29,2933,57],{"class":56},[29,2935,856],{"class":39},[29,2937,244],{"class":52},[29,2939,57],{"class":56},[29,2941,856],{"class":39},[29,2943,244],{"class":52},[29,2945,57],{"class":56},[29,2947,856],{"class":39},[29,2949,53],{"class":52},[29,2951,57],{"class":56},[29,2953,2833],{"class":39},[29,2955,2957,2959,2961,2963,2965,2967,2969,2971,2973,2975,2977,2979,2981,2983,2985,2987,2989,2991,2993],{"class":31,"line":2956},126,[29,2958,2794],{"class":56},[29,2960,63],{"class":52},[29,2962,57],{"class":56},[29,2964,856],{"class":39},[29,2966,63],{"class":52},[29,2968,57],{"class":56},[29,2970,856],{"class":39},[29,2972,63],{"class":52},[29,2974,57],{"class":56},[29,2976,856],{"class":39},[29,2978,244],{"class":52},[29,2980,57],{"class":56},[29,2982,856],{"class":39},[29,2984,244],{"class":52},[29,2986,57],{"class":56},[29,2988,856],{"class":39},[29,2990,53],{"class":52},[29,2992,57],{"class":56},[29,2994,2833],{"class":39},[29,2996,2998,3000,3002,3004,3006,3008,3010,3012,3014,3016,3018,3020,3022,3024,3026,3028,3030,3032,3034,3036],{"class":31,"line":2997},127,[29,2999,2794],{"class":56},[29,3001,63],{"class":52},[29,3003,57],{"class":56},[29,3005,60],{"class":39},[29,3007,1987],{"class":56},[29,3009,63],{"class":52},[29,3011,57],{"class":56},[29,3013,856],{"class":39},[29,3015,63],{"class":52},[29,3017,57],{"class":56},[29,3019,856],{"class":39},[29,3021,244],{"class":52},[29,3023,57],{"class":56},[29,3025,856],{"class":39},[29,3027,244],{"class":52},[29,3029,57],{"class":56},[29,3031,856],{"class":39},[29,3033,53],{"class":52},[29,3035,57],{"class":56},[29,3037,2833],{"class":39},[29,3039,3041],{"class":31,"line":3040},128,[29,3042,1858],{"class":39},[29,3044,3046],{"class":31,"line":3045},129,[29,3047,3048],{"class":212},"    \u002F\u002F 后面\n",[29,3050,3052,3054,3056,3058,3060,3062,3064,3066,3068,3070,3072,3074,3076,3078,3080,3082,3084,3086,3088,3090,3092,3094],{"class":31,"line":3051},130,[29,3053,2794],{"class":56},[29,3055,63],{"class":52},[29,3057,57],{"class":56},[29,3059,60],{"class":39},[29,3061,1987],{"class":56},[29,3063,63],{"class":52},[29,3065,57],{"class":56},[29,3067,60],{"class":39},[29,3069,1987],{"class":56},[29,3071,63],{"class":52},[29,3073,57],{"class":56},[29,3075,856],{"class":39},[29,3077,244],{"class":52},[29,3079,57],{"class":56},[29,3081,856],{"class":39},[29,3083,244],{"class":52},[29,3085,57],{"class":56},[29,3087,60],{"class":39},[29,3089,1987],{"class":56},[29,3091,53],{"class":52},[29,3093,57],{"class":56},[29,3095,2833],{"class":39},[29,3097,3099,3101,3103,3105,3107,3109,3111,3113,3115,3117,3119,3121,3123,3125,3127,3129,3131,3133,3135,3137,3139],{"class":31,"line":3098},131,[29,3100,2839],{"class":52},[29,3102,57],{"class":56},[29,3104,60],{"class":39},[29,3106,1987],{"class":56},[29,3108,63],{"class":52},[29,3110,57],{"class":56},[29,3112,60],{"class":39},[29,3114,1987],{"class":56},[29,3116,63],{"class":52},[29,3118,57],{"class":56},[29,3120,856],{"class":39},[29,3122,244],{"class":52},[29,3124,57],{"class":56},[29,3126,856],{"class":39},[29,3128,244],{"class":52},[29,3130,57],{"class":56},[29,3132,60],{"class":39},[29,3134,1987],{"class":56},[29,3136,53],{"class":52},[29,3138,57],{"class":56},[29,3140,2833],{"class":39},[29,3142,3144,3146,3148,3150,3152,3154,3156,3158,3160,3162,3164,3166,3168,3170,3172,3174,3176,3178,3180,3182],{"class":31,"line":3143},132,[29,3145,2839],{"class":52},[29,3147,57],{"class":56},[29,3149,856],{"class":39},[29,3151,63],{"class":52},[29,3153,57],{"class":56},[29,3155,60],{"class":39},[29,3157,1987],{"class":56},[29,3159,63],{"class":52},[29,3161,57],{"class":56},[29,3163,856],{"class":39},[29,3165,244],{"class":52},[29,3167,57],{"class":56},[29,3169,856],{"class":39},[29,3171,244],{"class":52},[29,3173,57],{"class":56},[29,3175,60],{"class":39},[29,3177,1987],{"class":56},[29,3179,53],{"class":52},[29,3181,57],{"class":56},[29,3183,2833],{"class":39},[29,3185,3187,3189,3191,3193,3195,3197,3199,3201,3203,3205,3207,3209,3211,3213,3215,3217,3219,3221,3223,3225],{"class":31,"line":3186},133,[29,3188,2839],{"class":52},[29,3190,57],{"class":56},[29,3192,856],{"class":39},[29,3194,63],{"class":52},[29,3196,57],{"class":56},[29,3198,60],{"class":39},[29,3200,1987],{"class":56},[29,3202,63],{"class":52},[29,3204,57],{"class":56},[29,3206,856],{"class":39},[29,3208,244],{"class":52},[29,3210,57],{"class":56},[29,3212,856],{"class":39},[29,3214,244],{"class":52},[29,3216,57],{"class":56},[29,3218,60],{"class":39},[29,3220,1987],{"class":56},[29,3222,53],{"class":52},[29,3224,57],{"class":56},[29,3226,2833],{"class":39},[29,3228,3230,3232,3234,3236,3238,3240,3242,3244,3246,3248,3250,3252,3254,3256,3258,3260,3262,3264,3266,3268,3270],{"class":31,"line":3229},134,[29,3231,2794],{"class":56},[29,3233,63],{"class":52},[29,3235,57],{"class":56},[29,3237,856],{"class":39},[29,3239,63],{"class":52},[29,3241,57],{"class":56},[29,3243,60],{"class":39},[29,3245,1987],{"class":56},[29,3247,63],{"class":52},[29,3249,57],{"class":56},[29,3251,856],{"class":39},[29,3253,244],{"class":52},[29,3255,57],{"class":56},[29,3257,856],{"class":39},[29,3259,244],{"class":52},[29,3261,57],{"class":56},[29,3263,60],{"class":39},[29,3265,1987],{"class":56},[29,3267,53],{"class":52},[29,3269,57],{"class":56},[29,3271,2833],{"class":39},[29,3273,3275,3277,3279,3281,3283,3285,3287,3289,3291,3293,3295,3297,3299,3301,3303,3305,3307,3309,3311,3313,3315,3317],{"class":31,"line":3274},135,[29,3276,2794],{"class":56},[29,3278,63],{"class":52},[29,3280,57],{"class":56},[29,3282,60],{"class":39},[29,3284,1987],{"class":56},[29,3286,63],{"class":52},[29,3288,57],{"class":56},[29,3290,60],{"class":39},[29,3292,1987],{"class":56},[29,3294,63],{"class":52},[29,3296,57],{"class":56},[29,3298,856],{"class":39},[29,3300,244],{"class":52},[29,3302,57],{"class":56},[29,3304,856],{"class":39},[29,3306,244],{"class":52},[29,3308,57],{"class":56},[29,3310,60],{"class":39},[29,3312,1987],{"class":56},[29,3314,53],{"class":52},[29,3316,57],{"class":56},[29,3318,2833],{"class":39},[29,3320,3322],{"class":31,"line":3321},136,[29,3323,1858],{"class":39},[29,3325,3327],{"class":31,"line":3326},137,[29,3328,3329],{"class":212},"    \u002F\u002F 左面\n",[29,3331,3333,3335,3337,3339,3341,3343,3345,3347,3349,3351,3353,3355,3357,3359,3361,3363,3365,3367,3369,3371],{"class":31,"line":3332},138,[29,3334,2794],{"class":56},[29,3336,63],{"class":52},[29,3338,57],{"class":56},[29,3340,856],{"class":39},[29,3342,63],{"class":52},[29,3344,57],{"class":56},[29,3346,856],{"class":39},[29,3348,63],{"class":52},[29,3350,57],{"class":56},[29,3352,60],{"class":39},[29,3354,1987],{"class":56},[29,3356,53],{"class":52},[29,3358,57],{"class":56},[29,3360,856],{"class":39},[29,3362,244],{"class":52},[29,3364,57],{"class":56},[29,3366,856],{"class":39},[29,3368,244],{"class":52},[29,3370,57],{"class":56},[29,3372,2833],{"class":39},[29,3374,3376,3378,3380,3382,3384,3386,3388,3390,3392,3394,3396,3398,3400,3402,3404,3406,3408,3410,3412,3414,3416],{"class":31,"line":3375},139,[29,3377,2794],{"class":56},[29,3379,63],{"class":52},[29,3381,57],{"class":56},[29,3383,856],{"class":39},[29,3385,63],{"class":52},[29,3387,57],{"class":56},[29,3389,60],{"class":39},[29,3391,1987],{"class":56},[29,3393,63],{"class":52},[29,3395,57],{"class":56},[29,3397,60],{"class":39},[29,3399,1987],{"class":56},[29,3401,53],{"class":52},[29,3403,57],{"class":56},[29,3405,856],{"class":39},[29,3407,244],{"class":52},[29,3409,57],{"class":56},[29,3411,856],{"class":39},[29,3413,244],{"class":52},[29,3415,57],{"class":56},[29,3417,2833],{"class":39},[29,3419,3421,3423,3425,3427,3429,3431,3433,3435,3437,3439,3441,3443,3445,3447,3449,3451,3453,3455,3457,3459,3461,3463],{"class":31,"line":3420},140,[29,3422,2794],{"class":56},[29,3424,63],{"class":52},[29,3426,57],{"class":56},[29,3428,60],{"class":39},[29,3430,1987],{"class":56},[29,3432,63],{"class":52},[29,3434,57],{"class":56},[29,3436,60],{"class":39},[29,3438,1987],{"class":56},[29,3440,63],{"class":52},[29,3442,57],{"class":56},[29,3444,60],{"class":39},[29,3446,1987],{"class":56},[29,3448,53],{"class":52},[29,3450,57],{"class":56},[29,3452,856],{"class":39},[29,3454,244],{"class":52},[29,3456,57],{"class":56},[29,3458,856],{"class":39},[29,3460,244],{"class":52},[29,3462,57],{"class":56},[29,3464,2833],{"class":39},[29,3466,3468,3470,3472,3474,3476,3478,3480,3482,3484,3486,3488,3490,3492,3494,3496,3498,3500,3502,3504,3506,3508,3510],{"class":31,"line":3467},141,[29,3469,2794],{"class":56},[29,3471,63],{"class":52},[29,3473,57],{"class":56},[29,3475,60],{"class":39},[29,3477,1987],{"class":56},[29,3479,63],{"class":52},[29,3481,57],{"class":56},[29,3483,60],{"class":39},[29,3485,1987],{"class":56},[29,3487,63],{"class":52},[29,3489,57],{"class":56},[29,3491,60],{"class":39},[29,3493,1987],{"class":56},[29,3495,53],{"class":52},[29,3497,57],{"class":56},[29,3499,856],{"class":39},[29,3501,244],{"class":52},[29,3503,57],{"class":56},[29,3505,856],{"class":39},[29,3507,244],{"class":52},[29,3509,57],{"class":56},[29,3511,2833],{"class":39},[29,3513,3515,3517,3519,3521,3523,3525,3527,3529,3531,3533,3535,3537,3539,3541,3543,3545,3547,3549,3551,3553,3555],{"class":31,"line":3514},142,[29,3516,2794],{"class":56},[29,3518,63],{"class":52},[29,3520,57],{"class":56},[29,3522,60],{"class":39},[29,3524,1987],{"class":56},[29,3526,63],{"class":52},[29,3528,57],{"class":56},[29,3530,856],{"class":39},[29,3532,63],{"class":52},[29,3534,57],{"class":56},[29,3536,60],{"class":39},[29,3538,1987],{"class":56},[29,3540,53],{"class":52},[29,3542,57],{"class":56},[29,3544,856],{"class":39},[29,3546,244],{"class":52},[29,3548,57],{"class":56},[29,3550,856],{"class":39},[29,3552,244],{"class":52},[29,3554,57],{"class":56},[29,3556,2833],{"class":39},[29,3558,3560,3562,3564,3566,3568,3570,3572,3574,3576,3578,3580,3582,3584,3586,3588,3590,3592,3594,3596,3598],{"class":31,"line":3559},143,[29,3561,2794],{"class":56},[29,3563,63],{"class":52},[29,3565,57],{"class":56},[29,3567,856],{"class":39},[29,3569,63],{"class":52},[29,3571,57],{"class":56},[29,3573,856],{"class":39},[29,3575,63],{"class":52},[29,3577,57],{"class":56},[29,3579,60],{"class":39},[29,3581,1987],{"class":56},[29,3583,53],{"class":52},[29,3585,57],{"class":56},[29,3587,856],{"class":39},[29,3589,244],{"class":52},[29,3591,57],{"class":56},[29,3593,856],{"class":39},[29,3595,244],{"class":52},[29,3597,57],{"class":56},[29,3599,2833],{"class":39},[29,3601,3603],{"class":31,"line":3602},144,[29,3604,1858],{"class":39},[29,3606,3608],{"class":31,"line":3607},145,[29,3609,3610],{"class":212},"    \u002F\u002F 右面\n",[29,3612,3614,3616,3618,3620,3622,3624,3626,3628,3630,3632,3634,3636,3638,3640,3642,3644,3646,3648],{"class":31,"line":3613},146,[29,3615,2839],{"class":52},[29,3617,57],{"class":56},[29,3619,856],{"class":39},[29,3621,63],{"class":52},[29,3623,57],{"class":56},[29,3625,856],{"class":39},[29,3627,63],{"class":52},[29,3629,57],{"class":56},[29,3631,856],{"class":39},[29,3633,53],{"class":52},[29,3635,57],{"class":56},[29,3637,856],{"class":39},[29,3639,244],{"class":52},[29,3641,57],{"class":56},[29,3643,856],{"class":39},[29,3645,244],{"class":52},[29,3647,57],{"class":56},[29,3649,2833],{"class":39},[29,3651,3653,3655,3657,3659,3661,3663,3665,3667,3669,3671,3673,3675,3677,3679,3681,3683,3685,3687,3689],{"class":31,"line":3652},147,[29,3654,2839],{"class":52},[29,3656,57],{"class":56},[29,3658,856],{"class":39},[29,3660,63],{"class":52},[29,3662,57],{"class":56},[29,3664,60],{"class":39},[29,3666,1987],{"class":56},[29,3668,63],{"class":52},[29,3670,57],{"class":56},[29,3672,856],{"class":39},[29,3674,53],{"class":52},[29,3676,57],{"class":56},[29,3678,856],{"class":39},[29,3680,244],{"class":52},[29,3682,57],{"class":56},[29,3684,856],{"class":39},[29,3686,244],{"class":52},[29,3688,57],{"class":56},[29,3690,2833],{"class":39},[29,3692,3694,3696,3698,3700,3702,3704,3706,3708,3710,3712,3714,3716,3718,3720,3722,3724,3726,3728,3730,3732],{"class":31,"line":3693},148,[29,3695,2839],{"class":52},[29,3697,57],{"class":56},[29,3699,60],{"class":39},[29,3701,1987],{"class":56},[29,3703,63],{"class":52},[29,3705,57],{"class":56},[29,3707,60],{"class":39},[29,3709,1987],{"class":56},[29,3711,63],{"class":52},[29,3713,57],{"class":56},[29,3715,856],{"class":39},[29,3717,53],{"class":52},[29,3719,57],{"class":56},[29,3721,856],{"class":39},[29,3723,244],{"class":52},[29,3725,57],{"class":56},[29,3727,856],{"class":39},[29,3729,244],{"class":52},[29,3731,57],{"class":56},[29,3733,2833],{"class":39},[29,3735,3737,3739,3741,3743,3745,3747,3749,3751,3753,3755,3757,3759,3761,3763,3765,3767,3769,3771,3773,3775],{"class":31,"line":3736},149,[29,3738,2839],{"class":52},[29,3740,57],{"class":56},[29,3742,60],{"class":39},[29,3744,1987],{"class":56},[29,3746,63],{"class":52},[29,3748,57],{"class":56},[29,3750,60],{"class":39},[29,3752,1987],{"class":56},[29,3754,63],{"class":52},[29,3756,57],{"class":56},[29,3758,856],{"class":39},[29,3760,53],{"class":52},[29,3762,57],{"class":56},[29,3764,856],{"class":39},[29,3766,244],{"class":52},[29,3768,57],{"class":56},[29,3770,856],{"class":39},[29,3772,244],{"class":52},[29,3774,57],{"class":56},[29,3776,2833],{"class":39},[29,3778,3780,3782,3784,3786,3788,3790,3792,3794,3796,3798,3800,3802,3804,3806,3808,3810,3812,3814,3816],{"class":31,"line":3779},150,[29,3781,2839],{"class":52},[29,3783,57],{"class":56},[29,3785,60],{"class":39},[29,3787,1987],{"class":56},[29,3789,63],{"class":52},[29,3791,57],{"class":56},[29,3793,856],{"class":39},[29,3795,63],{"class":52},[29,3797,57],{"class":56},[29,3799,856],{"class":39},[29,3801,53],{"class":52},[29,3803,57],{"class":56},[29,3805,856],{"class":39},[29,3807,244],{"class":52},[29,3809,57],{"class":56},[29,3811,856],{"class":39},[29,3813,244],{"class":52},[29,3815,57],{"class":56},[29,3817,2833],{"class":39},[29,3819,3821,3823,3825,3827,3829,3831,3833,3835,3837,3839,3841,3843,3845,3847,3849,3851,3853,3855],{"class":31,"line":3820},151,[29,3822,2839],{"class":52},[29,3824,57],{"class":56},[29,3826,856],{"class":39},[29,3828,63],{"class":52},[29,3830,57],{"class":56},[29,3832,856],{"class":39},[29,3834,63],{"class":52},[29,3836,57],{"class":56},[29,3838,856],{"class":39},[29,3840,53],{"class":52},[29,3842,57],{"class":56},[29,3844,856],{"class":39},[29,3846,244],{"class":52},[29,3848,57],{"class":56},[29,3850,856],{"class":39},[29,3852,244],{"class":52},[29,3854,57],{"class":56},[29,3856,2833],{"class":39},[29,3858,3860],{"class":31,"line":3859},152,[29,3861,1858],{"class":39},[29,3863,3865],{"class":31,"line":3864},153,[29,3866,3867],{"class":212},"    \u002F\u002F 下面\n",[29,3869,3871,3873,3875,3877,3879,3881,3883,3885,3887,3889,3891,3893,3895,3897,3899,3901,3903,3905,3907,3909,3911,3913],{"class":31,"line":3870},154,[29,3872,2794],{"class":56},[29,3874,63],{"class":52},[29,3876,57],{"class":56},[29,3878,60],{"class":39},[29,3880,1987],{"class":56},[29,3882,63],{"class":52},[29,3884,57],{"class":56},[29,3886,60],{"class":39},[29,3888,1987],{"class":56},[29,3890,63],{"class":52},[29,3892,57],{"class":56},[29,3894,856],{"class":39},[29,3896,244],{"class":52},[29,3898,57],{"class":56},[29,3900,60],{"class":39},[29,3902,1987],{"class":56},[29,3904,53],{"class":52},[29,3906,57],{"class":56},[29,3908,856],{"class":39},[29,3910,244],{"class":52},[29,3912,57],{"class":56},[29,3914,2833],{"class":39},[29,3916,3918,3920,3922,3924,3926,3928,3930,3932,3934,3936,3938,3940,3942,3944,3946,3948,3950,3952,3954,3956,3958],{"class":31,"line":3917},155,[29,3919,2839],{"class":52},[29,3921,57],{"class":56},[29,3923,60],{"class":39},[29,3925,1987],{"class":56},[29,3927,63],{"class":52},[29,3929,57],{"class":56},[29,3931,60],{"class":39},[29,3933,1987],{"class":56},[29,3935,63],{"class":52},[29,3937,57],{"class":56},[29,3939,856],{"class":39},[29,3941,244],{"class":52},[29,3943,57],{"class":56},[29,3945,60],{"class":39},[29,3947,1987],{"class":56},[29,3949,53],{"class":52},[29,3951,57],{"class":56},[29,3953,856],{"class":39},[29,3955,244],{"class":52},[29,3957,57],{"class":56},[29,3959,2833],{"class":39},[29,3961,3963,3965,3967,3969,3971,3973,3975,3977,3979,3981,3983,3985,3987,3989,3991,3993,3995,3997,3999,4001],{"class":31,"line":3962},156,[29,3964,2839],{"class":52},[29,3966,57],{"class":56},[29,3968,60],{"class":39},[29,3970,1987],{"class":56},[29,3972,63],{"class":52},[29,3974,57],{"class":56},[29,3976,856],{"class":39},[29,3978,63],{"class":52},[29,3980,57],{"class":56},[29,3982,856],{"class":39},[29,3984,244],{"class":52},[29,3986,57],{"class":56},[29,3988,60],{"class":39},[29,3990,1987],{"class":56},[29,3992,53],{"class":52},[29,3994,57],{"class":56},[29,3996,856],{"class":39},[29,3998,244],{"class":52},[29,4000,57],{"class":56},[29,4002,2833],{"class":39},[29,4004,4006,4008,4010,4012,4014,4016,4018,4020,4022,4024,4026,4028,4030,4032,4034,4036,4038,4040,4042,4044],{"class":31,"line":4005},157,[29,4007,2839],{"class":52},[29,4009,57],{"class":56},[29,4011,60],{"class":39},[29,4013,1987],{"class":56},[29,4015,63],{"class":52},[29,4017,57],{"class":56},[29,4019,856],{"class":39},[29,4021,63],{"class":52},[29,4023,57],{"class":56},[29,4025,856],{"class":39},[29,4027,244],{"class":52},[29,4029,57],{"class":56},[29,4031,60],{"class":39},[29,4033,1987],{"class":56},[29,4035,53],{"class":52},[29,4037,57],{"class":56},[29,4039,856],{"class":39},[29,4041,244],{"class":52},[29,4043,57],{"class":56},[29,4045,2833],{"class":39},[29,4047,4049,4051,4053,4055,4057,4059,4061,4063,4065,4067,4069,4071,4073,4075,4077,4079,4081,4083,4085,4087,4089],{"class":31,"line":4048},158,[29,4050,2794],{"class":56},[29,4052,63],{"class":52},[29,4054,57],{"class":56},[29,4056,60],{"class":39},[29,4058,1987],{"class":56},[29,4060,63],{"class":52},[29,4062,57],{"class":56},[29,4064,856],{"class":39},[29,4066,63],{"class":52},[29,4068,57],{"class":56},[29,4070,856],{"class":39},[29,4072,244],{"class":52},[29,4074,57],{"class":56},[29,4076,60],{"class":39},[29,4078,1987],{"class":56},[29,4080,53],{"class":52},[29,4082,57],{"class":56},[29,4084,856],{"class":39},[29,4086,244],{"class":52},[29,4088,57],{"class":56},[29,4090,2833],{"class":39},[29,4092,4094,4096,4098,4100,4102,4104,4106,4108,4110,4112,4114,4116,4118,4120,4122,4124,4126,4128,4130,4132,4134,4136],{"class":31,"line":4093},159,[29,4095,2794],{"class":56},[29,4097,63],{"class":52},[29,4099,57],{"class":56},[29,4101,60],{"class":39},[29,4103,1987],{"class":56},[29,4105,63],{"class":52},[29,4107,57],{"class":56},[29,4109,60],{"class":39},[29,4111,1987],{"class":56},[29,4113,63],{"class":52},[29,4115,57],{"class":56},[29,4117,856],{"class":39},[29,4119,244],{"class":52},[29,4121,57],{"class":56},[29,4123,60],{"class":39},[29,4125,1987],{"class":56},[29,4127,53],{"class":52},[29,4129,57],{"class":56},[29,4131,856],{"class":39},[29,4133,244],{"class":52},[29,4135,57],{"class":56},[29,4137,2833],{"class":39},[29,4139,4141],{"class":31,"line":4140},160,[29,4142,1858],{"class":39},[29,4144,4146],{"class":31,"line":4145},161,[29,4147,4148],{"class":212},"    \u002F\u002F 上面\n",[29,4150,4152,4154,4156,4158,4160,4162,4164,4166,4168,4170,4172,4174,4176,4178,4180,4182,4184,4186,4188,4190],{"class":31,"line":4151},162,[29,4153,2794],{"class":56},[29,4155,63],{"class":52},[29,4157,57],{"class":56},[29,4159,856],{"class":39},[29,4161,63],{"class":52},[29,4163,57],{"class":56},[29,4165,60],{"class":39},[29,4167,1987],{"class":56},[29,4169,63],{"class":52},[29,4171,57],{"class":56},[29,4173,856],{"class":39},[29,4175,244],{"class":52},[29,4177,57],{"class":56},[29,4179,856],{"class":39},[29,4181,53],{"class":52},[29,4183,57],{"class":56},[29,4185,856],{"class":39},[29,4187,244],{"class":52},[29,4189,57],{"class":56},[29,4191,2833],{"class":39},[29,4193,4195,4197,4199,4201,4203,4205,4207,4209,4211,4213,4215,4217,4219,4221,4223,4225,4227,4229,4231],{"class":31,"line":4194},163,[29,4196,2839],{"class":52},[29,4198,57],{"class":56},[29,4200,856],{"class":39},[29,4202,63],{"class":52},[29,4204,57],{"class":56},[29,4206,60],{"class":39},[29,4208,1987],{"class":56},[29,4210,63],{"class":52},[29,4212,57],{"class":56},[29,4214,856],{"class":39},[29,4216,244],{"class":52},[29,4218,57],{"class":56},[29,4220,856],{"class":39},[29,4222,53],{"class":52},[29,4224,57],{"class":56},[29,4226,856],{"class":39},[29,4228,244],{"class":52},[29,4230,57],{"class":56},[29,4232,2833],{"class":39},[29,4234,4236,4238,4240,4242,4244,4246,4248,4250,4252,4254,4256,4258,4260,4262,4264,4266,4268,4270],{"class":31,"line":4235},164,[29,4237,2839],{"class":52},[29,4239,57],{"class":56},[29,4241,856],{"class":39},[29,4243,63],{"class":52},[29,4245,57],{"class":56},[29,4247,856],{"class":39},[29,4249,63],{"class":52},[29,4251,57],{"class":56},[29,4253,856],{"class":39},[29,4255,244],{"class":52},[29,4257,57],{"class":56},[29,4259,856],{"class":39},[29,4261,53],{"class":52},[29,4263,57],{"class":56},[29,4265,856],{"class":39},[29,4267,244],{"class":52},[29,4269,57],{"class":56},[29,4271,2833],{"class":39},[29,4273,4275,4277,4279,4281,4283,4285,4287,4289,4291,4293,4295,4297,4299,4301,4303,4305,4307,4309],{"class":31,"line":4274},165,[29,4276,2839],{"class":52},[29,4278,57],{"class":56},[29,4280,856],{"class":39},[29,4282,63],{"class":52},[29,4284,57],{"class":56},[29,4286,856],{"class":39},[29,4288,63],{"class":52},[29,4290,57],{"class":56},[29,4292,856],{"class":39},[29,4294,244],{"class":52},[29,4296,57],{"class":56},[29,4298,856],{"class":39},[29,4300,53],{"class":52},[29,4302,57],{"class":56},[29,4304,856],{"class":39},[29,4306,244],{"class":52},[29,4308,57],{"class":56},[29,4310,2833],{"class":39},[29,4312,4314,4316,4318,4320,4322,4324,4326,4328,4330,4332,4334,4336,4338,4340,4342,4344,4346,4348,4350],{"class":31,"line":4313},166,[29,4315,2794],{"class":56},[29,4317,63],{"class":52},[29,4319,57],{"class":56},[29,4321,856],{"class":39},[29,4323,63],{"class":52},[29,4325,57],{"class":56},[29,4327,856],{"class":39},[29,4329,63],{"class":52},[29,4331,57],{"class":56},[29,4333,856],{"class":39},[29,4335,244],{"class":52},[29,4337,57],{"class":56},[29,4339,856],{"class":39},[29,4341,53],{"class":52},[29,4343,57],{"class":56},[29,4345,856],{"class":39},[29,4347,244],{"class":52},[29,4349,57],{"class":56},[29,4351,2833],{"class":39},[29,4353,4355,4357,4359,4361,4363,4365,4367,4369,4371,4373,4375,4377,4379,4381,4383,4385,4387,4389,4391],{"class":31,"line":4354},167,[29,4356,2794],{"class":56},[29,4358,63],{"class":52},[29,4360,57],{"class":56},[29,4362,856],{"class":39},[29,4364,63],{"class":52},[29,4366,57],{"class":56},[29,4368,60],{"class":39},[29,4370,1987],{"class":56},[29,4372,63],{"class":52},[29,4374,57],{"class":56},[29,4376,856],{"class":39},[29,4378,244],{"class":52},[29,4380,57],{"class":56},[29,4382,856],{"class":39},[29,4384,53],{"class":52},[29,4386,57],{"class":56},[29,4388,856],{"class":39},[29,4390,244],{"class":52},[29,4392,4393],{"class":56},"f\n",[29,4395,4397],{"class":31,"line":4396},168,[29,4398,4399],{"class":39},"};\n",[29,4401,4403],{"class":31,"line":4402},169,[29,4404,1858],{"class":39},[29,4406,4408],{"class":31,"line":4407},170,[29,4409,4410],{"class":212},"    \u002F\u002F 创建VAO\n",[29,4412,4414,4417,4419],{"class":31,"line":4413},171,[29,4415,4416],{"class":56},"    unsigned",[29,4418,599],{"class":56},[29,4420,4421],{"class":39}," VAO;\n",[29,4423,4425,4428,4430,4432,4434,4436],{"class":31,"line":4424},172,[29,4426,4427],{"class":35},"    glGenVertexArrays",[29,4429,49],{"class":39},[29,4431,612],{"class":52},[29,4433,60],{"class":39},[29,4435,617],{"class":56},[29,4437,4438],{"class":39},"VAO);\n",[29,4440,4442,4445,4448],{"class":31,"line":4441},173,[29,4443,4444],{"class":35},"    glBindVertexArray",[29,4446,4447],{"class":39},"(VAO);",[29,4449,4450],{"class":212}," \u002F\u002F 绑定VAO\n",[29,4452,4454],{"class":31,"line":4453},174,[29,4455,488],{"emptyLinePlaceholder":487},[29,4457,4459],{"class":31,"line":4458},175,[29,4460,4461],{"class":212},"    \u002F\u002F 创建VBO\n",[29,4463,4465,4467,4469],{"class":31,"line":4464},176,[29,4466,4416],{"class":56},[29,4468,599],{"class":56},[29,4470,4471],{"class":39}," VBO;\n",[29,4473,4475,4478,4480,4482,4484,4486],{"class":31,"line":4474},177,[29,4476,4477],{"class":35},"    glGenBuffers",[29,4479,49],{"class":39},[29,4481,612],{"class":52},[29,4483,60],{"class":39},[29,4485,617],{"class":56},[29,4487,4488],{"class":39},"VBO);\n",[29,4490,4492,4495,4498],{"class":31,"line":4491},178,[29,4493,4494],{"class":35},"    glBindBuffer",[29,4496,4497],{"class":39},"(GL_ARRAY_BUFFER, VBO);",[29,4499,4500],{"class":212}," \u002F\u002F 绑定VBO\n",[29,4502,4504,4507,4510,4513],{"class":31,"line":4503},179,[29,4505,4506],{"class":35},"    glBufferData",[29,4508,4509],{"class":39},"(GL_ARRAY_BUFFER, ",[29,4511,4512],{"class":56},"sizeof",[29,4514,4515],{"class":39},"(vertices), vertices, GL_STATIC_DRAW);\n",[29,4517,4519],{"class":31,"line":4518},180,[29,4520,488],{"emptyLinePlaceholder":487},[29,4522,4524,4527],{"class":31,"line":4523},181,[29,4525,4526],{"class":56},"    int",[29,4528,4529],{"class":39}," nrAttributes;\n",[29,4531,4533,4536,4539,4541],{"class":31,"line":4532},182,[29,4534,4535],{"class":35},"    glGetIntegerv",[29,4537,4538],{"class":39},"(GL_MAX_VERTEX_ATTRIBS, ",[29,4540,617],{"class":56},[29,4542,4543],{"class":39},"nrAttributes);\n",[29,4545,4547,4550,4552,4554,4557,4559,4562,4564,4566],{"class":31,"line":4546},183,[29,4548,4549],{"class":35},"    std",[29,4551,1889],{"class":39},[29,4553,1892],{"class":56},[29,4555,4556],{"class":821}," \"Maximum nr of vertex attributes supported: \"",[29,4558,2244],{"class":56},[29,4560,4561],{"class":39}," nrAttributes ",[29,4563,1892],{"class":56},[29,4565,1906],{"class":35},[29,4567,1909],{"class":39},[29,4569,4571],{"class":31,"line":4570},184,[29,4572,488],{"emptyLinePlaceholder":487},[29,4574,4576],{"class":31,"line":4575},185,[29,4577,4578],{"class":212},"    \u002F\u002F 位置属性\n",[29,4580,4582,4585,4587,4589,4591,4593,4595,4598,4600,4602,4604,4606,4608,4610,4612,4614],{"class":31,"line":4581},186,[29,4583,4584],{"class":35},"    glVertexAttribPointer",[29,4586,49],{"class":39},[29,4588,656],{"class":52},[29,4590,60],{"class":39},[29,4592,661],{"class":52},[29,4594,664],{"class":39},[29,4596,4597],{"class":52},"6",[29,4599,669],{"class":56},[29,4601,672],{"class":56},[29,4603,49],{"class":39},[29,4605,677],{"class":56},[29,4607,680],{"class":39},[29,4609,683],{"class":56},[29,4611,686],{"class":39},[29,4613,656],{"class":52},[29,4615,75],{"class":39},[29,4617,4619,4622,4624,4626,4628],{"class":31,"line":4618},187,[29,4620,4621],{"class":35},"    glEnableVertexAttribArray",[29,4623,49],{"class":39},[29,4625,656],{"class":52},[29,4627,936],{"class":39},[29,4629,4630],{"class":212}," \u002F\u002F 启用顶点属性\n",[29,4632,4634],{"class":31,"line":4633},188,[29,4635,1858],{"class":39},[29,4637,4639],{"class":31,"line":4638},189,[29,4640,4641],{"class":212},"    \u002F\u002F 法线属性\n",[29,4643,4645,4647,4649,4651,4653,4655,4657,4659,4661,4663,4665,4667,4669,4671,4674,4676,4678,4680,4682,4684],{"class":31,"line":4644},190,[29,4646,4584],{"class":35},[29,4648,49],{"class":39},[29,4650,612],{"class":52},[29,4652,60],{"class":39},[29,4654,661],{"class":52},[29,4656,664],{"class":39},[29,4658,4597],{"class":52},[29,4660,669],{"class":56},[29,4662,672],{"class":56},[29,4664,49],{"class":39},[29,4666,677],{"class":56},[29,4668,680],{"class":39},[29,4670,683],{"class":56},[29,4672,4673],{"class":39},")(",[29,4675,661],{"class":52},[29,4677,669],{"class":56},[29,4679,672],{"class":56},[29,4681,49],{"class":39},[29,4683,677],{"class":56},[29,4685,4686],{"class":39},")));\n",[29,4688,4690,4692,4694,4696,4698],{"class":31,"line":4689},191,[29,4691,4621],{"class":35},[29,4693,49],{"class":39},[29,4695,612],{"class":52},[29,4697,936],{"class":39},[29,4699,4700],{"class":212}," \u002F\u002F 启用法线属性\n",[29,4702,4704],{"class":31,"line":4703},192,[29,4705,488],{"emptyLinePlaceholder":487},[29,4707,4709],{"class":31,"line":4708},193,[29,4710,4711],{"class":212},"    \u002F\u002F 构建并编译着色器程序\n",[29,4713,4715,4718,4721,4723,4726,4728,4731],{"class":31,"line":4714},194,[29,4716,4717],{"class":35},"    Shader",[29,4719,4720],{"class":35}," lightingShader",[29,4722,49],{"class":39},[29,4724,4725],{"class":821},"\"shader\u002Flighting.vert\"",[29,4727,60],{"class":39},[29,4729,4730],{"class":821},"\"shader\u002Flighting.frag\"",[29,4732,75],{"class":39},[29,4734,4736,4738,4741,4743,4746,4748,4751],{"class":31,"line":4735},195,[29,4737,4717],{"class":35},[29,4739,4740],{"class":35}," lampShader",[29,4742,49],{"class":39},[29,4744,4745],{"class":821},"\"shader\u002Flamp.vert\"",[29,4747,60],{"class":39},[29,4749,4750],{"class":821},"\"shader\u002Flamp.frag\"",[29,4752,75],{"class":39},[29,4754,4756],{"class":31,"line":4755},196,[29,4757,488],{"emptyLinePlaceholder":487},[29,4759,4761],{"class":31,"line":4760},197,[29,4762,4763],{"class":212},"    \u002F\u002F 创建光源的 VAO\n",[29,4765,4767,4769,4771],{"class":31,"line":4766},198,[29,4768,4416],{"class":56},[29,4770,599],{"class":56},[29,4772,602],{"class":39},[29,4774,4776,4778,4780,4782,4784,4786],{"class":31,"line":4775},199,[29,4777,4427],{"class":35},[29,4779,49],{"class":39},[29,4781,612],{"class":52},[29,4783,60],{"class":39},[29,4785,617],{"class":56},[29,4787,620],{"class":39},[29,4789,4791,4793],{"class":31,"line":4790},200,[29,4792,4444],{"class":35},[29,4794,628],{"class":39},[29,4796,4798,4800],{"class":31,"line":4797},201,[29,4799,4494],{"class":35},[29,4801,641],{"class":39},[29,4803,4805],{"class":31,"line":4804},202,[29,4806,488],{"emptyLinePlaceholder":487},[29,4808,4810,4812,4814,4816,4818,4820,4822,4824,4826,4828,4830,4832,4834,4836,4838,4840],{"class":31,"line":4809},203,[29,4811,4584],{"class":35},[29,4813,49],{"class":39},[29,4815,656],{"class":52},[29,4817,60],{"class":39},[29,4819,661],{"class":52},[29,4821,664],{"class":39},[29,4823,4597],{"class":52},[29,4825,669],{"class":56},[29,4827,672],{"class":56},[29,4829,49],{"class":39},[29,4831,677],{"class":56},[29,4833,680],{"class":39},[29,4835,683],{"class":56},[29,4837,686],{"class":39},[29,4839,656],{"class":52},[29,4841,75],{"class":39},[29,4843,4845,4847,4849,4851],{"class":31,"line":4844},204,[29,4846,4621],{"class":35},[29,4848,49],{"class":39},[29,4850,656],{"class":52},[29,4852,75],{"class":39},[29,4854,4856],{"class":31,"line":4855},205,[29,4857,488],{"emptyLinePlaceholder":487},[29,4859,4861],{"class":31,"line":4860},206,[29,4862,4863],{"class":212},"    \u002F\u002F 光源位置\n",[29,4865,4867,4870,4872,4874,4876,4878,4880,4882,4884,4886,4888,4890],{"class":31,"line":4866},207,[29,4868,4869],{"class":35},"    vec3",[29,4871,994],{"class":35},[29,4873,49],{"class":39},[29,4875,999],{"class":52},[29,4877,57],{"class":56},[29,4879,60],{"class":39},[29,4881,53],{"class":52},[29,4883,57],{"class":56},[29,4885,60],{"class":39},[29,4887,1012],{"class":52},[29,4889,57],{"class":56},[29,4891,75],{"class":39},[29,4893,4895],{"class":31,"line":4894},208,[29,4896,488],{"emptyLinePlaceholder":487},[29,4898,4900],{"class":31,"line":4899},209,[29,4901,4902],{"class":212},"    \u002F\u002F 渲染循环\n",[29,4904,4906,4909,4911,4913,4916],{"class":31,"line":4905},210,[29,4907,4908],{"class":56},"    while",[29,4910,1748],{"class":39},[29,4912,2338],{"class":56},[29,4914,4915],{"class":35},"glfwWindowShouldClose",[29,4917,4918],{"class":39},"(window)) {\n",[29,4920,4922],{"class":31,"line":4921},211,[29,4923,4924],{"class":212},"        \u002F\u002F 处理输入\n",[29,4926,4928,4931],{"class":31,"line":4927},212,[29,4929,4930],{"class":35},"        processInput",[29,4932,1734],{"class":39},[29,4934,4936,4939],{"class":31,"line":4935},213,[29,4937,4938],{"class":35},"        glfwPollEvents",[29,4940,809],{"class":39},[29,4942,4944],{"class":31,"line":4943},214,[29,4945,488],{"emptyLinePlaceholder":487},[29,4947,4949],{"class":31,"line":4948},215,[29,4950,4951],{"class":212},"        \u002F\u002F 从 DPI 稳定器获取 view 矩阵\n",[29,4953,4955,4958,4960,4962,4964,4967],{"class":31,"line":4954},216,[29,4956,4957],{"class":35},"        mat4",[29,4959,545],{"class":39},[29,4961,200],{"class":56},[29,4963,1895],{"class":39},[29,4965,4966],{"class":35},"getViewMatrix",[29,4968,809],{"class":39},[29,4970,4972],{"class":31,"line":4971},217,[29,4973,2503],{"class":39},[29,4975,4977,4979,4982,4984,4987],{"class":31,"line":4976},218,[29,4978,2555],{"class":56},[29,4980,4981],{"class":39}," currentFrame ",[29,4983,200],{"class":56},[29,4985,4986],{"class":35}," glfwGetTime",[29,4988,809],{"class":39},[29,4990,4992,4995,4997,4999,5001],{"class":31,"line":4991},219,[29,4993,4994],{"class":39},"        deltaTime ",[29,4996,200],{"class":56},[29,4998,4981],{"class":39},[29,5000,1987],{"class":56},[29,5002,5003],{"class":39}," lastFrame;\n",[29,5005,5007,5010,5012],{"class":31,"line":5006},220,[29,5008,5009],{"class":39},"        lastFrame ",[29,5011,200],{"class":56},[29,5013,5014],{"class":39}," currentFrame;\n",[29,5016,5018],{"class":31,"line":5017},221,[29,5019,488],{"emptyLinePlaceholder":487},[29,5021,5023],{"class":31,"line":5022},222,[29,5024,5025],{"class":212},"        \u002F\u002F 渲染指令\n",[29,5027,5029,5032,5034,5036,5038,5040,5043,5045,5047,5049,5051,5053,5055,5057],{"class":31,"line":5028},223,[29,5030,5031],{"class":35},"        glClearColor",[29,5033,49],{"class":39},[29,5035,1084],{"class":52},[29,5037,57],{"class":56},[29,5039,60],{"class":39},[29,5041,5042],{"class":52},"0.3",[29,5044,57],{"class":56},[29,5046,60],{"class":39},[29,5048,5042],{"class":52},[29,5050,57],{"class":56},[29,5052,60],{"class":39},[29,5054,53],{"class":52},[29,5056,57],{"class":56},[29,5058,75],{"class":39},[29,5060,5062,5065,5068,5071],{"class":31,"line":5061},224,[29,5063,5064],{"class":35},"        glClear",[29,5066,5067],{"class":39},"(GL_COLOR_BUFFER_BIT ",[29,5069,5070],{"class":56},"|",[29,5072,5073],{"class":39}," GL_DEPTH_BUFFER_BIT);\n",[29,5075,5077],{"class":31,"line":5076},225,[29,5078,2503],{"class":39},[29,5080,5082],{"class":31,"line":5081},226,[29,5083,5084],{"class":212},"        \u002F\u002F 使用光照着色器绘制物体\n",[29,5086,5088,5091,5093],{"class":31,"line":5087},227,[29,5089,5090],{"class":39},"        lightingShader.",[29,5092,806],{"class":35},[29,5094,809],{"class":39},[29,5096,5098,5100,5103,5105,5108],{"class":31,"line":5097},228,[29,5099,5090],{"class":39},[29,5101,5102],{"class":35},"setMat4",[29,5104,49],{"class":39},[29,5106,5107],{"class":821},"\"projection\"",[29,5109,5110],{"class":39},", projection);\n",[29,5112,5114,5116,5118,5120,5123],{"class":31,"line":5113},229,[29,5115,5090],{"class":39},[29,5117,5102],{"class":35},[29,5119,49],{"class":39},[29,5121,5122],{"class":821},"\"view\"",[29,5124,5125],{"class":39},", view);\n",[29,5127,5129,5131,5133,5135,5138],{"class":31,"line":5128},230,[29,5130,5090],{"class":39},[29,5132,5102],{"class":35},[29,5134,49],{"class":39},[29,5136,5137],{"class":821},"\"model\"",[29,5139,5140],{"class":39},", model);\n",[29,5142,5144],{"class":31,"line":5143},231,[29,5145,2503],{"class":39},[29,5147,5149],{"class":31,"line":5148},232,[29,5150,5151],{"class":212},"        \u002F\u002F 设置物体颜色和光源颜色\n",[29,5153,5155,5157,5159,5161,5163,5165,5167,5169,5171,5173,5175,5177,5179,5181,5183],{"class":31,"line":5154},233,[29,5156,5090],{"class":39},[29,5158,816],{"class":35},[29,5160,49],{"class":39},[29,5162,822],{"class":821},[29,5164,60],{"class":39},[29,5166,53],{"class":52},[29,5168,57],{"class":56},[29,5170,60],{"class":39},[29,5172,63],{"class":52},[29,5174,57],{"class":56},[29,5176,60],{"class":39},[29,5178,70],{"class":52},[29,5180,57],{"class":56},[29,5182,936],{"class":39},[29,5184,5185],{"class":212}," \u002F\u002F 珊瑚红\n",[29,5187,5189,5191,5193,5195,5197,5199,5201,5203,5205,5207,5209,5211,5213,5215,5217],{"class":31,"line":5188},234,[29,5190,5090],{"class":39},[29,5192,816],{"class":35},[29,5194,49],{"class":39},[29,5196,853],{"class":821},[29,5198,60],{"class":39},[29,5200,53],{"class":52},[29,5202,57],{"class":56},[29,5204,60],{"class":39},[29,5206,53],{"class":52},[29,5208,57],{"class":56},[29,5210,60],{"class":39},[29,5212,53],{"class":52},[29,5214,57],{"class":56},[29,5216,936],{"class":39},[29,5218,5219],{"class":212},"   \u002F\u002F 白色光源\n",[29,5221,5223,5225,5227,5229,5232],{"class":31,"line":5222},235,[29,5224,5090],{"class":39},[29,5226,816],{"class":35},[29,5228,49],{"class":39},[29,5230,5231],{"class":821},"\"lightPos\"",[29,5233,5234],{"class":39},", lightPos.x, lightPos.y, lightPos.z);\n",[29,5236,5238],{"class":31,"line":5237},236,[29,5239,2503],{"class":39},[29,5241,5243,5246],{"class":31,"line":5242},237,[29,5244,5245],{"class":35},"        glBindVertexArray",[29,5247,5248],{"class":39},"(VAO);\n",[29,5250,5252,5255,5257,5259,5261,5263],{"class":31,"line":5251},238,[29,5253,5254],{"class":35},"        glDrawArrays",[29,5256,1135],{"class":39},[29,5258,656],{"class":52},[29,5260,60],{"class":39},[29,5262,1142],{"class":52},[29,5264,75],{"class":39},[29,5266,5268],{"class":31,"line":5267},239,[29,5269,2503],{"class":39},[29,5271,5273],{"class":31,"line":5272},240,[29,5274,5275],{"class":212},"        \u002F\u002F 绘制光源立方体\n",[29,5277,5279,5282,5284],{"class":31,"line":5278},241,[29,5280,5281],{"class":39},"        lampShader.",[29,5283,806],{"class":35},[29,5285,809],{"class":39},[29,5287,5289,5291,5293,5295,5297],{"class":31,"line":5288},242,[29,5290,5281],{"class":39},[29,5292,5102],{"class":35},[29,5294,49],{"class":39},[29,5296,5107],{"class":821},[29,5298,5110],{"class":39},[29,5300,5302,5304,5306,5308,5310],{"class":31,"line":5301},243,[29,5303,5281],{"class":39},[29,5305,5102],{"class":35},[29,5307,49],{"class":39},[29,5309,5122],{"class":821},[29,5311,5125],{"class":39},[29,5313,5315],{"class":31,"line":5314},244,[29,5316,2503],{"class":39},[29,5318,5320],{"class":31,"line":5319},245,[29,5321,5322],{"class":212},"        \u002F\u002F 光源立方体的模型矩阵\n",[29,5324,5326,5328,5331,5333,5335,5337,5339,5341],{"class":31,"line":5325},246,[29,5327,4957],{"class":35},[29,5329,5330],{"class":39}," lampModel ",[29,5332,200],{"class":56},[29,5334,1958],{"class":35},[29,5336,49],{"class":39},[29,5338,53],{"class":52},[29,5340,57],{"class":56},[29,5342,75],{"class":39},[29,5344,5346,5349,5351,5354],{"class":31,"line":5345},247,[29,5347,5348],{"class":39},"        lampModel ",[29,5350,200],{"class":56},[29,5352,5353],{"class":35}," translate",[29,5355,5356],{"class":39},"(lampModel, lightPos);\n",[29,5358,5360,5362,5364,5367,5370,5372,5374,5376,5378,5381],{"class":31,"line":5359},248,[29,5361,5348],{"class":39},[29,5363,200],{"class":56},[29,5365,5366],{"class":35}," scale",[29,5368,5369],{"class":39},"(lampModel, ",[29,5371,43],{"class":35},[29,5373,49],{"class":39},[29,5375,1084],{"class":52},[29,5377,57],{"class":56},[29,5379,5380],{"class":39},"));",[29,5382,5383],{"class":212}," \u002F\u002F 缩小光源立方体\n",[29,5385,5387,5389,5391,5393,5395],{"class":31,"line":5386},249,[29,5388,5281],{"class":39},[29,5390,5102],{"class":35},[29,5392,49],{"class":39},[29,5394,5137],{"class":821},[29,5396,5397],{"class":39},", lampModel);\n",[29,5399,5401],{"class":31,"line":5400},250,[29,5402,2503],{"class":39},[29,5404,5406,5408],{"class":31,"line":5405},251,[29,5407,5245],{"class":35},[29,5409,628],{"class":39},[29,5411,5413,5415,5417,5419,5421,5423],{"class":31,"line":5412},252,[29,5414,5254],{"class":35},[29,5416,1135],{"class":39},[29,5418,656],{"class":52},[29,5420,60],{"class":39},[29,5422,1142],{"class":52},[29,5424,75],{"class":39},[29,5426,5428],{"class":31,"line":5427},253,[29,5429,2503],{"class":39},[29,5431,5433],{"class":31,"line":5432},254,[29,5434,5435],{"class":212},"        \u002F\u002F 使用 ImGui 管理器渲染所有窗口\n",[29,5437,5439,5441,5444],{"class":31,"line":5438},255,[29,5440,1766],{"class":39},[29,5442,5443],{"class":35},"render",[29,5445,809],{"class":39},[29,5447,5449],{"class":31,"line":5448},256,[29,5450,488],{"emptyLinePlaceholder":487},[29,5452,5454],{"class":31,"line":5453},257,[29,5455,5456],{"class":212},"        \u002F\u002F 交换缓冲区和检查IO事件\n",[29,5458,5460,5463],{"class":31,"line":5459},258,[29,5461,5462],{"class":35},"        glfwSwapBuffers",[29,5464,1734],{"class":39},[29,5466,5468],{"class":31,"line":5467},259,[29,5469,1782],{"class":39},[29,5471,5473],{"class":31,"line":5472},260,[29,5474,488],{"emptyLinePlaceholder":487},[29,5476,5478],{"class":31,"line":5477},261,[29,5479,5480],{"class":212},"    \u002F\u002F 清理 ImGui 管理器\n",[29,5482,5484,5486,5489],{"class":31,"line":5483},262,[29,5485,2395],{"class":39},[29,5487,5488],{"class":35},"shutdown",[29,5490,809],{"class":39},[29,5492,5494],{"class":31,"line":5493},263,[29,5495,488],{"emptyLinePlaceholder":487},[29,5497,5499],{"class":31,"line":5498},264,[29,5500,5501],{"class":212},"    \u002F\u002F 清理资源\n",[29,5503,5505,5508,5510,5512,5514,5516],{"class":31,"line":5504},265,[29,5506,5507],{"class":35},"    glDeleteVertexArrays",[29,5509,49],{"class":39},[29,5511,612],{"class":52},[29,5513,60],{"class":39},[29,5515,617],{"class":56},[29,5517,4438],{"class":39},[29,5519,5521,5523,5525,5527,5529,5531],{"class":31,"line":5520},266,[29,5522,5507],{"class":35},[29,5524,49],{"class":39},[29,5526,612],{"class":52},[29,5528,60],{"class":39},[29,5530,617],{"class":56},[29,5532,620],{"class":39},[29,5534,5536,5539,5541,5543,5545,5547],{"class":31,"line":5535},267,[29,5537,5538],{"class":35},"    glDeleteBuffers",[29,5540,49],{"class":39},[29,5542,612],{"class":52},[29,5544,60],{"class":39},[29,5546,617],{"class":56},[29,5548,4488],{"class":39},[29,5550,5552,5555],{"class":31,"line":5551},268,[29,5553,5554],{"class":35},"    glfwTerminate",[29,5556,809],{"class":39},[29,5558,5560,5563,5565],{"class":31,"line":5559},269,[29,5561,5562],{"class":56},"    return",[29,5564,479],{"class":52},[29,5566,1499],{"class":39},[29,5568,5570],{"class":31,"line":5569},270,[29,5571,568],{"class":39},[19,5573,5577],{"className":5574,"code":5575,"language":5576,"meta":24,"style":24},"language-glsl shiki shiki-themes github-light github-dark","#version 330 core\nlayout (location = 0) in vec3 aPos;\nlayout (location = 1) in vec3 aNormal;\n\nuniform mat4 model;\nuniform mat4 view;\nuniform mat4 projection;\n\nout vec3 Normal;\nout vec3 FragPos;\n\nvoid main()\n{\n    gl_Position = projection * view * model * vec4(aPos, 1.0);\n    FragPos = vec3(model * vec4(aPos, 1.0));\n    Normal = mat3(transpose(inverse(model))) * aNormal;\n}\n\n#version 330 core\nout vec4 FragColor;\n\nuniform vec3 objectColor;\nuniform vec3 lightColor;\nuniform vec3 lightPos;\n\nin vec3 Normal;\nin vec3 FragPos;\n\nvoid main()\n{\n    \u002F\u002F 环境光\n    float ambientStrength = 0.1;\n    vec3 ambient = ambientStrength * lightColor;\n    \n    \u002F\u002F 漫反射\n    vec3 norm = normalize(Normal);\n    vec3 lightDir = normalize(lightPos - FragPos);\n    float diff = max(dot(norm, lightDir), 0.0);\n    vec3 diffuse = diff * lightColor;\n    \n    \u002F\u002F 最终结果\n    vec3 result = (ambient + diffuse) * objectColor;\n    FragColor = vec4(result, 1.0);\n}\n\n","glsl",[26,5578,5579,5587,5609,5628,5632,5642,5651,5660,5664,5674,5683,5687,5695,5699,5725,5747,5774,5778,5782,5790,5799,5803,5812,5821,5830,5834,5842,5850,5854,5862,5866,5871,5885,5898,5902,5907,5920,5937,5961,5974,5978,5983,6003,6018],{"__ignoreMap":24},[29,5580,5581,5583,5585],{"class":31,"line":32},[29,5582,460],{"class":39},[29,5584,463],{"class":52},[29,5586,466],{"class":39},[29,5588,5589,5591,5593,5595,5597,5600,5603,5606],{"class":31,"line":160},[29,5590,471],{"class":35},[29,5592,474],{"class":39},[29,5594,200],{"class":56},[29,5596,479],{"class":52},[29,5598,5599],{"class":39},") ",[29,5601,5602],{"class":56},"in",[29,5604,5605],{"class":56}," vec3",[29,5607,5608],{"class":39}," aPos;\n",[29,5610,5611,5613,5615,5617,5619,5621,5623,5625],{"class":31,"line":192},[29,5612,471],{"class":35},[29,5614,474],{"class":39},[29,5616,200],{"class":56},[29,5618,1387],{"class":52},[29,5620,5599],{"class":39},[29,5622,5602],{"class":56},[29,5624,5605],{"class":56},[29,5626,5627],{"class":39}," aNormal;\n",[29,5629,5630],{"class":31,"line":491},[29,5631,488],{"emptyLinePlaceholder":487},[29,5633,5634,5637,5639],{"class":31,"line":497},[29,5635,5636],{"class":56},"uniform",[29,5638,1958],{"class":56},[29,5640,5641],{"class":39}," model;\n",[29,5643,5644,5646,5648],{"class":31,"line":503},[29,5645,5636],{"class":56},[29,5647,1958],{"class":56},[29,5649,5650],{"class":39}," view;\n",[29,5652,5653,5655,5657],{"class":31,"line":509},[29,5654,5636],{"class":56},[29,5656,1958],{"class":56},[29,5658,5659],{"class":39}," projection;\n",[29,5661,5662],{"class":31,"line":514},[29,5663,488],{"emptyLinePlaceholder":487},[29,5665,5666,5669,5671],{"class":31,"line":526},[29,5667,5668],{"class":56},"out",[29,5670,5605],{"class":56},[29,5672,5673],{"class":39}," Normal;\n",[29,5675,5676,5678,5680],{"class":31,"line":532},[29,5677,5668],{"class":56},[29,5679,5605],{"class":56},[29,5681,5682],{"class":39}," FragPos;\n",[29,5684,5685],{"class":31,"line":565},[29,5686,488],{"emptyLinePlaceholder":487},[29,5688,5689,5691,5693],{"class":31,"line":1483},[29,5690,517],{"class":56},[29,5692,520],{"class":35},[29,5694,523],{"class":39},[29,5696,5697],{"class":31,"line":1488},[29,5698,529],{"class":39},[29,5700,5701,5703,5705,5707,5709,5711,5713,5715,5717,5719,5721,5723],{"class":31,"line":1502},[29,5702,535],{"class":39},[29,5704,200],{"class":56},[29,5706,540],{"class":39},[29,5708,206],{"class":56},[29,5710,545],{"class":39},[29,5712,206],{"class":56},[29,5714,550],{"class":39},[29,5716,206],{"class":56},[29,5718,555],{"class":35},[29,5720,558],{"class":39},[29,5722,53],{"class":52},[29,5724,75],{"class":39},[29,5726,5727,5730,5732,5734,5737,5739,5741,5743,5745],{"class":31,"line":1507},[29,5728,5729],{"class":39},"    FragPos ",[29,5731,200],{"class":56},[29,5733,5605],{"class":35},[29,5735,5736],{"class":39},"(model ",[29,5738,206],{"class":56},[29,5740,555],{"class":35},[29,5742,558],{"class":39},[29,5744,53],{"class":52},[29,5746,1089],{"class":39},[29,5748,5749,5752,5754,5757,5759,5762,5764,5767,5770,5772],{"class":31,"line":1513},[29,5750,5751],{"class":39},"    Normal ",[29,5753,200],{"class":56},[29,5755,5756],{"class":35}," mat3",[29,5758,49],{"class":39},[29,5760,5761],{"class":35},"transpose",[29,5763,49],{"class":39},[29,5765,5766],{"class":35},"inverse",[29,5768,5769],{"class":39},"(model))) ",[29,5771,206],{"class":56},[29,5773,5627],{"class":39},[29,5775,5776],{"class":31,"line":1550},[29,5777,568],{"class":39},[29,5779,5780],{"class":31,"line":1567},[29,5781,488],{"emptyLinePlaceholder":487},[29,5783,5784,5786,5788],{"class":31,"line":1572},[29,5785,460],{"class":39},[29,5787,463],{"class":52},[29,5789,466],{"class":39},[29,5791,5792,5794,5796],{"class":31,"line":1577},[29,5793,5668],{"class":56},[29,5795,555],{"class":56},[29,5797,5798],{"class":39}," FragColor;\n",[29,5800,5801],{"class":31,"line":1582},[29,5802,488],{"emptyLinePlaceholder":487},[29,5804,5805,5807,5809],{"class":31,"line":1588},[29,5806,5636],{"class":56},[29,5808,5605],{"class":56},[29,5810,5811],{"class":39}," objectColor;\n",[29,5813,5814,5816,5818],{"class":31,"line":1609},[29,5815,5636],{"class":56},[29,5817,5605],{"class":56},[29,5819,5820],{"class":39}," lightColor;\n",[29,5822,5823,5825,5827],{"class":31,"line":1628},[29,5824,5636],{"class":56},[29,5826,5605],{"class":56},[29,5828,5829],{"class":39}," lightPos;\n",[29,5831,5832],{"class":31,"line":1633},[29,5833,488],{"emptyLinePlaceholder":487},[29,5835,5836,5838,5840],{"class":31,"line":1639},[29,5837,5602],{"class":56},[29,5839,5605],{"class":56},[29,5841,5673],{"class":39},[29,5843,5844,5846,5848],{"class":31,"line":1671},[29,5845,5602],{"class":56},[29,5847,5605],{"class":56},[29,5849,5682],{"class":39},[29,5851,5852],{"class":31,"line":1676},[29,5853,488],{"emptyLinePlaceholder":487},[29,5855,5856,5858,5860],{"class":31,"line":1682},[29,5857,517],{"class":56},[29,5859,520],{"class":35},[29,5861,523],{"class":39},[29,5863,5864],{"class":31,"line":1688},[29,5865,529],{"class":39},[29,5867,5868],{"class":31,"line":1693},[29,5869,5870],{"class":212},"    \u002F\u002F 环境光\n",[29,5872,5873,5875,5878,5880,5883],{"class":31,"line":1699},[29,5874,2762],{"class":56},[29,5876,5877],{"class":39}," ambientStrength ",[29,5879,200],{"class":56},[29,5881,5882],{"class":52}," 0.1",[29,5884,1499],{"class":39},[29,5886,5887,5890,5892,5894,5896],{"class":31,"line":1719},[29,5888,5889],{"class":39},"    vec3 ambient ",[29,5891,200],{"class":56},[29,5893,5877],{"class":39},[29,5895,206],{"class":56},[29,5897,5820],{"class":39},[29,5899,5900],{"class":31,"line":1725},[29,5901,1858],{"class":39},[29,5903,5904],{"class":31,"line":1737},[29,5905,5906],{"class":212},"    \u002F\u002F 漫反射\n",[29,5908,5909,5912,5914,5917],{"class":31,"line":1742},[29,5910,5911],{"class":39},"    vec3 norm ",[29,5913,200],{"class":56},[29,5915,5916],{"class":35}," normalize",[29,5918,5919],{"class":39},"(Normal);\n",[29,5921,5922,5925,5927,5929,5932,5934],{"class":31,"line":1763},[29,5923,5924],{"class":39},"    vec3 lightDir ",[29,5926,200],{"class":56},[29,5928,5916],{"class":35},[29,5930,5931],{"class":39},"(lightPos ",[29,5933,1987],{"class":56},[29,5935,5936],{"class":39}," FragPos);\n",[29,5938,5939,5941,5944,5946,5949,5951,5954,5957,5959],{"class":31,"line":1779},[29,5940,2762],{"class":56},[29,5942,5943],{"class":39}," diff ",[29,5945,200],{"class":56},[29,5947,5948],{"class":35}," max",[29,5950,49],{"class":39},[29,5952,5953],{"class":35},"dot",[29,5955,5956],{"class":39},"(norm, lightDir), ",[29,5958,244],{"class":52},[29,5960,75],{"class":39},[29,5962,5963,5966,5968,5970,5972],{"class":31,"line":1785},[29,5964,5965],{"class":39},"    vec3 diffuse ",[29,5967,200],{"class":56},[29,5969,5943],{"class":39},[29,5971,206],{"class":56},[29,5973,5820],{"class":39},[29,5975,5976],{"class":31,"line":1801},[29,5977,1858],{"class":39},[29,5979,5980],{"class":31,"line":1815},[29,5981,5982],{"class":212},"    \u002F\u002F 最终结果\n",[29,5984,5985,5988,5990,5993,5996,5999,6001],{"class":31,"line":1820},[29,5986,5987],{"class":39},"    vec3 result ",[29,5989,200],{"class":56},[29,5991,5992],{"class":39}," (ambient ",[29,5994,5995],{"class":56},"+",[29,5997,5998],{"class":39}," diffuse) ",[29,6000,206],{"class":56},[29,6002,5811],{"class":39},[29,6004,6005,6007,6009,6011,6014,6016],{"class":31,"line":1836},[29,6006,761],{"class":39},[29,6008,200],{"class":56},[29,6010,555],{"class":35},[29,6012,6013],{"class":39},"(result, ",[29,6015,53],{"class":52},[29,6017,75],{"class":39},[29,6019,6020],{"class":31,"line":1850},[29,6021,568],{"class":39},[1314,6023,6024],{"id":6024},"最后一件事",[10,6026,6027,6028,6031,6032,6035,6036,6039],{},"​\t现在我们已经",[14,6029,6030],{},"把法向量从顶点着色器传到了片段着色器","。可是，目前",[14,6033,6034],{},"片段着色器里的计算都是在世界空间坐标中进行的","。所以，我们是不是",[14,6037,6038],{},"应该把法向量也转换为世界空间坐标","？基本正确，但是这不是简单地把它乘以一个模型矩阵就能搞定的。",[10,6041,6042,6043,6046,6047,6050,6051,6054,6055,6058,6059,1186],{},"​\t首先，法向量",[14,6044,6045],{},"只是一个方向向量，不能表达空间中的特定位置","。同时，",[14,6048,6049],{},"法向量没有齐次坐标（顶点位置中的w分量）","。这意味着，",[14,6052,6053],{},"位移不应该影响到法向量","。因此，",[14,6056,6057],{},"如果我们打算把法向量乘以一个模型矩阵，我们就要从矩阵中移除位移部分，只选用模型矩阵左上角3×3的矩阵","（注意，我们也可以把法向量的w分量设置为0，再乘以4×4矩阵；这同样可以移除位移）。对于法向量，",[14,6060,6061],{},"我们只希望对它实施缩放和旋转变换",[10,6063,6064,6065,978],{},"​\t其次，如果模型矩阵执行了不等比缩放，顶点的改变会导致法向量不再垂直于表面了。因此，我们不能用这样的模型矩阵来变换法向量。下面的图展示了应用了",[14,6066,6067],{},"不等比缩放的模型矩阵对法向量的影响",[10,6069,6070],{},[86,6071],{"alt":86,"src":6072},".\u002Fassets\u002Fbasic_lighting_normal_transformation.png",[10,6074,6075],{},"​\t每当我们应用一个不等比缩放时（注意：等比缩放不会破坏法线，因为法线的方向没被改变，仅仅改变了法线的长度，而这很容易通过标准化来修复），法向量就不会再垂直于对应的表面了，这样光照就会被破坏。",[10,6077,6078,6079,6082,6083,1186],{},"​\t修复这个行为的诀窍是",[14,6080,6081],{},"使用一个为法向量专门定制的模型矩阵。这个矩阵称之为法线矩阵(Normal Matrix)，它使用了一些线性代数的操作来移除对法向量错误缩放的影响","。如果你想知道这个矩阵是如何计算出来的，建议去阅读这个",[425,6084,6087],{"href":6085,"rel":6086},"http:\u002F\u002Fwww.lighthouse3d.com\u002Ftutorials\u002Fglsl-tutorial\u002Fthe-normal-matrix\u002F",[429],"文章",[10,6089,6090],{},"​\t法线矩阵被定义为**「模型矩阵左上角3x3部分的逆矩阵的转置矩阵」**。真是拗口，如果你不明白这是什么意思，别担心，我们还没有讨论逆矩阵(Inverse Matrix)和转置矩阵(Transpose Matrix)。注意，大部分的资源都会将法线矩阵定义为应用到模型-观察矩阵(Model-view Matrix)上的操作，但是由于我们只在世界空间中进行操作（不是在观察空间），我们只使用模型矩阵。",[10,6092,6093,6094,6097,6098,1186],{},"​\t在顶点着色器中，",[14,6095,6096],{},"我们可以使用inverse和transpose函数自己生成这个法线矩阵","，这两个函数对所有类型矩阵都有效。注意",[14,6099,6100,6101,6103],{},"我们还要把被处理过的矩阵强制转换为3×3矩阵，来保证它失去了位移属性以及能够乘以",[26,6102,43],{},"的法向量",[19,6105,6107],{"className":21,"code":6106,"language":23,"meta":24,"style":24},"Normal = mat3(transpose(inverse(model))) * aNormal;\n",[26,6108,6109],{"__ignoreMap":24},[29,6110,6111,6114,6116,6118,6120,6122,6124,6126,6128,6130],{"class":31,"line":32},[29,6112,6113],{"class":39},"Normal ",[29,6115,200],{"class":56},[29,6117,5756],{"class":35},[29,6119,49],{"class":39},[29,6121,5761],{"class":35},[29,6123,49],{"class":39},[29,6125,5766],{"class":35},[29,6127,5769],{"class":39},[29,6129,206],{"class":56},[29,6131,5627],{"class":39},[93,6133,6134,6138],{},[10,6135,6136],{},[29,6137,99],{},[10,6139,6140,6141,6144,6145,6148],{},"矩阵求逆是一项对于着色器开销很大的运算，",[14,6142,6143],{},"因为它必须在场景中的每一个顶点上进行，所以应该尽可能地避免在着色器中进行求逆运算","。以学习为目的的话这样做还好，但是对于一个高效的应用来说，你",[14,6146,6147],{},"最好先在CPU上计算出法线矩阵，再通过uniform把它传递给着色器","（就像模型矩阵一样）。",[10,6150,6151,6152,6155],{},"​\t在漫反射光照部分，光照表现并没有问题，这是因为我们没有对物体进行任何缩放操作，",[14,6153,6154],{},"所以我们并不真的需要使用一个法线矩阵，而是仅以模型矩阵乘以法线就可以","。但是如果你会进行不等比缩放，使用法线矩阵去乘以法向量就是必须的了。",[407,6157,6158],{"id":6158},"镜面光照",[10,6160,6161],{},"​\t如果你还没被这些光照计算搞得精疲力尽，我们就再把镜面高光(Specular Highlight)加进来，这样风氏光照才算完整。",[10,6163,6164],{},"​\t和漫反射光照一样，镜面光照也决定于光的方向向量和物体的法向量，但是它也决定于观察方向，例如玩家是从什么方向看向这个片段的。镜面光照决定于表面的反射特性。如果我们把物体表面设想为一面镜子，那么镜面光照最强的地方就是我们看到表面上反射光的地方。你可以在下图中看到效果：",[10,6166,6167],{},[86,6168],{"alt":86,"src":6169},".\u002Fassets\u002Fbasic_lighting_specular_theory.png",[10,6171,6172,6173,6176,6177,6180],{},"​\t我们通过",[14,6174,6175],{},"根据法向量翻折入射光的方向来计算反射向量","。然后我们",[14,6178,6179],{},"计算反射向量与观察方向的角度差，它们之间夹角越小，镜面光的作用就越大","。由此产生的效果就是，我们看向在入射光在表面的反射方向时，会看到一点高光。",[10,6182,78,6183,6186,6187,6190],{},[14,6184,6185],{},"观察向量是我们计算镜面光照时需要的一个额外变量","，我们",[14,6188,6189],{},"可以使用观察者的世界空间位置和片段的位置来计算它","。之后我们计算出镜面光照强度，用它乘以光源的颜色，并将它与环境光照和漫反射光照部分加和。",[93,6192,6193,6197],{},[10,6194,6195],{},[29,6196,99],{},[10,6198,6199,6200,6203,6204,6207],{},"我们",[14,6201,6202],{},"选择在世界空间进行光照计算","，但是",[14,6205,6206],{},"大多数人趋向于更偏向在观察空间进行光照计算","。在观察空间计算的优势是，观察者的位置总是在(0, 0, 0)，所以你已经零成本地拿到了观察者的位置。然而，若以学习为目的，我认为在世界空间中计算光照更符合直觉。如果你仍然希望在观察空间计算光照的话，你需要将所有相关的向量也用观察矩阵进行变换（不要忘记也修改法线矩阵）。",[10,6209,6210,6211,6214],{},"​\t要得到观察者的世界空间坐标，",[14,6212,6213],{},"我们直接使用摄像机的位置向量即可（它当然就是那个观察者）","。那么让我们把另一个uniform添加到片段着色器中，并把摄像机的位置向量传给着色器：",[19,6216,6218],{"className":21,"code":6217,"language":23,"meta":24,"style":24},"uniform vec3 viewPos;\nlightingShader.setVec3(\"viewPos\", camera.Position);\n",[26,6219,6220,6225],{"__ignoreMap":24},[29,6221,6222],{"class":31,"line":32},[29,6223,6224],{"class":39},"uniform vec3 viewPos;\n",[29,6226,6227,6229,6231,6233,6236],{"class":31,"line":160},[29,6228,803],{"class":39},[29,6230,816],{"class":35},[29,6232,49],{"class":39},[29,6234,6235],{"class":821},"\"viewPos\"",[29,6237,6238],{"class":39},", camera.Position);\n",[10,6240,6241,6242,6245,6246,6249],{},"​\t现在我们",[14,6243,6244],{},"已经获得所有需要的变量，可以计算高光强度了","。首先，我们",[14,6247,6248],{},"定义一个镜面强度(Specular Intensity)变量","，给镜面高光一个中等亮度颜色，让它不要产生过度的影响。",[19,6251,6253],{"className":21,"code":6252,"language":23,"meta":24,"style":24},"float specularStrength = 0.5;\n",[26,6254,6255],{"__ignoreMap":24},[29,6256,6257,6259,6262,6264,6267],{"class":31,"line":32},[29,6258,677],{"class":56},[29,6260,6261],{"class":39}," specularStrength ",[29,6263,200],{"class":56},[29,6265,6266],{"class":52}," 0.5",[29,6268,1499],{"class":39},[10,6270,6271,6272,6276,6277,978],{},"​\t如果我们把它设置为1.0f，我们会得到一个非常亮的镜面光分量，这对于一个珊瑚色的立方体来说有点太多了。[下一节教程](",[425,6273,6274],{"href":6274,"rel":6275},"https:\u002F\u002Flearnopengl-cn.github.io\u002F02",[429]," Lighting\u002F03 Materials\u002F)中我们会讨论如何合理设置这些光照强度，以及它们是如何影响物体的。下一步，我们",[14,6278,6279],{},"计算视线方向向量，和对应的沿着法线轴的反射向量",[19,6281,6286],{"className":6282,"code":6284,"language":6285},[6283],"language-text","vec3 viewDir = normalize(viewPos - FragPos);\nvec3 reflectDir = reflect(-lightDir, norm);\n","text",[26,6287,6284],{"__ignoreMap":24},[10,6289,6290,6291,1186,6298,6301,6302,6203,6305,6307,6308,6311,6312,6314,6315,6317,6318,6320,6321,6324],{},"​\t需要注意的",[14,6292,6293,6294,6297],{},"是我们对",[26,6295,6296],{},"lightDir","向量进行了取反",[26,6299,6300],{},"reflect","函数要求第一个向量是",[14,6303,6304],{},"从光源指向片段位置的向量",[26,6306,6296],{},"当前正好相反，是从片段",[14,6309,6310],{},"指向","光源（由先前我们计算",[26,6313,6296],{},"向量时，减法的顺序决定）。为了保证我们得到正确的",[26,6316,6300],{},"向量，我们通过对",[26,6319,6296],{},"向量取反来获得相反的方向。第二个参数要求是一个法向量，所以我们提供的是已标准化的",[26,6322,6323],{},"norm","向量。",[10,6326,6327],{},"剩下要做的是计算镜面分量。下面的代码完成了这件事：",[19,6329,6331],{"className":21,"code":6330,"language":23,"meta":24,"style":24},"float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);\nvec3 specular = specularStrength * spec * lightColor;\n",[26,6332,6333,6366],{"__ignoreMap":24},[29,6334,6335,6337,6340,6342,6345,6347,6350,6352,6354,6357,6359,6361,6364],{"class":31,"line":32},[29,6336,677],{"class":56},[29,6338,6339],{"class":39}," spec ",[29,6341,200],{"class":56},[29,6343,6344],{"class":35}," pow",[29,6346,49],{"class":39},[29,6348,6349],{"class":35},"max",[29,6351,49],{"class":39},[29,6353,5953],{"class":35},[29,6355,6356],{"class":39},"(viewDir, reflectDir), ",[29,6358,244],{"class":52},[29,6360,1995],{"class":39},[29,6362,6363],{"class":52},"32",[29,6365,75],{"class":39},[29,6367,6368,6371,6373,6375,6377,6379,6381],{"class":31,"line":160},[29,6369,6370],{"class":39},"vec3 specular ",[29,6372,200],{"class":56},[29,6374,6261],{"class":39},[29,6376,206],{"class":56},[29,6378,6339],{"class":39},[29,6380,206],{"class":56},[29,6382,5820],{"class":39},[10,6384,6385,6386,6389],{},"​\t我们先计算视线方向与反射方向的点乘（并确保它不是负值），然后取它的32次幂。这个32是高光的反光度(Shininess)。",[14,6387,6388],{},"一个物体的反光度越高，反射光的能力越强，散射得越少，高光点就会越小","。在下面的图片里，你会看到不同反光度的视觉效果影响：",[10,6391,6392],{},[86,6393],{"alt":86,"src":6394},".\u002Fassets\u002Fbasic_lighting_specular_shininess.png",[10,6396,6397],{},"我们不希望镜面成分过于显眼，所以我们把指数保持为32。剩下的最后一件事情是把它加到环境光分量和漫反射分量里，再用结果乘以物体的颜色：",[19,6399,6401],{"className":21,"code":6400,"language":23,"meta":24,"style":24},"vec3 result = (ambient + diffuse + specular) * objectColor;\nFragColor = vec4(result, 1.0);\n",[26,6402,6403,6426],{"__ignoreMap":24},[29,6404,6405,6408,6410,6412,6414,6417,6419,6422,6424],{"class":31,"line":32},[29,6406,6407],{"class":39},"vec3 result ",[29,6409,200],{"class":56},[29,6411,5992],{"class":39},[29,6413,5995],{"class":56},[29,6415,6416],{"class":39}," diffuse ",[29,6418,5995],{"class":56},[29,6420,6421],{"class":39}," specular) ",[29,6423,206],{"class":56},[29,6425,5811],{"class":39},[29,6427,6428,6431,6433,6435,6437,6439],{"class":31,"line":160},[29,6429,6430],{"class":39},"FragColor ",[29,6432,200],{"class":56},[29,6434,555],{"class":35},[29,6436,6013],{"class":39},[29,6438,53],{"class":52},[29,6440,75],{"class":39},[10,6442,6443,6444,6447],{},"​\t我们现在为",[14,6445,6446],{},"风氏光照计算了全部的光照分量","。根据你的视角，你可以看到类似下面的画面：",[10,6449,6450],{},[86,6451],{"alt":86,"src":6452},".\u002Fassets\u002Fbasic_lighting_specular.png",[6454,6455,6456],"style",{},"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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}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);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":24,"searchDepth":160,"depth":160,"links":6458},[6459,6460],{"id":1316,"depth":160,"text":1316},{"id":6024,"depth":160,"text":6024},"​\t现实世界中有无数种颜色，每一个物体都有它们自己的颜色。我们需要使用（有限的）数值来模拟真实世界中（无限）的颜色，所以并不是所有现实世界中的颜色都可以用数值来表示的。然而我们仍能通过数值来表现出非常多的颜色，甚至你可能都不会注意到与现实的颜色有任何的差异。颜色可以数字化的由红色(Red)、绿色(Green)和蓝色(Blue)三个分量组成，它们通常被缩写为RGB。仅仅用这三个值就可以组合出任意一种颜色。例如，要获取一个**珊瑚红(Coral)**色的话，我们可以定义这样的一个颜色向量：","md",{},"\u002Fgraphy\u002Fopengel\u002F光照",{"description":6461},"graphy\u002Fopengel\u002F光照","B6H4blYF_NnirERRFTWQzy-4-v8dGUc7qTng4Dy-_bw",1792338203085]