docs.unity3d.com
    目次を表示する/隠す

    Visual Compositor カスタムノードクラスの作成

    ノードクラスの作成

    カスタムノードクラスは Unity.VisualCompositor.CompositorNode ベースクラスを継承する必要があります。 このクラスは抽象的な基底クラスで、1つのメソッドを実装する必要があります。

    using Unity.VisualCompositor;
    
    public class MyCustomNode : CompositorNode {
        public override void Render() { }
    }
    

    Visual Compositor は、 Compositor Graph アセットを実行する際にこのメソッドを呼び出します。

    UI コントロールの作成

    カスタムノードのUIを作成するために、[ExposeField] 属性でフィールドを宣言します。

    例:

    [ExposeField] public float param;
    

    これはパラメータとして、ノード内に FloatField を作成します。

    ほぼすべてのシリアライズ可能な型を使用することができ、それぞれが対応するUnity UIを作成することになります。

    Node Inputs/Outputs

    Node Inputs

    他のノードの出力からカスタムノードへの入力を得るために、フィールドを [InputPort] という特別な属性で飾ります。 そのフィールドを [InputPort] という特別な属性で装飾します。 カスタムノードで Render() メソッドを呼び出すと、これらのフィールドに値が格納されます。 これらのフィールドには、すぐに使える値が格納されます。

    例:

    [InputPort(name: "input")] private RenderTexture m_input;
    

    これは m_input というフィールドを宣言しており、input というラベルを付けて、他のノードから RenderTexture を受け取ることができます。

    入力ノードは、任意の種類をいくつでも定義することができます。 例えば、Selection Group というノードがあります。 は HashSet<GameObject> という型を持つ値を出力します。

    Node Outputs

    Visual Compositorは、すべての出力フィールドの値を、接続されているノードに自動的にコピーします。 これらの出力フィールドは [OutputPort] 属性で修飾されており、カスタムノードの Render() メソッド内で値を設定することができます。

    例:

    [OutputPort(name: "output")] private RenderTexture m_output;
    

    これは m_output という名前の出力ポートを宣言しており、output というラベルを付けて、接続されているノードに RenderTexture を送ることができます。 RenderTextureを接続されたノードに送信することができます。

    Node UI Extension

    カスタムノードは [CompositorNode] 属性でデコレートすることができ、さらにカスタマイズするために以下のパラメータで指定します。

    番号 名前 解説 必須
    1 title 'Create Node' メニューで使用する名前です。 ✔️
    2 tooltip ノードのドキュメント文字列(ツールチップ)です。 ✔️
    3 w エディタに表示されるノードウィジェットの幅。
    4 userCreatable trueの場合、ユーザーが 'Create Node' メニューからノードを作成できるようにします。
    5 icon ノードのカスタムアイコンへのパス。

    また、カスタムノードのUIを拡張するには、CustomCompositorNodeEditorを継承したクラスを [CompositorNodeEditor] 属性付きで記述します。 例えば

    using Unity.VisualCompositor.Editor;
    
    [CompositorNodeEditor(typeof(MyCustomNode))]
    public class MyCustomNodeEditor : CustomCompositorNodeEditor {
    
        public override NodeUI CreateUI(CompositorNode compositorNode) {
            // This method is called first. If you need use the node
            // in the below method, capture it here.
            // Eg: this.node = compositorNode;
            return new NodeUI();
        }
    
        public override void ConfigureUI(NodeUI ui) {
            // This is called last, you can modify the default UI here.
        }
    }
    

    完成例

    Runtime Script

    using UnityEngine;
    using Unity.VisualCompositor;
    
    // Any class that inherits Compositor Node will be available to use in the Compositor.
    [CompositorNode("My Custom Node", "A node.", icon: "SpeechBubble.png")]
    public class MyCustomNode : CompositorNode {
    
        // This method implements the functionality of the node. You can read from input port fields and write to output port fields,
        // the Compositor takes care of moving those values around the graph.
        public override void Render() {
            if (null == m_input) {
                if (null != m_output) {
                    ClearRenderTexture(m_output);
                }
                return;
            }
    
            if (null == m_output) {
                m_output           = new RenderTexture(m_input);
                m_output.hideFlags = HideFlags.DontSaveInEditor;
            }
    
            if(m_material == null) {
                Shader shader = Shader.Find("MyCustomNodeShader");
                m_material           = new Material(shader);
                m_material.hideFlags = HideFlags.DontSaveInEditor;
            } 
    
            Graphics.Blit(m_input, m_output, m_material);
        }
    
        private static void ClearRenderTexture(RenderTexture rt) {
            RenderTexture prevRT = RenderTexture.active;
            RenderTexture.active = rt;
            GL.Clear(true, true, Color.clear);
            RenderTexture.active = prevRT;
        }
    
    //--------------------------------------------------------------------------------------------------------------------------------------------------------------
    
        // Fields that have an InputPort attribute will become input ports in the Compositor Editor. 
        [InputPort(name: "input")] private RenderTexture m_input;
    
        // Fields that have an OutputPort attribute will become output ports in the Compositor Editor.
        [OutputPort(name: "output")] private RenderTexture m_output;
    
        // Fields that have an ExposeField attribute will have the appropriate ui controls created in
        // the node editor. Any serializable field types can be used. 
        [ExposeField] public float param;
    
        Material m_material;
    }
    

    Shader

    Shader "MyCustomNodeShader" {
        Properties {
            _MainTex ("Texture", 2D) = "white" {}
        }
        SubShader {
            // No culling or depth
            Cull Off ZWrite Off ZTest Always
    
            Pass {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
    
                #include "UnityCG.cginc"
    
                struct appdata{
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                struct v2f{
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
                };
    
                v2f vert(appdata v) {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    return o;
                }
    
                sampler2D _MainTex;
    
                float4 frag(v2f i) : SV_Target {
                    float4 c = tex2D(_MainTex, i.uv);
                    const half t = c.r;
                    c.r = c.g;
                    c.g = c.b;
                    c.b = t;
                    return c;
                }
                ENDCG
            }
    
    
        }
    }
    

    Editor Script

    using Unity.VisualCompositor;
    using Unity.VisualCompositor.Editor;
    using UnityEditor.UIElements;
    using UnityEngine;
    using UnityEngine.UIElements;
    
    [CompositorNodeEditor(typeof(MyCustomNode))]
    public class MyCustomNodeEditor : CustomCompositorNodeEditor {
    
        public override NodeUI CreateUI(CompositorNode compositorNode) {
    
            NodeUI nodeUI = new NodeUI();
            m_node = compositorNode as MyCustomNode;
            return nodeUI;
        }
    
        public override void ConfigureUI(NodeUI ui) {
            ui.tooltip = "Hey, this is a customised tool tip!";
    
            Label label = new Label("Custom Editor");
            ui.extensionContainer.Add(label);
    
            TextField textField = new TextField() {
                label = "Foo",
                value = "Bar",
            };
            ui.extensionContainer.Add(textField);
    
            ui.extensionContainer.Add(new ObjectField("HogeObj") { objectType = typeof(GameObject) });
    
            Button button = new Button() { text = "Increase Param", };
            button.clicked += () => { ++m_node.param; };
            ui.extensionContainer.Add(button);
        }
    
        private MyCustomNode m_node = null;
    
    }
    
    トップに戻る
    Copyright © 2023 Unity Technologies — 商標と利用規約
    • 法律関連
    • プライバシーポリシー
    • クッキー
    • 私の個人情報を販売または共有しない
    • Your Privacy Choices (Cookie Settings)