C++幼女先輩

プログラミング成分多め

UNITYタイムラインをカスタマイズするよ

タイムラインとは

ムービーシーンとかカットシーンと呼ばれるもの
カメラワーク、モーション、エフェクト等を定義しておき、実行時にキャラをバインドする事で
同じ動作のムービーを別キャラ(別衣装)で行うことが出来る
例えばモンハンのムービーシーンのような、その時装備しているものと容姿でムービーを再生できる
そのような便利な機能がUNITYにはある

Timelineの作成

メニューの Windows->Sequencies->Timeline でタイムラインのタブを表示
f:id:murasame-labo:20210602143900p:plain

適切なGameObjectを作成しTimelineタブでCreateを押すと
Timeline.playable というファイルが作成される

と同時にPlayableDirectorのコンポーネントが追加される
f:id:murasame-labo:20210602154712p:plain

タイムラインの左側をクリックすると各種のトラックを作成する事が出来る
f:id:murasame-labo:20210602155523p:plain

動画編集などをした事ある人なら分ると思うが、各要素はトラックという単位で区切られ
例えば音声トラックには音声のクリップを複数配置して、タイムライン上でいつ再生するかを設定する
UNITYのタイムラインではさらに複数トラックをトラックグループという単位でまとめる事が可能

UNITYから準備されているトラックは6種類だが、自分でカスタムトラックを作る事が出来る
実際のゲーム開発では、独自のトラックを作る事がほとんどだと思うが
まずはUNITYが提供するトラックを紹介しよう

ActivationTrack

GameObjectの有効化、無効化を制御する
ActivationTrack を作成しタイムライン上で右クリックを行い、Add ActivationClipを選択すると
タイムライン上にActivationClipを作成できる
今回はテストにCubeオブジェクトを作りそれを少しして表示する

シーンにCubeを作りTrackにバインド(左側のNoneにCubeを設定)する
注意するのはこの時のGameObjectはシーン上にインスタンス化されたものを指定する必要があり
Prefab等のインスタンス化されたものは指定できない(後で説明するが、Prefab等を表示する場合は動的にBindingsを設定する必要がある)
クリップを調整して表示非表示のタイムラインを組み、Previewの再生をすると
Game画面にタイムラインのレンダリングが表示される

streamable.com

トラックにオブジェクトをバインディングしたら、PlayableDirectorのBindingsにも同じようにバインディングされています
(というか、同じものです)
f:id:murasame-labo:20210602164541p:plain
PlayableDirectorは再生時にトラックに適切なオブジェクトをバインドしたり、クリップをロードしたりアンロードしたり
全体の司令塔です(だからDirector)

シーンの方もクリップの設定に従ってオブジェクトのON/OFFが切り替わります
(もちろんTransformやらの値も、タイムラインに合わせて変化します)
で、タイムラインで使用するGameObjectは、タイムラインの子に追加すると管理が楽です
f:id:murasame-labo:20210602170544p:plain

AnimationTrack

AnimationTrackは、オブジェクトの移動や回転、アニメーションの再生等を行います
Animatorコンポーネントを持つオブジェクトをBindできます
トラックを右クリックで Add from AnimationClipで再生するアニメーションを選ぶ
当然トラックに複数のアニメーションを並べて連続再生する事も可能
クリップのつなぎ目をオーバーライドさせれば、アニメーションの補間もできます

ClipTransformOffset等を設定すれば、移動アニメーションも可能です
それらの動作に関しては、アニメーションカーブや、キーフレームを設定できます
今回はそのへんは行わず、適当なアニメーションを再生してみます

streamable.com
それっぽくなってきた

AudioTrack

音の再生を行うClipを作成します
動作は推測の通りなので割愛

ControlTrack

シーンあるいはPrefabからオブジェクトを有効化/無効化します
ActivationTrackに似てますが、こちらは主にParticle等を生成する時に使います
タイムライン上で右クリックで、Add from GameObjectします
シーン上のGameObjectを指定する事も可能ですが、今回はPrefabから直接生成します
何か適当なParticleを作成してPrefab化しておきそれを設定します
ParentObjectにシーン上のオブジェクトを指定すれば3D空間上のその位置にパーティクルを出す事が出来ます

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

SignalTrack

任意のタイミングでイベントを発行する機能です
わりと最近のUNITYで追加されました
今度調べます

PlayableTrask

非常に有用な機能です
スクリプトを書いて独自のトラック、クリップを作成します
だいたいゲームの仕事でプログラマがやるべきは、このトラックの作成です
例えばブレンドシェイプをコントロールしたり、揺れもの、まばたき、目線
その他標準のトラックでは制御できない事をスクリプトで行います

非常に有用ですが、非常に複雑なためこちらは後程調査します

会社WebページをさくらVPSからAWS S3の静的ホスティングに変更

今まで
ドメインをお名前.comで
メールをお名前メール
Webページその他ホスティングをさくらVPS
にしていた

ただ、お名前メールのMXのサーバ名が不明だったので
お名前comからお名前メールにNSを転送してお名前メールの方でさくらに転送
という無駄な流れだった
今回、お名前メールのMXのURLも手に入れたため思い切ってS3にしてみた

結果は非常に簡単にできた
DNSも、お名前.comからNSをRoute53に飛ばし
Route53でAレコードを S3に飛ばし wwwサブドメインをCNAMEでS3に飛ばし
MXをお名前メールに飛ばせば動いた気がする

【UNITY 2020.2.6f1】Cinemachine FreeLookカメラでTPS操作をする。New InputSystemにも対応

Cinemachineとは

UNITYの提供するカメラ制御のコンポーネント

Cinemachineのインストール

PackageManagerよりインストールをする
その後メニューから Cinemachine-Cinemachine FreeLook を選ぶ
シーンに CM FreeLook1というGameObjectが作成されており、CinemachineFreeLookコンポーネントが付いており
MainCameraに、CinemachineBrainコンポーネントが付いている

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

キャラクターをカメラが追従してほしいので
CinemachineFreeLookのFollowにキャラクターのGameObjectを、LookAtに注視点(キャラの頭とか)を設定する

このまま実行すると、Cinemachineが昔のInputを使っているため、New InputSystem を使っている場合はエラーになる
InputSystemに変更するには、CM FreeLook1 GameObjectにAddComponentで Cinemachine Input Providerスクリプトを付ける
これでNew Input Systemに変更されるのでエラーはなくなる
ただし、現状ではマウスの動きをキャプチャできないので、CinemachineInputProviderのXY Axisに Look Actionを設定する
f:id:murasame-labo:20210307214238p:plain

これで、キャラクターの動きにカメラが追従し、マウスでカメラを回す事ができる

ただし、このままではキャラの向いている方向に動くので
例えばキャラが右を向いている時に上を押すと右に歩き
もちろんそういう操作のゲームもあるが、今回はカメラの向きでキャラが動くように
つまりキャラがどの方向を向いていても上を押すとキャラがカメラからみて奥へ動くようにする

そのために、カメラからみたforwardベクトルとrightベクトルがワールド座標でのベクトルに変換するため
TransformDirectionを使いワールド座標に変換し
それにキー入力のx,zを掛ければ求まる(もちろんy成分は無視する)
また、移動方向を向くためにLookAtを使う
もちろん、キャラ座標で計算したり最適化は出来るがとりあえずいいだろう

            Vector2 m = move.ReadValue<Vector2>();
            // カメラの方向で移動させる
            Vector3 forward = playerCamera.transform.TransformDirection(Vector3.forward);
            Vector3 right = playerCamera.transform.TransformDirection(Vector3.right);
            moveDir = (m.x * right +  m.y * forward) * speed;
            moveDir.y = 0;

            // 進行方向を向く(今のところ補完入れてない)
            transform.LookAt(transform.position + moveDir);

コメントの通り補完は入れていないけど、今後はCharactorControllerを別のアセットに置き換える可能性もあるので
とりあえずここはこれで。

これで、カメラの回転とキャラ移動が思い通りになった

あとは、横に歩いている時に少しずつカメラが回って正面を向くようにするカメラが最近多いので
Cinemachineでも、BindingModeを変更すればそのあたりの挙動かわるようだ(よくわかってない)

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

とりあえず今までのまとめ(まだゲーム内容決めてないけど)

www.youtube.com

【UNITY 2020.2.6f1】New Input System(1.0.2)を使ってみる

New Input Systemについて

新しい入力が出来ました
引き続き旧来の入力方法を使う事が出来ますが
これを期にInputSystemを覚えようと思います

旧来の方法

旧来のInputではInputManagerのAxes
下記のように入力を取得していました

h = Input.GetAxis("Horizontal");
v = Input.GetAxis("Vertical");
j = Input.GetButton("Jump");

New Input Systemを使う

細かいことはほかの人のブログで・・

まず、PackageManagerからInputSystemをInstallします
メニューのWindows PackageManagerから PackageManagerを起動し
左上のリストから、Packages: Unity Registryを選び、 Input Systemを検索
今回は最新の1.0.2 を選択しInstallします
ちなみに、Samplesからサンプルのパッケージをインストールする事もできます

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

インストールを終えたら、New Input Systemを使うように変更します
ProjectSettingsのPlayerSettingから、Active Input Handlingを Input System Package(New)に変更します
そうするとUNITYエディタが再起動します

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

これで、InputSystemが有効になり、旧来の入力が効かなくなります

InputSystemを設定する

InputSystemの設定は少し面倒です

まず、右クリックから Create-InputActionを行い、InputActionAssetを作成する
ダブルクリックを行い、ActionMaps、Actionsと、入力キーのぶんだけ作成します
例えば、移動はxzの2軸なのでMoveという名前で Vector2で作成します
キーボードだとWASDと矢印キーにそれぞれUp、Down、Left、Rightを設定
他にもGamePad、Touch、Joystick、XRも設定する
FireはActionTypeはButtonで、マウス左クリックやゲームパッドの右トリガーなどに割り当てる
そのほか使うボタンを登録します

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

そしてそれを登録すればよいのですが
めんどくさいので UNITYが用意したものを使います

PlayerInputコンポーネント

動かしたいオブジェクトに、AddComponentから PlayerInput を追加します
すでに基本的なキー入力のAssetが設定されているので、非常に手抜きができます
DefaultMapをPlayerにし、BehaviorをSendMessagesにする

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

実際に値を取得する時は下記のようになる

    public void OnMove(InputValue value) => move = value.Get<Vector2>();
    public void OnLook(InputValue value) => look = value.Get<Vector2>();
    public void OnFire(InputValue value) => fire = value.Get<float>() > 0;
    public void OnJump(InputValue value) => jump = value.Get<float>() > 0;

このように、Onアクション名 というCallbackで指定したアクションが取れます
コールバックで処理するのが面倒だなー、従来のようなポーリングで取りたい場合は
上記のようにコールバックで値を変数に保存してもいいですが
PlayerInputには、ポーリングをするインタフェースも用意されてます

    // InputActionを定義
    Private InputAction move, jump;

    void Start()
    {
        PlayerInput playerInput = GetComponent<PlayerInput>();

        // 各アクションの参照を取得
        move = playerInput.actions["Move"];
        jump = playerInput.actions["Jump"];
    }

    void Update()
    {
        Vector2 m = move.ReadValue<Vector2>();  // 2軸の値はVector2で取得
        bool j = jump.ReadValue<float>() > 0;  // bool値はfloat値で 0より大きいかで判断する
    {

これで、ポーリングでもイベントでも取得が可能です

AWSの操作をするSlackBotを作りたいのである

作りたいもの

クラウドを本格的に使う際に色々心配事もあるし、結局仕事でも使うでしょ?
例えば今月の利用金額を調べたり、Metrixを表示したり、起動中のインスタンス表示したり落としたり
これがPCでログインしたりせず、Slack上でコメントするだけで制御できることがどれだけ便利か

以前のプロジェクト

github.com

以前、Go言語の勉強用にGOSICKという名前で作った
これは当初、Goの色々な機能を使いたくてPluginを使いたかったりしたのでGoにした

ところが、この程度のBotであればデプロイを行わず、サーバレスにすべきなので
Serverless Frameworkで作り直そうとしていた
当初はAzureのBizSparkに入ってたりでAzureCSharpで作ろうとしていたが
SlackBot程度の処理負荷の低いものであればNodeがベストではないかと思い
AWS Nodeで作り直そうと思う

使用フレームワーク

サーバレスは実際は直接AWS上にコード書いてもいいが、ローカルでテストしたりするために
ServerlessFrameworkや AWS SAMといったものを使うと便利
その2者が有名なのでどちらを使おうかな・・

権限

権限をつける等は将来的には必要だろう
例えば社員誰でも請求額見れるとか問題がある会社も多いだろう
ユーザー単位でAPIに権限をつけるか、チャンネル単位か等の設計などは後回しで
まずは全員フル権限にする

コマンド形式(仮

メンションにするか、@なしで反応するかは後程考えるとして
まずは文字で単純に解析したいな
xxx をコマンド名として下記のようなコマンドを想定
また、; で複数コマンドを同時に出来るようにしようかな?

xxx ec2 list  
xxx ec2 down インスタンス名  
xxx ec2 list; lambda list; ...  

使用言語

Nodeで動かす。Typescriptにしたい

WSL2のポートをlocalhostに公開する

WSL2で開発する時、Dockerなどを使わなくともポートをWindows側から叩くことができる
これでWeb開発がよりスムーズになる
また、WSL2に使うメモリやプロセッサ数、Swapなども細かく設定し、PCリソースを管理することも可能

devblogs.microsoft.com

URP10.2.2 URPシェーダーをShaderLabで書いてみる

URPのシェーダーを作ろうとすると、UNITYからは全部ShaderGraphになる
ので、テキストエディタでShaderを書く事は推奨されていないようだし
実際に仕事でも、アーティストと会話するために、ノードエディタのShaderGraphを使う方が良いと思うが
勉強のためにあえてShaderLabで書いてみる

URP UnlitShaderを調査

まずは、最も簡単なはずのUnlitから書いてみる
テンプレートからはShaderLabは作れないので、ShaderGraphのUnlitを新規で作成します

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

床に適用してみた。テクスチャも影もなくなった
f:id:murasame-labo:20210205123921p:plain

作成されたUnlitShaderGraphからShaderLabのコードを取得するには、Inspectorから ViewGeneratedShader を選択するとコードが表示される
f:id:murasame-labo:20210205123528p:plain

パスは、通常パス、ShadowCaster、DepthOnlyの3つで、それがTransparent、Opaqueの両方あるので合計6個のパスがあります
おそらく、SRP Batchingのために、TransparentとOpaqueの両方のシェーダをまとめているんだと思う

Feature関連

                // Pragmas
                #pragma target 2.0
                #pragma only_renderers gles gles3 glcore
                #pragma multi_compile_instancing
                #pragma multi_compile_fog
                #pragma vertex vert
                #pragma fragment frag
    
                // DotsInstancingOptions: <None>
                // HybridV1InjectedBuiltinProperties: <None>
    
                // Keywords
                #pragma multi_compile _ LIGHTMAP_ON
                #pragma multi_compile _ DIRLIGHTMAP_COMBINED
                #pragma shader_feature _ _SAMPLE_GI
                // GraphKeywords: <None>
    
                // Defines
                #define ATTRIBUTES_NEED_NORMAL
                #define ATTRIBUTES_NEED_TANGENT
                #define FEATURES_GRAPH_VERTEX
                /* WARNING: $splice Could not find named fragment 'PassInstancing' */
                #define SHADERPASS SHADERPASS_UNLIT
                /* WARNING: $splice Could not find named fragment 'DotsInstancingVars' */

色々と書いてあるが、おおざっぱに
Instancing、Fog、dots instancing、ライトマップ系、アトリビュート、そしてshaderpass unlit
等設定しています
dots instancingとか、最近のUNITYの高速化、Data-Oriented Technology Stackの事でしょう
今度ちゃんと調べてみたいです

Includes

      #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
        #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureStack.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl"

これらのコードをIncludeしています
BuiltIn-Shaderではなく、URPのシェーダーを見に行ってますね

入出力

      struct Attributes
        {
            float3 positionOS : POSITION;
            float3 normalOS : NORMAL;
            float4 tangentOS : TANGENT;
            #if UNITY_ANY_INSTANCING_ENABLED
            uint instanceID : INSTANCEID_SEMANTIC;
            #endif
        };
        struct Varyings
        {
            float4 positionCS : SV_POSITION;
            #if UNITY_ANY_INSTANCING_ENABLED
            uint instanceID : CUSTOM_INSTANCE_ID;
            #endif
            #if (defined(UNITY_STEREO_MULTIVIEW_ENABLED)) || (defined(UNITY_STEREO_INSTANCING_ENABLED) && (defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE)))
            uint stereoTargetEyeIndexAsBlendIdx0 : BLENDINDICES0;
            #endif
            #if (defined(UNITY_STEREO_INSTANCING_ENABLED))
            uint stereoTargetEyeIndexAsRTArrayIdx : SV_RenderTargetArrayIndex;
            #endif
            #if defined(SHADER_STAGE_FRAGMENT) && defined(VARYINGS_NEED_CULLFACE)
            FRONT_FACE_TYPE cullFace : FRONT_FACE_SEMANTIC;
            #endif
        };

おそらく入力パラメータAttributesは #defineで有効にしたものが入ってくるのだろう
それにInstancing用のID

Varyingsは、InstancingやVR等で条件コンパイルされてるが、ポジション程度しかない

それらの入力値を使い、データを抽象化しているのか、シェーダーライブラリ内で使う構造体に変換しているのだと思われる
SurfaceDescriptionInputs、VertexDescriptionInputs、PackedVaryingsと変換関数がある

実際の処理

          #include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/ShaderPass.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/Varyings.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Editor/ShaderGraph/Includes/UnlitPass.hlsl"

実際の処理はシェーダーライブラリの中だ
その中でも実際にシェーダーの中身は下記になる

PackedVaryings vert(Attributes input)
{
    Varyings output = (Varyings)0;
    output = BuildVaryings(input);
    PackedVaryings packedOutput = PackVaryings(output);
    return packedOutput;
}

half4 frag(PackedVaryings packedInput) : SV_TARGET 
{    
    Varyings unpacked = UnpackVaryings(packedInput);
    UNITY_SETUP_INSTANCE_ID(unpacked);
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(unpacked);

    SurfaceDescriptionInputs surfaceDescriptionInputs = BuildSurfaceDescriptionInputs(unpacked);
    SurfaceDescription surfaceDescription = SurfaceDescriptionFunction(surfaceDescriptionInputs);

    #if _AlphaClip
        half alpha = surfaceDescription.Alpha;
        clip(alpha - surfaceDescription.AlphaClipThreshold);
    #elif _SURFACE_TYPE_TRANSPARENT
        half alpha = surfaceDescription.Alpha;
    #else
        half alpha = 1;
    #endif

#ifdef _ALPHAPREMULTIPLY_ON
    surfaceDescription.BaseColor *= surfaceDescription.Alpha;
#endif

    return half4(surfaceDescription.BaseColor, alpha);
}

まあ、ここだけ見ると単純です

シンプルなUnlitシェーダー

白く塗りつぶすだけの非常に簡単なシェーダーを作った

Shader "MyShader/URPUnlit"
{
    SubShader
    {
        Tags
        {
            "RenderPipeline" = "UniversalPipeline"
            "RenderType" = "Opaque"
            "UniversalMaterialType" = "Unlit"
            "Queue" = "Geometry"
        }
        Pass
        {
            Name "Pass"

            // Render State
            Cull Back
            Blend One Zero
            ZTest LEqual
            ZWrite On

            HLSLPROGRAM

            // Pragmas
            #pragma target 4.5
            #pragma exclude_renderers gles gles3 glcore
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/UnityInput.hlsl"

            struct Attributes
            {
                float3 positionOS : POSITION;
                float3 normalOS : NORMAL;
                float4 tangentOS : TANGENT;
            };
            struct Varyings
            {
                float4 positionCS : SV_POSITION;
            };

            
            float3 TransformObjectToWorld(float3 positionOS)
            {
                return mul(unity_ObjectToWorld, float4(positionOS, 1.0)).xyz;
            }

            float4 TransformWorldToHClip(float3 positionWS)
            {
                return mul(unity_MatrixVP, float4(positionWS, 1.0));
            }

            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;
                output.positionCS = TransformWorldToHClip(TransformObjectToWorld(input.positionOS));
                return output;
            }

            half4 frag(Varyings input) : SV_TARGET
            {
                return half4(1,1,1,1);
            }
            ENDHLSL
        }
    }
}

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

includeは2個あるが、UNITYのシェーダーのCORE的なものと、RPから入ってくるUNITY定数の宣言
思ったより簡単に書けたが、色々な処理を無視しているし
影とかは難しそうな気配・・
PS,XBOX,Switch等の対応とかも考えると、ShaderGraphで書く方が楽だろうなあ・・・

テクスチャマッピングする

これは非常に簡単だった
今までのBuiltIn-Shaderの作法にのっとり
Texture2DをPropertiesに出し、Texture2DとSamplerを宣言し
AttributesとVaryingsに TEXCOORD0を入れて
SAMPLE_TEXTURE2Dでマッピングした結果をリターンすればいい

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

意外と素直

影を落とす

"Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
をIncludeすることにした

そして今回はメインライトからの影のみを取るので
pragma multi_compile _ _MAIN_LIGHT_SHADOWS をつけた

shadowAttenuationを計算するのだが、ShadowMapをSampleするには、ワールド座標のPositionが必要なので
positionWSを計算することに
TransformObjectToWorld(input.positionOS)
で簡単に取得できた

ワールド座標系のオブジェクトポジションを使い
TransformWorldToShadowCoord(input.positionWS)
をすると、ShadowCoordが取得できるので、そこから
GetMainLightを使い、メインライトの情報、影の情報が取れる
今回はライトカラーなどは使わずshadowAttenuationだけを使い、テクスチャにそのまま乗算した

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

とりあえず影はできた

今回の完成シェーダコード

Shader "MyShader/URPUnlit"
{
    Properties
    {
            [NoScaleOffset] MainTex("_MainTex", 2D) = "white" {}
            [HideInInspector][NoScaleOffset]unity_Lightmaps("unity_Lightmaps", 2DArray) = "" {}
            [HideInInspector][NoScaleOffset]unity_LightmapsInd("unity_LightmapsInd", 2DArray) = "" {}
            [HideInInspector][NoScaleOffset]unity_ShadowMasks("unity_ShadowMasks", 2DArray) = "" {}
    }


    SubShader
    {
        Tags
        {
            "RenderPipeline" = "UniversalPipeline"
            "RenderType" = "Opaque"
            "UniversalMaterialType" = "Unlit"
            "Queue" = "Geometry"
        }
        Pass
        {
            Name "Pass"

            // Render State
            Cull Back
            Blend One Zero
            ZTest LEqual
            ZWrite On

            HLSLPROGRAM

            // Pragmas
            #pragma target 4.5
            #pragma exclude_renderers gles gles3 glcore
            #pragma vertex vert
            #pragma fragment frag


            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS

            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/UnityInput.hlsl"

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            TEXTURE2D(MainTex);
            SAMPLER(samplerMainTex);

            struct Attributes
            {
                float3 positionOS : POSITION;
                float3 normalOS : NORMAL;
                float4 tangentOS : TANGENT;
                float4 uv0 : TEXCOORD0;
            };
            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float4 texCoord0 : TEXCOORD0;
                float3 positionWS: TEXCOORD1;
            };

            Varyings vert(Attributes input)
            {
                Varyings output = (Varyings)0;
                output.positionCS = TransformWorldToHClip(TransformObjectToWorld(input.positionOS));
                output.texCoord0 = input.uv0;
                output.positionWS = TransformObjectToWorld(input.positionOS);
                return output;
            }

            half4 frag(Varyings input) : SV_TARGET
            {
                half4 o = SAMPLE_TEXTURE2D(MainTex, samplerMainTex, input.texCoord0.xy);
                Light l = GetMainLight(TransformWorldToShadowCoord(input.positionWS));

                return o*l.shadowAttenuation;
            }
            ENDHLSL
        }
    }
}

Instancingやメイン以外のライト等、いろいろすっ飛ばしているので実用性はないが
URPのシェーダーは、こんな感じで書く事は可能だ
ShadowCasterやDepthOnlyパスは消してしまったが同じように簡単に作る事ができそう

ただし、URPはバージョンアップで色々変わってしまうので
ShaderGraphを使う方が楽と思う