夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

Kode lifeで作成したShaderをUnityで再現する。

本日はShader枠です。

今回は[Kode life]で作成したShaderをUnityでも再現していきます。

○Kode lifeとは?

[Kode life]はリアルタイムGPUシェーダーエディタです。

リアルタイムとある通り、Kode lifeのエディタ上で編集したコードがすぐに見たに反映されるエディタです。

Unityなどの場合Shaderを編集してエディタ上で保存して初めてコンパイルが行われ、リアルタイムにシェーダーを確認することはできません。

その点Kode lifeはShaderの勉強や簡単なプロトタイプの作成などにおいて非常に強力なツールです。

hexler.net

○UnityのShaderとの違い

UnityのShaderは独自言語の[Shaderlab]で書かれています。

Shaderlabの内部は[cg/HLSL]言語という別の言語で書かれています。

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

〇Kode lifeのShaderをUnityで再現

[Kode life]を起動するとデフォルトで以下のようにコードが記述されています。

#ifdef GL_ES
precision highp float;
#endif

uniform float time;
uniform vec2 resolution;
uniform vec2 mouse;
uniform vec3 spectrum;

uniform sampler2D texture0;
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform sampler2D texture3;
uniform sampler2D prevFrame;
uniform sampler2D prevPass;

varying vec3 v_normal;
varying vec2 v_texcoord;

void main(void)
{
    vec2 uv = -1. + 2. * v_texcoord;
    gl_FragColor = vec4(
        abs(sin(cos(time+3.*uv.y)*2.*uv.x+time)),
        abs(cos(sin(time+2.*uv.x)*3.*uv.y+time)),
        abs(tan(time+2)),//ここだけデフォルトから値を変えています。
        1.0);
}

f:id:Holomoto-Sumire:20201016085317j:plain

これはGLSL(OpenGL Shading Language)言語で書かれておりUnityで扱うには変換が必要です。

デフォルトのコードではmain関数gl_FragColorvec4型でRGBAが与えられています。

f:id:Holomoto-Sumire:20201016090854g:plain

デフォルトのコードではuvを使用しているほか、別の処理を行っていないシンプルなコードなのでこれをUnityのShaderに移植します。

①Unityで基とする基本的なShaderを用意します

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/Rainbow"
{
    Properties
    {
        [Header(Rainbow)]
        _Power("Power",Range(0.1,10))=1

        _MainTex("Emissive Map", 2D) = "white" {}
    }
        SubShader
        {
            Tags { "LightMode" = "ForwardBase" }

            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"

                struct v2f {
                    float4 pos : SV_POSITION;
                    float2 uv : TEXCOORD0;
                };

                sampler2D _MainTex;
                float4 _MainTex_ST;

                v2f vert(appdata_base v) {
                    v2f o;
                    o.pos = UnityObjectToClipPos(v.vertex);
                    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                    return o;
                }

                fixed4 frag(v2f i) : SV_Target{
              
                     return fixed4(1,1,1, 1);
                }
                ENDCG
            }
        }
}

このShaderではuvを使用してRGBA=(1,1,1,1)の白色が表示されるようなShaderです。

f:id:Holomoto-Sumire:20201016092253j:plain

②Kode lifeのGLSLのmain関数内をUnityのShaderのフラグメントシェーダー内にコピーします。

               fixed4 frag(v2f i) : SV_Target{
     //ここからKode lifeからコピーしたGLSL
                     vec2 uv = -1. + 2. * v_texcoord;
    gl_FragColor = vec4(
        abs(sin(cos(time + 3. * uv.y) * 2. * uv.x + time)),
        abs(cos(sin(time + 2. * uv.x) * 3. * uv.y + time)),
        abs(tan(time + 2)),
        1.0);
     //ここまで
                     return fixed4(1,1,1, 1);
                }

もちろんこのままでは構文が違うのでエラーを出してしまいます。

GLSLをcg/HLSLに変換していきます。

〇GLSLからcg/HLSLへ変換

GLSLの関数をcg/HLSLへ書き換えていきます。

この対応は以下の記事が参考になりました。

qiita.com

①vec2型をfloat2型に変換する

 vec2 uv = -1. + 2. * v_texcoord; →  float2 uv = -1. + 2. * i.uv;

② gl_FragColorをfixed4に変換する

 gl_FragColor(,,,1);=> return fixed4(,,,1);

③gl_FragColorの引数(R,G,B,1)を移植する

return fixed4(abs(sin(cos(time + 3. * uv.y) * 2. * uv.x + time)),abs(cos(sin(time + 2. * uv.x) * 3. * uv.y + time)), abs(tan(time + 2)),1):

この際にtimeでエラーが出ます。 これはcg/HLSLでは時間を扱うtimeは_Timeと表記する必要があるからです。

④timeを_Timeに変換する

    float time = _Time ;
return fixed4(abs(sin(cos(time + 3. * uv.y) * 2. * uv.x + time)),abs(cos(sin(time + 2. * uv.x) * 3. * uv.y + time)), abs(tan(time + 2)),1):

以上で移植が完了しました。 Unityで確認するとkode life同様の見た目になることが確認できます。

f:id:Holomoto-Sumire:20201016090854g:plain
Kode life

f:id:Holomoto-Sumire:20201016094332g:plain
Unity

〇完成したRainbowシェーダー

最後にPowerという変数を加えてUnityのマテリアル側でスピードを調整できるようにしたものが次になります。

Shader "Custom/Rainbow"
{
    Properties
    {
        [Header(Rainbow)]
        _Power("Power",Range(0.1,10))=1
      
        _MainTex("Emissive Map", 2D) = "white" {}

    }
        SubShader
        {
            Tags { "LightMode" = "ForwardBase" }

            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"

                struct v2f {
                    float4 pos : SV_POSITION;
                    float2 uv : TEXCOORD0;
                };

                sampler2D _MainTex;
                float4 _MainTex_ST;

                v2f vert(appdata_base v) {
                    v2f o;
                    o.pos = UnityObjectToClipPos(v.vertex);
                    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                    return o;
                }
                float _Power;

                fixed4 frag(v2f i) : SV_Target{

                     float2 uv = -1. + 2. * i.uv;
                     float time = _Time * _Power;

                     return fixed4(abs(sin(cos(time + 3. * uv.y) * 2. * uv.x + time)), abs(cos(sin(time + 2. * uv.x) * 3. * uv.y + time)), abs(tan(time + 2)), 1);
                }

                ENDCG
            }
        }
}