Apple Engine

Apple, iPhone, iOS, その周辺のことについて

Xcode 10 の Scene Editor で Metal のカスタムシェーダーを設定する

Xcode の多分 10.1 から Metal のシェーダーファイルを Material Inspetor > Propaties > Shading で設定できるようになった。

f:id:x67x6fx74x6f:20190128191925p:plain

 

内容的には SCNProgram が Scene Editor でできるようになった感じ。

 

Metal のカスタムシェーダーファイルを作成する

新規ファイル(Command + N)で Metal File をつくるか、
.metal の拡張子を持つテキストファイルを作成して以下のものを書く。
内容的にはテクスチャを表示するだけのもの。

#include <metal_stdlib>
using namespace metal;

#include <SceneKit/scn_metal >

// 頂点
struct VertexInput {
    float3 position [[ attribute(SCNVertexSemanticPosition) ]];
    float2 texcoord [[ attribute(SCNVertexSemanticTexcoord0) ]];
};

// モデル
struct NodeBuffer {
    float4x4 modelViewProjectionTransform;
};

// 変数
struct CustomBuffer {
    float4 color;
};

struct VertexOut {
    float4 position [[ position ]];
    float2 texcoord;
    float4 color;
};

vertex VertexOut textureVertex(VertexInput in [[ stage_in ]],
                               constant SCNSceneBuffer& scn_frame [[ buffer(0) ]],
                               constant NodeBuffer& scn_node [[ buffer(1) ]],
                               constant CustomBuffer& custom [[ buffer(2) ]]) {
    VertexOut out;
    out.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);
    out.texcoord = in.texcoord;
    out.color = custom.color;
    return out;
}

fragment float4 textureFragment(VertexOut in [[ stage_in ]],
                               texture2d<float> texture [[ texture(0) ]]) {
    constexpr sampler defaultSampler;
    float4 color;
    color = texture.sample(defaultSampler, in.texcoord) + in.color;
    
    return color;
}

 

こちらを保存すると .metal ファイルを自動的に metallib に変換されて Scene Editor から呼び出せるような振る舞いになる。(と思っている)

 

Scene Editor でつくったカスタムシェーダーを設定してみる

Scene Editor の Material Inspetor (Command + Option + 4) で Shading を Custom にすると
Diffuse、Source File、Vertex Function、Fragment Function と Opaque が設定できるようになる。

f:id:x67x6fx74x6f:20190128191717p:plain

 

よくわからないが次開くと Diffuse の項目が設定できなくなる。謎。

 

先ほど作成した metal を選択すると設定した Vertex Function、Fragment Function が選択できるのだが、「#include <SceneKit/scn_metal>」がインクルードできないとエラーが出てマテリアルがマゼンタピンクで塗りつぶされたままになる。

f:id:x67x6fx74x6f:20190128191801p:plain

 

ということで、SceneKit フレームワークの scn_metal のヘッダーの記述を持っていこう。
「#include <SceneKit/scn_metal>」の部分を以下のコードに変えてみる。
一応、ヘッダの記述全てを書いたが全て使う必要はないのいらないものは消してもよいと思われる、

enum {
    SCNVertexSemanticPosition,
    SCNVertexSemanticNormal,
    SCNVertexSemanticTangent,
    SCNVertexSemanticColor,
    SCNVertexSemanticBoneIndices,
    SCNVertexSemanticBoneWeights,
    SCNVertexSemanticTexcoord0,
    SCNVertexSemanticTexcoord1,
    SCNVertexSemanticTexcoord2,
    SCNVertexSemanticTexcoord3,
    SCNVertexSemanticTexcoord4,
    SCNVertexSemanticTexcoord5,
    SCNVertexSemanticTexcoord6,
    SCNVertexSemanticTexcoord7
};

struct SCNSceneBuffer {
    float4x4    viewTransform;
    float4x4    inverseViewTransform; // transform from view space to world space
    float4x4    projectionTransform;
    float4x4    viewProjectionTransform;
    float4x4    viewToCubeTransform; // transform from view space to cube texture space (canonical Y Up space)
    float4x4    lastFrameViewProjectionTransform;
    float4      ambientLightingColor;
    float4        fogColor;
    float3        fogParameters; // x:-1/(end-start) y:1-start*x z:exp
    float2      inverseResolution;
    float       time;
    float       sinTime;
    float       cosTime;
    float       random01;
    float       motionBlurIntensity;
    // new in macOS 10.12 and iOS 10
    float       environmentIntensity;
    float4x4    inverseProjectionTransform;
    float4x4    inverseViewProjectionTransform;
    // new in macOS 10.13 and iOS 11
    float2      nearFar; // x: near, y: far
    float4      viewportSize; // xy:size, zw:origin
    // new in macOS 10.14 and iOS 12
    float4x4    inverseTransposeViewTransform;
    
    // internal, DO NOT USE
    float4      clusterScale; // w contains z bias
};

 

これで動くかと思ったが、Diffuse が消えてしまうため外部からテクスチャを取る手段がなくテクスチャは割り当てられない。
ドキュメントがないのでバグなのか仕様なのかわからない。

f:id:x67x6fx74x6f:20190128192042p:plain

 

諦めて Fragment Shader だけ設定してみる

ということで、書き直ししてみることにする。
フラグメントの return で float4(1.0, 0.0, 0.0, 1.0) に設定しているためマテリアルが真っ赤になる。

fragment float4 textureFragment(VertexOut in [[ stage_in ]],
                               texture2d<float> texture [[ texture(0) ]]) {
//    constexpr sampler defaultSampler;
//    float4 color;
//    color = texture.sample(defaultSampler, in.texcoord) + in.color;
    
    return float4(1.0, 0.0, 0.0, 1.0);
}

 

f:id:x67x6fx74x6f:20190128192208p:plain

 

ちなみに SCNSceneBuffer で定義されているものは使用可能なので呼び出してみる。

以下の VertexOut の構造体と vertex、fragment を修正。
再生ボタンを押したり、カメラ移動させたりすると Scene Editor のフレームがすすみランダムで赤く点滅する。

struct VertexOut {
    float4 position [[ position ]];
    float2 texcoord;
    float4 color;
    float random01;
};

vertex VertexOut textureVertex(VertexInput in [[ stage_in ]],
                               constant SCNSceneBuffer& scn_frame [[ buffer(0) ]],
                               constant NodeBuffer& scn_node [[ buffer(1) ]],
                               constant CustomBuffer& custom [[ buffer(2) ]]) {
    VertexOut out;
    out.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);
    out.texcoord = in.texcoord;
    out.color = custom.color;
    out.random01 = scn_frame.random01;
    return out;
}

fragment float4 textureFragment(VertexOut in [[ stage_in ]],
                               texture2d<float> texture [[ texture(0) ]]) {
//    constexpr sampler defaultSampler;
//    float4 color;
//    color = texture.sample(defaultSampler, in.texcoord) + in.color;
    
    VertexOut out = in;
    float random = out.random01;
    
    return float4(random, 0.0, 0.0, 1.0);
}

 

サンプルファイル

github.com

 

まとめ

軽く触った感じではテクスチャの設定方法がわからないが、Metal シェーダーを Scene Editor で試すことができるため、わざわざビルドしなくても済む。

動作チェックが手軽になるかと思われる。