題目:給出鵲橋(路徑);牛郎織女分別在橋兩頭走到一起??梢允菆D形方式,也可以是圖像方式
效果

一、輸入圖片
繪制出路徑動(dòng)畫(huà),依然從簡(jiǎn)單的做起。先把靜態(tài)的繪制出來(lái):包括牛郎、織女、鵲橋、背景,此外可自主添加素材。
于是我們第一步便是將這個(gè)4張圖片傳入CPU,根據(jù)上一個(gè)實(shí)驗(yàn),已經(jīng)實(shí)現(xiàn)了一張圖片灌進(jìn)CPU,那么接下來(lái)的便可以如法炮制。
注意下面這部分,這是一張貼圖沒(méi)有傳遞參數(shù)的原因
你可能會(huì)奇怪為什么sampler2D變量是個(gè)uniform,我們卻不用glUniform給它賦值。使用glUniform1i,我們可以給紋理采樣器分配一個(gè)位置值,這樣的話我們能夠在一個(gè)片段著色器中設(shè)置多個(gè)紋理。一個(gè)紋理的位置值通常稱(chēng)為一個(gè)紋理單元(Texture Unit)。一個(gè)紋理的默認(rèn)紋理單元是0,它是默認(rèn)的激活紋理單元,所以教程前面部分我們沒(méi)有分配一個(gè)位置值。
依照教程的方法
//激活紋理單元
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
//fragmentshader內(nèi)部
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}
法一
觀察以上代碼可知:多個(gè)紋理單元應(yīng)用在于一張圖的的多重效果,如例子中的mix效果。這個(gè)效果類(lèi)似PS中圖層混合。
而我們需要的是完全覆蓋的效果,那么只能設(shè)想一個(gè)條件語(yǔ)句:
if(x)do (FragColor =ImageA)
if(y)do (FragColor =ImageB)
此時(shí)出現(xiàn)了問(wèn)題,條件如何判定?
- 只能是在人物和背景都出現(xiàn)的位置,繪制人物
- 只出現(xiàn)背景,繪制背景
判斷每一個(gè)像素后,在決定繪制什么
技術(shù)有限,還未想到如何實(shí)現(xiàn),故拋棄??梢灶A(yù)見(jiàn),此法只需一次glDrawArrays函數(shù)
法二
不用紋理單元。glDrawArrays繪制多次
glBindTexture();
glDrawArrays();
此為一組繪制一張貼圖,后繪制的會(huì)覆蓋先前繪制的。
所以的傳輸圖片數(shù)據(jù)、bindTexture都是一樣的。
二、VAO、VBO設(shè)置
我們需要VBO來(lái)獲取貼圖繪制到視窗的位置(xyz)以及截取自貼圖的位置(uv),同時(shí)需要設(shè)置VAO來(lái)決定從VBO取值的規(guī)則。
我們當(dāng)然可以為每一張貼圖建立單獨(dú)對(duì)應(yīng)的VAO和VBO,我原本本便是這么想的。
通過(guò)設(shè)定z軸的不同來(lái)決定貼圖的先后位置,而非改變繪制的順序決定前后關(guān)系。但最終失敗了,不知是何原因。
那么只建一個(gè)VAO和一個(gè)VBO就好了,因?yàn)樽罱K呈現(xiàn)在一個(gè)平面上,那么所有的xyz都確定成一樣,uv值也選擇整張圖片。
float vertices[] =
{
-1.0f,-1.0f,0, 0.0f,0.0f,
-1.0f,1.0f,0, 0.0f,1.0f,
1.0f,1.0f,0, 1.0f,1.0f,
1.0f,-1.0f,0, 1.0f,0.0f
};
glBindVertexArray(VAO[0]);
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(0 * sizeof(float)));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
值得一提的是標(biāo)準(zhǔn)化設(shè)備坐標(biāo)(Normalized Device Coordinates, NDC)(xyz)和紋理坐標(biāo)(uv)的差異。
xyz坐標(biāo)系是以中心為原點(diǎn)

而uv坐標(biāo)系是以左下角為原點(diǎn)

關(guān)于透明度
由于選擇覆蓋的方式繪制,那么我們不得不考慮一張人物的貼圖的邊界問(wèn)題。
其實(shí)我們存在兩種選擇:
- xyz坐標(biāo)確定人物形狀邊界,uv貼圖只作為紋理填充。
- xyz坐標(biāo)中為規(guī)則形狀,uv貼圖即人物圖。
選擇法一存在以下問(wèn)題:
- 人物形狀難以用頂點(diǎn)確定。
- 不利于實(shí)驗(yàn)三人物變形
所以選擇法2。那么問(wèn)題又來(lái)了,規(guī)則邊界和人物的差集部分需要用透明,否則會(huì)覆蓋底層圖片。所以我們需要將素材不需要的部分扣成透明,這一步需要在PS中操作。(牛郎)
需要注意的,只有png格式存在透明區(qū)域,若轉(zhuǎn)成jpg格式,透明區(qū)域?qū)?huì)變化成白色或黑色。
同時(shí),傳輸圖片數(shù)據(jù)的代碼也要少許修改。
stbi_set_flip_vertically_on_load(true); // 為防止圖像顛倒,在圖像加載時(shí)翻轉(zhuǎn)y軸
data = stbi_load("background.png", &width, &height, &nrChannel, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);//第3、7個(gè)參數(shù)均要把a(bǔ)lpha透明通道加上
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
cout << "load image failed." << endl;
};
stbi_image_free(data);
還要添加透明區(qū)域的函數(shù)
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
看起來(lái)和實(shí)驗(yàn)一差別不大,同樣會(huì)有讓人抓狂的地方