本章主要解決一個(gè)問(wèn)題:
如何用OpenGL模擬基本光照臊岸?
引言
之前的章節(jié)中,我們?yōu)轫旤c(diǎn)設(shè)置了顏色激况,也將紋理圖貼到了立方體的模型上,算是可以渲染3D場(chǎng)景了膘魄。然而乌逐,僅僅如此并不能讓我們感覺(jué)到有趣。在現(xiàn)實(shí)世界中创葡,我們看到的各種絢爛的場(chǎng)景無(wú)一不是各種光照交織而成的浙踢。所以,為了讓我們的場(chǎng)景更酷更炫灿渴,我們必須要學(xué)習(xí)光照的知識(shí)洛波。我們一定要在計(jì)算機(jī)中也能渲染出那種逼真的酷炫效果。
在本文中骚露,你將能看到:
- 顏色的原理
- 環(huán)境光原理
- 漫反射光原理
- 鏡面高光原理
- 顯示一個(gè)包含所有這些光的場(chǎng)景
一蹬挤、顏色
在現(xiàn)實(shí)世界中,每個(gè)物體都有自己的顏色荸百,所有的顏色幾乎可以說(shuō)是無(wú)窮無(wú)盡的闻伶。在計(jì)算機(jī)世界中,我們需要把無(wú)窮無(wú)盡的現(xiàn)實(shí)世界的顏色“映射”到計(jì)算機(jī)世界中有限的顏色上够话。不過(guò)不用擔(dān)心蓝翰,現(xiàn)代計(jì)算機(jī)可以模擬非常多的顏色,多到你根本分辨不出兩種顏色的區(qū)別女嘲。
在OpenGL中畜份,我們用一個(gè)紅色,綠色和藍(lán)色的分量組成一個(gè)RGB格式的向量來(lái)表示顏色欣尼。舉個(gè)例子爆雹,一個(gè)珊瑚紅色的顏色向量是這樣的:
glm::vec3 coral(1.0f, 0.5f, 0.31f);
事實(shí)上停蕉,物體本身并沒(méi)有顏色。我們看到的顏色是物體表面對(duì)光照的反射屬性不同钙态,反射出來(lái)的不同的光的顏色慧起。
可以看到,物體反射出來(lái)的紅色最多册倒,將其他的顏色掩蓋之后蚓挤,我們看到的就是一個(gè)紅色的物體,未被反射出來(lái)的光都被物體吸收了驻子。
那么如何在OpenGL中模擬這種情況呢灿意?其實(shí)我們已經(jīng)定義好了。在上面定義的coral變量中崇呵,我們指明了紅色會(huì)完全反射(1.0f)缤剧,綠色會(huì)反射一半(0.5f),而藍(lán)色會(huì)反射31%(0.31f)域慷。當(dāng)有一束白色的光(1.0f, 1.0f, 1.0f)光照射到coral上時(shí)荒辕,將兩個(gè)向量相乘,所得到的向量就是最終物體上的顏色向量芒粹。
glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f)
顏色的部分就講到這里兄纺,接下來(lái)我們來(lái)說(shuō)說(shuō)光照。
實(shí)現(xiàn)一個(gè)有光照的場(chǎng)景
在之后的章節(jié)中化漆,我們會(huì)通過(guò)模擬現(xiàn)實(shí)生活中的光照實(shí)現(xiàn)一些非常有趣的效果估脆。所以,撇開(kāi)之前的紋理座云,我們重新來(lái)弄一個(gè)簡(jiǎn)單的場(chǎng)景疙赠,再往里面添加光照效果。
我們首先要做的就是創(chuàng)建一個(gè)可以接收光照的物體朦拖,方便起見(jiàn)圃阳,還是用一個(gè)立方體盒子。我們還需要一個(gè)東西來(lái)模擬光源璧帝,東西越簡(jiǎn)單越好捍岳,最好還是一個(gè)立方體盒子,嘿嘿睬隶。
填充VBO锣夹,設(shè)置頂點(diǎn)屬性等等的工作我們已經(jīng)非常熟悉,在這里就不啰嗦了苏潜。不熟悉的童鞋可以參照之前的例子银萍。
新的立方體上,我們只需要位置屬性恤左,去除掉之前的紋理贴唇,在頂點(diǎn)著色器中對(duì)頂點(diǎn)進(jìn)行轉(zhuǎn)換就可以了搀绣。精簡(jiǎn)之后的頂點(diǎn)著色器如下:
//頂點(diǎn)著色器代碼
#version 330 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4 (aPos, 1.0f);
}
確保你已經(jīng)更新了頂點(diǎn)屬性的配置。只有位置屬性的頂點(diǎn)跨度為3*sizeof(float)戳气。
//链患!頂點(diǎn)屬性環(huán)境
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//頂點(diǎn)屬性設(shè)置
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
這些代碼應(yīng)該是非常直觀易懂的。定義完立方體之后物咳,我們?cè)賮?lái)弄一個(gè)片元著色器锣险。
#version 330 core
out vec4 FragColor;
uniform vec3 objectColor;
uniform vec3 lightColor;
void main()
{
FragColor = vec4(lightColor * objectColor, 1.0);
}
片元著色器需要物體顏色和光照顏色兩個(gè)uniform變量來(lái)計(jì)算蹄皱。這里我們根據(jù)之前的理解览闰,直接將兩個(gè)變量相乘,再添加一個(gè)齊次分量后賦值給片元顏色巷折。我們來(lái)設(shè)置物體顏色和光照顏色压鉴。
lightingShader.use();
lightingShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
lightingShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
當(dāng)我們改變著色器的時(shí)候,我們只希望物體的顏色變化锻拘,而希望連光源的顏色都變了油吭,所以,這里我們還要多定義一個(gè)片元著色器用于對(duì)光源進(jìn)行渲染署拟。
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0);
}
很簡(jiǎn)單婉宰,只需要將光源設(shè)置成白色就行。當(dāng)我們繪制盒子立方體時(shí)推穷,我們使用前面的那個(gè)著色器心包,當(dāng)我們繪制光源立方體時(shí),我們使用這個(gè)著色器馒铃。
定義一個(gè)光源的位置蟹腾,然后將光源立方體移動(dòng)到指定位置。
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);
...
model = glm::mat4();
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f));
在渲染的時(shí)候区宇,對(duì)盒子立方體娃殖,要用lightShader,對(duì)光源立方體议谷,要用lampShader炉爆。所有的這些都弄完之后,運(yùn)行出來(lái)的效果應(yīng)該是這樣的:
如果有問(wèn)題卧晓,參考這里的源碼芬首。
二、基礎(chǔ)光照
現(xiàn)實(shí)世界中的光照受太多因素的影響禀崖,以至于我們根本就沒(méi)有足夠的計(jì)算力來(lái)計(jì)算其所有的因素衩辟。OpenGL中使用一些簡(jiǎn)單的模型來(lái)模擬真實(shí)世界中的光照,將這些模型組合起來(lái)后波附,我們也能得到非常逼真的效果艺晴。有一種模型叫做:馮氏光照模型(Phong light model)昼钻。這種模型有三種光照組成:環(huán)境光(ambient),漫反射光(diffuse)和鏡面高光(specular)封寞。Phong光照模型是圖形學(xué)中最常用的模型然评,用好之后效果也非常贊。來(lái)看一組效果圖:
- 環(huán)境光(Ambient):即便是在漆黑的夜晚也還是有少許亮光的存在(月亮狈究,遠(yuǎn)處的燈光碗淌,星光),所以抖锥,物體不會(huì)是完全的黑色亿眠。為了模擬這種情況,我們通常會(huì)給定一個(gè)常量顏色值充當(dāng)環(huán)境光磅废。
- 漫反射光(diffuse):模擬光直接照射到物體上的情況纳像。這是光照模型中最具有特點(diǎn)的組成部分。越朝向光源的部分看起來(lái)就越亮拯勉。
- 鏡面高光(specular):模擬一個(gè)非常光滑的物體上的聚光燈效果竟趾。鏡面高光受光照顏色的影響比受物體顏色的影響更大。
想要讓場(chǎng)景中的效果逼真宫峦,我們至少需要模擬這三種光照岔帽。先從最簡(jiǎn)單的開(kāi)始:環(huán)境光(Ambient)
環(huán)境光(Ambient)
光照通常不會(huì)來(lái)自一個(gè)單一的光源,而是在場(chǎng)景中各個(gè)光源导绷,各種反射或折射綜合而成的一個(gè)效果犀勒。將這些因素都考慮進(jìn)去的算法稱作全局光照算法,但是這些算法非常難懂而且運(yùn)行起來(lái)代價(jià)高昂诵次。
于是账蓉,我們自然而然地尋找一些簡(jiǎn)單的方法來(lái)這些高昂的算法,終于逾一,我們找到了環(huán)境光這種模型铸本。就如之前所說(shuō),我們使用一個(gè)小常量顏色來(lái)代替照射到物體的環(huán)境光遵堵。
使用環(huán)境光非常的簡(jiǎn)單箱玷,我們只需要設(shè)置一個(gè)環(huán)境光強(qiáng)度,用這個(gè)強(qiáng)度值乘上光源的顏色得到環(huán)境光顏色陌宿。最后锡足,用環(huán)境光顏色乘上物體的顏色,得到物體在光照下的最終顏色值壳坪。使用了環(huán)境光的代碼如下:
void main()
{
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;
vec3 result = ambient * objectColor;
FragColor = vec4(result, 1.0f);
}
附上運(yùn)行的效果圖:
漫反射光(Diffuse)
相對(duì)于環(huán)境光(ambient)來(lái)說(shuō)舶得,漫反射光(Diffuse)在物體上的表現(xiàn)更明顯,也給了物體最直觀的特征爽蝴。為了能更好的理解漫反射沐批,請(qǐng)看下面的一張圖:
我們的物體表面并不會(huì)那么光滑平整纫骑,光線照射到物體表面的時(shí)候會(huì)產(chǎn)生方向不同的反射效果,這些反射就是漫反射九孩。漫反射光(Diffuse)期望模擬的先馆,就是這種光線照射上來(lái)之后,經(jīng)過(guò)物體復(fù)雜的漫反射之后所呈現(xiàn)出的整體的光照效果躺彬。
那么煤墙,我們?nèi)绾斡?jì)算漫反射呢?
- 法向量(Normal Vector):垂直于頂點(diǎn)表面的一個(gè)向量
- 光線向量:片元位置和光源之間的一個(gè)方向向量宪拥,用于計(jì)算與法向量之間的夾角仿野。
- 計(jì)算光線向量和法向量之間的夾角,如果夾角<90度江解,說(shuō)明物體是對(duì)著光源的设预,再根據(jù)cos值來(lái)計(jì)算強(qiáng)度。如果夾角>90度犁河,說(shuō)明物體背對(duì)光源,光照也就沒(méi)效果了魄梯。
法向量(Normal Vector)
法向量是垂直于頂點(diǎn)表面的單位向量桨螺。因?yàn)轫旤c(diǎn)本身是沒(méi)有表面這個(gè)概念的,所以我們會(huì)參考其周?chē)捻旤c(diǎn)來(lái)確定它的表面酿秸,從而計(jì)算出法向量灭翔。在計(jì)算法向量的時(shí)候,我們用了一些小技巧(叉乘)辣苏。因?yàn)?D立方體并不復(fù)雜肝箱,所以我們手動(dòng)計(jì)算了所有頂點(diǎn)的法向量。完整的頂點(diǎn)結(jié)構(gòu)如下:
float vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f
};
當(dāng)我們加入了額外的頂點(diǎn)屬性之后稀蟋,條件反射式的就要去修改傳遞給OpenGL的信息煌张。請(qǐng)注意,兩個(gè)立方體雖然共用一組數(shù)據(jù)退客,但是物體需要法線信息骏融,而光源不需要法線信息。因此萌狂,一個(gè)重要的操作就是把渲染物體的VAO和渲染光源的VAO分開(kāi)档玻,各自設(shè)置自己的頂點(diǎn)屬性:物體需要全部的頂點(diǎn)屬性,光源只需要位置的屬性茫藏。
// VBO误趴,物體VAO
unsigned int VBO, cubeVAO;
glGenVertexArrays(1, &cubeVAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(cubeVAO);
// 位置屬性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 法向量屬性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
// 光源VAO (VBO相同,因?yàn)轫旤c(diǎn)數(shù)據(jù)是同一組)
unsigned int lightVAO;
glGenVertexArrays(1, &lightVAO);
glBindVertexArray(lightVAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 位置屬性(只需要更新跨度就可以了)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
接著光照頂點(diǎn)著色器中要接收法向量進(jìn)行操作务傲,然后輸出法向量凉当,片元著色器中也要接收法向量進(jìn)行計(jì)算碧囊。
//頂點(diǎn)著色器
...
layout (location = 1) in vec3 aNormal;
...
out vec3 Normal;
...
void main()
{
gl_Position = projection * view * model * vec4 (aPos, 1.0f);
Normal = aNormal;
}
//片元著色器
...
in vec3 Normal;
...
計(jì)算漫反射顏色
現(xiàn)在我們的頂點(diǎn)有了法向量,還缺光源位置和片元位置纤怒。光源位置是一個(gè)靜態(tài)的變量我們直接在片元著色器中定義成uniform變量糯而。
//片元著色器
uniform vec3 lightPos;
而后流码,在主函數(shù)中設(shè)置光源位置芍殖。
lightingShader.setVec3("lightPos", lightPos);
最后,我們還要計(jì)算出片元的位置劝赔。要計(jì)算片元位置烘豹,我們只要將位置與模型矩陣相乘就可以了瓜贾。
out vec3 FragPos;
out vec3 Normal;
void main()
{
gl_Position = projection * view * model * vec4 (aPos, 1.0f);
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = aNormal;
}
對(duì)了,別忘了在片元著色器中加上接受片元位置的變量携悯。in vec3 FragPos;
緊接著祭芦,我們來(lái)計(jì)算漫反射顏色。思路是:
- 計(jì)算片元指向光源的向量(光源向量)
- 規(guī)范化光源向量和法線向量憔鬼。
- 計(jì)算光源向量和法線向量的點(diǎn)積龟劲,將該值乘上光源顏色值
//計(jì)算光源向量,規(guī)范化兩個(gè)向量
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
//計(jì)算點(diǎn)積轴或,修正點(diǎn)積值昌跌,乘上光源顏色值
float diff = max(dot(norm, lightDir), 0.0); //負(fù)數(shù)沒(méi)有意義
vec3 diffuse = diff * lightColor;
最后的最后,將環(huán)境光(ambient)和漫反射(diffuse)相加起來(lái)照雁,再乘上物體顏色蚕愤,得到最終的結(jié)果。
如果你運(yùn)行的效果和上圖不同饺蚊,可以參考這里的源碼萍诱。
還有一件事
前面的代碼中,我們直接將法向量從頂點(diǎn)著色器傳遞到了片元著色器污呼。不過(guò)裕坊,我們?cè)谟?jì)算的時(shí)候,用的都是世界空間中的坐標(biāo)曙求,難道我們不該把法向量也轉(zhuǎn)換成世界空間中的坐標(biāo)嗎碍庵?原則上,是的悟狱,不過(guò)轉(zhuǎn)換操作并不是乘上一個(gè)模型矩陣那么簡(jiǎn)單静浴。
首先,法向量只有方向有意義(因?yàn)槲覀儾恍枰诳臻g中的位置信息)挤渐。所以苹享,我們只對(duì)法向量的比例變換和旋轉(zhuǎn)變換感興趣。要去除模型矩陣中的平移操作的話,需要將法向量的齊次坐標(biāo)(w)設(shè)置為0.
然后得问,如果模型矩陣進(jìn)行了一個(gè)不規(guī)則的比例變換囤攀,那么即使法向量乘上模型矩陣,法向量也不會(huì)和表面垂直了宫纬,如下圖所示:
法向量不和表面垂直會(huì)嚴(yán)重扭曲光照效果焚挠。
解決這個(gè)問(wèn)題的方法是為法向量量身定制一個(gè)模型矩陣。這個(gè)矩陣被稱為向量矩陣(normal matrix)漓骚,其使用了一些線性代數(shù)的操作來(lái)消除對(duì)法向量的比例變換影響蝌衔。
詳細(xì)的原理超出了本文的討論范圍,具體內(nèi)容有興趣的童鞋可以去網(wǎng)上找蝌蹂。下面直接給出計(jì)算的方法:
Normal = mat3(transpose(inverse(model)) * aNormal;
模型矩陣的逆矩陣的轉(zhuǎn)置矩陣噩斟,就是這貨。
前面我們沒(méi)對(duì)法向量轉(zhuǎn)換卻沒(méi)出問(wèn)題純粹是僥幸孤个,因?yàn)槲覀儧](méi)有對(duì)模型進(jìn)行任何比例變換或者是旋轉(zhuǎn)的操作剃允。但是,一旦有這兩種操作齐鲤,就必須對(duì)法向量進(jìn)行變換斥废。
鏡面高光(Specular)
挺住,我們還剩最有一個(gè)光照內(nèi)容了佳遂!
和漫反射很類似营袜,鏡面高光液是基于光源方向向量和物體表面法向量的。不同的是丑罪,鏡面高光也取決于觀察者看物體的角度。想象一下如果物體像鏡子一樣光滑凤壁,我們就能在某個(gè)位置看到非常強(qiáng)烈的光照吩屹。原理如下圖所示:
將光源方向沿著法向量對(duì)稱一下,我們就得到了反射光向量拧抖。然后煤搜,計(jì)算出反射光向量和觀察者方向的角度差,角度差越小唧席,光照越強(qiáng)擦盾。
觀察者方向是一個(gè)額外我們需要計(jì)算的東西,可以通過(guò)觀察者位置(世界空間)和片元位置計(jì)算出來(lái)淌哟。然后迹卢,計(jì)算出鏡面反射強(qiáng)度,乘以光照顏色徒仓,將它與環(huán)境光和漫反射光加起來(lái)腐碱,得到的就是完整的光照效果。
在片元著色器中添加觀察者位置變量掉弛,然后在主循環(huán)中設(shè)置症见。
uniform vec3 viewPos;
lightingShader.setVec3("viewPos", camera.Position);
設(shè)置一個(gè)鏡面反射強(qiáng)度喂走,控制鏡面反射光對(duì)物體的影響程度。
float specularStrength = 0.5;
計(jì)算反射光向量谋作。
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
最后芋肠,計(jì)算鏡面高光的強(qiáng)度,用下面的公式:
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
我們先計(jì)算了觀察方向和反射方向的夾角(并確保其大于0)遵蚜,然后計(jì)算了它的32次方值帖池。這個(gè)32表示了高光的光澤度信息。光澤度越高谬晕,高光范圍越集中碘裕。如下圖所示:
通常,光澤度設(shè)置成32就可以了攒钳。最后帮孔,把鏡面高光的部分添加到總的結(jié)果里面去。我們就完成了馮氏光照模型不撑。
完整的代碼在這里文兢。
現(xiàn)在,你應(yīng)該領(lǐng)教了著色器的強(qiáng)大之處了吧焕檬。只需要少量的信息姆坚,著色器就能計(jì)算出光照效果。之后我們會(huì)把光照效果挖掘地更深实愚!
總結(jié)
在這篇文章里兼呵,我們學(xué)習(xí)了顏色的原理,學(xué)習(xí)了如何用環(huán)境光腊敲,漫反射光击喂,鏡面高光來(lái)模擬現(xiàn)實(shí)中復(fù)雜的光照。同時(shí)碰辅,我們也實(shí)踐了學(xué)到的知識(shí)懂昂,渲染了一個(gè)使用馮氏光照模型的場(chǎng)景。收拾收拾腦中的知識(shí)没宾,將這些知識(shí)都聯(lián)系起來(lái)凌彬。
參考資料:
www.learningopengl.com(非常好的網(wǎng)站,建議學(xué)習(xí))