OpenGL的官方文檔中有一個(gè)例子:
#version 330 core
layout (location = 0) in vec3 aPos; // 位置變量的屬性位置值為 0
layout (location = 1) in vec3 aColor; // 顏色變量的屬性位置值為 1
out vec3 ourColor; // 向片段著色器輸出一個(gè)顏色
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor; // 將ourColor設(shè)置為我們從頂點(diǎn)數(shù)據(jù)那里得到的輸入顏色
}
意思是在頂點(diǎn)著色器中猛拴,傳入3個(gè)頂點(diǎn)的顏色羹铅,最終會生成一個(gè)有漸變色彩的三角形。
剛學(xué)到這個(gè)東西的時(shí)候感覺非常反直覺愉昆,因?yàn)轫旤c(diǎn)著色器實(shí)際上只提供了3個(gè)顏色职员,為什么會生成這么多的顏色并且填滿整個(gè)三角形呢?
官方給的解答是:
這個(gè)圖片可能不是你所期望的那種跛溉,因?yàn)槲覀冎惶峁┝?個(gè)顏色焊切,而不是我們現(xiàn)在看到的大調(diào)色板。這是在片段著色器中進(jìn)行的所謂 片段插值(Fragment Interpolation) 的結(jié)果芳室。當(dāng)渲染一個(gè)三角形時(shí)专肪,光柵化(Rasterization)階段通常會造成比原指定頂點(diǎn)更多的片段。光柵會根據(jù)每個(gè)片段在三角形形狀上所處相對位置決定這些片段的位置堪侯。
基于這些位置嚎尤,它會插值(Interpolate)所有片段著色器的輸入變量。比如說伍宦,我們有一個(gè)線段芽死,上面的端點(diǎn)是綠色的乏梁,下面的端點(diǎn)是藍(lán)色的。如果一個(gè)片段著色器在線段的70%的位置運(yùn)行收奔,它的顏色輸入屬性就會是一個(gè)綠色和藍(lán)色的線性結(jié)合掌呜;更精確地說就是30%藍(lán) + 70%綠滓玖。
這正是在這個(gè)三角形中發(fā)生了什么坪哄。我們有3個(gè)頂點(diǎn),和相應(yīng)的3個(gè)顏色势篡,從這個(gè)三角形的像素來看它可能包含50000左右的片段翩肌,片段著色器為這些像素進(jìn)行插值顏色。如果你仔細(xì)看這些顏色就應(yīng)該能明白了:紅首先變成到紫再變?yōu)樗{(lán)色禁悠。片段插值會被應(yīng)用到片段著色器的所有輸入屬性上念祭。
也就是說在渲染管線中,進(jìn)行片段著色之前碍侦,可能會對要渲染的每一個(gè)像素進(jìn)行插值粱坤。但是它是如何進(jìn)行插值的呢?
起初我認(rèn)為瓷产,在片段著色器里站玄,會首先渲染這個(gè)圖形每個(gè)頂點(diǎn)的顏色,然后進(jìn)行線性插值濒旦,最后渲染每一個(gè)像素的顏色株旷。
直到我學(xué)到漫反射之后,感覺我的理解有點(diǎn)不對:
如圖尔邓,利用OpenGL渲染出來的漫反射圖:
片段著色器代碼如下:
#version 330 core
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
uniform vec3 objColor;
uniform vec3 ambientColor;
uniform vec3 lightPos;
uniform vec3 lightColor;
void main(){
vec3 lightDir = normalize(lightPos - FragPos);
vec3 diffuse = max(dot(lightDir,Normal),0) * lightColor;//點(diǎn)乘
FragColor = vec4((ambientColor + diffuse) * objColor,1.0);
}
我把光源設(shè)置在第一個(gè)立方體中心前面一點(diǎn)的位置晾剖,渲染出來了比較真實(shí)的光照效果。
你可能會注意到梯嗽,正方體中心位置會比頂點(diǎn)周圍的像素要亮得多齿尽。如果按照我之前的理解,光柵化之后灯节,片段著色器會先渲染每個(gè)頂點(diǎn)的顏色循头,然后插值運(yùn)算每個(gè)像素的顏色,那么結(jié)果肯定和渲染出來的不同显晶。
因?yàn)榘秧旤c(diǎn)帶入到片段著色器計(jì)算的話贷岸,每個(gè)頂點(diǎn)的顏色是用(ambientColor + diffuse) * objColor即環(huán)境光和漫反射計(jì)算出來的,由于光源和每個(gè)頂點(diǎn)的夾角相同磷雇,所以計(jì)算出每個(gè)頂點(diǎn)的顏色相同偿警,然后進(jìn)行插值運(yùn)算填充像素的話,那么渲染出來的結(jié)果肯定是這個(gè)立方體正對著我們的一面都是同一種的顏色唯笙,不會出現(xiàn)中心要亮一點(diǎn)的情況螟蒸。
這時(shí)候我突然發(fā)現(xiàn)盒使,之前的理解肯定有問題,于是又花了些時(shí)間退回去看渲染管線的一些知識七嫌。
我認(rèn)為OpenGL從頂點(diǎn)著色器 -> 片段著色器可能會經(jīng)歷這么一個(gè)過程:
這里貼上頂點(diǎn)著色器代碼:
#version 330 core
layout (location = 0) in vec3 aPos; // 位置變量的屬性位置值為 0
layout (location = 3) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main(){
gl_Position = projection * view * model * vec4(aPos, 1.0); // 注意乘法要從右向左讀
FragPos = (model * vec4(aPos, 1.0)).xyz;
Normal = mat3(model) * aNormal;
}
OpenGL的片段著色器在進(jìn)行渲染之前少办,實(shí)際上會對out里面的數(shù)據(jù),例如FragPos,Normal進(jìn)行線性插值例如頂點(diǎn)(1,0,0,1)和(3,0,0,1)诵原,就會對中間的像素插值(2,0,0,1)英妓。由于一個(gè)面上的頂點(diǎn)法向量都是相同的,所以Normal進(jìn)行插值的時(shí)候绍赛,同一個(gè)面上生成的值都是相同的蔓纠。
在片段著色器中,會對面一個(gè)插值出來的像素計(jì)算它的顏色吗蚌,進(jìn)行(ambientColor + diffuse) * objColor的計(jì)算腿倚,由于每個(gè)像素點(diǎn)FragPos不同,所以會計(jì)算出如上圖那樣立方體面中心很亮的結(jié)果蚯妇,而不是像我理解那樣先計(jì)算正方形面的四個(gè)頂點(diǎn)敷燎,然后進(jìn)行插值渲染,這樣就會出現(xiàn)一個(gè)顏色都相同的立方體面箩言。
以上知識本人的一點(diǎn)理解硬贯,可能是錯(cuò)的,初學(xué)OpenGL分扎,感覺它還是蠻硬核的澄成,很多問題都要糾結(jié)很久...