タグに「Metal」を持つ
1〜1件目 / 1件
1

 SCNShadable.shaderModifiers の色に要注意 / magicien 

SCNShadable.shaderModifiers でシェーダを書くと、なぜか想定より色が暗くなってしまう。丸一日悩んだ結果、内部で sRGB→RGB の変換がされていることがわかった。
(もしかしたら SceneKitのバージョンによって挙動が違うかもしれないし、macOS/iOS/tvOS/watchOS でそれぞれ挙動が違うことが多々あるので要注意。)

例えば、SCNMaterial の ambient.contents に NSColor(0.5, 0.5, 0.5, 1.0) を設定すると、shaderModifiers の _surface.ambient は float4(0.214, 0.214, 0.214, 1.0) ぐらいの値になる。
逆に、_output.color を float4(0.214, 0.214, 0.214, 1.0) にすると、画面出力は (0.5, 0.5, 0.5, 1.0) になる。
ので、あまり気にしなくても良いかもしれないが、シェーダ内でライトの色と物体の色を掛けたり足したりすると、どんどん値が想定とずれていってしまう…
真面目にやるなら、RGBからsRGBに変換して色を計算、最後にsRGBからRGBに戻すことになる。二度手間だなぁ。
計算式は、Cygames Engineers' Blogを参考に近似式を使ってこんな感じにした。

inline float3 linearToSrgb(float3 c) {
    return pow(c, float3(1.0/2.2));
}

inline float4 linearToSrgb(float4 c) {
    return pow(c, float4(1.0/2.2));
}

inline float3 srgbToLinear(float3 c) {
    return pow(c, float3(2.2));
}

inline float4 srgbToLinear(float4 c) {
    return pow(c, float4(2.2));
}

#pragma arguments

...

#pragma transparent
#pragma body

float4 materialDiffuse = linearToSrgb(_surface.diffuse);
…
float3 lightAmbient = linearToSrgb(_lightingContribution.ambient);
…

_output.color = srgbToLinear(_output.color);
Metalはinline命令使えないと思ったけど使えた。ちゃんとインライン展開されているのかな?
float4の場合はアルファ値を変換しないのが正しい気がする。後でSceneKitの挙動を確認しよう。
SceneKit側は厳密な変換をしているようなので、上の方法だとわずかにずれるけど実用上問題ないと思う。
SCNShadable のページで、自作関数を #pragma arguments の下に書いてある例があるけど、関数は #pragma arguments の前に書かないと、うまく動作しなかった。

2017/08/03(Thu) 00:22:54