<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 第五課:帶紋理的立方體 # 第五課:紋理立方體 本課學習如下幾點: - 什么是UV坐標 - 怎樣自行加載紋理 - 怎樣在OpenGL中使用紋理 - 什么是濾波?什么是mipmap?怎樣使用? - 怎樣利用GLFW更加有效地加載紋理? - 什么是alpha通道? ## 關于UV坐標 給一個模型貼紋理時,需要通過某種方式告訴OpenGL用哪一塊圖像來填充三角形。這是借助UV坐標來實現的。 每個頂點除了位置坐標外還有兩個浮點數坐標:U和V。這兩個坐標用于獲取紋理,如下圖所示: ![](https://box.kancloud.cn/2015-11-02_5636f30402a11.png) 注意紋理是怎樣在三角形上扭曲的。 ## 自行加載.BMP圖片 了解BMP文件格式并不重要:很多庫可以幫你做這個。但BMP格式極為簡單,可以幫助你理解那些庫的工作原理。所以,我們從頭開始寫一個BMP文件加載器,以便你理解其工作原理,不過(在實際工程中)**千萬別再用這個實驗品**。 如下是加載函數的聲明: ``` <pre class="calibre16">``` GLuint <span class="token3">loadBMP_custom</span><span class="token1">(</span>const char <span class="token">*</span> imagepath<span class="token1">)</span><span class="token1">;</span> ``` ``` 使用方式如下: ``` <pre class="calibre16">``` GLuint image <span class="token">=</span> <span class="token3">loadBMP_custom</span><span class="token1">(</span><span class="token5">"./my_texture.bmp"</span><span class="token1">)</span><span class="token1">;</span> ``` ``` 接下來看看如何讀取BMP文件。 首先需要一些數據。讀取文件時將設置這些變量。 ``` <pre class="calibre16">``` <span class="token2">// Data read from the header of the BMP file</span> unsigned char header<span class="token1">[</span><span class="token6">54</span><span class="token1">]</span><span class="token1">;</span> <span class="token2">// Each BMP file begins by a 54-bytes header</span> unsigned int dataPos<span class="token1">;</span> <span class="token2">// Position in the file where the actual data begins</span> unsigned int width<span class="token1">,</span> height<span class="token1">;</span> unsigned int imageSize<span class="token1">;</span> <span class="token2">// = width*height*3</span> <span class="token2">// Actual RGB data</span> unsigned char <span class="token">*</span> data<span class="token1">;</span> ``` ``` 現在正式開始打開文件。 ``` <pre class="calibre16">``` <span class="token2">// Open the file</span> FILE <span class="token">*</span> file <span class="token">=</span> <span class="token3">fopen</span><span class="token1">(</span>imagepath<span class="token1">,</span><span class="token5">"rb"</span><span class="token1">)</span><span class="token1">;</span> <span class="token4">if</span> <span class="token1">(</span><span class="token">!</span>file<span class="token1">)</span> <span class="token1">{</span><span class="token3">printf</span><span class="token1">(</span><span class="token5">"Image could not be openedn"</span><span class="token1">)</span><span class="token1">;</span> <span class="token4">return</span> <span class="token6">0</span><span class="token1">;</span><span class="token1">}</span> ``` ``` 文件一開始是54字節長的文件頭,用于標識“這是不是一個BMP文件”、圖像大小、像素位等等。來讀取文件頭吧: ``` <pre class="calibre16">``` <span class="token4">if</span> <span class="token1">(</span> <span class="token3">fread</span><span class="token1">(</span>header<span class="token1">,</span> <span class="token6">1</span><span class="token1">,</span> <span class="token6">54</span><span class="token1">,</span> file<span class="token1">)</span><span class="token">!=</span><span class="token6">54</span> <span class="token1">)</span><span class="token1">{</span> <span class="token2">// If not 54 bytes read : problem</span> <span class="token3">printf</span><span class="token1">(</span><span class="token5">"Not a correct BMP filen"</span><span class="token1">)</span><span class="token1">;</span> <span class="token4">return</span> <span class="token6">false</span><span class="token1">;</span> <span class="token1">}</span> ``` ``` 文件頭總是以“BM”開頭。實際上,如果用十六進制編輯器打開BMP文件,你會看到如下情形: ![](https://box.kancloud.cn/2015-11-02_5636f30427858.png) 因此,得檢查一下頭兩個字節是否確為‘B’和‘M’: ``` <pre class="calibre16">``` <span class="token4">if</span> <span class="token1">(</span> header<span class="token1">[</span><span class="token6">0</span><span class="token1">]</span><span class="token">!=</span><span class="token5">'B'</span> <span class="token">||</span> header<span class="token1">[</span><span class="token6">1</span><span class="token1">]</span><span class="token">!=</span><span class="token5">'M'</span> <span class="token1">)</span><span class="token1">{</span> <span class="token3">printf</span><span class="token1">(</span><span class="token5">"Not a correct BMP filen"</span><span class="token1">)</span><span class="token1">;</span> <span class="token4">return</span> <span class="token6">0</span><span class="token1">;</span> <span class="token1">}</span> ``` ``` 現在可以讀取文件中圖像大小、數據位置等信息了: ``` <pre class="calibre16">``` <span class="token2">// Read ints from the byte array</span> dataPos <span class="token">=</span> <span class="token">*</span><span class="token1">(</span>int<span class="token">*</span><span class="token1">)</span><span class="token">&</span><span class="token1">(</span>header<span class="token1">[</span><span class="token6">0x0A</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span> imageSize <span class="token">=</span> <span class="token">*</span><span class="token1">(</span>int<span class="token">*</span><span class="token1">)</span><span class="token">&</span><span class="token1">(</span>header<span class="token1">[</span><span class="token6">0x22</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span> width <span class="token">=</span> <span class="token">*</span><span class="token1">(</span>int<span class="token">*</span><span class="token1">)</span><span class="token">&</span><span class="token1">(</span>header<span class="token1">[</span><span class="token6">0x12</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span> height <span class="token">=</span> <span class="token">*</span><span class="token1">(</span>int<span class="token">*</span><span class="token1">)</span><span class="token">&</span><span class="token1">(</span>header<span class="token1">[</span><span class="token6">0x16</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span> ``` ``` 如果這些信息缺失得手動補齊: ``` <pre class="calibre16">``` <span class="token2">// Some BMP files are misformatted, guess missing information</span> <span class="token4">if</span> <span class="token1">(</span>imageSize<span class="token">==</span><span class="token6">0</span><span class="token1">)</span> imageSize<span class="token">=</span>width<span class="token">*</span>height<span class="token">*</span><span class="token6">3</span><span class="token1">;</span> <span class="token2">// 3 : one byte for each Red, Green and Blue component</span> <span class="token4">if</span> <span class="token1">(</span>dataPos<span class="token">==</span><span class="token6">0</span><span class="token1">)</span> dataPos<span class="token">=</span><span class="token6">54</span><span class="token1">;</span> <span class="token2">// The BMP header is done that way</span> ``` ``` 現在我們知道了圖像的大小,可以為之分配一些內存,把圖像讀進去: ``` <pre class="calibre16">``` <span class="token2">// Create a buffer</span> data <span class="token">=</span> <span class="token4">new</span> <span class="token3">unsigned</span> char <span class="token1">[</span>imageSize<span class="token1">]</span><span class="token1">;</span> <span class="token2">// Read the actual data from the file into the buffer</span> <span class="token3">fread</span><span class="token1">(</span>data<span class="token1">,</span><span class="token6">1</span><span class="token1">,</span>imageSize<span class="token1">,</span>file<span class="token1">)</span><span class="token1">;</span> <span class="token2">//Everything is in memory now, the file can be closed</span> <span class="token3">fclose</span><span class="token1">(</span>file<span class="token1">)</span><span class="token1">;</span> ``` ``` 到了真正的OpenGL部分了。創建紋理和創建頂點緩沖器差不多:創建一個紋理、綁定、填充、配置。 在glTexImage2D函數中,GL\_RGB表示顏色由三個分量構成,GL\_BGR則說明在內存中顏色值是如何存儲的。實際上,BMP存儲的并不是RGB,而是BGR,因此得把這個告訴OpenGL。 ``` <pre class="calibre16">``` <span class="token2">// Create one OpenGL texture</span> GLuint textureID<span class="token1">;</span> <span class="token3">glGenTextures</span><span class="token1">(</span><span class="token6">1</span><span class="token1">,</span> <span class="token">&</span>textureID<span class="token1">)</span><span class="token1">;</span> <span class="token2">// "Bind" the newly created texture : all future texture functions will modify this texture</span> <span class="token3">glBindTexture</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> textureID<span class="token1">)</span><span class="token1">;</span> <span class="token2">// Give the image to OpenGL</span> <span class="token3">glTexImage2D</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> <span class="token6">0</span><span class="token1">,</span>GL_RGB<span class="token1">,</span> width<span class="token1">,</span> height<span class="token1">,</span> <span class="token6">0</span><span class="token1">,</span> GL_BGR<span class="token1">,</span> GL_UNSIGNED_BYTE<span class="token1">,</span> data<span class="token1">)</span><span class="token1">;</span> <span class="token3">glTexParameteri</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> GL_TEXTURE_MAG_FILTER<span class="token1">,</span> GL_NEAREST<span class="token1">)</span><span class="token1">;</span> <span class="token3">glTexParameteri</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> GL_TEXTURE_MIN_FILTER<span class="token1">,</span> GL_NEAREST<span class="token1">)</span><span class="token1">;</span> ``` ``` 稍后再解釋最后兩行代碼。同時,得在C++代碼中使用剛寫好的函數加載一個紋理: ``` <pre class="calibre16">``` GLuint Texture <span class="token">=</span> <span class="token3">loadBMP_custom</span><span class="token1">(</span><span class="token5">"uvtemplate.bmp"</span><span class="token1">)</span><span class="token1">;</span> ``` ``` 另外十分重要的一點: 使用2次冪(power-of-two)的紋理! - 優質紋理: 128*128*, 256*256, 1024*1024, 2\*2… - 劣質紋理: 127*128, 3*5, … - 勉強可以但很怪異的紋理: 128\*256 ## 在OpenGL中使用紋理 先來看看片斷著色器。大部分代碼一目了然: ``` <pre class="calibre16">``` #version <span class="token6">330</span> core <span class="token2">// Interpolated values from the vertex shaders</span> <span class="token4">in</span> vec2 UV<span class="token1">;</span> <span class="token2">// Ouput data</span> out vec3 color<span class="token1">;</span> <span class="token2">// Values that stay constant for the whole mesh.</span> uniform sampler2D myTextureSampler<span class="token1">;</span> void <span class="token3">main</span><span class="token1">(</span><span class="token1">)</span><span class="token1">{</span> <span class="token2">// Output color = color of the texture at the specified UV</span> color <span class="token">=</span> <span class="token3">texture</span><span class="token1">(</span> myTextureSampler<span class="token1">,</span> UV <span class="token1">)</span><span class="token1">.</span>rgb<span class="token1">;</span> <span class="token1">}</span> ``` ``` 注意三個點: - 片斷著色器需要UV坐標。看似合情合理。 - 同時也需要一個“Sampler2D”來獲知要加載哪一個紋理(同一個著色器中可以訪問多個紋理) - 最后一點,用texture()訪問紋理,該方法返回一個(R,G,B,A)的vec4變量。馬上就會了解到分量A。 頂點著色器也很簡單,只需把UV坐標傳給片斷著色器: ``` <pre class="calibre16">``` #version <span class="token6">330</span> core <span class="token2">// Input vertex data, different for all executions of this shader.</span> <span class="token3">layout</span><span class="token1">(</span>location <span class="token">=</span> <span class="token6">0</span><span class="token1">)</span> <span class="token4">in</span> vec3 vertexPosition_modelspace<span class="token1">;</span> <span class="token3">layout</span><span class="token1">(</span>location <span class="token">=</span> <span class="token6">1</span><span class="token1">)</span> <span class="token4">in</span> vec2 vertexUV<span class="token1">;</span> <span class="token2">// Output data ; will be interpolated for each fragment.</span> out vec2 UV<span class="token1">;</span> <span class="token2">// Values that stay constant for the whole mesh.</span> uniform mat4 MVP<span class="token1">;</span> void <span class="token3">main</span><span class="token1">(</span><span class="token1">)</span><span class="token1">{</span> <span class="token2">// Output position of the vertex, in clip space : MVP * position</span> gl_Position <span class="token">=</span> MVP <span class="token">*</span> <span class="token3">vec4</span><span class="token1">(</span>vertexPosition_modelspace<span class="token1">,</span><span class="token6">1</span><span class="token1">)</span><span class="token1">;</span> <span class="token2">// UV of the vertex. No special space for this one.</span> UV <span class="token">=</span> vertexUV<span class="token1">;</span> <span class="token1">}</span> ``` ``` 還記得第四課中的“layout(location = 1) in vec2 vertexUV” 嗎?我們得在這兒把相同的事情再做一遍,但這次的緩沖器中放的不是(R,G,B)三元組,而是(U,V)數對。 ``` <pre class="calibre16">``` <span class="token2">// Two UV coordinatesfor each vertex. They were created with Blender. You'll learn shortly how to do this yourself.</span> static const GLfloat g_uv_buffer_data<span class="token1">[</span><span class="token1">]</span> <span class="token">=</span> <span class="token1">{</span> <span class="token6">0.000059</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.000004</span>f<span class="token1">,</span> <span class="token6">0.000103</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.336048</span>f<span class="token1">,</span> <span class="token6">0.335973</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335903</span>f<span class="token1">,</span> <span class="token6">1.000023</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.000013</span>f<span class="token1">,</span> <span class="token6">0.667979</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335851</span>f<span class="token1">,</span> <span class="token6">0.999958</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.336064</span>f<span class="token1">,</span> <span class="token6">0.667979</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335851</span>f<span class="token1">,</span> <span class="token6">0.336024</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.671877</span>f<span class="token1">,</span> <span class="token6">0.667969</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.671889</span>f<span class="token1">,</span> <span class="token6">1.000023</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.000013</span>f<span class="token1">,</span> <span class="token6">0.668104</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.000013</span>f<span class="token1">,</span> <span class="token6">0.667979</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335851</span>f<span class="token1">,</span> <span class="token6">0.000059</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.000004</span>f<span class="token1">,</span> <span class="token6">0.335973</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335903</span>f<span class="token1">,</span> <span class="token6">0.336098</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.000071</span>f<span class="token1">,</span> <span class="token6">0.667979</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335851</span>f<span class="token1">,</span> <span class="token6">0.335973</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335903</span>f<span class="token1">,</span> <span class="token6">0.336024</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.671877</span>f<span class="token1">,</span> <span class="token6">1.000004</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.671847</span>f<span class="token1">,</span> <span class="token6">0.999958</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.336064</span>f<span class="token1">,</span> <span class="token6">0.667979</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335851</span>f<span class="token1">,</span> <span class="token6">0.668104</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.000013</span>f<span class="token1">,</span> <span class="token6">0.335973</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335903</span>f<span class="token1">,</span> <span class="token6">0.667979</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335851</span>f<span class="token1">,</span> <span class="token6">0.335973</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335903</span>f<span class="token1">,</span> <span class="token6">0.668104</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.000013</span>f<span class="token1">,</span> <span class="token6">0.336098</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.000071</span>f<span class="token1">,</span> <span class="token6">0.000103</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.336048</span>f<span class="token1">,</span> <span class="token6">0.000004</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.671870</span>f<span class="token1">,</span> <span class="token6">0.336024</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.671877</span>f<span class="token1">,</span> <span class="token6">0.000103</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.336048</span>f<span class="token1">,</span> <span class="token6">0.336024</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.671877</span>f<span class="token1">,</span> <span class="token6">0.335973</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335903</span>f<span class="token1">,</span> <span class="token6">0.667969</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.671889</span>f<span class="token1">,</span> <span class="token6">1.000004</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.671847</span>f<span class="token1">,</span> <span class="token6">0.667979</span>f<span class="token1">,</span> <span class="token6">1.0</span>f<span class="token">-</span><span class="token6">0.335851</span>f <span class="token1">}</span><span class="token1">;</span> ``` ``` 上述UV坐標對應于下面的模型: ![](https://box.kancloud.cn/2015-11-02_5636f30435bf7.png) 其余的就很清楚了。創建一個緩沖器、綁定、填充、配置,與往常一樣繪制頂點緩沖器對象。要注意把glVertexAttribPointer的第二個參數(大小)3改成2。 結果如下: ![](https://box.kancloud.cn/2015-11-02_5636f30448791.png) 放大后: ![](https://box.kancloud.cn/2015-11-02_5636f30457f09.png) ## 什么是濾波和mipmap?怎樣使用? 正如在上面截圖中看到的,紋理質量不是很好。這是因為在loadBMP\_custom函數中,有兩行這樣寫道: ``` <pre class="calibre16">``` <span class="token3">glTexParameteri</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> GL_TEXTURE_MAG_FILTER<span class="token1">,</span> GL_NEAREST<span class="token1">)</span><span class="token1">;</span> <span class="token3">glTexParameteri</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> GL_TEXTURE_MIN_FILTER<span class="token1">,</span> GL_NEAREST<span class="token1">)</span><span class="token1">;</span> ``` ``` 這意味著在片斷著色器中,texture()將直接提取位于(U,V)坐標的紋素(texel)。 ![](https://box.kancloud.cn/2015-11-02_5636f30466268.png) 有幾種方法可以改善這一狀況。 ### 線性濾波(Linear filtering) 若采用線性濾波。texture()會查看周圍的紋素,然后根據UV坐標距離各紋素中心的距離來混合顏色。這就避免了前面看到的鋸齒狀邊緣。 ![](https://box.kancloud.cn/2015-11-02_5636f30474ea3.png) 線性濾波可以顯著改善紋理質量,應用的也很多。但若想獲得更高質量的紋理,可以采用各向異性濾波,不過速度上有些慢。 ### 各向異性濾波(Anisotropic filtering) 這種方法逼近了真正片斷中的紋素區塊。例如下圖中稍稍旋轉了的紋理,各向異性濾波將沿藍色矩形框的主方向,作一定數量的采樣(即所謂的“各向異性層級”),計算出其內的顏色。 ![](https://box.kancloud.cn/2015-11-02_5636f3048247b.png) ### Mipmaps 線性濾波和各向異性濾波都存在一個共同的問題。那就是如果從遠處觀察紋理,只對4個紋素作混合顯得不夠。實際上,如果3D模型位于很遠的地方,屏幕上只看得見一個片斷(像素),那計算平均值得出最終顏色值時,圖像所有的紋素都應該考慮在內。很顯然,這樣做沒有考慮性能問題。相反,人們引入了mipmap這一概念: ![](https://box.kancloud.cn/2015-11-02_5636f3048d3f8.jpg) - 一開始,把圖像縮小到原來的1/2,接著一次做下去,直到圖像只有1×1大小(應該是圖像所有紋素的平均值) - 繪制模型時,根據紋素大小選擇合適的mipmap。 - 可以選用nearest、linear、anisotropic等任意一種濾波方式來對mipmap采樣。 - 要想效果更好,可以對兩個mipmap采樣然后混合,得出結果。 好在這個比較簡單,OpenGL都幫我們做好了,只需一個簡單的調用: ``` <pre class="calibre16">``` <span class="token2">// When MAGnifying the image (no bigger mipmap available), use LINEAR filtering</span> <span class="token3">glTexParameteri</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> GL_TEXTURE_MAG_FILTER<span class="token1">,</span> GL_LINEAR<span class="token1">)</span><span class="token1">;</span> <span class="token2">// When MINifying the image, use a LINEAR blend of two mipmaps, each filtered LINEARLY too</span> <span class="token3">glTexParameteri</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> GL_TEXTURE_MIN_FILTER<span class="token1">,</span> GL_LINEAR_MIPMAP_LINEAR<span class="token1">)</span><span class="token1">;</span> <span class="token2">// Generate mipmaps, by the way.</span> <span class="token3">glGenerateMipmap</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">)</span><span class="token1">;</span> ``` ``` ## 怎樣利用GLFW加載紋理? 我們的loadBMP\_custom函數很棒,因為這是我們自己寫的!不過用專門的庫更好。GLFW就可以加載紋理(僅限TGA文件): ``` <pre class="calibre16">``` GLuint <span class="token3">loadTGA_glfw</span><span class="token1">(</span>const char <span class="token">*</span> imagepath<span class="token1">)</span><span class="token1">{</span> <span class="token2">// Create one OpenGL texture</span> GLuint textureID<span class="token1">;</span> <span class="token3">glGenTextures</span><span class="token1">(</span><span class="token6">1</span><span class="token1">,</span> <span class="token">&</span>textureID<span class="token1">)</span><span class="token1">;</span> <span class="token2">// "Bind" the newly created texture : all future texture functions will modify this texture</span> <span class="token3">glBindTexture</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> textureID<span class="token1">)</span><span class="token1">;</span> <span class="token2">// Read the file, call glTexImage2D with the right parameters</span> <span class="token3">glfwLoadTexture2D</span><span class="token1">(</span>imagepath<span class="token1">,</span> <span class="token6">0</span><span class="token1">)</span><span class="token1">;</span> <span class="token2">// Nice trilinear filtering.</span> <span class="token3">glTexParameteri</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> GL_TEXTURE_WRAP_S<span class="token1">,</span> GL_REPEAT<span class="token1">)</span><span class="token1">;</span> <span class="token3">glTexParameteri</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> GL_TEXTURE_WRAP_T<span class="token1">,</span> GL_REPEAT<span class="token1">)</span><span class="token1">;</span> <span class="token3">glTexParameteri</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> GL_TEXTURE_MAG_FILTER<span class="token1">,</span> GL_LINEAR<span class="token1">)</span><span class="token1">;</span> <span class="token3">glTexParameteri</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> GL_TEXTURE_MIN_FILTER<span class="token1">,</span> GL_LINEAR_MIPMAP_LINEAR<span class="token1">)</span><span class="token1">;</span> <span class="token3">glGenerateMipmap</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">)</span><span class="token1">;</span> <span class="token2">// Return the ID of the texture we just created</span> <span class="token4">return</span> textureID<span class="token1">;</span> <span class="token1">}</span> ``` ``` ## 壓縮紋理 學到這兒,你可能會想怎樣加載JPEG文件而不是TGA文件呢? 簡單的說:別這么干。還有更好的選擇。 ### 創建壓縮紋理 - 下載[The Compressonator](http://developer.amd.com/tools-and-sdks/archive/legacy-cpu-gpu-tools/the-compressonator/),一款ATI工具 - 用它加載一個二次冪紋理 - 將其壓縮成DXT1、DXT3或DXT5格式(這些格式之間的差別請參考[Wikipedia](http://en.wikipedia.org/wiki/S3_Texture_Compression)): ![](https://box.kancloud.cn/2015-11-02_5636f30499c2e.png) - 生成mipmap,這樣就不用在運行時生成mipmap了。 - 導出為.DDS文件。 至此,圖像已壓縮為可被GPU直接使用的格式。在著色中隨時調用texture()均可以實時解壓。這一過程看似很慢,但由于它節省了很多內存空間,傳輸的數據量就少了。傳輸內存數據開銷很大;紋理解壓縮卻幾乎不耗時(有專門的硬件負責此事)。一般情況下,才用壓縮紋理可使性能提升20%。 ### 使用壓縮紋理 來看看怎樣加載壓縮紋理。這和加載BMP的代碼很相似,只不過文件頭的結構不一樣: ``` <pre class="calibre16">``` GLuint <span class="token3">loadDDS</span><span class="token1">(</span>const char <span class="token">*</span> imagepath<span class="token1">)</span><span class="token1">{</span> unsigned char header<span class="token1">[</span><span class="token6">124</span><span class="token1">]</span><span class="token1">;</span> FILE <span class="token">*</span>fp<span class="token1">;</span> <span class="token2">/* try to open the file */</span> fp <span class="token">=</span> <span class="token3">fopen</span><span class="token1">(</span>imagepath<span class="token1">,</span> <span class="token5">"rb"</span><span class="token1">)</span><span class="token1">;</span> <span class="token4">if</span> <span class="token1">(</span>fp <span class="token">==</span> NULL<span class="token1">)</span> <span class="token4">return</span> <span class="token6">0</span><span class="token1">;</span> <span class="token2">/* verify the type of file */</span> char filecode<span class="token1">[</span><span class="token6">4</span><span class="token1">]</span><span class="token1">;</span> <span class="token3">fread</span><span class="token1">(</span>filecode<span class="token1">,</span> <span class="token6">1</span><span class="token1">,</span> <span class="token6">4</span><span class="token1">,</span> fp<span class="token1">)</span><span class="token1">;</span> <span class="token4">if</span> <span class="token1">(</span><span class="token3">strncmp</span><span class="token1">(</span>filecode<span class="token1">,</span> <span class="token5">"DDS "</span><span class="token1">,</span> <span class="token6">4</span><span class="token1">)</span> <span class="token">!=</span> <span class="token6">0</span><span class="token1">)</span> <span class="token1">{</span> <span class="token3">fclose</span><span class="token1">(</span>fp<span class="token1">)</span><span class="token1">;</span> <span class="token4">return</span> <span class="token6">0</span><span class="token1">;</span> <span class="token1">}</span> <span class="token2">/* get the surface desc */</span> <span class="token3">fread</span><span class="token1">(</span><span class="token">&</span>header<span class="token1">,</span> <span class="token6">124</span><span class="token1">,</span> <span class="token6">1</span><span class="token1">,</span> fp<span class="token1">)</span><span class="token1">;</span> unsigned int height <span class="token">=</span> <span class="token">*</span><span class="token1">(</span>unsigned int<span class="token">*</span><span class="token1">)</span><span class="token">&</span><span class="token1">(</span>header<span class="token1">[</span><span class="token6">8</span> <span class="token1">]</span><span class="token1">)</span><span class="token1">;</span> unsigned int width <span class="token">=</span> <span class="token">*</span><span class="token1">(</span>unsigned int<span class="token">*</span><span class="token1">)</span><span class="token">&</span><span class="token1">(</span>header<span class="token1">[</span><span class="token6">12</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span> unsigned int linearSize <span class="token">=</span> <span class="token">*</span><span class="token1">(</span>unsigned int<span class="token">*</span><span class="token1">)</span><span class="token">&</span><span class="token1">(</span>header<span class="token1">[</span><span class="token6">16</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span> unsigned int mipMapCount <span class="token">=</span> <span class="token">*</span><span class="token1">(</span>unsigned int<span class="token">*</span><span class="token1">)</span><span class="token">&</span><span class="token1">(</span>header<span class="token1">[</span><span class="token6">24</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span> unsigned int fourCC <span class="token">=</span> <span class="token">*</span><span class="token1">(</span>unsigned int<span class="token">*</span><span class="token1">)</span><span class="token">&</span><span class="token1">(</span>header<span class="token1">[</span><span class="token6">80</span><span class="token1">]</span><span class="token1">)</span><span class="token1">;</span> ``` ``` 文件頭之后是真正的數據:緊接著是mipmap層級。可以一次性批量地讀取: ``` <pre class="calibre16">``` unsigned char <span class="token">*</span> buffer<span class="token1">;</span> unsigned int bufsize<span class="token1">;</span> <span class="token2">/* how big is it going to be including all mipmaps? */</span> bufsize <span class="token">=</span> mipMapCount <span class="token">></span> <span class="token6">1</span> <span class="token">?</span> linearSize <span class="token">*</span> <span class="token6">2</span> <span class="token1">:</span> linearSize<span class="token1">;</span> buffer <span class="token">=</span> <span class="token1">(</span>unsigned char<span class="token">*</span><span class="token1">)</span><span class="token3">malloc</span><span class="token1">(</span>bufsize <span class="token">*</span> <span class="token3">sizeof</span><span class="token1">(</span>unsigned char<span class="token1">)</span><span class="token1">)</span><span class="token1">;</span> <span class="token3">fread</span><span class="token1">(</span>buffer<span class="token1">,</span> <span class="token6">1</span><span class="token1">,</span> bufsize<span class="token1">,</span> fp<span class="token1">)</span><span class="token1">;</span> <span class="token2">/* close the file pointer */</span> <span class="token3">fclose</span><span class="token1">(</span>fp<span class="token1">)</span><span class="token1">;</span> ``` ``` 這里要處理三種格式:DXT1、DXT3和DXT5。我們得把“fourCC”標識轉換成OpenGL能識別的值。 ``` <pre class="calibre16">``` unsigned int components <span class="token">=</span> <span class="token1">(</span>fourCC <span class="token">==</span> FOURCC_DXT1<span class="token1">)</span> <span class="token">?</span> <span class="token6">3</span> <span class="token1">:</span> <span class="token6">4</span><span class="token1">;</span> unsigned int format<span class="token1">;</span> <span class="token3">switch</span><span class="token1">(</span>fourCC<span class="token1">)</span> <span class="token1">{</span> case FOURCC_DXT1<span class="token1">:</span> format <span class="token">=</span> GL_COMPRESSED_RGBA_S3TC_DXT1_EXT<span class="token1">;</span> <span class="token4">break</span><span class="token1">;</span> case FOURCC_DXT3<span class="token1">:</span> format <span class="token">=</span> GL_COMPRESSED_RGBA_S3TC_DXT3_EXT<span class="token1">;</span> <span class="token4">break</span><span class="token1">;</span> case FOURCC_DXT5<span class="token1">:</span> format <span class="token">=</span> GL_COMPRESSED_RGBA_S3TC_DXT5_EXT<span class="token1">;</span> <span class="token4">break</span><span class="token1">;</span> default<span class="token1">:</span> <span class="token3">free</span><span class="token1">(</span>buffer<span class="token1">)</span><span class="token1">;</span> <span class="token4">return</span> <span class="token6">0</span><span class="token1">;</span> <span class="token1">}</span> ``` ``` 像往常一樣創建紋理: ``` <pre class="calibre16">``` <span class="token2">// Create one OpenGL texture</span> GLuint textureID<span class="token1">;</span> <span class="token3">glGenTextures</span><span class="token1">(</span><span class="token6">1</span><span class="token1">,</span> <span class="token">&</span>textureID<span class="token1">)</span><span class="token1">;</span> <span class="token2">// "Bind" the newly created texture : all future texture functions will modify this texture</span> <span class="token3">glBindTexture</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> textureID<span class="token1">)</span><span class="token1">;</span> ``` ``` 現在只需逐個填充mipmap: ``` <pre class="calibre16">``` unsigned int blockSize <span class="token">=</span> <span class="token1">(</span>format <span class="token">==</span> GL_COMPRESSED_RGBA_S3TC_DXT1_EXT<span class="token1">)</span> <span class="token">?</span> <span class="token6">8</span> <span class="token1">:</span> <span class="token6">16</span><span class="token1">;</span> unsigned int offset <span class="token">=</span> <span class="token6">0</span><span class="token1">;</span> <span class="token2">/* load the mipmaps */</span> <span class="token4">for</span> <span class="token1">(</span>unsigned int level <span class="token">=</span> <span class="token6">0</span><span class="token1">;</span> level <span class="token"><</span> mipMapCount <span class="token">&&</span> <span class="token1">(</span>width <span class="token">||</span> height<span class="token1">)</span><span class="token1">;</span> <span class="token">++</span>level<span class="token1">)</span> <span class="token1">{</span> unsigned int size <span class="token">=</span> <span class="token1">(</span><span class="token1">(</span>width<span class="token">+</span><span class="token6">3</span><span class="token1">)</span><span class="token">/</span><span class="token6">4</span><span class="token1">)</span><span class="token">*</span><span class="token1">(</span><span class="token1">(</span>height<span class="token">+</span><span class="token6">3</span><span class="token1">)</span><span class="token">/</span><span class="token6">4</span><span class="token1">)</span><span class="token">*</span>blockSize<span class="token1">;</span> <span class="token3">glCompressedTexImage2D</span><span class="token1">(</span>GL_TEXTURE_2D<span class="token1">,</span> level<span class="token1">,</span> format<span class="token1">,</span> width<span class="token1">,</span> height<span class="token1">,</span> <span class="token6">0</span><span class="token1">,</span> size<span class="token1">,</span> buffer <span class="token">+</span> offset<span class="token1">)</span><span class="token1">;</span> offset <span class="token">+</span><span class="token">=</span> size<span class="token1">;</span> width <span class="token">/</span><span class="token">=</span> <span class="token6">2</span><span class="token1">;</span> height <span class="token">/</span><span class="token">=</span> <span class="token6">2</span><span class="token1">;</span> <span class="token1">}</span> <span class="token3">free</span><span class="token1">(</span>buffer<span class="token1">)</span><span class="token1">;</span> <span class="token4">return</span> textureID<span class="token1">;</span> ``` ``` ### 反轉UV坐標 DXT壓縮源自DirectX。和OpenGL相比,DirectX中的V紋理坐標是反過來的。所以使用壓縮紋理時,得用(coord.v, 1.0-coord.v)來獲取正確的紋素。這步操作何時做都可以:可以在導出腳本中做,可以在加載器中做,也可以在著色器中做…… ## 總結 剛剛學習的是創建、加載以及在OpenGL中使用紋理。 總的來說,壓縮紋理體積小、加載迅速、使用便捷,應該只用壓縮紋理;主要的缺點是得用The Compressonator來轉換圖像格式。 ## 練習 - 源代碼中實現了DDS加載器,但沒有做紋理坐標的改動(譯者注:指文中講述的反轉 UV坐標)。在適當的位置添加該功能,以使正方體正確顯示。 - 試試各種DDS格式。所得結果有何不同?壓縮率呢? - 試試在The Compressonator不生成mipmap。結果如何?請給出3種方案解決這一問題。 ## 參考文獻 - [Using texture compression in OpenGL](http://www.oldunreal.com/editing/s3tc/ARB_texture_compression.pdf) , Sébastien Domine, NVIDIA
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看