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?

【VRChat】ジオメトリシェーダが利用可能かどうかをスクリプト側で取得する

0
Posted at

実行環境でジオメトリシェーダが利用可能かどうかを知ることができるAPIはUnityの機能としていくつか用意されているそうです.ChatGPTに教えてもらいました.

  1. SystemInfo.graphicsShaderLevel >= 40 であれば大体使える(Metal等の例外あり)
  2. 上記に加えて SystemInfo.GraphicsDeviceType でMetal等の判定をする
  3. ジオメトリシェーダを含むシェーダーを作成し,shader.isSupported を読む

しかし,3つともUdonにはExposeされていないので,VRChatでは使えません.残念.

方法

できないものは仕方ないので,実際に描画した結果で判定します.
VRCGraphics.Blit() はジオメトリシェーダを含むシェーダーでも描画できるので,適当なRenderTextureに描画し,VRCAsyncGpuReadbackで結果を確認します.

実装

シェーダーで緑(#00FF00FF)を書き込み,スクリプト側で読み出して緑だったらジオメトリシェーダが使える環境だとわかります.

GeometryShaderSupportTest.shader
Shader "Test/GeometryShaderSupportTest"
{
    Properties
    {
        /* none */
    }
    SubShader
    {
        Tags { "Queue"="Overlay" "RenderType"="Transparent" }
        
        Pass
        {
            Blend One Zero
            ZWrite Off
            ZTest Always
            Cull Off

            CGPROGRAM
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag
            #pragma target 4.0

            #include "UnityCG.cginc"
            
            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2g
            {
                float4 pos : POSITION;
            };

            struct g2f
            {
                float4 pos : SV_POSITION;
            };

            v2g vert ( appdata v )
            {
                v2g o;
                o.pos = UnityObjectToClipPos( v.vertex );
                return o;
            }

            [ maxvertexcount( 1 ) ]
            void geom ( triangle v2g IN[ 3 ], inout PointStream<g2f> stream )
            {
                g2f o;
                o.pos = float4( 0, 0, 0.5, 1 );
                stream.Append( o );
                stream.RestartStrip();
            }

            float4 frag ( g2f IN ) : SV_Target
            {
                return float4( 0, 1, 0, 1 );
            }
            ENDCG
        }
    }
}

スクリプトの mat に上記シェーダーのマテリアルをセットします.
判定結果はとりあえずUIテキストに表示させます.

GeometryShaderSupportTest.cs
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.SDK3.Rendering;
using VRC.Udon.Common.Interfaces;

namespace Test.GeometryShaderSupportTest
{
    public class GeometryShaderSupportTest : UdonSharpBehaviour
    {
        [SerializeField] private TMPro.TextMeshProUGUI debugText;
        [SerializeField] private Material mat;
        private RenderTexture rt;
        private bool isChecking = false;
        private bool isCheckCompleted = false;
        private bool isGeometryShaderSupported = false;

        void Start()
        {
            // 開幕0秒でReadbackをリクエストすると失敗するのでちょっと待つ.
            SendCustomEventDelayedFrames(nameof(Check), 10);
        }

        void Update()
        {
            if (isCheckCompleted)
            {
                if (isGeometryShaderSupported)
                {
                    debugText.text = "YES";
                }
                else
                {
                    debugText.text = "NO";
                }
            }
            else
            {
                debugText.text = "Checking";
            }
        }

        public override void OnAsyncGpuReadbackComplete(VRCAsyncGPUReadbackRequest request)
        {
            OnReadbackComplete(request);
        }

        public void Check()
        {
            if (isCheckCompleted)
            {
                if (rt != null)
                {
                    rt.Release();
                    rt = null;
                }
                return;
            }

            if (rt == null)
            {
                rt = new RenderTexture(1, 1, 0, RenderTextureFormat.ARGBFloat);
                rt.Create();
            }

            VRCGraphics.Blit(null, rt, mat, 0);
            
            if (!isChecking)
            {
                isChecking = true;
                VRCAsyncGPUReadback.Request(
                    src: rt,
                    mipIndex: 0,
                    x: 0, width: 1,
                    y: 0, height: 1,
                    z: 0, depth: 1,
                    dstFormat: TextureFormat.RGBAFloat,
                    udonBehaviour: (IUdonEventReceiver)this
                );
            }

            SendCustomEventDelayedSeconds(nameof(Check), 5f);
        }
        
        private void OnReadbackComplete(VRCAsyncGPUReadbackRequest request)
        {
            isChecking = false;

            if (request == null)
            {
                return;
            }

            if (request.hasError)
            {
                return;
            }
            
            int dataCount = request.width * request.height;
            Color[] data = new Color[dataCount];
            if (request.TryGetData(data))
            {
                if (data[0] == Color.green)
                {
                    isGeometryShaderSupported = true;
                }
                else
                {
                    isGeometryShaderSupported = false;
                }
                isCheckCompleted = true;
            }
        }
    }
}

Windowsビルドだと"YES"でiOSビルドだと"NO"が表示されます.Androidはたぶん端末によると思いますが,筆者の環境だと"YES"でした.Quest3も"YES"です.

雑記

Start() で VRCAsyncGPUReadback.Request() するとリードバックが失敗するみたいです.SendCustomEventDelayedFrames() で遅らせるか,リトライ処理を入れておくのがいいと思います.

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?