Slide 1

Slide 1 text

ʰSwiftUIʹΑΔΧελϜϏδϡΞϧΤ ϑΣΫτͷ࡞੒ʱΛਅࣅͯ͠࡞ͬͯΈͨ Έ͸ΔΜ 2024/09/20 Chiba.swift #1

Slide 2

Slide 2 text

🗼

Slide 3

Slide 3 text

ΧελϜϏδϡΞϧΤϑΣΫτ ͝ଘ஌Ͱ͔͢ʁ

Slide 4

Slide 4 text

͜͏͍͏΋ͷͰ͢ Ҿ༻ݩίʔυ: https://developer.apple.com/documentation/SwiftUI/Creating-visual-e ff ects-with-SwiftUI

Slide 5

Slide 5 text

αϯϓϧॆ࣮ͯ͠·͢ ✨ • WWDCಈը • Create custom visual e ff ects with SwiftUI • https://developer.apple.com/wwdc24/10151 • Sample Code • https://developer.apple.com/documentation/SwiftUI/Creating-visual- e ff ects-with-SwiftUI • iOS 18.0+ | iPadOS 18.0+ | Xcode 16.0+

Slide 6

Slide 6 text

ࠓճਅࣅͨ͠ର৅ • ಛ௃ • σόΠεͷGPU্ͰγΣʔ μʔΛ࣮ߦ͍ͯ͠Δɻ

Slide 7

Slide 7 text

γΣʔμʔʁ ༻ޠઆ໌ • γΣʔμʔϓϩάϥϜͱ͸ɺGPUʢάϥϑΟοΫεॲཧϢχοτʣͰ࣮ߦ͞ΕΔ ϓϩάϥϜͰɺ3DάϥϑΟοΫεͷඳը΍ө૾ͷॲཧΛߦ͏ͨΊʹ࢖༻͞Ε· ͢ɻಛʹɺ௖఺ͷҐஔ΍ϐΫηϧͷ৭ɺޫͷ౰ͨΓํͳͲɺάϥϑΟοΫεͷݟ ͨ໨Λܾఆ͢Δॏཁͳ໾ׂΛՌͨ͠·͢ɻ • γΣʔμʔϓϩάϥϜ͸ɺήʔϜ΍3DΞϓϦέʔγϣϯɺVRͳͲͰϦΞϧλΠ ϜʹϨϯμϦϯά͢Δࡍʹॏཁͳ໾ׂΛ࣋ͪ·͢ɻ • XcodeͰ͸ɺ͜ΕΒͷγΣʔμʔ͸ɺMetal Shading LanguageʢMSLʣͱݺ͹ ΕΔC++ʹࣅͨݴޠͰهड़͞Ε·͢ɻ

Slide 8

Slide 8 text

ਅࣅͯ͠Ͱ͖ͨͷ͕ͪ͜ΒͰ͢ λοϓͨ͠ҐஔΛத৺ʹ೾໲͕޿͕Γ·͢

Slide 9

Slide 9 text

ॏࢹͨ͜͠ͱ • খ͞ͳมߋͰ΋͍͍͔ΒɺԿ͔͠Β΋ͷΛ࡞Δ͜ͱ

Slide 10

Slide 10 text

Ͳ͏΍ͬͯਅࣅ͍͔ͯͬͨ͠ • αϯϓϧίʔυͷViewͷೖΓޱΛோΊΔɻ • Ұ൪ͷಛ௃Ͱ͋ΔγΣʔμʔΛோΊΔʢॳݟͰͨ͠ʣɻ • ෦෼తʹίʔυΛม͑ͯΈΔɻ

Slide 11

Slide 11 text

View import SwiftUI #Preview("Ripple") { @Previewable @State var counter: Int = 0 @Previewable @State var origin: CGPoint = .zero VStack { Spacer() Image("palm_tree") .resizable() .aspectRatio(contentMode: .fit) .clipShape(RoundedRectangle(cornerRadius: 24)) .onPressingChanged { point in if let point { origin = point counter += 1 } } .modifier(RippleEffect(at: origin, trigger: counter)) Spacer() } .padding() }

Slide 12

Slide 12 text

View import SwiftUI #Preview("Ripple") { @Previewable @State var counter: Int = 0 @Previewable @State var origin: CGPoint = .zero VStack { Spacer() Image("palm_tree") .resizable() .aspectRatio(contentMode: .fit) .clipShape(RoundedRectangle(cornerRadius: 24)) .onPressingChanged { point in if let point { origin = point counter += 1 } } .modifier(RippleEffect(at: origin, trigger: counter)) Spacer() } .padding() } onPressingChanged͔ΒλοϓҐ ஔΛड͚औ͍ͬͯΔɻ Modi f ierͷதͰɺ͍Ζ͍Ζ΍ͬͯɺ Metal ShaderΛSwiftUIʹม׵͢Δ ίʔυΛݺΜͰ͍Δɻ

Slide 13

Slide 13 text

Metal Shader Ripple.metalɹίʔυͷશମ #include #include using namespace metal; [[ stitchable ]] half4 Ripple( float2 position, SwiftUI : : Layer layer, float2 origin, float time, float amplitude, float frequency, float decay, float speed ) { / / The distance of the current pixel position from `origin`. float distance = length(position - origin); / / The amount of time it takes for the ripple to arrive at the current pixel position. float delay = distance / speed; / / Adjust for delay, clamp to 0. time -= delay; time = max(0.0, time); / / The ripple is a sine wave that Metal scales by an exponential decay / / function. float rippleAmount = amplitude * sin(frequency * time) * exp(-decay * time); / / A vector of length `amplitude` that points away from position. float2 n = normalize(position - origin); / / Scale `n` by the ripple amount at the current pixel position and add it / / to the current pixel position. / / / / This new position moves toward or away from `origin` based on the / / sign and magnitude of `rippleAmount`. float2 newPosition = position + rippleAmount * n; / / Sample the layer at the new position. half4 color = layer.sample(newPosition); / / Lighten or darken the color based on the ripple amount and its alpha / / component. color.rgb += 0.3 * (rippleAmount / amplitude) * color.a; return color; }

Slide 14

Slide 14 text

Metal Shader Ripple.metalɹҾ਺ͱ໭Γ஋͸͜Μͳײ͡ [[ stitchable ]] half4 Ripple( float2 position, SwiftUI : : Layer layer, float2 origin, float time, float amplitude, float frequency, float decay, float speed ) { // தུ return color; } ֤ϐΫηϧͷҐஔͷ৭Λมߋͯ͠ฦ͍ͯ͠Δɻ ʬϙΠϯτʭ half4: 16Ϗοτුಈখ਺఺਺ͷ̐੒෼͔ΒͳΔ ϕΫτϧɻ͜ͷܕ͸ɺ৭ͷ੺ɾ྘ɾ੨ɾΞϧϑΝ ஋ͷ֤੒෼ΛΤϯίʔυ͢Δɻ f loat2: 32Ϗοτුಈখ਺఺਺ͷ̎੒෼͔Βͳ ΔϕΫτϧɻ2Dͷ఺΍࣍ݩʹΑ͘࢖ΘΕΔ ʢposition: ϐΫηϧͷҐஔʣɻ

Slide 15

Slide 15 text

Metal Shader Ripple.metalɹ೾໲͔ͩΒsin࢖ͬͯΔ // The ripple is a sine wave that Metal scales by an exponential decay // function. float rippleAmount = amplitude * sin(frequency * time) * exp(-decay * time); // Lighten or darken the color based on the ripple amount and its alpha // component. color.rgb += 0.3 * (rippleAmount / amplitude) * color.a; ৼ෯ʢ೾ͷߴ͞΍ڧ͞ʣ प೾਺ʢ೾ͷ଎͞ɺ೾ͷࡉ͔͞ʣ ݮਰ ೾ͷӨڹʹΑͬͯϐΫηϧͷ৭ͷ໌Δ͕͞มΘΔ͜ͱΛදݱ͍ͯ͠Δɻ RGB͸Ճ๏ࠞ৭Λجʹ͓ͯ͠Γɺ஋͕େ͖͘ͳΔ΄Ͳ৭͕໌Δ͘ͳΔɻ

Slide 16

Slide 16 text

• ೾໲ڧௐ͍ͤͨ͞ͳ͊ɻը૾͡Όͳͯ͘୯ͳ ΔColor. f ill(…)ʹ͠Α͏ɻ • αϯϓϧͰ͸໌Δ͞มߋͯ͠Δ͔Βɺ৭૬Λ มߋͯ͠ΈΑ͏ɻ 🤔

Slide 17

Slide 17 text

࠶ܝ λοϓͨ͠ҐஔΛத৺ʹ೾໲͕޿͕Γ·͢

Slide 18

Slide 18 text

ࠓޙ΍Γ͍ͨ͜ͱ • Metal Shading LanguageͷཧղΛਂΊ͍ͨɻ • ʮMetal Shading Language Speci f icationʯ • https://developer.apple.com/metal/Metal-Shading- Language-Speci f ication.pdf • ଞͷΞΠσΞ΋ग़ͯ͠࡞ͬͯΈ͍ͨɻ • ଞʹͲ͏͍͏͜ͱ͕Ͱ͖Δͷ͔΋ͬͱ஌Γ͍ͨɻ •