Edited at
NCCDay 5

MetalとSwiftで遊ぶ「エフェクトとKoara」

この記事はNCC Advent Calendar 2018の5日目の記事です.

アドベントカレンダー内で勝手にやってるMetalの連載も5日目です.


勝手にやってるMetal連載

最近Metalを初めて楽しくなっちゃったのでたくさんoutputしてみる会です.


  1. Rendererを作る

  2. プロシージャルモデリング的な何か

  3. フラグメントシェーダで遊ぶ

  4. テクスチャで遊ぶ

  5. エフェクトで遊ぶ ← イマココ

(そろそろしんどくなってきた)


Koaraにエフェクトをかけて遊ぶぞ!

フラグメントシェーダしか使いません.

他のソースコードはgithubのリポジトリ前回のTextureの記事を参考にしてください


RGBディストーション

やること


  • 赤色成分をx方向にずらす

  • 緑色成分をy方向にずらす

  • ずらす方向はそれぞれ,スクリーンの中心からの距離に比例する

出来上がり

コード


shades.cpp

fragment half4 fragmentDay5(VertexOut vertexIn [[stage_in]],

texture2d<float, access::sample> koaraTexture [[texture(0)]]) {
// 座標変換とそのまま用いたテクスチャ座標
float4 p = (vertexIn.screenCoord+1.0)/2.0;
p.y = 1.0-p.y;
// uniforms
float2 touch = vertexIn.touch;
float time = vertexIn.time;
constexpr sampler nearestSampler(coord::normalized, filter:: nearest);
float4 koara = koaraTexture.sample(nearestSampler, p.xy);
// return half4(koara) // ここからスタート

// RGBディストーション用のコード
// x方向とy方向にずらした座標でサンプリング
float2 d = vertexIn.screenCoord.xy*0.03;
float2 distortion_x = float2(p.x+d.x, p.y);
float2 distortion_y = float2(p.x, p.y+d.y);
float4 koara = koaraTexture.sample(nearestSampler, p.xy);
float4 Koara_distortion_x = koaraTexture.sample(nearestSampler, distortion_x);
float4 Koara_distortion_y = koaraTexture.sample(nearestSampler, distortion_y);
// 合わせて描画
return half4(Koara_distortion_x.x ,Koara_distortion_y.y, koara.z, 1.0);
}



ホワイトノイズ

やること

ノイズを生成して,サンプルした色と掛け合わせます.

使用するノイズは,コピペ伝承のfract-sinノイズです.

出来上がり

コード


shaders.cpp

float rnd(float2 n) {

return fract(sin(dot(n, float2(12.9898,4.1414)))*43758.5453);
}

fragment half4 fragmentDay5(VertexOut vertexIn [[stage_in]],
texture2d<float, access::sample> koaraTexture [[texture(0)]]) {
// 座標変換とそのまま用いたテクスチャ座標
float4 p = (vertexIn.screenCoord+1.0)/2.0;
p.y = 1.0-p.y;
// uniforms
float2 touch = vertexIn.touch;
float time = vertexIn.time;
constexpr sampler nearestSampler(coord::normalized, filter:: nearest);
float4 koara = koaraTexture.sample(nearestSampler, p.xy);

// ホワイトノイズ
// return half4(half3(koara.xyz*rnd(p.xy)),1.0);

// ホワイトノイズで2値化
return half4(half3(koara.xyz*step(0.5, rnd(p.xy))),1.0);

// 砂嵐化
// return half4(half3(koara.xyz*step(0.5, rnd(p.xy+time))),1.0);
}



横にずらす(エフェクトの名前がわからない)

ホワイトノイズで使ったrnd関数を利用して,各textureのrowを同じだけ横にずらします.

説明分かりづらいので,画像を見て

出来上がり


shaders.cpp


float rnd(float2 n) {
return fract(sin(dot(n, float2(12.9898,4.1414)))*43758.5453);
}

fragment half4 fragmentDay5(VertexOut vertexIn [[stage_in]],
texture2d<float, access::sample> koaraTexture [[texture(0)]]) {
// 座標変換とそのまま用いたテクスチャ座標
float4 p = (vertexIn.screenCoord+1.0)/2.0;
p.y = 1.0-p.y;
// uniforms
float2 touch = vertexIn.touch;
float time = vertexIn.time;
constexpr sampler nearestSampler(coord::normalized, filter:: nearest);
float4 koara = koaraTexture.sample(nearestSampler, p.xy);
float2 dx_p = float2(p.x+0.03*(rnd(float2(p.y))*2-1), p.y);
float4 dx_Koara = koaraTexture.sample(nearestSampler, dx_p);

return half4(dx_Koara);
}



ポスタリゼーション

やること

それぞれの画素で赤色成分,緑色成分,青色成分をn段階に分けます.

まあ見ればわかるはず

出来上がり

コード


shaders.cpp

half3 posterization(float3 c, int n) {

return half3(ceil(c.x / (1.0/n)) / n,
ceil(c.y / (1.0/n)) / n,
ceil(c.z / (1.0/n)) / n);
}

fragment half4 fragmentDay5(VertexOut vertexIn [[stage_in]],
texture2d<float, access::sample> koaraTexture [[texture(0)]]) {
// 座標変換とそのまま用いたテクスチャ座標
float4 p = (vertexIn.screenCoord+1.0)/2.0;
p.y = 1.0-p.y;
// uniforms
float2 touch = vertexIn.touch;
float time = vertexIn.time;
constexpr sampler nearestSampler(coord::normalized, filter:: nearest);
float4 koara = koaraTexture.sample(nearestSampler, p.xy);

return half4(posterization(koara.xyz,4),1.0);
}



ソラリゼーション

やること

ソラリゼーションは,トーンカーブを色々したものです.

白黒画像ではなく,赤色成分,緑色成分,青色成分それぞれ独立にソラリゼーションしました,

こんな感じに,部分的に反転します.

出来上がり



こちらからスクショしました.

画像処理検定の参考書にもこのような図がありましたね.

コード


shaders.cpp

half3 solarisation(float3 c) {

return half3((1-cos(3*3.14*c.x))/2,
(1-cos(3*3.14*c.y))/2,
(1-cos(3*3.14*c.z))/2);
}

fragment half4 fragmentDay5(VertexOut vertexIn [[stage_in]],
texture2d<float, access::sample> koaraTexture [[texture(0)]]) {
// 座標変換とそのまま用いたテクスチャ座標
float4 p = (vertexIn.screenCoord+1.0)/2.0;
p.y = 1.0-p.y;
// uniforms
float2 touch = vertexIn.touch;
float time = vertexIn.time;
constexpr sampler nearestSampler(coord::normalized, filter:: nearest);
float4 koara = koaraTexture.sample(nearestSampler, p.xy);

// ソラリゼーション
return half4(solarisation(koara.xyz),1.0);
}



コードはこちら

githubにあります.