上一節的3D幾何模型,看起來感覺與真實物件還有點距離,差在哪裡呢?這些模型有其形而無其質,外表就像用黏土捏出來的粗胚,既未上色,又缺乏質感。如何讓3D模型具有真實的外觀呢?其中關鍵就在燈光與材質。

本節想要製作一個大理石巧克力口味的甜甜圈,先看看下圖執行的結果,不管是光影、紋理、色澤,是不是比起上一節逼真多了:

截圖 2024-03-06 晚上11.25.22.png

要讓3D模型展現真實質感,理論上並不容易,因為要考慮色澤、材質、表面紋理、反光特性、光源角度…等等非常多因素,想想看,若僅靠(視覺)圖片,我們如何區分同樣紅色的蘋果與籃球?當然只能靠外觀 — 兩者的形狀、圖案(紋理)、表面(光滑或粗糙、會不會反光)等不同特性。

這也是過去50年來研究3D繪圖的核心課題,所幸,經過多年的發展,困難的部分都已在 SceneKit 套件中解決了,現在很輕易就能用 Swift 程式做出逼真的3D模型。

有多容易呢?只要寫20幾行即可,完整程式碼如下:

// 6-1c 材質(SCNMaterial)與燈光(SCNLight)
// Created by Heman, 2024/03/05
import SceneKit
import SwiftUI

struct 材質與燈光: View {
    let 幾何場景 = SCNScene()
    var body: some View {
        SceneView(scene: 幾何場景, options: [.allowsCameraControl])
            .onAppear {
                let 甜甜圈 = SCNTorus(ringRadius: 1.0, pipeRadius: 0.4)
                let 甜甜圈節點 = SCNNode(geometry: 甜甜圈)
                甜甜圈節點.rotation = SCNVector4(x: 1.0, y: 0.0, z: 1.0, w: .pi/3)
                
                let 大理石材質 = SCNMaterial()
                大理石材質.diffuse.contents = UIImage(named: "大理石紋")
                大理石材質.specular.contents = UIColor.white
                甜甜圈.materials = [大理石材質]
                
                let 光源 = SCNLight()
                let 燈光節點 = SCNNode()
                燈光節點.light = 光源
                燈光節點.position = SCNVector3(x: 0, y: 10, z: 0)
                
                幾何場景.rootNode.addChildNode(甜甜圈節點)
                幾何場景.rootNode.addChildNode(燈光節點)
                幾何場景.background.contents = UIColor.green
            }
    }
}

import PlaygroundSupport
PlaygroundPage.current.setLiveView(材質與燈光())

這其中,讓甜甜圈改頭換面的關鍵,就是用 SceneKit 的材質物件 SCNMaterial:

let 大理石材質 = SCNMaterial()
大理石材質.diffuse.contents = UIImage(named: "大理石紋")
大理石材質.specular.contents = UIColor.white
甜甜圈.materials = [大理石材質]

SCNMaterial 物件有非常多屬性(30+個,畢竟影響質感的因素很多),其中有3個最基本,是材質外觀的主要屬性:

  1. .diffuse: 模型的漫射表面,預設值為白色(如上節6-1b的幾何模型)
  2. .metalness: 金屬或非金屬色澤(須設定材質.lightingModel = .physicallyBased)
  3. .roughness: 光滑或粗糙程度(須設定材質.lightingModel = .physicallyBased)

其它屬性則用來修飾細部或特殊效果,例如本範例將高光部位(.specular)設為白色,讓反光更顯眼。材質的屬性內容(contents)可以設定為某個數值、顏色、圖片、影片或甚至動畫效果。