SpriteKitのブレンドモード(alpha & blendMode プロパティとシェーダー)
はしがき
『GLSLシェーダーで遊ぼう with SwiftUI × SpriteKit』シリーズの 第5回の記事 で説明を省略した、ノードの色の透過(アルファブレンド)を中心としたブレンドモードについてと、SKShaderで半透明な色を計算・出力する方法についてまとめたいと思います。
環境
以下の環境で動作確認を行っています。
- Xcode 16.2 (16C5032a)
- iOS Deployment Target 15.0
- Apple Swift version 6.0.3 (swiftlang-6.0.3.1.10 clang-1600.0.30.1)
- Swift Playgrounds 4.6.3 (*)
- macOS Sonoma 14.7.3
- MacBook Air M2 2022
* XcodeのPlayground(PlaygroundSupport)では動作しません
貼付しているスクリーンショットはシミュレータの iPhone 13(iOS 15.5)で撮影したものをベースにしています。
ノードの色のブレンドに関連するプロパティ
ノードの色の表示について、背後の色とのブレンド(合成)に関連するプロパティには次のようなものがあります。
プロパティ名 | 概要 |
---|---|
alpha | ノードの透明度を指定できます |
blendMode | ノードの色とノードの背後にある色をどのような方法で合成するか指定できます このプロパティは SKSpriteNode , SKShapeNode , SKEmitterNode (particleBlendMode) , SKLabelNode SKTileMapNode , SKEffectNode のノードが対応しています |
color | ノードの色を指定できます 指定した UIColor (または SKColor 。以降、UIColorのみ例として記載)のRGB値とあわせて、A値が合成に影響します |
texture | ノードに貼るテクスチャを指定できます テクスチャ画像の色のRGB値とあわせて、A値が合成に影響します |
colorBlendFactor | テクスチャの色に対して、colorプロパティの色を混ぜる割合を指定できます colorプロパティで指定したUIColorのA値も混ぜる対象になるため合成に影響します [1] |
本セクションでは alpha
と blendMode
のプロパティについて説明します。
color
, texture
, colorBlendFactor
の3プロパティの基本については 以前の記事 でまとめていますので、適宜ご参照ください。
alpha プロパティ
ノードの alpha プロパティで、 ノードの透明度 を指定できます。指定した値に応じてノードの色とノードの背後にある色が合成され、透明(0.0)〜半透明〜不透明(1.0)に見える色でノードが表示されます。デフォルト値は 1.0
(不透明)です。
alpha
プロパティの値が異なる SKSpriteNode を並べて比べてみましょう。
上のスクリーンショットでは、白色のシーンに以下のようなノードを配置しています。
<背後にある縦長のノード>
-
color
プロパティで 青色 を指定する
backNode.color = UIColor(red: 0.2, green: 0.5, blue: 1.0, alpha: 1.0)
<前面の横長のノード ×7>
-
color
プロパティで オレンジ色 を指定する
frontNode.color = UIColor(red: 1.0, green: 0.4, blue: 0.1, alpha: 1.0)
-
alpha
プロパティで 透明度 を指定する(上側のノードから下側のノードへ徐々に値を小さくする)
frontNode.alpha = <1.0 〜 0.0>
alpha
プロパティの値が小さいほど前面のノードのオレンジ色が薄くなり、背後のノードの青色が透けて見える量が多くなっています。
計算式
出力される色は次の式で計算できます。 [2]
先ほどのスクリーンショットにある alpha = 0.7
の例でRGBA値を計算してみると、次のようになります。 [3]
RGBAの種類 | 前面の ノードの色 |
背後の ノードの色 |
計算 |
---|---|---|---|
計算の内容を簡単にまとめると、オレンジ色の70%分と青色の30%分を合計した色がノードに表示される色になっています。
サンプルコード
alphaプロパティのサンプルコード
import SwiftUI
import SpriteKit
struct ContentView: View {
var currentScene: SKScene {
let scene = MySKScene()
scene.scaleMode = .resizeFill
return scene
}
var body: some View {
SpriteView(scene: self.currentScene)
.ignoresSafeArea()
.statusBarHidden()
}
}
class MySKScene: SKScene {
override func didMove(to view: SKView) {
self.backgroundColor = .white
self.anchorPoint = CGPoint(x: 0.5, y: 1.0)
// 透明度の値の配列を用意します
let alphaValues = [1.0, 0.9, 0.7, 0.5, 0.3, 0.1, 0.0]
// 青色のノードを作成して配置します
let backNode = {
let node = SKSpriteNode()
node.size = CGSize(
width: self.frame.width * 0.86,
height: self.frame.height
)
node.anchorPoint = CGPoint(x: 0.5, y: 1.0)
node.color = UIColor(red: 0.2, green: 0.5, blue: 1.0, alpha: 1.0)
return node
}()
self.addChild(backNode)
// オレンジ色のノードの基本形(テンプレ用)を作成します
let frontNode = {
let node = SKSpriteNode()
node.size = CGSize(
width: self.frame.width,
height: self.frame.height * 0.9 / CGFloat(alphaValues.count)
)
node.anchorPoint = CGPoint(x: 0.5, y: 1.0)
node.position.y = self.frame.height * -0.05
node.color = UIColor(red: 1.0, green: 0.4, blue: 0.1, alpha: 1.0)
return node
}()
// オレンジ色のノードを、配列で用意した透明度ごとに作成し
// 上から下へ順番に並べて配置します
for (i, alphaValue) in alphaValues.enumerated() {
let node = frontNode.copy() as! SKSpriteNode
node.position.y -= node.frame.height * CGFloat(i)
// ノードの透明度を指定します
node.alpha = alphaValue
self.addChild(node)
}
}
}
alpha プロパティの子ノードへの伝播
alpha
プロパティは、親ノードに指定した値が 子ノードにも伝播して適用されます 。計算上は、子ノードの alpha
プロパティの値に親ノードの alpha
プロパティの値が掛け算されることになります。
たとえば、親ノード、子ノード、孫ノードを配置して、それぞれの alpha
プロパティを 0.8
、 0.6
、 0.7
と指定したとします。
// 親ノード
let parentNode = SKSpriteNode(color: .white, size: self.frame.size)
parentNode.alpha = 0.8
self.addChild(parentNode)
// 子ノード
let childNode = SKSpriteNode(color: .white, size: self.frame.size)
childNode.alpha = 0.6
parentNode.addChild(childNode)
// 孫ノード
let grandchildNode = SKSpriteNode(color: .white, size: self.frame.size)
grandchildNode.alpha = 0.7
childNode.addChild(grandchildNode)
この場合、各ノードの色と背後にある色との合成で使われる
ノード | alpha プロパティ |
ノードの色と背後にある色との 合成で使われる |
---|---|---|
親ノード | ||
子ノード | ||
孫ノード |
になります。
blendMode プロパティ
ノードの blendMode プロパティで、 ノードの色とノードの背後にある色との混ぜかた を指定できます。
プロパティに指定できる SKBlendMode は全部で8種類あります。デフォルト値は SKBlendMode.alpha
です。
種類 | 概要 |
---|---|
alpha | ノードの色と背後の色をノードのalpha値に基づいて足します 背後の色が透けているような色になります |
add | 背後の色にノードの色を足します |
subtract | 背後の色からノードの色を引きます |
multiply | ノードの色と背後の色を掛けます もとの色と同じか、より暗い色になります |
multiplyX2 | ノードの色と背後の色を掛けて、それを2倍します |
screen | ノードの色の反転色(1−ノードの色)と背後の色を掛けて、その色をノードの色と足します multiplyと対になるモードで、もとの色と同じか、より明るい色になります |
replace | ノードの色を表示します alpha値が1.0未満であっても背後の色は混ざりません |
multiplyAlpha | multiplyとalphaを組み合わせたモードです ノードの色と背後の色を掛けて(multiply)、その色と背後の色をノードのalpha値に基づいて足します(alpha) |
blendMode
プロパティの値が異なる SKSpriteNode を並べて比べてみましょう。
alpha = 1.0 | alpha = 0.4 |
---|---|
![]() |
![]() |
上のスクリーンショットでは、白色のシーンに以下のようなノードを配置しています。
<背後にある縦長のノード>
-
color
プロパティで 青色 を指定する
backNode.color = UIColor(red: 0.2, green: 0.5, blue: 1.0, alpha: 1.0)
<前面の横長のノード ×8>
-
color
プロパティで オレンジ色 を指定する
frontNode.color = UIColor(red: 1.0, green: 0.4, blue: 0.1, alpha: 1.0)
-
blendMode
プロパティで 合成方法 を指定する
frontNode.blendMode = <各ブレンドモード>
-
alpha
プロパティで 透明度 を指定する
frontNode.alpha = 1.0
(左側のスクリーンショット)
frontNode.alpha = 0.4
(右側のスクリーンショット)
計算式
出力される色はそれぞれ次の式で計算できます。 [2:1]
種類 | ※計算式 |
---|---|
alpha | |
add |
※加算後のRGBA値が1を上回った場合は1に切り捨てられます |
subtract |
※引く側のノードのRGB値は 乗算済みアルファでalpha値を掛けた上に、さらにalpha値を掛けたもの になります ※減算後のRGB値が0を下回った場合は0に切り上げられます |
multiply | |
multiplyX2 |
※乗算後のRGB値が1を上回った場合は1に切り捨てられます |
screen | |
replace | |
multiplyAlpha |
サンプルコード
blendModeプロパティのサンプルコード
import SwiftUI
import SpriteKit
struct ContentView: View {
var currentScene: SKScene {
let scene = MySKScene()
scene.scaleMode = .resizeFill
return scene
}
var body: some View {
SpriteView(scene: self.currentScene)
.ignoresSafeArea()
.statusBarHidden()
}
}
class MySKScene: SKScene {
override func didMove(to view: SKView) {
self.backgroundColor = .white
self.anchorPoint = CGPoint(x: 0.5, y: 1.0)
// ブレンドモードの値の配列を用意します
let blendModeValues: [SKBlendMode] = [
.alpha,
.add,
.subtract,
.multiply,
.multiplyX2,
.screen,
.replace,
.multiplyAlpha
]
// 青色のノードを作成して配置します
let backNode = {
let node = SKSpriteNode()
node.size = CGSize(
width: self.frame.width * 0.86,
height: self.frame.height
)
node.anchorPoint = CGPoint(x: 0.5, y: 1.0)
node.color = UIColor(red: 0.2, green: 0.5, blue: 1.0, alpha: 1.0)
return node
}()
self.addChild(backNode)
// オレンジ色のノードの基本形(テンプレ用)を作成します
let frontNode = {
let node = SKSpriteNode()
node.size = CGSize(
width: self.frame.width,
height: self.frame.height * 0.9 / CGFloat(blendModeValues.count)
)
node.anchorPoint = CGPoint(x: 0.5, y: 1.0)
node.position.y = self.frame.height * -0.05
node.color = UIColor(red: 1.0, green: 0.4, blue: 0.1, alpha: 1.0)
// ノードの透明度を指定します
node.alpha = 0.4
return node
}()
// オレンジ色のノードを、配列で用意したブレンドモードごとに作成し
// 上から下へ順番に並べて配置します
for (i, blendModeValue) in blendModeValues.enumerated() {
let node = frontNode.copy() as! SKSpriteNode
node.position.y -= node.frame.height * CGFloat(i)
// ノードのブレンドモードを指定します
node.blendMode = blendModeValue
self.addChild(node)
}
}
}
シェーダーで半透明な色を出力
gl_FragColor に格納する色
ノードにシェーダーを適用して半透明な色を出力したいとき、 gl_FragColor.a
に格納する 透明度の値だけを指定しても期待する色にならない ということを意識しておきましょう。透明度だけでなく gl_FragColor.rgb
も前述の 乗算済みアルファ 方式の値にして格納する必要があります。
これもスクリーンショットを並べて比べてみます。
1. プロパティで色を指定 (期待する表示) |
2. シェーダーで色を出力 (乗算済みアルファにしない) |
3. シェーダーで色を出力 (乗算済みアルファにする) |
---|---|---|
![]() |
![]() |
![]() |
上のスクリーンショットでは、白色のシーンに以下のようなノードを配置しています。
<背後のノード>
-
color
プロパティで 青色 を指定する
backNode.color = UIColor(red: 0.2, green: 0.5, blue: 1.0, alpha: 1.0)
<前面のノード>
- 3つのスクリーンショットの左から順に、次の方法で 透明度0.7のオレンジ色 を表示する
- ノードのプロパティでcolorとalphaの値を指定(期待する表示)
frontNode.color = UIColor(red: 1.0, green: 0.4, blue: 0.1, alpha: 1.0)
frontNode.alpha = 0.7
- シェーダーで色を出力(期待する表示と異なる例)
- オレンジ色のRGB値のままで、A値のみ透明度の0.7にする
gl_FragColor = vec4(1.0, 0.4, 0.1, 0.7);
- シェーダーで色を出力(期待する表示と同じになる例)
- オレンジ色のRGB値に透明度の0.7を掛けて 乗算済みアルファ 方式の値(red: 0.7, green: 0.28, blue: 0.07, alpha: 0.7)にする
gl_FragColor = vec4(1.0, 0.4, 0.1, 1.0) * 0.7;
- ノードのプロパティでcolorとalphaの値を指定(期待する表示)
gl_FragColor
変数に格納する色を乗算済みアルファにしなかった2番目のスクリーンショットでは、背後の色と合成した結果、
の色が表示されており、期待する表示である
の色よりも明るいオレンジ色になっています。 [3:1]
サンプルコード
シェーダーで半透明な色を出力するサンプルコード
import SwiftUI
import SpriteKit
struct ContentView: View {
var currentScene: SKScene {
let scene = MySKScene()
scene.scaleMode = .resizeFill
return scene
}
var body: some View {
SpriteView(scene: self.currentScene)
.ignoresSafeArea()
.statusBarHidden()
}
}
class MySKScene: SKScene {
override func didMove(to view: SKView) {
self.backgroundColor = .white
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
// 青色のノードを作成して配置します
let backNode = {
let node = SKSpriteNode()
node.size = CGSize(
width: self.frame.width * 0.86,
height: self.frame.height
)
node.color = UIColor(red: 0.2, green: 0.5, blue: 1.0, alpha: 1.0)
return node
}()
self.addChild(backNode)
// オレンジ色のノードを作成して配置します
let frontNode = {
let node = SKSpriteNode()
node.size = CGSize(
width: self.frame.width,
height: self.frame.height * 0.8
)
// 透明度0.7のオレンジ色をシェーダーで出力します
node.shader = SKShader(source: """
void main() {
vec4 orange = vec4(1.0, 0.4, 0.1, 1.0);
float alpha = 0.7;
gl_FragColor = orange * alpha;
}
""")
return node
}()
self.addChild(frontNode)
}
}
ノードの色の取得に関連する組み込み変数と関数
ノードの色が取得できる SKShaderの組み込み変数と関数 が3種類あります。
変数名 / 関数名 | 概要 |
---|---|
u_texture | ノードのtextureプロパティに格納されたテクスチャに紐づくサンプラーです |
v_color_mix | ノードのcolorプロパティの色にcolorBlendFactorプロパティの比率が反映された色の値です alphaプロパティの指定がある場合はその値も反映されます |
SKDefaultShading() | ノードがデフォルトで表示する色が得られる関数です |
これらの変数および関数の基本は 以前の記事 でも説明していますので、適宜ご参照ください。
本記事では、取得できる色の値と透明度について説明します。
u_texture 変数
u_texture
変数を介してノードに貼られたテクスチャの色を取得することができます。
この色の値はノードの alpha
プロパティの影響を受けません。 テクスチャデータのRGBA値がそのまま返ってきます 。
画像データに半透明な色が含まれている場合は、 SKTexture(imageNamed:) や SKTexture(image:) のようなイニシャライザでSKTextureオブジェクトを作成した時点で 自動的に乗算済みアルファになる ので、 u_texture
で取得できる色も乗算済みアルファになっています。 [4]
v_color_mix 変数
v_color_mix
変数では、ノードの color
プロパティで指定した色に、 colorBlendFactor
プロパティで指定したブレンド比率を反映させた色を取得できます。
この色は、RGB値にalpha値を掛けた 乗算済みアルファ 方式の値になっています 。
計算式
v_color_mix
で取得できる色がどのように計算されているのかを、 color
, alpha
, colorBlendFactor
のプロパティを順番に追加しながら見てみます。 [2:2]
ノードにcolorプロパティのみ指定した場合
ノードの color
プロパティで半透明な色を指定したときは、次のような計算の結果が v_color_mix
の値になります。
※ 計算式中の color
プロパティで指定したUIColorのRGBA値です。
color
プロパティで指定したUIColorのA値が、RGB値に反映されます。
ノードにcolorプロパティとalphaプロパティを指定した場合
color
プロパティに加えて alpha
プロパティも指定したときは、次のような計算の結果が v_color_mix
の値になります。
※ 計算式中の alpha
プロパティの値です。親ノードからの alpha
プロパティの伝播を反映した状態です。
alpha
プロパティの値が反映され、そのalpha値を使ってRGB値の乗算済みアルファの計算が行われます。
ノードにcolorプロパティ、alphaプロパティ、colorBlendFactorプロパティを指定した場合
さらに、ノードにテクスチャを貼った上で colorBlendFactor
プロパティを指定すると、次のような計算の結果が v_color_mix
の値になります。
※ノードにテクスチャを貼っていない場合は colorBlendFactor
プロパティで指定した値は無視されます。
※ 計算式中の colorBlendFactor
プロパティの値です。
colorBlendFactor
プロパティの値を使って 1.0
と RGBA値
とのあいだでの線形補間が行われます。そして線形補間後のRGBA値にalpha値を掛けて、乗算済みアルファの計算をします。
具体的な値で確認してみましょう。ノードに以下のプロパティを指定する例で考えます。
frontNode.texture = <任意のSKTextureオブジェクト>
frontNode.color = UIColor(red: 1.0, green: 0.4, blue: 0.1, alpha: 0.9)
frontNode.colorBlendFactor = 0.4
frontNode.alpha = 0.7
frontNode.blendMode = .replace
blendMode
プロパティで .replace
を指定しているので、前面のノードの半透明な色が、背後の緑色とは合成されずにそのまま表示されている状態です。
各プロパティの値をさきほどの計算式に当てはめると、次のようになります。 [3:2]
サンプルコード
v_color_mixの色を出力するサンプルコード
import SwiftUI
import SpriteKit
struct ContentView: View {
var currentScene: SKScene {
let scene = MySKScene()
scene.scaleMode = .resizeFill
return scene
}
var body: some View {
SpriteView(scene: self.currentScene)
.ignoresSafeArea()
.statusBarHidden()
}
}
class MySKScene: SKScene {
override func didMove(to view: SKView) {
self.backgroundColor = .white
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
// 緑色のノードを作成して配置します
let backNode = {
let node = SKSpriteNode()
node.size = CGSize(
width: self.frame.width * 0.86,
height: self.frame.height
)
node.color = UIColor(red: 0.6, green: 0.8, blue: 0.4, alpha: 1.0)
return node
}()
self.addChild(backNode)
// オレンジ色のノードを作成して配置します
let frontNode = {
let node = SKSpriteNode()
node.size = CGSize(
width: self.frame.width,
height: self.frame.height * 0.8
)
// 白色のデータオブジェクトから
// テクスチャオブジェクトを作成してノードに適用します
node.texture = SKTexture(
data: Data([UInt8](repeating: 255, count: 4)),
size: CGSize(width: 1, height: 1)
)
node.color = UIColor(red: 1.0, green: 0.4, blue: 0.1, alpha: 0.9)
node.colorBlendFactor = 0.4
node.alpha = 0.7
node.blendMode = .replace
// v_color_mixの色をシェーダーで出力します
node.shader = SKShader(source: """
void main() {
gl_FragColor = v_color_mix;
}
""")
return node
}()
self.addChild(frontNode)
}
}
SKDefaultShading 関数
u_texture
変数を介して取得したテクスチャの色に v_color_mix
変数で取得した色を掛けると、 texture
, color
, colorBlendFactor
, alpha
の4プロパティを合わせた色が算出できます。
SKShaderの組み込み関数 SKDefaultShading()
で取得できるのがこの値で、ノードがデフォルトで表示する色になります。
取得したノードの色を使った計算
SKDefaultShading()
などの組み込み関数や変数で取得した色をもとにシェーダーでRGB値を計算するときも、 乗算済みアルファ 方式の値になっていること考慮しないと目的の色を作れません。
ノードの色を反転させる例で比べてみましょう。
1. CIColorInvertフィルタで 色を反転 (期待する表示) |
2. シェーダーで色を反転 (乗算済みアルファのまま 計算) |
3. シェーダーで色を反転 (ストレートアルファ [5] に してから計算) |
---|---|---|
![]() |
![]() |
![]() |
上のスクリーンショットでは、白色のシーンに以下のようなノードを配置しています。
<背後のノード>
-
color
プロパティで 緑色 を指定する
backNode.color = UIColor(red: 0.6, green: 0.8, blue: 0.4, alpha: 1.0)
<前面のノード>
-
texture
プロパティで 透明度 0.8の灰色のテクスチャ を指定する
frontNode.texture = <UIColor(white: 0.6, alpha: 0.8) の色で塗りつぶしたSKTextureオブジェクト>
-
color
プロパティで 透明度 0.9のオレンジ色 を指定する
frontNode.color = UIColor(red: 1.0, green: 0.4, blue: 0.1, alpha: 0.9)
-
colorBlendFactor
プロパティで テクスチャの色にオレンジ色を0.4の割合で混ぜる
frontNode.colorBlendFactor = 0.4
-
alpha
プロパティで 透明度 0.7 を指定する
frontNode.alpha = 0.7
- 3つのスクリーンショットの左から順に、次の方法でノードの色を反転する
- CIFilter の CIColorInvert フィルタで色を反転(期待する表示)
- 前面のノードの親ノードとして SKEffectNode を配置し、 filter プロパティでフィルタを適用する
effectNode.addChild(frontNode)
effectNode.filter = CIFilter(name: "CIColorInvert")
- シェーダーで色を出力(期待する表示と異なる例)
- 乗算済みアルファのRGB値のままで反転色を計算する
gl_FragColor = vec4(1.0 - SKDefaultShading().rgb, 1.0) * SKDefaultShading().a;
- シェーダーで色を出力(期待する表示と同じになる例)
- 乗算済みアルファのRGB値をA値で割り、ストレートアルファ [5:1] の値にしてから反転色を計算する
gl_FragColor = vec4(1.0 - SKDefaultShading().rgb / SKDefaultShading().a, 1.0) * SKDefaultShading().a;
= vec4(SKDefaultShading().a - SKDefaultShading().rgb, SKDefaultShading().a);
乗算済みアルファのRGB値のままで反転色を計算した2番目のスクリーンショットでは、背後の色と合成した結果、
の色が表示されており、期待する表示である
の色よりも淡くなっています。 [3:3]
サンプルコード
SKDefaultShading関数を使うサンプルコード
import SwiftUI
import SpriteKit
struct ContentView: View {
var currentScene: SKScene {
let scene = MySKScene()
scene.scaleMode = .resizeFill
return scene
}
var body: some View {
SpriteView(scene: self.currentScene)
.ignoresSafeArea()
.statusBarHidden()
}
}
class MySKScene: SKScene {
override func didMove(to view: SKView) {
self.backgroundColor = .white
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
// 緑色のノードを作成して配置します
let backNode = {
let node = SKSpriteNode()
node.size = CGSize(
width: self.frame.width * 0.86,
height: self.frame.height
)
node.color = UIColor(red: 0.6, green: 0.8, blue: 0.4, alpha: 1.0)
return node
}()
self.addChild(backNode)
// オレンジ色のノードを作成して配置します
let frontNode = {
let node = SKSpriteNode()
node.size = CGSize(
width: self.frame.width,
height: self.frame.height * 0.8
)
// 透明度0.8の60%グレーで塗りつぶしたUIImageオブジェクトから
// テクスチャオブジェクトを作成してノードに適用します
node.texture = SKTexture(
image: UIGraphicsImageRenderer(
size: node.size
).image { context in
context.cgContext.setFillColor(
UIColor(white: 0.6, alpha: 0.8).cgColor
)
context.fill(CGRect(origin: .zero, size: node.size))
}
)
node.color = UIColor(red: 1.0, green: 0.4, blue: 0.1, alpha: 0.9)
node.colorBlendFactor = 0.4
node.alpha = 0.7
// ノードの色の反対色をシェーダーで出力します
node.shader = SKShader(source: """
void main() {
vec3 premultiplied = SKDefaultShading().rgb;
float alpha = SKDefaultShading().a;
vec3 unpremultiplied = premultiplied / alpha;
vec3 inverted = vec3(1.0 - unpremultiplied);
gl_FragColor = vec4(inverted, 1.0) * alpha;
}
""")
return node
}()
self.addChild(frontNode)
}
}
参考リンク
- Blending a Sprite with Different Interpretations of Alpha | Apple Developer Documentation
- Premultiplied alpha - TomF's Tech Blog
- Inigo Quilez :: articles :: premultiplied alpha - 2022
- Premultiplied alpha - Windows apps | Microsoft Learn
- Alpha Blending: To Pre or Not To Pre | NVIDIA Developer
- Compositing and Blending Level 1 - W3C
- Blending modes in Adobe Photoshop - Adobe Help Center
- Chapter 8. Combining Images - GNU Image Manipulation Program User Manual
- D3DTEXTUREOP enumeration (D3D9Types.h) - Win32 apps | Microsoft Learn
- Drawing SpriteKit Content in a View | Apple Developer Documentation
- Applying Shaders to a Sprite | Apple Developer Documentation
- Creating a Custom Fragment Shader | Apple Developer Documentation
- Loading and Using Textures | Apple Developer Documentation
- Tinting a Sprite | Apple Developer Documentation
- About Node Drawing Order | Apple Developer Documentation
- The Book of Shaders
- OpenGL ES 2.0 API Quick Reference Card (PDF)
-
colorプロパティの公式ドキュメントでは テクスチャが貼られているときはalphaプロパティは無視される と記載されています。しかし筆者の環境では、colorBlendFactorプロパティの指定に従ってUIColorのA値がブレンドされており、alphaプロパティの指定も反映されていました。 ↩︎
-
計算式は、Appleの公式ドキュメント、一般的なブレンディングの計算方法、SpriteKitのヘッダファイルのコメントや実動作、Metalワークロードなどから推測したものであり、フレームワークの実際の処理とは異なる場合があります。また、説明をわかりやすくシンプルにするため計算のわけかたや順序を調整しています。 ↩︎ ↩︎ ↩︎
-
SKTexture(data:size:) などでRAWピクセルデータからSKTextureオブジェクトを作成する場合は、公式ドキュメントにも記載されているとおり、あらかじめ乗算済みアルファにしたRGBA値でピクセルデータを用意しないといけません。 ↩︎
-
RGB値にalpha値を事前に掛け算しておく乗算済みアルファとは逆に、事前に乗算しない方式を ストレートアルファ と呼びます。 ↩︎ ↩︎
Discussion