路徑動(dòng)畫(huà)——牛郎織女鵲橋會(huì)

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

效果

image.png

一、輸入圖片

繪制出路徑動(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)

xyz示意

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


uv坐標(biāo)示意
關(guān)于透明度

由于選擇覆蓋的方式繪制,那么我們不得不考慮一張人物的貼圖的邊界問(wèn)題。
其實(shí)我們存在兩種選擇:

  1. xyz坐標(biāo)確定人物形狀邊界,uv貼圖只作為紋理填充。
  2. 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ì)有讓人抓狂的地方

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容