🐉

SwiftUIでドラゴンボールを描こう

2024/11/11に公開


©️東映アニメーション
ドラゴンボールDAIMAが放送中ですね。
それではSwiftUIでドラゴンボールを描いていきましょう(唐突)。
段階を踏みながら、よりそれっぽい球体を描いていきます。

まずはここから。ただの円です。

DragonBall.swift
import SwiftUI

struct DragonBall: View {
    var body: some View {
        Circle()
            .fill(Color.orange)
            .frame(width: 200, height: 200)
    }
}

#Preview {
    DragonBall()
}

鏡面反射

物体に当たった光を人間が「視る」と言った場合、そこには大きく分けて2つの光が存在します。1つは、物体内部に進入した光が再度放射される「拡散反射光」と、もう1つは内部には進入せず物体表面で反射する「鏡面反射光」です。

参考:
https://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20090914

先ほどの円の状態は言わば拡散反射光のみの状態になります。なので次は鏡面反射光っぽいものを足してあげましょう。

DragonBall.swift
import SwiftUI

struct DragonBall: View {
    var body: some View {
        Circle()
            .fill(Color.orange)
            .frame(width: 200, height: 200)
            .overlay( // 鏡面反射
                Circle()
                    .fill(Color.white)
                    .frame(width: 80, height: 80)
                    .blur(radius: 16)
                    .offset(x: -50, y: -50)
            )
    }
}

#Preview {
    DragonBall()
}

白い円をブラーをかけて、立体的に見えるように斜め上からoverlayしました。
これだけでもそれっぽいですね。

透過光

半透明の物体に光を当てた時、光が当たってる方向と逆の方が明るい、ということを見たことはないでしょうか。

左から照明が当たっているが右の方が明るい
出典:
https://www.tiktok.com/@unknown1211388/video/7402599079701761313
これは物体内部に進入した光が反射しきらず反対側から放射されるために起きます。
先ほどと同じようにそれっぽい円を重ねてあげましょう。

DragonBall.swift
import SwiftUI

struct DragonBall: View {
    var body: some View {
        Circle()
            .fill(Color.orange)
            .frame(width: 200, height: 200)
            .overlay( // 透過光
                Circle()
                    .fill(Color.white.opacity(0.6))
                    .frame(width: 60, height: 60)
                    .offset(x: 60, y: 40)
                    .blur(radius: 20)
            )
            .overlay( // 鏡面反射
                Circle()
                    .fill(Color.white)
                    .frame(width: 80, height: 80)
                    .blur(radius: 16)
                    .offset(x: -50, y: -50)
            )
    }
}

#Preview {
    DragonBall()
}

奥行き感を出すために鏡面反射よりもblurをきつくし、opacityで馴染ませるようにしました。
透明感が増したんじゃないでしょうか。

表面下散乱

「表面下散乱」とは、先ほどの拡散反射が物体表面よりも少し透過した状態で起こるものです。人間の肌、大理石、バーベキューソース、などの表面の感じがそれです。

表面の膜、表皮、のような感じを表現していきます。

DragonBall.swift
import SwiftUI

struct DragonBall: View {
    var body: some View {
        Circle()
            .fill(Color.orange.opacity(0.3))
            .frame(width: 200, height: 200)
            .overlay( // 表面化散乱
                Circle()
                    .fill(Color.orange.opacity(0.9))
                    .frame(width: 180, height: 180)
                    .blur(radius: 6)
            )
            .overlay( // 透過光
                Circle()
                    .fill(Color.white.opacity(0.6))
                    .frame(width: 60, height: 60)
                    .offset(x: 60, y: 40)
                    .blur(radius: 20)
            )
            .overlay( // 鏡面反射
                Circle()
                    .fill(Color.white)
                    .frame(width: 80, height: 80)
                    .blur(radius: 16)
                    .offset(x: -50, y: -50)
            )
    }
}

#Preview {
    DragonBall()
}

最初の円にopacityを付与し、ちょっと小さい円をblurをかけて重ねました。
てりっとして魚卵っぽくて美味しそうなドラゴンボールの感じが出たんじゃないでしょうか。

完成

最後に星を添えて出来上がりです。

DragonBall.swift
import SwiftUI

struct DragonBall: View {
    var body: some View {
        Circle()
            .fill(Color.orange.opacity(0.3))
            .frame(width: 200, height: 200)
            .overlay( // 星
                Text("★")
                    .font(.title)
            )
            .overlay( // 表面化散乱
                Circle()
                    .fill(Color.orange.opacity(0.9))
                    .frame(width: 180, height: 180)
                    .blur(radius: 6)
            )
            .overlay( // 透過光
                Circle()
                    .fill(Color.white.opacity(0.6))
                    .frame(width: 60, height: 60)
                    .offset(x: 60, y: 40)
                    .blur(radius: 20)
            )
            .overlay( // 鏡面反射
                Circle()
                    .fill(Color.white)
                    .frame(width: 80, height: 80)
                    .blur(radius: 16)
                    .offset(x: -50, y: -50)
            )
    }
}

#Preview {
    DragonBall()
}

いかがだったでしょうか。
SwiftUIは学習したてなので書き方が下手くそな箇所があった場合はご容赦ください。
鏡面反射や表面下散乱などの知識も持っておくと理屈でお絵描きができるようになるので、ぜひコンピュータグラフィックスなどを学習されてみることをおすすめします。

Discussion