乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      【learnOpenGL學(xué)習(xí)筆記_18】OpenGL高級(jí)教程 - 透明與混合學(xué)習(xí)實(shí)踐

       小張學(xué)AI 2025-06-24 發(fā)布于山東

      • · 大家好,我是 同學(xué)小張,日常分享AI知識(shí)和實(shí)戰(zhàn)案例

      • · 歡迎 點(diǎn)贊 + 關(guān)注 ??,持續(xù)學(xué)習(xí),持續(xù)干貨輸出

      • · +v: jasper_8017 一起交流??,一起進(jìn)步??,更有專業(yè)資料領(lǐng)??!

      • 微信私信免費(fèi)領(lǐng)取更多 AI、C++等相關(guān)資料,持續(xù)收集更新中!包括但不限于:


      本文繼續(xù)學(xué)習(xí)OpenGL的高級(jí)技術(shù):混合(Blending)。

      1. 什么是混合

      在計(jì)算機(jī)圖形學(xué)中,透明效果是實(shí)現(xiàn)逼真場(chǎng)景的重要技術(shù)之一。透明物體能夠“混合”自身顏色與背后物體的顏色,從而呈現(xiàn)出獨(dú)特的視覺(jué)效果。這種技術(shù)在 OpenGL 中被稱為 混合(Blending)。無(wú)論是玻璃窗、煙霧,還是半透明的液體,透明效果的核心都在于如何將不同物體的顏色融合在一起。

      1.1 透明度:從純色到混合色

      透明物體的視覺(jué)特性來(lái)源于其顏色的“混合性”。一個(gè)有色玻璃窗就是一個(gè)典型的例子:它不僅有自己的顏色,還會(huì)與背后物體的顏色混合,最終呈現(xiàn)出獨(dú)特的視覺(jué)效果。透明度由顏色的 alpha 值 決定,這是顏色向量的第四個(gè)分量。當(dāng) alpha 值為 1.0 時(shí),物體完全不透明;當(dāng) alpha 值為 0.0 時(shí),物體完全透明;而介于兩者之間的值則表示半透明。

      在這里插入圖片描述

      紋理是實(shí)現(xiàn)透明效果的關(guān)鍵工具。除了常見(jiàn)的 RGB(紅、綠、藍(lán))通道外,許多紋理還包含一個(gè) alpha 通道,用于定義每個(gè)像素的透明度。例如,一個(gè)窗戶紋理的玻璃部分可能具有較低的 alpha 值(如 0.25),而窗框部分的 alpha 值為 1.0,表示完全不透明。

      在這里插入圖片描述

      2. 忽略透明像素:實(shí)現(xiàn)部分不可見(jiàn)效果

      并非所有紋理都需要半透明效果。例如,草葉紋理通常包含完全透明和完全不透明的區(qū)域,而沒(méi)有中間的半透明部分。為了只渲染不透明部分,我們可以利用 OpenGL 的 discard 命令丟棄透明像素。

      以草葉紋理為例,展示如何忽略透明像素,下面來(lái)修改我們之前的代碼:

      2.1 將草紋理顯示到場(chǎng)景中

      先將草紋理放到工程中顯示出來(lái)。

      2.1.1 加載紋理

      之前我們封裝的 loadTexture 函數(shù)其實(shí)已經(jīng)支持加載 RGBA 格式的紋理。format = GL_RGBA;。注意這個(gè) format 用在了 glTexImage2D 函數(shù)中。

      unsigned int loadTexture(char const *path)
      {
          unsignedint textureID;
          glGenTextures(1, &textureID);

          int width, height, nrComponents;
          unsignedchar *data = stbi_load(path, &width, &height, &nrComponents, 0);
          if (data)
          {
              GLenum format;
              if (nrComponents == 1)
                  format = GL_RED;
              elseif (nrComponents == 3)
                  format = GL_RGB;
              elseif (nrComponents == 4)
                  format = GL_RGBA;

              glBindTexture(GL_TEXTURE_2D, textureID);
              glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
              glGenerateMipmap(GL_TEXTURE_2D);

              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
              glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

              stbi_image_free(data);
          }
          else
          {
              std::cout << "Texture failed to load at path: " << path << std::endl;
              stbi_image_free(data);
          }

          return textureID;
      }

      2.1.2 修改片段著色器

      現(xiàn)在加載的紋理包含 alpha 通道,是個(gè) vec4 類型了。所以,需要如下使用紋理:

      // color = vec4(vec3(texture(texture1, TexCoords)), 1.0);
      color = texture(texture1, TexCoords);

      2.1.3 主程序修改

      (1)加載草的紋理

      unsigned int grassTexture = loadTexture(std::string(PROJECT_PATH + "/resource/grass.png").c_str());

      (2)設(shè)置草的頂點(diǎn)數(shù)據(jù)

      float transparentVertices[] = {
          // Positions         // Texture Coords (swapped y coordinates because texture is flipped upside down)
          0.0f,  0.5f,  0.0f,  0.0f,  0.0f,
          0.0f-0.5f,  0.0f,  0.0f,  1.0f,
          1.0f-0.5f,  0.0f,  1.0f,  1.0f,

          0.0f,  0.5f,  0.0f,  0.0f,  0.0f,
          1.0f-0.5f,  0.0f,  1.0f,  1.0f,
          1.0f,  0.5f,  0.0f,  1.0f,  0.0f
      };

      (3)添加草的位置

      vector<glm::vec3> vegetation;
      vegetation.push_back(glm::vec3(-1.5f,  0.0f-0.48f));
      vegetation.push_back(glm::vec31.5f,  0.0f,  0.51f));
      vegetation.push_back(glm::vec30.0f,  0.0f,  0.7f));
      vegetation.push_back(glm::vec3(-0.3f,  0.0f-2.3f));
      vegetation.push_back(glm::vec30.5f,  0.0f-0.6f));

      (4)創(chuàng)建草的VAO、VBO

      unsigned int transparentVAO, transparentVBO;
      glGenVertexArrays(1, &transparentVAO);
      glGenBuffers(1, &transparentVBO);
      glBindVertexArray(transparentVAO);
      glBindBuffer(GL_ARRAY_BUFFER, transparentVBO);
      glBufferData(GL_ARRAY_BUFFER, sizeof(transparentVertices), transparentVertices, GL_STATIC_DRAW);
      glEnableVertexAttribArray(0);
      glVertexAttribPointer(03, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
      glEnableVertexAttribArray(1);
      glVertexAttribPointer(12, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
      glBindVertexArray(0);

      (5)繪制草

      glBindVertexArray(transparentVAO);
      glBindTexture(GL_TEXTURE_2D, grassTexture);  
      for(GLuint i = 0; i < vegetation.size(); i++)
      {
          model = glm::mat4();
          model = glm::translate(model, vegetation[i]);
          ourShader.setMat4("model", model);
          glDrawArrays(GL_TRIANGLES, 06);
      }  
      glBindVertexArray(0);

      2.1.4 運(yùn)行效果

      在這里插入圖片描述

      2.2 忽略透明像素:實(shí)現(xiàn)草葉效果

      出現(xiàn)上述顯示情況是因?yàn)镺penGL默認(rèn)是不知道如何處理alpha值的,不知道何時(shí)忽略(丟棄)它們。GLSL為我們提供了discard命令,它保證了片段不會(huì)被進(jìn)一步處理,這樣就不會(huì)進(jìn)入顏色緩沖。有了這個(gè)命令我們就可以在片段著色器中檢查一個(gè)片段是否有在一定的閾限下的alpha值,如果有,那么丟棄這個(gè)片段。

      修改片段著色器如下:

      void main()
      {    
          vec4 texColor = texture(texture1, TexCoords);
          if(texColor.a < 0.1)
              discard;
          color = texColor;
      }

      運(yùn)行效果:

      在這里插入圖片描述

      通過(guò)這種方式,我們可以只渲染紋理中不透明的部分,而忽略透明區(qū)域,從而實(shí)現(xiàn)類似草葉的效果。

      細(xì)心的朋友可能也發(fā)現(xiàn)了,上述運(yùn)行效果種,草的上方會(huì)有一條類似花屏之后的線條。這是因?yàn)椋?/span>

      當(dāng)在紋理邊緣進(jìn)行采樣時(shí),OpenGL會(huì)在邊界值和下一個(gè)重復(fù)的紋理值之間進(jìn)行插值,因?yàn)槲覀儗⑵浞胖梅绞皆O(shè)置為GL_REPEAT。但由于我們使用的是透明值(alpha值),紋理圖片的上部會(huì)得到一個(gè)與底部純色值插值后的透明值。這就導(dǎo)致了一個(gè)稍微半透明的邊緣。

      為了避免這種情況,當(dāng)使用帶有alpha通道的紋理時(shí),應(yīng)該將紋理環(huán)繞方式設(shè)置為GL_CLAMP_TO_EDGE:

      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

      這樣設(shè)置后,紋理在邊緣處的采樣就不會(huì)進(jìn)行插值,而是會(huì)使用邊緣的值,從而避免出現(xiàn)半透明的邊緣。

      修改后運(yùn)行效果:

      在這里插入圖片描述

      3. 混合技術(shù):實(shí)現(xiàn)半透明效果

      上述丟棄片段的方式,不能使我們獲得渲染半透明圖像,我們要么渲染出像素,要么完全地丟棄它。

      如果需要渲染半透明物體(如玻璃窗),則需要啟用 OpenGL 的 混合功能。混合的核心思想是將當(dāng)前片段的顏色與顏色緩沖中的顏色按一定規(guī)則融合。

      3.1 混合功能相關(guān)接口

      啟用混合的步驟如下:

      1. 1. 啟用混合功能:glEnable(GL_BLEND);
      2. 2. 設(shè)置混合方程:glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

      混合方程參數(shù):

      在這里插入圖片描述

      混合方程參數(shù)的選項(xiàng):

      在這里插入圖片描述

      也可以為RGB和alpha通道各自設(shè)置不同的選項(xiàng),使用glBlendFuncSeperate

      // 這個(gè)方程設(shè)置了RGB元素,但是只讓最終的alpha元素被源alpha值影響到。
      glBlendFuncSeperate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,GL_ONE, GL_ZERO);

      還有一個(gè)接口,glBlendEquation。上面的混合方式都是源和目標(biāo)元素相加,這個(gè)方程可以設(shè)置成相減等。

      在這里插入圖片描述

      3.2 實(shí)踐:渲染半透明紋理

      (1)開(kāi)啟混合

      glEnable(GL_BLEND);
      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

      (2)修改片段著色器,不用discard

      void main()
      {    
          vec4 texColor = texture(texture1, TexCoords);
          color = texColor;
      }

      (3)加載窗戶紋理

      unsigned int windowTexture = loadTexture(std::string(PROJECT_PATH + "/resource/blending_transparent_window.png").c_str());

      (4)渲染

      這里復(fù)用之前草的頂點(diǎn)數(shù)據(jù)、VAO、VBO和位置數(shù)據(jù),只需要在繪制時(shí)綁定窗戶的紋理即可。

      glBindTexture(GL_TEXTURE_2D, windowTexture);  

      運(yùn)行效果:

      在這里插入圖片描述

      上面這個(gè)效果可以看到混合了,但是還有點(diǎn)問(wèn)題。前面的窗子,雖然半透明,但是后面的窗子還是沒(méi)有完全渲染出來(lái),被前面的窗子遮擋了。

      3.3 透明物體排序

      透明物體的渲染順序?qū)ψ罱K效果至關(guān)重要。由于深度緩沖無(wú)法正確處理透明像素,透明物體需要按照 從遠(yuǎn)到近 的順序渲染。否則,由于深度測(cè)試的作用,前面的透明物體可能會(huì)遮擋后面的物體。

      要讓混合在多物體上有效,我們必須先繪制最遠(yuǎn)的物體,最后繪制最近的物體。

      實(shí)現(xiàn)透明物體排序的步驟如下:

      1. 1. 計(jì)算每個(gè)物體到攝像機(jī)的距離。
      2. 2. 使用 std::map 存儲(chǔ),key為距離,std::map會(huì)自動(dòng)按距離從小到大排序。
      3. 3. 按排序后的順序渲染透明物體。

      排序代碼:

      std::map<float, glm::vec3> sorted;
      for (unsigned int i = 0; i < vegetation.size(); i++) // windows contains all window positions
      {
          GLfloat distance = glm::length(cameraPos - vegetation[i]);
          sorted[distance] = vegetation[i];
      }

      然后逆序繪制:

      for(std::map<float,glm::vec3>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it)
      {
          model = glm::mat4();
          model = glm::translate(model, it->second);
          ourShader.setMat4("model", model);
          glDrawArrays(GL_TRIANGLES, 06);
      }  

      通過(guò)這種方式,我們可以確保透明物體的正確顯示。運(yùn)行效果:

      在這里插入圖片描述

      雖然這個(gè)按照它們的距離對(duì)物體進(jìn)行排序的方法在這個(gè)特定的場(chǎng)景中能夠良好工作,但它不能進(jìn)行旋轉(zhuǎn)、縮放或者進(jìn)行其他的變換,奇怪形狀的物體需要一種不同的方式,而不能簡(jiǎn)單的使用位置向量。
      在場(chǎng)景中排序物體是個(gè)有難度的技術(shù),它很大程度上取決于你場(chǎng)景的類型,更不必說(shuō)會(huì)耗費(fèi)額外的處理能力了。完美地渲染帶有透明和不透明的物體的場(chǎng)景并不那么容易。有更高級(jí)的技術(shù)例如次序無(wú)關(guān)透明度(order independent transparency)。

      4. 總結(jié)

      透明與混合是 OpenGL 中實(shí)現(xiàn)復(fù)雜視覺(jué)效果的重要技術(shù)。

      通過(guò)合理設(shè)置 alpha 通道、啟用混合功能以及對(duì)透明物體進(jìn)行排序,我們能夠渲染出逼真的透明效果。

      最后,繪制物體的過(guò)程可以總結(jié)為:

      (1)先繪制所有不透明物體

      (2)為所有透明物體排序

      (3)按順序繪制透明物體

      篇幅有限,完整程序可私信我(+v:jasper_8017)獲取。

      如果覺(jué)得本文對(duì)你有幫助,麻煩點(diǎn)個(gè)贊和關(guān)注唄 ~~~


        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多