0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Unity】シェーダーでLineジオメトリを出力する

Posted at

概要

ジオメトリシェーダーの出力形式は3種類あります.1

Type 説明
PointStream
LineStream 線分
TriangleStream 三角形

普段使うのはTriangleStreamですが,エディタ表示用に線をたくさん書きたくなったので,LineStreamの使い方を調べました.

基本的な使い方

使い方はTriangleStreamのときと同じです.線分なので面の向きとかはありませんが,UVは使えます(1次元なのでuだけです).

LineTest1.shader
Shader "Test/LineTest1"
{
    Properties {}
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
        
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag
			#pragma multi_compile_instancing
            #pragma target 4.6
            
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
				UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2g
            {
                float4 vertex : POSITION;
				UNITY_VERTEX_INPUT_INSTANCE_ID
				UNITY_VERTEX_OUTPUT_STEREO
            };

            // sizeof(g2f) <= 7
            struct g2f
            {
                float4 vertex : SV_POSITION;
                float uv : TEXCOORD0;
				UNITY_VERTEX_OUTPUT_STEREO
            };

            float3 h2rgb(float h)
            {
                return ((clamp(abs(frac(h + float3(0, 2, 1) / 3.0) * 6.0 - 3.0) - 1.0, 0.0, 1.0) - 1.0) + 1.0);
            }

            v2g vert (appdata v)
            {
                v2g o;
				UNITY_SETUP_INSTANCE_ID(v);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
				UNITY_TRANSFER_INSTANCE_ID(v, o);
                o.vertex = v.vertex;
                return o;
            }

            [maxvertexcount(2)]
            void geom (point v2g IN[1], uint primitive_index : SV_PrimitiveID, inout LineStream<g2f> stream)
            {
                // 1つだけ図形を描く
                if (primitive_index > 0)
                {
                    return;
                }

                g2f o;
                UNITY_SETUP_INSTANCE_ID(IN[0]);
                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN[0]);
                UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(IN[0], o);

                // 書きたい線分の端点
                float3 p0 = float3(0, 0, 0);
                float3 p1 = float3(1, 0, 0);

                // 点をAppendする
                o.vertex = UnityObjectToClipPos(float4(p0, 1));
                o.uv = 0.0;
                stream.Append(o);
                o.vertex = UnityObjectToClipPos(float4(p1, 1));
                o.uv = 1.0;
                stream.Append(o);

                // 図形を終了
                stream.RestartStrip();
            }

            float4 frag (g2f i) : SV_Target
            {
                return float4(h2rgb(i.uv), 1.0);
            }
            ENDCG
        }
    }
}

Cubeとかにアタッチすると線分が表示されます.幅は1ピクセルです.調節はできません.

image.png

RestartStrip のタイミング

TriangleStreamのときは,三角形の頂点をAppendしたら,その都度RestartStrip()しますが,LineStreamのときは,線を切りたいタイミングでRestartStrip()します.

LineTest2.shader
// geom 以外は LineTest1.shader と同じ
[maxvertexcount(6)]
void geom (point v2g IN[1], uint primitive_index : SV_PrimitiveID, inout LineStream<g2f> stream)
{
    // 1つだけ図形を描く
    if (primitive_index > 0)
    {
        return;
    }

    g2f o;
    UNITY_SETUP_INSTANCE_ID(IN[0]);
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN[0]);
    UNITY_TRANSFER_VERTEX_OUTPUT_STEREO(IN[0], o);

    // 点をAppendする
    [unroll(6)]
    for (float i = 0; i < 6; i++)
    {
        float angle = 2.0 * (i / 5.0f) * UNITY_TWO_PI;
        float3 p = float3(-sin(angle), cos(angle), 0);
        o.vertex = UnityObjectToClipPos(float4(p, 1));
        o.uv = i / 6.0f;
        stream.Append(o);
    }

    // 図形を終了
    stream.RestartStrip();
}

RestartStrip()するまでは線がつながって描画されます.

image.png

ちなみに,RestartStrip()しなくても,geomを抜けると強制的に図形が終了します.そのため,複数の入力プリミティブにわたって連続した線を描画したい場合は,前回終了時の頂点から描画を再開しないと線が途切れてしまうので注意が必要です.

雑記

デバッグ用に線の表示をしたい場合は普通,スクリプトでDebug.DrawLine()とかDebug.DrawRay()を使って表示しますが,表示したい線の本数が数十万オーダー & Custom Render Texture の内容を読み取ってリアルタイムに表示したい,という事情から,シェーダーだけで表示する方法を調べていました.
一度テンプレートを作ると結構使いやすくて重宝しています.

▼こんな感じ.
image.png

  1. Stream-Output Object

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?