二維紋理映射(2D textures)【轉】

http://blog.csdn.net/wangdingqiaoit/article/details/51457675

前面兩節介紹了向量和矩陣,以及坐標和轉換相關的數學,再繼續討論模型變換等其他包含數學內容的部分之前,本節介紹二維紋理映射,為后面學習做一個準備。紋理映射本身也是比較大的主題,本節只限于討論二維紋理的基本使用,對于紋理映射的其他方法,后面會繼續學習??梢詮?a target="_blank" rel="nofollow">我的github下載本節代碼。

通過本節可以了解到

紋理映射的概念和原理

二維紋理映射的處理方法

使用紋理增加物體表面細節

要使渲染的物體更加逼真,一方面我們可以使用更多的三角形來建模,通過復雜的模型來逼近物體,但是這種方法會增加繪制流水線的負荷,而且很多情況下不是很方便的。使用紋理,將物體表面的細節映射到建模好的物體表面,這樣不僅能使渲染的模型表面細節更豐富,而且比較方便高效。紋理映射就是這樣一種方法,在程序中通過為物體指定紋理坐標,通過紋理坐標獲取紋理對象中的紋理,最終顯示在屏幕區域上,已達到更加逼真的效果。

紋素(texel)和紋理坐標

使用紋素這個術語,而不是像素來表示紋理對象中的顯示元素,主要是為了強調紋理對象的應用方式。紋理對象通常是通過紋理圖片讀取到的,這個數據保存到一個二維數組中,這個數組中的元素稱為紋素(texel),紋素包含顏色值和alpha值。紋理對象的大小的寬度和高度應該為2的整數冪,例如16, 32, 64, 128, 256。要想獲取紋理對象中的紋素,需要使用紋理坐標(texture coordinate)指定。

紋理坐標應該與紋理對象大小無關,這樣指定的紋理坐標當紋理對象大小變更時,依然能夠工作,比如從256x256大小的紋理,換到512x256時,紋理坐標依然能夠工作。因此紋理坐標使用規范化的值,大小范圍為[0,1],紋理坐標使用uv表示,如下圖所示(來自:Basic Texture Mapping):這里有錯誤,紋理坐標在左上角

u軸從左至右,v軸從底向上指向。右上角為(1,1),左下角為(0,0)。

通過指定紋理坐標,可以映射到紋素。例如一個256x256大小的二維紋理,坐標(0.5,1.0)對應的紋素即是(128,256)。(256x0.5 = 128, 256x1.0 = 256)。

紋理映射時只需要為物體的頂點指定紋理坐標即可,其余部分由片元著色器插值完成,如下圖所示(來自A textured cube):

模型變換和紋理坐標

所謂模型變換,就是對物體進行縮放、旋轉、平移等操作,后面會著重介紹。當對物體進行這些操作時,頂點對應的紋理坐標不會進行改變,通過插值后,物體的紋理也像緊跟著物體發生了變化一樣。如下圖所示為變換前物體的紋理坐標(來自:Basic Texture Mapping):

經過旋轉等變換后,物體和對應的紋理坐標如下圖所示,可以看出上面圖中紋理部分的房子也跟著發生了旋轉。(來自:Basic Texture Mapping):

注意有一些技術可以使紋理坐標有控制地發生改變,本節不深入討論,這里我們的紋理坐標在模型變換下保持不變。

創建紋理對象

創建紋理對象的過程同前面講述的創建VBO,VAO類似:

GLuint textureId;glGenTextures(1, &textureId);glBindTexture(GL_TEXTURE_2D, textureId);

1

2

3

這里我們綁定到GL_TEXTURE_2D目標,表示二維紋理。

WRAP參數

上面提到紋理坐標(0.5, 1.0)到紋素的映射,恰好為(128,256)。如果紋理坐標超出[0,0]到[1,1]的范圍該怎么處理呢? 這個就是wrap參數由來,它使用以下方式來處理:

GL_REPEAT:坐標的整數部分被忽略,重復紋理,這是OpenGL紋理默認的處理方式.

GL_MIRRORED_REPEAT: 紋理也會被重復,但是當紋理坐標的整數部分是奇數時會使用鏡像重復。

GL_CLAMP_TO_EDGE: 坐標會被截斷到[0,1]之間。結果是坐標值大的被截斷到紋理的邊緣部分,形成了一個拉伸的邊緣(stretched edge pattern)。

GL_CLAMP_TO_BORDER: 不在[0,1]范圍內的紋理坐標會使用用戶指定的邊緣顏色。

當紋理坐標超出[0,1]范圍后,使用不同的選項,輸出的效果如下圖所示(來自Textures objects and parameters):

在OpenGL中設置wrap參數方式如下:

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

1

2

上面的幾個選項對應的都是整數,因此使用glTexParameteri來設置。

Filter參數

當使用紋理坐標映射到紋素數組時,正好得到對應紋素的中心位置的情況,很少出現。例如上面的(0.5,1.0)對應紋素(128,256)的情況是比較少的。如果紋理坐標映射到紋素位置(152.34,745.14)該怎么辦呢 ?

一種方式是對這個坐標進行取整,使用最佳逼近點來獲取紋素,這種方式即點采樣(point sampling),也就是最近鄰濾波( nearest neighbor filtering)。這種方式容易導致走樣誤差,明顯有像素塊的感覺。最近鄰濾波方法的示意圖如下所示(來自A Textured Cube):

圖中目標紋素位置,離紅色這個紋素最近,因此選擇紅色作為最終輸出紋素。

另外還存在其他濾波方法,例如線性濾波方法(linear filtering),它使用紋素位置(152.34,745.14)附近的一組紋素的加權平均值來確定最終的紋素值。例如使用 ( (152,745), (153,745), (152,744) and (153,744) )這四個紋素值的加權平均值。權系數通過與目標點(152.34,745.14)的距離遠近反映,距離(152.34,745.14)越近,權系數越大,即對最終的紋素值影響越大。線性濾波的示意圖如下圖所示(來自A Textured Cube):

圖中目標紋素位置周圍的4個紋素通過加權平均計算出最終輸出紋素。

還存在其他的濾波方式,如三線性濾波(Trilinear filtering)等,感興趣的可以參考texture filtering wiki。最近鄰濾波和線性濾波的對比效果如下圖所示(來自Textures objects and parameters):

可以看出最近鄰方法獲取的紋素看起來有明顯的像素塊,而線性濾波方法獲取的紋素看起來比較平滑。兩種方法各自有不同的應用場合,不能說線性濾波一定比最近鄰濾波方法好,例如要制造8位圖形效果(8 bit graphics,每個像素使用8位字節表示)需要使用最近鄰濾波。作為一個興趣了解,8位圖形效果看起來也是很酷的(可以查看Welcome 8-bit, Pixel-Art Images Gallery!)獲得更多8位圖形),例如下面這張使用Excel制作的8位圖(來自Excel is a great for making 8 bit graphics!):

另外一個問題是,紋理應用到物體上,最終要繪制在顯示設備上,這里存在一個紋素到像素的轉換問題。有三種情形(參考自An Introduction to Texture Filtering):

一個紋素最終對應屏幕上的多個像素 這稱之為放大(magnification)

一個紋素對應屏幕上的一個像素 這種情況不需要濾波方法

一個紋素對應少于一個像素,或者說多個紋素對應屏幕上的一個像素 這個稱之為縮小(minification)

放大和縮小的示意圖如下:

在OpenGL中通過使用下面的函數,為紋理的放大和縮小濾波設置相關的控制選項:

glTexParameteri(GL_TEXTURE_2D,

GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D,

GL_TEXTURE_MIN_FILTER, GL_NEAREST);

1

2

3

4

其中GL_LINEAR對應線性濾波,GL_NEAREST對應最近鄰濾波方式。

使用Mipmaps

考慮一個情景:當物體在場景中離觀察者很遠,最終只用一個屏幕像素來顯示時,這個像素該如何通過紋素確定呢?如果使用最近鄰濾波來獲取這個紋素,那么顯示效果并不理想。需要使用紋素的均值來反映物體在場景中離我們很遠這個效果,對于一個 256×256的紋理,計算平均值是一個耗時工作,不能實時計算,因此可以通過提前計算一組這樣的紋理用來滿足這種需求。這組提前計算的按比例縮小的紋理就是Mipmaps。Mipmaps紋理大小每級是前一等級的一半,按大小遞減順序排列為:

原始紋理 256×256

Mip 1 = 128×128

Mip 2 = 64×64

Mip 3 = 32×32

Mip 4 = 16×16

Mip 5 = 8×8

Mip 6 = 4×4

Mip 7 = 2×2

Mip 8 = 1×1

OpenGL會根據物體離觀察者的距離選擇使用合適大小的Mipmap紋理。Mipmap紋理示意圖如下所示(來自wiki Mipmap):

OpenGL中通過函數glGenerateMipmap(GL_TEXTURE_2D);來生成Mipmap,前提是已經指定了原始紋理。原始紋理必須自己通過讀取紋理圖片來加載,這個后面會介紹。

如果直接在不同等級的MipMap之間切換,會形成明顯的邊緣,因此對于Mipmap也可以同紋素一樣使用濾波方法在不同等級的Mipmap之間濾波。要在不同等級的MipMap之間濾波,需要將之前設置的GL_TEXTURE_MIN_FILTER選項更改為以下選項之一:

GL_NEAREST_MIPMAP_NEAREST: 使用最接近像素大小的Mipmap,紋理內部使用最近鄰濾波。

GL_LINEAR_MIPMAP_NEAREST: 使用最接近像素大小的Mipmap,紋理內部使用線性濾波。

GL_NEAREST_MIPMAP_LINEAR: 在兩個最接近像素大小的Mipmap中做線性插值,紋理內部使用最近鄰濾波。

GL_LINEAR_MIPMAP_LINEAR: 在兩個最接近像素大小的Mipmap中做線性插值,紋理內部使用線性濾波。

Mipmap使用注意使用使用glGenerateMipmap(GL_TEXTURE_2D)產生Mipmap的前提是你已經加載了原始的紋理對象。使用MipMap時設置GL_TEXTURE_MIN_FILTER選項才能起作用,設置GL_TEXTURE_MAG_FILTER的Mipmap選項將會導致無效操作,OpenGL錯誤碼為GL_INVALID_ENUM。

設置Mipmap選項如下代碼所示:

glTexParameteri(GL_TEXTURE_2D,

GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

1

2

加載原始紋理

從圖片加載紋理這部分工作不是OpenGL函數完成的,可以通過外部庫實現。這里我們使用SOIL(Simple OpenGL Image Library)庫完成。下載完這個庫后,你需要編譯到本地平臺對應版本。你可以從我的github處下載已經編譯好的32位庫。

使用SOIL加載紋理的代碼如下:

GLubyte *imageData =NULL;intpicWidth, picHeight;imageData = SOIL_load_image("wood.png",? ? &picWidth, &picHeight,0, SOIL_LOAD_RGB);// 讀取圖片數據glTexImage2D(GL_TEXTURE_2D,0, GL_RGB,? ? picWidth,picHeight,0, GL_RGB,? ? GL_UNSIGNED_BYTE, imageData);// 定義紋理圖像

1

2

3

4

5

6

7

其中glTexImage2D函數定義紋理圖像的格式,寬度和高度等信息,具體參數如下:

APIvoidglTexImage2D( GLenum target,

GLint level,

GLint internalFormat,

GLsizei width,

GLsizei height,

GLint border,

GLenum format,

GLenum type,

const GLvoid * data);

1.target參數指定設置的紋理目標,必須是GL_TEXTURE_2D, GL_PROXY_TEXTURE_2D等參數。

2.level指定紋理等級,0代表原始紋理,其余等級對應Mipmap紋理等級。

3.internalFormat指定OpenGL存儲紋理的格式,我們讀取的圖片格式包含RGB顏色,因此這里也是用RGB顏色。

4.width和height參數指定存儲的紋理大小,我們之前利用SOIL讀取圖片時已經獲取了圖片大小,這里直接使用即可。

5. border 參數為歷史遺留參數,只能設置為0.

6. 最后三個參數指定原始圖片數據的格式(format)和數據類型(type,為GL_UNSIGNED_BYTE, GL_BYTE等值),以及數據的內存地址(data指針)。

使用紋理的完整過程

Step1首先要指定紋理坐標,這個坐標和頂點位置、頂點顏色一樣處理,使用索引繪制,代碼如下所示:

// 指定頂點屬性數據 頂點位置 顏色 紋理GLfloat vertices[] = {? ? ? ? -0.5f, -0.5f,0.0f,1.0f,0.0f,0.0f,0.0f,0.0f,// 00.5f,? -0.5f,0.0f,0.0f,1.0f,0.0f,1.0f,0.0f,// 10.5f,0.5f,0.0f,0.0f,0.0f,1.0f,1.0f,1.0f,// 2-0.5f,0.5f,0.0f,1.0f,1.0f,0.0f,0.0f,1.0f// 3};? ? GLushort indices[] = {0,1,2,// 第一個三角形0,2,3// 第二個三角形};

1

2

3

4

5

6

7

8

9

10

11

同頂點位置和顏色一樣,需要指定紋理坐標的解析方式。上面的數據格式如下圖所示(來自www.learnopengl.com):

這個格式的說明在OpenGL學習腳印: 繪制一個三角形已經講過,如果不清楚,可以回過頭去查看。通過查看上圖,我們按照如下方式設置glVertexAttribPointer,讓OpenGL知道如何解析上述數據:

// 頂點位置屬性glVertexAttribPointer(0,3, GL_FLOAT, GL_FALSE,8*sizeof(GL_FLOAT), (GLvoid*)0);glEnableVertexAttribArray(0);// 頂點顏色屬性glVertexAttribPointer(1,3, GL_FLOAT, GL_FALSE,8*sizeof(GL_FLOAT), (GLvoid*)(3 *sizeof(GL_FLOAT)));glEnableVertexAttribArray(1);// 頂點紋理坐標glVertexAttribPointer(2,2, GL_FLOAT, GL_FALSE,8*sizeof(GL_FLOAT), (GLvoid*)(6 *sizeof(GL_FLOAT)));glEnableVertexAttribArray(2);

1

2

3

4

5

6

7

8

9

10

11

12

對應的頂點著色器如下:

#version 330layout(location =0)invec3position;layout(location =1)invec3color;layout(location =2)invec2textCoord;// 紋理坐標outvec3VertColor;outvec2TextCoord;voidmain(){gl_Position=vec4(position,1.0);? ? VertColor = color;? ? TextCoord = textCoord;}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

Step2:然后需要設置OpenGL紋理參數;最后通過讀取紋理圖片,定義紋理圖像格式等信息。紋理數據最終傳遞到了顯卡中存儲。

// Section3 準備紋理對象// Step1 創建并綁定紋理對象GLuint textureId;? ? glGenTextures(1, &textureId);? ? glBindTexture(GL_TEXTURE_2D, textureId);// Step2 設定wrap參數glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);? ? glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);// Step3 設定filter參數glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);? ? glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,? ? ? ? GL_LINEAR_MIPMAP_LINEAR);// 為MipMap設定filter方法// Step4 加載紋理GLubyte *imageData =NULL;intpicWidth, picHeight;? ? imageData = SOIL_load_image("wood.png",? ? ? ? &picWidth, &picHeight,0, SOIL_LOAD_RGB);? ? glTexImage2D(GL_TEXTURE_2D,0, GL_RGB, picWidth, picHeight,0, GL_RGB, GL_UNSIGNED_BYTE, imageData);? ? glGenerateMipmap(GL_TEXTURE_2D);// Step5 釋放紋理圖片資源SOIL_free_image_data(imageData);? ? glBindTexture(GL_TEXTURE_2D,0);

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

注意圖片資源在創建完紋理后就可以釋放了,使用SOIL_free_image_data完成。

Step3著色器中使用紋理對象

在頂點著色器中我們傳遞了紋理坐標,有了紋理坐標,獲取最終的紋素使用過在片元著色器中完成的。由于紋理對象通過使用uniform變量來像片元著色器傳遞,實際上這里傳遞的是對應紋理單元(texture unit)的索引號。紋理單元、紋理對象對應關系如下圖所示:

著色器通過紋理單元的索引號索引紋理單元,每個紋理單元可以綁定多個紋理到不同的目標(1D,2D)。OpenGL可以支持的紋理單元數目,一般至少有16個,依次為GL_TEXTURE0 到GL_TEXTURE15,紋理單元最大支持數目可以通過查詢GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS常量獲取。這些常量值是按照順序定義的,因此可以采用 GL_TEXTURE0 + i 的形式書寫常量,其中整數i在[0, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)范圍內。

作為一個了解,紋理對象不僅包含紋理數據,還包含采樣參數,這些采樣參數稱之為采樣狀態(sampling state)。而采樣對象(sampler object)就是只包含采樣參數的對象,將它綁定到紋理單元時,它會覆蓋紋理對象中的采樣狀態,從而重新配置采樣方式。這里不再繼續討論采樣對象的使用了。

要使用紋理必須在使用之前激活對應的紋理單元,默認狀態下0號紋理單元是激活的,因此即使沒有顯式地激活也能工作。激活并使用紋理的代碼如下:

// 使用0號紋理單元glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, textureId);glUniform1i(glGetUniformLocation(shader.programId,"tex"),0);

1

2

3

4

上述glUniform1i將0號紋理單元作為整數傳遞給片元著色器,片元著色器中使用uniform變量對應這個紋理采樣器,使用變量類型為:

uniformsampler2Dtex;

1

uniform變量與attribute變量uniform變量與頂點著色器中使用的屬性變量(attribute variables)不同,

屬性變量首先進入頂點著色器,如果要傳遞給片元著色器,需要在頂點著色器中定義輸出變量輸出到片元著色器。而uniform變量則類似于全局變量,在整個著色器程序中都可見。

完整的片元著色器代碼為:

#version 330invec3VertColor;invec2TextCoord;uniformsampler2Dtex;outvec4color;voidmain(){? ? color =texture(tex, TextCoord);}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

其中texture函數根據紋理坐標,獲取紋理對象中的紋素。

運行程序,效果如下圖所示:

這里為繪制的矩形添加了紋理,可以從我的github下載程序完整代碼。

重構代碼

將上面處理紋理部分的代碼整理成一個函數,放在textureHelper類里,可以從我的github查看這個類的代碼。使用textureHelper類加載紋理的代碼為:

GLint textureId = TextureHelper::load2DTexture("wood.png");

1

在上面的頂點著色器中,我們也傳遞了頂點顏色屬性,將頂點顏色和紋理混合,修改片元著色器中代碼為:

color =texture(tex, TextCoord) *vec4(VertColor,1.0f);

1

使用多個紋理單元

上面介紹了一個紋理單元支持多個紋理綁定到不同的目標,一個程序中也可以使用多個紋理單元加載多個2D紋理。使用多個紋理單元的代碼如下:

shader.use();// 使用0號紋理單元glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, textureId1);glUniform1i(glGetUniformLocation(shader.programId,"tex1"),0);// 使用1號紋理單元glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, textureId2);glUniform1i(glGetUniformLocation(shader.programId,"tex2"),1);

1

2

3

4

5

6

7

8

9

在著色器中,對兩個紋理的顏色進行混合:

#version 330invec3VertColor;invec2TextCoord;uniformsampler2Dtex1;uniformsampler2Dtex2;uniformfloatmixValue;outvec4color;voidmain(){vec4color1 =texture(tex1, TextCoord);vec4color2 =texture(tex2, TextCoord);? ? color =mix(color1, color2, mixValue);}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

其中mix函數完成顏色插值,函數原型為:

APIgenType mix( genType x,

genType y,

genType a);

最終值得計算方法為:x×(1?a)+y×a。

mixValue通過程序傳遞,可以通過鍵盤上的A和S鍵,調整紋理混合值,改變混合效果。

運行效果如下:

畫面中這只貓是倒立的,主要原因是加載圖片時,圖片的(0,0)位置一般在左上角,而OpenGL紋理坐標的(0,0)在左下角,這樣y軸順序相反。有的圖片加載庫提供了相應的選項用來翻轉y軸,SOIL沒有這個選項。我們可以修改頂點數據中的紋理坐標來達到目的,或者對于我們這里的簡單情況使用如下代碼實現y軸的翻轉:

vec4color2 =texture(tex2,vec2(TextCoord.s,1.0- TextCoord.t));

1

2

修改后的運行效果如下所示:

上述程序完整的代碼可以從我的github下載。

說明限于時間關系,文中的示例圖片部分來源于網絡,均注明了出處,向原作者表示感謝。

參考資料

Android Lesson Six: An Introduction to Texture Filtering

www.learnopengl.comTextures

Basic Texture Mapping

Textures objects and parameters

Tutorial 5 : A Textured Cube

推薦閱讀

關于Texture filteringShawn Hargreaves Blog-Texture filtering

關于Mipmap的Shawn Hargreaves Blog-Texture filtering: mipmaps

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內容

  • 一、紋理基礎 3D圖形渲染中最基本的操作就是對一個表面應用紋理。紋理可以表現只從網格的幾何形狀無法得到的附加細節。...
    cain_huang閱讀 8,891評論 0 7
  • 紋理只是一種能夠應用到場景中的三角形上的圖像數據,它通過經過過濾的紋理單元(texel,相當于紋理的像素)填充到實...
    Gaolex閱讀 2,844評論 0 1
  • 紋理(Textures) 我們已經了解到,我們可以為每個頂點使用顏色來增加圖形的細節,從而創建出有趣的圖像。但是通...
    IceMJ閱讀 5,655評論 2 13
  • 前言 本文主要是對OpegGL ES的api做一些資料上的搜集,給大家推薦一個中文詳解網站:鏈接,雖然作者還沒有寫...
    Link913閱讀 2,514評論 1 6
  • 【90天目標】 一、變美 晚十早五 堅持每周運動3-4次 泡腳 二、為自己做早餐 每周采購食材2-3次 三、堅持時...
    言西早_elena閱讀 170評論 2 2