質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.13%

Unity シェーダー 頂点を引き伸ばす

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 563
退会済みユーザー

退会済みユーザー

前提・実現したいこと

描画しているメッシュの頂点をシェーダーで引き延ばしたい

該当のソースコード

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class PlacementDraw : MonoBehaviour
{
    public Material material;
    Mesh mesh;
    List<Vector2> ObjectPositionList = new List<Vector2>();
    public Placement.StateType stateType;
    // Start is called before the first frame update
    void Start()
    {
        mesh = new Mesh();
        mesh.vertices = new Vector3[] {
        new Vector3 (0, 0, 0),
        new Vector3 (0f, 1f, 0),
        new Vector3 (1f, 0f, 0),
        new Vector3 (1f, 1f, 0),
    };

        mesh.uv = new Vector2[] {
        new Vector2 (0f, 0f),
        new Vector2 (0f, 1f),
        new Vector2 (1f, 0f),
        new Vector2 (1f, 1f),
    };

        mesh.triangles = new int[] {
        0, 1, 2,
        1, 3, 2,
    };

        mesh.RecalculateNormals();
        mesh.RecalculateBounds();
    }

    // Update is called once per frame
    void Update()
    {
        PlacementObject();
        for (int i = 0; i < ObjectPositionList.Count; i++)
        {
            Graphics.DrawMesh(mesh, ObjectPositionList[i], Quaternion.identity, material, 0);
        }
    }
    void PlacementObject()
    {
        var pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        var x = Mathf.FloorToInt(pos.x);
        var y = Mathf.FloorToInt(pos.y);
        var pos2 = new Vector2(x, y);
        Placement placement = GetComponent<Placement>();
        if (Input.GetMouseButton(0) && stateType == placement.state)
        {
            if (EventSystem.current.IsPointerOverGameObject())
            {
                return;
            }
            ObjectPositionList.Add(pos2);
        }
    }
}
Shader "Unlit/StencilObject"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
    }

    SubShader
    {
        Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        /*Stencil
        {
            Ref 1
            Comp equal
        }*/

        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha

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

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
            };

            fixed4 _Color;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.vertex = UnityObjectToClipPos(IN.vertex);
                OUT.texcoord = IN.texcoord;
                OUT.color = IN.color * _Color;
                #ifdef PIXELSNAP_ON
                OUT.vertex = UnityPixelSnap (OUT.vertex);
                #endif

                return OUT;
            }

            sampler2D _MainTex;
            sampler2D _AlphaTex;
            float _AlphaSplitEnabled;

            fixed4 SampleSpriteTexture (float2 uv)
            {
                fixed4 color = tex2D (_MainTex, uv);

#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
                if (_AlphaSplitEnabled)
                    color.a = tex2D (_AlphaTex, uv).r;
#endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED

                return color;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
                fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
                c.rgb *= c.a;
                return c;
            }
        ENDCG
        }
    }
}


追加スクリプト

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlacementShadow : MonoBehaviour
{
    public Material material;
    Mesh mesh;
    List<Vector2> ObjectPositionList = new List<Vector2>();
    // Start is called before the first frame update
    void Start()
    {
        mesh = new Mesh();
        mesh.vertices = new Vector3[]
        {
            new Vector3(0f,0f,0f),
            new Vector3(0f,1f,0f),
            new Vector3(1f,0f,0f),

            new Vector3(0f,1f,1f),
            new Vector3(1f,0f,1f),
            new Vector3(1f,1f,1f),
        };
        mesh.uv = new Vector2[]
        {
            new Vector2(0f,0f),
            new Vector2(0f,1f),
            new Vector2(1f,0f),
            new Vector2(0f,1f),
            new Vector2(1f,0f),
            new Vector2(1f,1f),
        };
        mesh.triangles = new int[]
        {
            0,1,2,

            3,2,1,
            2,3,4,

            5,4,3,
        };
        mesh.RecalculateBounds();
        mesh.RecalculateNormals();
    }

    // Update is called once per frame
    void Update()
    {
        Placement();
        for(int i = 0;i < ObjectPositionList.Count; i++)
        {
            Graphics.DrawMesh(mesh,Vector3.zero, Quaternion.identity, material, 0);
        }
    }
    void Placement()
    {
        var pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        var x = Mathf.FloorToInt(pos.x);
        var y = Mathf.FloorToInt(pos.y);
        var pos2 = new Vector2(x, y);
        if (Input.GetMouseButton(0))
        {
            ObjectPositionList.Add(pos2);
        }
    }
}

試したこと

イメージ説明
イメージ説明
正方形に描画しているメッシュの3つの頂点を引き伸ばして、ボリュームシャドウのようにしたかったのですが、どうすればシェーダーで描画しているメッシュの頂点をいじれるのか、調べても分かりませんでした。
どのようにすればシェーダーでverticesを変更することができますか?
回答お願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Bongo

    2020/03/08 08:19

    ご提示いただいた図はどういう状況なのでしょうか?
    もし試行錯誤した結果の失敗図なのでしたら、他にも外部画像処理ソフトで模擬的な理想図を描いていただいた方がいいかもしれません。

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2020/03/08 11:37

    添付した画像は理想図です

    キャンセル

回答 1

checkベストアンサー

+1

ご参考にされたサイトの「ボリュームシャドウ」の方に言及がありますが、正方形のような単純な形状をそのまま引き伸ばしたのでは六角形の影に変形させることはできないかと思います。

さいわい今回は単純な正方形ですので、正方形に特化したシンプルな加工ができそうです。
プランとしては、まず正方形を固定側と可動側の2つの三角形に分け、それらの間には2つをつなぐように面を張っておきます。
そしてシェーダー内では、引き伸ばし方向に応じて正方形を90°ずつ回転させることで引き伸ばしたい方向へ可動側三角形が向くようにしてみました。

PlacementDrawStartを下記のように変更しました。

        // Start is called before the first frame update
        void Start()
        {
            mesh = new Mesh();
            mesh.vertices = new Vector3[] {
                // 左下の三角形と...
                new Vector3 (0f, 0f, 0f),
                new Vector3 (0f, 1f, 0f),
                new Vector3 (1f, 0f, 0f),

                // 右上の三角形に分ける
                // さらに、使用していないZ成分を固定側か可動側かを
                // 区別するための目印として使うことにし、右上三角形の
                // Zを1にしておく
                new Vector3 (0f, 1f, 1f),
                new Vector3 (1f, 0f, 1f),
                new Vector3 (1f, 1f, 1f),
            };

            mesh.uv = new Vector2[] {
                // UVも頂点数に合わせて6つに増やす
                new Vector2 (0f, 0f),
                new Vector2 (0f, 1f),
                new Vector2 (1f, 0f),
                new Vector2 (0f, 1f),
                new Vector2 (1f, 0f),
                new Vector2 (1f, 1f),
            };

            mesh.triangles = new int[] {
                // 左下三角形
                0, 1, 2,

                // 2つの三角形をつなぐ長方形
                3, 2, 1,
                2, 3, 4,

                // 右上三角形
                5, 4, 3,
            };

            mesh.RecalculateNormals();
            mesh.RecalculateBounds();
        }

シェーダー側は下記のようにしてみました。
ご提示のコードはスプライト用のものをベースにしているご様子で、若干複雑な記述になっていましたが、DrawMeshで使う分にはもっとシンプルでよさそうに思いましたので、記述をいくらか掃除してあります。

Shader "Unlit/SquareShadow"
{
    Properties
    {
        _Color ("Shadow Color", Color) = (1,1,1,1)
        _Angle ("Shadow Angle", Range(0.0, 360.0)) = 210.0
        _Distance ("Shadow Distance", Range(0.0, 5.0)) = 2.0
    }

    SubShader
    {
        Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            // すでに影を描画済みの部分には描画させないようにすることで、
            // 影が重なったときに色が濃くなってしまうのを防ぐ
            Stencil
            {
                Ref 1
                ReadMask 1
                WriteMask 1
                Comp Greater
                Pass Replace
            }

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

            struct appdata_t
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            fixed4 _Color;
            float _Angle;
            float _Distance;

            v2f vert(appdata_t IN)
            {
                v2f OUT;

                // まず、引き伸ばし方向を指す単位ベクトルを作る
                float2 direction;
                sincos(_Angle * UNITY_PI / 180.0, direction.y, direction.x);

                // 引き伸ばし方向の成分の符号を求め...
                float2 directionSign = sign(direction);

                // 単純化のため、符号が0ならとりあえず1とし...
                directionSign += 1.0 - abs(directionSign);

                // 2つの軸に分け...
                float2 axisX = float2(directionSign.x, 0.0);
                float2 axisY = float2(0.0, directionSign.y);

                // 符号の積を求めて第1・3象限(+1)か第2・4象限(-1)かを調べ...
                float orthantSign = directionSign.x * directionSign.y;

                // 象限に応じて軸を選び、それを使って元の頂点座標を(0.5, 0.5)を中心に回転させる
                IN.vertex.xy = mul(IN.vertex.xy - 0.5, orthantSign > 0 ? float2x2(axisX, axisY) : float2x2(axisY, axisX)) + 0.5;

                // 可動側の三角形をdirectionの向きに_Distanceだけずらす
                IN.vertex.xy += (IN.vertex.z * _Distance) * direction;

                // 可動側判定に使ったZ成分はもう不要なので0に変える
                IN.vertex.z = 0.0;

                OUT.vertex = UnityObjectToClipPos(IN.vertex);

                return OUT;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
                return _Color;
            }
            ENDCG
        }
    }
}

図

こんな感じで影を描いた後、さらにその上に通常のシェーダーで本来の画像を描画すれば画像が影を落としているように見えるんじゃないかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/03/11 18:41

    象限をまたぐと位置が飛ぶということは、90°回転の部分に何か見落としがあったのかもしれません。のちほど調べてみます。
    おうかがいしますが、スクリプト側はご質問文に追記いただいた形(Startのメッシュ生成部分を変更、DrawMesh時の位置はObjectPositionListは使わずVector3.zeroとする)で、シェーダー側は私が回答内に提示しましたものということでいいでしょうか。もしシェーダー側のコードが私のものと全く同じならけっこうですが、もし何らかのアレンジが加わっているようでしたら念のためシェーダーコードもご提示いただけるとありがたいです。

    キャンセル

  • 2020/03/11 19:01

    すみません。瞬間移動したような動きになるのは、DrawMesh時の位置がObjectPositionList[i]の時でした。シェーダーコードは全く同じです。Vector3.zeroなら正常に動作します。

    キャンセル

  • 2020/03/12 07:37

    Vector3.zeroだと大丈夫でObjectPositionList[i]だとずれるとなるといよいよもって謎ですね...すみませんがまだその現象を再現できていません。
    Vector3.zeroだとずれないのなら、おそらくメッシュに異常はないんじゃないかと思われます。DrawMesh時の位置が違う場合にずれるとなると、たとえば(0, 0, 0)と(1, 0, 0)と(0, 1, 0)のような3地点に描画させた場合、Angleを回転させると(0, 0, 0)だけがずれなくて(1, 0, 0)と(0, 1, 0)は瞬間移動してしまう感じでしょうか。
    たびたびすみませんが、上記の3地点に描画させて動かした場合の映像を撮影していただけませんでしょうか。もしかすると瞬間移動するときの位置の飛び方が手がかりになるかもしれません。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.13%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る