C++幼女先輩

プログラミング成分多め

大逆転裁判

仕事をさぼったわけじゃないのだよ ワトソン君

本日は、従業員へのタスク割り振りやタスクの説明やら、管理的なことを行って お客さんとの受け答えを

その合間に 大逆転裁判

恥ずかしながら 逆転裁判シリーズは軽く動画見ただけで、初プレイだったのだけど 有名作品はかならずプレイしなければならない ゲームプログラマ故 やっとプレイしクリアした

アドベンチャーとも違う、推理ゲームとも違う ちょっと独特なかんじかなー

他にも有名ゲームプレイしていきたいです

UNITY ScriptableRenderLoop アウトラインのミス

前回調べた時に ポストエフェクトのアウトラインが入っていた・・・

EdgeDetectionというやつだ なので、Unity-chanシェーダーのアウトラインではなく ポストエフェクトのアウトラインが入っていた f:id:murasame-labo:20170627035739p:plain たしかこれはDepthや法線でアウトラインを出すタイプだったと思う

ということで、EdgeDetectionを切ったものを出力

f:id:murasame-labo:20170628025650p:plain

これえで 正しいアウトラインが出た

しかし ScriptableRenderLoopを使ったものは、上記のチェックを外しても 変わらずだった

UNITY EdgeDetection

ふと気になった 先ほど出力した画像のSphereに アウトラインが付いている・・・・・ なぜだ??

カメラに EdgeDetectionってやつがいた! こいつ怪しい! f:id:murasame-labo:20170627035739p:plain

ってことで こいつをOFFしたら、Sphereのアウトラインが消え Unity-Chanのアウトラインが、カラー反映したいいものになった

元々作っていたアウトラインの上に 黒で上書きされていたようだ・・

Unity - マニュアル: エッジ検出

アウトラインを付けるポストプロセスのようだ。

ってことで、アウトライン調査やり直しである

ScriptableRenderLoopでUNITY Chan表示

環境

ベータ版の機能なので EditorをUNITY2017のβを使います。今回は b10です

サンプル

GitHub - Unity-Technologies/ScriptableRenderLoop: Scriptable Render Loop 個々のサンプルを落とす 多分コンパイルエラー出るので、エラー出る所をつぶせばなんとかなるはず

UnityちゃんPluginをいれる

Unityちゃんと、Unity-Chan Toon2.0を入れてみましょう GUIが邪魔なので IdleChangeとFaceのスクリプトを無効にし

通常のレンダリングパイプライン

デフォルトの状態。 Edit->ProjectSettings->Graphicsの ScriptableRenderLoopを Noneにする カメラもForwardになっている事を確認

f:id:murasame-labo:20170627030132p:plain

正しく表示されてます

BasicRenderPipeline

上記の設定を BasicRenderPipelineに変更すると

f:id:murasame-labo:20170627030256p:plain

何も表示されなくなりました これは、Shaderパス名が違うからです。

今までのシェーダーではForwardの場合は ForwardBase というパスが実行されますが このRenderLoopでは BasicPass というパスを実行しているので その部分を修正してみる

BasicRenderPipeline.cs#Render が、実際のパイプライン

    public static void Render(ScriptableRenderContext context, IEnumerable<Camera> cameras)
    {
        foreach (var camera in cameras)
        {
            // Culling
            CullingParameters cullingParams;
            if (!CullResults.GetCullingParameters(camera, out cullingParams))
                continue;
            CullResults cull = CullResults.Cull(ref cullingParams, context);

            // Setup camera for rendering (sets render target, view/projection matrices and other
            // per-camera built-in shader variables).
            context.SetupCameraProperties(camera);

            // clear depth buffer
            var cmd = new CommandBuffer();
            cmd.ClearRenderTarget(true, false, Color.black);
            context.ExecuteCommandBuffer(cmd);
            cmd.Release();

            // Setup global lighting shader variables
            SetupLightShaderVariables(cull.visibleLights, context);

            // Draw opaque objects using BasicPass shader pass
//            var settings = new DrawRendererSettings(cull, camera, new ShaderPassName("BasicPass"));
//          パス名を変更する
            var settings = new DrawRendererSettings(cull, camera, new ShaderPassName("ForwardBase"));
            settings.sorting.flags = SortFlags.CommonOpaque;
            settings.inputFilter.SetQueuesOpaque();
            context.DrawRenderers(ref settings);

            // Draw skybox
            context.DrawSkybox(camera);

            // Draw transparent objects using BasicPass shader pass
            settings.sorting.flags = SortFlags.CommonTransparent;
            settings.inputFilter.SetQueuesTransparent();
            context.DrawRenderers(ref settings);

            context.Submit();
        }
    }

f:id:murasame-labo:20170627031316p:plain

なんとなく表示された。

ライティングが元と違う。原因調査中だけどライティングの計算式が違うのかな?

アウトラインが無くなった。 これに関しては デフォルトでは2パスでアウトラインを表示しています(オブジェクト拡大法で) 通常のレンダリングとは別に アウトラインのパスを暗黙的に実行します ところが ScriptableRenderLoopでは パスは明示的にしか指定できないため Outlineパスが呼ばれない ので、スクリプトと 先ほどのRenderを修正しなければならない

ScriptableRenderLoop概要

今までは情報がある程度まとまってから書いてたけど 試しに気にせずに毎日でも書くことにしたよ すぐ飽きるけど

ScriptableRenderLoopとは

UNITY2017から入る機能の1つで、現在はベータ版でお試しが可能 今までブラックボックスだった UNITYのレンダリングパイプラインをスクリプトでカスタマイズできる機能 具体的に何ができるかといわれると 難しいけど、G-Bufferとかライト計算とかいろいろと自由にできる

今までのRenderingPipeline

今までは レンダリングモードが Forwardの時は ForwardBase、Deferredは Deferred が実行されていた Unity - マニュアル: Unity のレンダリングパイプライン マルチパスは シェーダーにて 複数パスを書いて 暗黙的に実行させる ライトモードにより、実行するシェーダーパスは固定であり レンダリングパイプラインは変更する事が不可能

グラフィックスコマンドバッファ

Unity - マニュアル: グラフィックスコマンドバッファ

拡張され、指定されたタイミングで任意のGPU命令を実行させることが可能になった 上記の図の緑色の地点に コールバック関数を差し込むことが出来る 例えば ポストプロセスを行う時によく使われる

ただし、レンダリングパイプライン自体は変更する事が出来ない

ScriptableRenderLoop

まず シェーダーのパスを任意に実行可能。マテリアル事、ライト毎にパスを変える事も可能 また レンダリングパイプライン自体も自由に書くことが出来る そのため G-Bufferも自由に設計可能だし、ライトの計算なども自分で行う必要がある カリングやオブジェクトのソート等の一部の機能は 変更する事は出来ない

UNITYことはじめ~ShaderLab あたり

UNITY歴

実はあまり触ってない。プラグイン書く仕事はしたけど C++バインディングしたり、そんなのだし 一応簡単に画面を触るけど 正直表面すら理解してないので はじめてUNITYをちゃんと勉強する

幸いなことに、仕事でUNITYを使う機会があり、今回は深い部分を調査する R&D案件だ

ShaderLab

なぜ最初にShaderか? 一般的に一番難しそうではないか? だからこそ、ここを理解する間には UNITYの全部が理解できるはずである なんせ 心臓部であるので

ShaderLabって?

基本的に HLSLである。一部セマンティクスや型が違ったりするけど そのHLSLをラップして、 DirectX9の FXファイルのような感じで、パスを制御したり色々できる

意外と機能がおおいので ゆっくり勉強する

基礎構文とかはヘルプみて

Unity - マニュアル: シェーダーリファレンス

シェーダーの種類

サーフェースシェーダ、頂点シェーダとフラグメントシェーダ、固定関数シェーダ の3個がある 固定関数シェーダーは ご存知DirectX9以前にあったやつで、今の時代は使う事はないだろう

頂点シェーダとフラグメントシェーダは 一般的なシェーダーで ご存知のもので、 想像通りのものである ドメインシェーダ、ハルシェーダ、ジオメトリシェーダ、コンピュートシェーダも記述できる

問題のサーフェースシェーダだが、結論からいうと 頂点シェーダとフラグメントシェーダを簡単に書くものである pragmaで Lambertとか指定すると、自動的に 頂点シェーダとフラグメントシェーダを生成してくれているようだ もちろん、細かい制御は出来ないので その時は自力で書こう

Aboutシェーダ

細かいのはヘルプ見てもらうとして 頂点&フラグメントシェーダのサンプルを

Shader "Unlit/SimpleUnlitTexturedShader"
{
    Properties
    {
        // we have removed support for texture tiling/offset,
        // so make them not be displayed in material inspector
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    }

    SubShader
    {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"               
            }
            Cull Back

            CGPROGRAM
            // use "vert" function as the vertex shader
            #pragma vertex vert
            // use "frag" function as the pixel (fragment) shader
            #pragma fragment frag

            // vertex shader inputs
            struct appdata
            {
                float4 vertex : POSITION; // vertex position
                float2 uv : TEXCOORD0; // texture coordinate
            };

            // vertex shader outputs ("vertex to fragment")
            struct v2f
            {
                float2 uv : TEXCOORD0; // texture coordinate
                float4 vertex : SV_POSITION; // clip space position
            };

            // vertex shader
            v2f vert (appdata v)
            {
                v2f o;
                // transform position to clip space
                // (multiply with model*view*projection matrix)
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                // just pass the texture coordinate
                o.uv = v.uv;
                return o;
            }
            
            // texture we will sample
            sampler2D _MainTex;

            // pixel shader; returns low precision ("fixed4" type)
            // color ("SV_Target" semantic)
            fixed4 frag (v2f i) : SV_Target
            {
                // sample texture and return it
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

Shader “Unlit/SimpleUnlitTexturedShader” シェーダー名を設定。UNITY Editorから参照するときの名前

Properties マテリアルで設定するパラメータ テクスチャとか色々

SubShader シェーダーの始まり

SubShader/Tags 不透明オブジェクト、半透明オブジェクト等 シェーダーの設定をKeyValue方式で設定

SubShader/Pass レンダリングパス カリングやZTestなども設定できる

SubShader/Pass/Name 他のシェーダーから呼ぶ時に指定する名前

SubShader/Pass/Tags シェーダーの設定をKeyValue方式で。 例えば “LightMode”=“ForwardBase" であれば、Forwardレンダリングの時に使われるパスである Deferred等複数のパスを作れば、その時のレンダリングモードに応じたパスを実行する

CGPROGRAM~ENDCG シェーダーの具体的な中身。だいたいHLSL

pragma vertex vert pragma fragment frag 頂点シェーダ、フラグメントシェーダを使う&関数名を設定。 サーフェースシェーダだとここが surfaceになる。 その他 pragmaは色々あるのでヘルプ参照

などなど

とりあえず ヘルプに詳しく書いている

【UNITY】PostProcess

イメージエフェクト

ってUNITYでは 言うらしいよ ポストプロセスの事

概要

ポストプロセスに関しては ご存知と思うが UNITYは基本的に オブジェクトにスクリプトをくっつけていくタイプだ

UNITYではポストプロセスはカメラにつけるっぽい

MonoBehaviour.OnRenderImage(RenderTexture,RenderTexture) を実装したスクリプトを、カメラと同じ階層のGameObjectに入れれば ポストプロセスとして成立する

スクリプト作成

using UnityEngine;

[RequireComponent(typeof(Camera))]
//public abstract class Compose : MonoBehaviour
public class Compose : MonoBehaviour
{
    private Material _material;


    public string ShaderName;

    protected Material Material { get { return _material; } }


    protected virtual void Awake()
    {
        Shader shader = Shader.Find(ShaderName);
        _material = new Material(shader);
    }

    protected virtual void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination, _material);
    }

}

上記でかいたように OnRenderImageを実装する

sourceがエフェクト前の画像であり destinationにポストエフェクト適応させた出力を行う

ShaderNameプロパティで指定したシェーダーを読み込み それを実行している

Shaderは割愛。好きに書けばいい

スクリプト配置

GameObjectを作りそこにスクリプトをアタッチ そして 同じレイヤーにカメラを置けば完成