上一篇文章我們通過金字塔延伸到了正方體,然后到這篇正方體每一個(gè)面貼一張圖。
先看效果圖:Demo

接下來讓我們開始學(xué)習(xí)OpenGL 一個(gè)重要??的知識點(diǎn):紋理
借鑒博客:半紙淵--基礎(chǔ)紋理
前言:之前我們說過紋理可以簡單理解為圖片,但是紋理不簡簡單單??圖片。
-
1. Texture 是什么?
Texture 紋理,就是一堆被精心排列過的像素;
Texture 在 OpenGL 里面有很多種類,但在 ES 版本中就兩種:Texture_2D 、 Texture_CubeMap
-
Texture_2D:
就是 {x, y} 二維空間下的像素呈現(xiàn),也就是說,由效果圖上可知,很難做到使正方體的六個(gè)面出現(xiàn)不同的像素組合;圖片處理一般都使用這個(gè)模式;[x 、y 屬于 [0, 1] 這個(gè)范圍]
2D紋理坐標(biāo)
-
Texture_CubeMap:
就是 { x, y, z } 三維空間下的像素呈現(xiàn),也就如效果圖中演示的正方體的六個(gè)面可以出現(xiàn)不同的像素組合;它一般是用于做環(huán)境貼圖——就是制作一個(gè)環(huán)境,讓 3D 模型如同置身于真實(shí)環(huán)境中【卡通環(huán)境中也行】。[x、y、z 屬于 [-1, 1] 這個(gè)范圍,就是與 Vertex Position 的值范圍一致]
3D紋理坐標(biāo)
注:上面提到的所有坐標(biāo)范圍是指有效渲染范圍,也就是說你如果提供的紋理坐標(biāo)超出了這個(gè)范圍也沒有問題,只不過超出的部分就不渲染了;
頂點(diǎn)數(shù)據(jù)表示如下:
- Texture_2D:
//------------- 正方體 -------------
let attrArr: [GLfloat] = [
// 頂點(diǎn):(x, y, z) 顏色:(r, g, b) 紋理: (s, t)
// 前面
-0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, // 前左上 0
-0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 1.0, // 前左下 1
0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, // 前右下 2
0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, // 前右上 3
...
]
- Texture_CubeMap:
let attrArr: [GLfloat] = [
// 頂點(diǎn):(x, y, z) 顏色:(r, g, b) 紋理: (s, t, p)
// 前面
-1.0, 1.0, 1.0, 1.0, 0.0, 0.0, -1.0, 1.0, 1.0, // 前左上 0
-1.0, -1.0, 1.0, 0.0, 1.0, 0.0, -1.0, -1.0, 1.0, // 前左下 1
1.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, -1.0, 1.0, // 前右下 2
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 前右上 3
...
]
??ps: CubeMap 里面的紋理坐標(biāo)和頂點(diǎn)數(shù)據(jù)是一樣的
- 加載CubeMap紋理
CubeMap共有6個(gè)面,然后分別設(shè)置
GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
代碼如下:注意??:這里是cubeMap 6張圖片的寬高要一致
//7.1 設(shè)置立方體紋理
func setupCubeTexture() {
//7.綁定紋理到默認(rèn)的紋理ID(這里只有一張圖片,故而相當(dāng)于默認(rèn)于片元著色器里面的us2d_texture)
glBindTexture(GLenum(GL_TEXTURE_CUBE_MAP), 0)
for i in 0..<6 {
let spriteImage: CGImage = UIImage(named: "timg-\(i+1)")!.cgImage!
//2.讀取圖片的大?。簩捄透?注意??:這里是cubeMap 6張圖片的寬高要一致
let width = 512//spriteImage.width
let height = 512//spriteImage.height
//3.獲取圖片字節(jié)數(shù): 寬x高x4(RGBA)
// let spriteData: UnsafeMutablePointer = UnsafeMutablePointer<GLbyte>.allocate(capacity: MemoryLayout<GLbyte>.size * width * height * 4)
let spriteData: UnsafeMutableRawPointer = calloc(width * height * 4, MemoryLayout<GLbyte>.size)
//4.創(chuàng)建上下文
/*
參數(shù)1:data,指向要渲染的繪制圖像的內(nèi)存地址
參數(shù)2:width,bitmap的寬度,單位為像素
參數(shù)3:height,bitmap的高度,單位為像素
參數(shù)4:bitPerComponent,內(nèi)存中像素的每個(gè)組件的位數(shù),比如32位RGBA,就設(shè)置為8
參數(shù)5:bytesPerRow,bitmap的每一行的內(nèi)存所占的比特?cái)?shù)
參數(shù)6:colorSpace,bitmap上使用的顏色空間 kCGImageAlphaPremultipliedLast:RGBA
let colorSpace = CGColorSpaceCreateDeviceRGB()
*/
let spriteContext: CGContext = CGContext(data: spriteData, width: width, height: height, bitsPerComponent: 8, bytesPerRow: width * 4, space: spriteImage.colorSpace!, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
//5.在CGContextRef上繪圖
let rect = CGRect(x: 0, y: 0, width: width, height: height)
spriteContext.draw(spriteImage, in: rect)
//載入紋理2D數(shù)據(jù) 就是加載紋理像素到 GPU 的方法
/*
參數(shù)1:紋理模式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
參數(shù)2:加載的層次,一般設(shè)置為0
參數(shù)3:紋理的顏色值GL_RGBA
參數(shù)4:寬
參數(shù)5:高
參數(shù)6:border,邊界寬度
參數(shù)7:format
參數(shù)8:type
參數(shù)9:紋理數(shù)據(jù)
*/
glTexImage2D(GLenum(GL_TEXTURE_CUBE_MAP_POSITIVE_X + Int32(i)), 0, GL_RGBA, GLsizei(width), GLsizei(height), 0, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), spriteData)
//釋放spriteData
free(spriteData)
}
//設(shè)置紋理屬性
/*
參數(shù)1:紋理維度
參數(shù)2:線性過濾、為s,t坐標(biāo)設(shè)置模式
參數(shù)3:wrapMode,環(huán)繞模式
*/
glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR)
glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR)
glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE)
glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE)
//綁定紋理
/*
參數(shù)1:紋理維度
參數(shù)2:紋理ID,因?yàn)橹挥幸粋€(gè)紋理,給0就可以了。
*/
glBindTexture(GLenum(GL_TEXTURE_CUBE_MAP), 0)
}
因?yàn)槲覀兊脑O(shè)置的紋理坐標(biāo)由兩位:[s, t] --> [s, t, p],所以著色器中的紋理坐標(biāo)要做相應(yīng)的改變,還有渲染那里讀取數(shù)據(jù)的時(shí)候也做相應(yīng)改變。
- 頂點(diǎn)著色器代碼:
//紋理坐標(biāo)vec2 --> vec3
attribute vec4 position;
attribute vec4 positionColor; //頂點(diǎn)顏色
attribute vec3 textCoordinate; //紋理坐標(biāo)
uniform mat4 projectionMatrix; //投影矩陣
uniform mat4 modelViewMatrix; //模型視圖矩陣
varying lowp vec4 varyColor; //頂點(diǎn)顏色
varying lowp vec3 varyTextCoord; //傳遞給片元著色器紋理坐標(biāo)
void main()
{
varyColor = positionColor;
varyTextCoord = textCoordinate;
vec4 vPos;
vPos = projectionMatrix * modelViewMatrix * position;
gl_Position = vPos;
}
- 片元著色器代碼:
//紋理坐標(biāo)vec2 --> vec3
varying lowp vec4 varyColor; //頂點(diǎn)顏色
varying lowp vec3 varyTextCoord; //頂點(diǎn)著色器傳遞過來的紋理坐標(biāo)
//uniform sampler2D colorMap; //紋理
uniform samplerCube us2d_texture;
void main()
{
gl_FragColor = textureCube(us2d_texture, varyTextCoord) * varyColor;
}
渲染代碼:
步長和紋理坐標(biāo)做相應(yīng)的調(diào)整即可,就不貼了
到此正方體貼圖工作就完成了。詳細(xì)請查看源碼
但是從借鑒的博客半紙淵--基礎(chǔ)紋理,看到他能實(shí)現(xiàn)下圖像魔方的效果,既然都做到多面貼圖了,所以也想試試看。

通過查看他的源碼,這種實(shí)現(xiàn)方式也是:glTexImage2D,只不過最后一個(gè)參數(shù)數(shù)據(jù)是個(gè)顏色數(shù)組
- 加載紋理的代碼就變成這樣:
//7.2 設(shè)置立方體像素紋理
func setupCubePixelsTexture() {
//7.綁定紋理到默認(rèn)的紋理ID(這里只有一張圖片,故而相當(dāng)于默認(rèn)于片元著色器里面的us2d_texture)
glBindTexture(GLenum(GL_TEXTURE_CUBE_MAP), 0)
for i in 0..<6 {
glTexImage2D(GLenum(GL_TEXTURE_CUBE_MAP_POSITIVE_X + Int32(i)), 0, GL_RGBA, 2, 2, 0, GLenum(GL_RGBA), GLenum(GL_FLOAT), texCubemapPixelDatas[i])
}
//設(shè)置紋理屬性
/*
參數(shù)1:紋理維度
參數(shù)2:線性過濾、為s,t坐標(biāo)設(shè)置模式
參數(shù)3:wrapMode,環(huán)繞模式
*/
glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_MIN_FILTER), GL_LINEAR)
glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_MAG_FILTER), GL_LINEAR)
glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_WRAP_S), GL_CLAMP_TO_EDGE)
glTexParameteri(GLenum(GL_TEXTURE_CUBE_MAP), GLenum(GL_TEXTURE_WRAP_T), GL_CLAMP_TO_EDGE)
//綁定紋理
/*
參數(shù)1:紋理維度
參數(shù)2:紋理ID,因?yàn)橹挥幸粋€(gè)紋理,給0就可以了。
*/
glBindTexture(GLenum(GL_TEXTURE_CUBE_MAP), 0)
}

這并不是我們想要的效果,而且連方格都沒有顯示出來,雖然中間看似有分割線。但是離效果圖還是天差地別的。怎么回事呢?對比了一下發(fā)現(xiàn)在過濾方式不同:GL_LINEAR 和 GL_NEAREST
摘抄自:mChenys -- 六、OpenGL ES紋理的使用
當(dāng)紋理大小要被擴(kuò)大或者縮小的時(shí)候,我們需要使用紋理過濾明確說明會(huì)發(fā)生什么,當(dāng)我們在渲染表面上繪制一個(gè)紋理時(shí),那個(gè)紋理的紋理元素可能無法精確地映射到OpenGL生成的片段上,有2種情況:縮小或者放大。
當(dāng)我們盡力把幾個(gè)紋理元素?cái)D進(jìn)一個(gè)片段時(shí),縮小就會(huì)發(fā)生了,當(dāng)把一個(gè)紋理元素?cái)U(kuò)展到許多片段時(shí),放大就發(fā)生了。
針對每一種情況,我們都可以配置OpenGL使用一個(gè)紋理過濾器,我會(huì)使用下面的圖像闡述每一種過濾模式:
圖1
- GL_NEAREST(也叫鄰近過濾,Nearest Neighbor Filtering)是OpenGL默認(rèn)的紋理過濾方式。當(dāng)設(shè)置為GL_NEAREST的時(shí)候,OpenGL會(huì)選擇中心點(diǎn)最接近紋理坐標(biāo)的那個(gè)像素。下圖中你可以看到四個(gè)像素,加號代表紋理坐標(biāo)。左上角那個(gè)紋理像素的中心距離紋理坐標(biāo)最近,所以它會(huì)被選擇為樣本顏色:
圖2
這種方式為每個(gè)片段選擇最近的紋理元素,當(dāng)放大紋理時(shí)它的鋸齒效果看起來相當(dāng)明顯,每個(gè)紋理單元都清楚地顯示為一個(gè)小方塊。
圖3
當(dāng)我們縮小紋理時(shí),因?yàn)闆]有足夠的片段來繪制所有的紋理單元,許多細(xì)節(jié)將會(huì)丟失。
圖4- GL_LINEAR(也叫線性過濾,(Bi)linear Filtering)它會(huì)基于紋理坐標(biāo)附近的紋理像素,計(jì)算出一個(gè)插值,近似出這些紋理像素之間的顏色。一個(gè)紋理像素的中心距離紋理坐標(biāo)越近,那么這個(gè)紋理像素的顏色對最終的樣本顏色的貢獻(xiàn)越大。下圖中你可以看到返回的顏色是鄰近像素的混合色:
圖5
線性過濾使用雙線插值平滑像素之間的過渡,而不是每個(gè)片段使用最近的紋理元素,OpenGL會(huì)使用四個(gè)鄰接的紋理元素,并在他們之間用一個(gè)線性差值算法做差值,這個(gè)算法與前面介紹平滑著色的算法一樣,之所以叫它雙線性,是因?yàn)樗茄貎蓚€(gè)維度差值的,它會(huì)比近鄰過濾要平滑很多,但還是會(huì)有一些鋸齒顯示出來,因?yàn)槲覀儼堰@個(gè)紋理擴(kuò)展得太多了,但是鋸齒沒有最近鄰過濾那么明顯。
圖6
PS:紋理放大時(shí)使用線性過濾(GL_LINEAR),縮小時(shí)用鄰近過濾(GL_NEAREST)
然后我們修改過濾方式為:鄰近過濾(GL_NEAREST)
效果如下:

這里看到已經(jīng)差不多了和他的一樣了。但是總覺得怪怪的。就是每一個(gè)面都會(huì)有一塊是黑色的,而對照的卻不是這樣的。然后再去仔細(xì)看了看。后面發(fā)現(xiàn)原來還是在這個(gè)方法上出現(xiàn)了問題:
glTexImage2D(GLenum(GL_TEXTURE_CUBE_MAP_POSITIVE_X + Int32(i)), 0, GL_RGBA, 2, 2, 0, GLenum(GL_RGBA), GLenum(GL_FLOAT), texCubemapPixelDatas[i])
//載入紋理2D數(shù)據(jù) 就是加載紋理像素到 GPU 的方法
參數(shù)1:紋理模式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
參數(shù)2:加載的層次,一般設(shè)置為0
參數(shù)3:紋理的顏色值GL_RGBA
參數(shù)4:寬
參數(shù)5:高
參數(shù)6:border,邊界寬度
參數(shù)7:format
參數(shù)8:type
參數(shù)9:紋理數(shù)據(jù)
這里的(參數(shù)3:紋理的顏色值,參數(shù)7:format)我們傳的是GL_RGBA,而數(shù)組里面只有(r, g, b)并沒有a,所以出現(xiàn)取值有問題吧。改成 GL_RGB
運(yùn)行結(jié)果:








