獲取示例代碼
前言
上一篇我們介紹了材質的基本概念,這一篇我們將重點介紹材質中的一個概念 - 貼圖。本文將重點說明diffuse貼圖的運作方式。在例子中,為之前自定義的Cube幾何體每一個面貼上不同的圖片。下面是效果圖。
UV
為了能夠正確的將圖片貼合到幾何體的面上,幾何體的頂點數據中必須包含這種映射關系。在前面我們有提到頂點數據中有包含UV數據。所謂UV,就是圖片映射到幾何面的坐標。通常,我們將圖片的尺寸規范到1x1的范圍內,如下圖所示。U是水平軸,V是豎直軸。
如果一個頂點對應的UV是
(0,0)
,那么就相當于圖片的左上角會被貼到那個頂點的位置上。下面是這個面的頂點數據。
-0.5, 0.5, 0.5, 0, 0, 1, 0, 0,
-0.5, -0.5, 0.5, 0, 0, 1, 0, 1,
0.5, -0.5, 0.5, 0, 0, 1, 1, 1,
0.5, -0.5, 0.5, 0, 0, 1, 1, 1,
0.5, 0.5, 0.5, 0, 0, 1, 1, 0,
-0.5, 0.5, 0.5, 0, 0, 1, 0, 0,
每一行,前三個是頂點位置,中間三個是法線,也就是和這個面垂直的向量,后面兩個就是UV數據了。這個面和xy面平行,在z=0.5處。UV為(0,0)的頂點位置是-0.5,0.5,0.5,也就是左上角的頂點,其他的頂點讀者可以自行查看對應關系。通過這些UV映射,數字1的圖片則剛好貼在這個面上。
多材質
我們要為自定義的Cube幾何體每個面貼上不同的貼圖,就得對之前的代碼做修改。之前的代碼中只使用了一個SCNGeometryElement,所以只能使用一個材質。如果我們為每一個面創建一個SCNGeometryElement,那么就可以使用6種不同的材質了。
var elements: [SCNGeometryElement] = []
for i in 0..<6 {
var indices: [UInt32] = []
(0..<6).forEach { indices.append(UInt32(i * 6) + UInt32($0)) }
let element = SCNGeometryElement.init(indices: indices, primitiveType: .triangles)
elements.append(element)
}
self.init(sources: [verticeSource, uvSource, normalSource], elements: elements)
接著我們在ViewController中給Cube賦予6種材質。
var materials: [SCNMaterial] = []
var colors: [UIColor] = [
UIColor.init(red: 0xff / 255.0, green: 0xe5 / 255.0, blue: 0.0, alpha: 1.0),
UIColor.init(red: 0xe9 / 255.0, green: 0.0, blue: 0x3a / 255.0, alpha: 1.0),
UIColor.init(red: 0x07 / 255.0, green: 0x76 / 255.0, blue: 0xa0 / 255.0, alpha: 1.0),
UIColor.init(red: 0xf4 / 255.0, green: 0x43 / 255.0, blue: 0x36 / 255.0, alpha: 1.0),
UIColor.init(red: 0x68 / 255.0, green: 0x9f / 255.0, blue: 0x38 / 255.0, alpha: 1.0),
UIColor.init(red: 0xef / 255.0, green: 0x6c / 255.0, blue: 0.0, alpha: 1.0),
]
for i in 0..<6 {
let material = SCNMaterial()
material.lightingModel = .blinn
material.diffuse.contents = NumberImageGenerator.createImage(number: i + 1, foregroundColor: colors[(i + 1) % colors.count], backgroundColor: colors[i % colors.count], size: CGSize.init(width: 128, height: 128))
material.shininess = 1.0
materials.append(material)
}
geometry.materials = materials
我編寫了輔助方法NumberImageGenerator.createImage
來生成寫有數字的圖片。我生成的圖片大小是128x128,推薦使用2的次方尺寸的圖片作為貼圖,比如64,128,256,512,1024等。
貼圖配置
前面我們剛好將圖片貼滿一個面,如果我們對UV坐標做如下修改會怎么樣呢?我將UV的值放大了2倍。
// Z軸0.5處的平面
-0.5, 0.5, 0.5, 0, 0, 1, 0, 0,
-0.5, -0.5, 0.5, 0, 0, 1, 0, 2,
0.5, -0.5, 0.5, 0, 0, 1, 2, 2,
0.5, -0.5, 0.5, 0, 0, 1, 2, 2,
0.5, 0.5, 0.5, 0, 0, 1, 2, 0,
-0.5, 0.5, 0.5, 0, 0, 1, 0, 0,
如果不做任何其他處理的話,效果如下。
如果我們希望UV超出1的部分以重復的形式進行貼圖,需要進行一些設置。
material.diffuse.wrapS = .repeat
material.diffuse.wrapT = .repeat
這樣就可以得到下面的效果。
系統還提供了鏡像模式。
material.diffuse.wrapS = .mirror
material.diffuse.wrapT = .mirror
效果如下。
wrapS代表U軸上的重復模式,wrapT代表V軸上的重復模式。默認情況下取值都為.clamp
,也就是多出的部分重復邊緣的像素點。
我們還可以通過下面的代碼控制圖片放大和縮小是使用的采樣算法。默認是linear算法,效率高,效果略差。
material.diffuse.minificationFilter = .nearest
material.diffuse.magnificationFilter = .nearest
總結
本文主要介紹了如何使用diffuse貼圖以及相關配置,對于法線貼圖和specular貼圖來說,也遵從相同的原理。讀者可以自行構建出一些幾何體,然后對其進行貼圖來當作練習。