rn.log

備忘録など

【URP10.8.1】ConfigureTargetでDepthバッファを指定しないとZテストが機能しない件についてメモ

はじめに

自前で用意したレンダーターゲットにオブジェクトを描画する時、デプスバッファが正しく指定されていない場合にZテストが正常に機能しません。

今回の記事では、Zテストが正常に機能しないケースの実装と、Zテストが正常に機能するケースの2つを紹介したいと思います。

環境

Universal RP 10.8.1 Unity2020.3.32f1

ScriptableRenderPassのConfigure

ScriptableRenderPassを用いてオブジェクト描画する処理を自前で実装したいケースを考えます。

ScriptableRenderPassを継承したクラスでは、ConfigureTargetメソッドを利用してレンダーターゲットを切り替えることができます。 ConfigureTargetではカラーバッファのほかに、デプスバッファを指定することができます。

            ConfigureTarget(_colorTargetId); // カラーだけを指定
            ConfigureTarget(_colorTargetId, _depthTargetId); // カラーとデプスの両方を指定

検証1

以下のようにオブジェクトを配置してみました。

通常の描画

より手前のオブジェクトが画面に表示されます。 Zテストが正常に機能していることが分かります。

使用したシェーダー

シェーダー1

手前の白いオブジェクトには、以下のシェーダーをアタッチしています

Shader "Unlit/1"
{
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            Tags { "LightMode"="SRPDefaultUnlit" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            half4 _Color;

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

            half4 frag (v2f i) : SV_Target
            {
                return _Color;
            }
            ENDCG
        }
    }
}

シェーダー2

奥の赤いオブジェクトには、以下のシェーダーをアタッチしています

Shader "Unlit/2"
{
    Properties
    {
        _Color ("Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            Tags { "LightMode"="SRPDefaultUnlit" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            half4 _Color;

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

            half4 frag (v2f i) : SV_Target
            {
                return _Color;
            }
            ENDCG
        }
    }
}

検証2. 片方のオブジェクトを別のカラーバッファに書きこみ

シェーダー書き換え

シェーダー2のタグを以下のように変更します。

Tags { "LightMode"="MyTag" }

URPはMyTagタグを実行してはくれません。 実行させるためのレンダラー実行処理を記述する必要があります。

ScriptableRenderPass

オブジェクトを描画するScriptableRenderPassをまず作成します。 このRenderPassは任意のレンダーターゲットの指定が可能です。

URP内部のRenderObjectsPassを参考にして実装しました。

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering;

namespace MyRendering
{
    public class MyRenderObjectsPass : ScriptableRenderPass
    {
        RenderQueueType renderQueueType;
        FilteringSettings m_FilteringSettings;
        ProfilingSampler m_ProfilingSampler;

        public Material overrideMaterial { get; set; }
        public int overrideMaterialPassIndex { get; set; }

        List<ShaderTagId> m_ShaderTagIdList = new List<ShaderTagId>();
        RenderStateBlock m_RenderStateBlock;
        private int _colorTargetId;

        public void Setup(int colorTargetId)
        {
            _colorTargetId = colorTargetId;
        }

        public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
        {
            base.Configure(cmd, cameraTextureDescriptor);
            cmd.GetTemporaryRT(_colorTargetId, cameraTextureDescriptor);
            
            ConfigureTarget(_colorTargetId);
        }

        public override void OnCameraCleanup(CommandBuffer cmd)
        {
            base.OnCameraCleanup(cmd);
            cmd.ReleaseTemporaryRT(_colorTargetId);
        }

        public MyRenderObjectsPass(string profilerTag, RenderPassEvent renderPassEvent, string[] shaderTags, RenderQueueType renderQueueType, int layerMask)
        {
            base.profilingSampler = new ProfilingSampler(nameof(MyRenderObjectsPass));

            m_ProfilingSampler = new ProfilingSampler(profilerTag);
            this.renderPassEvent = renderPassEvent;
            this.renderQueueType = renderQueueType;
            this.overrideMaterial = null;
            this.overrideMaterialPassIndex = 0;
            RenderQueueRange renderQueueRange = (renderQueueType == RenderQueueType.Transparent)
                ? RenderQueueRange.transparent
                : RenderQueueRange.opaque;
            m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);

            if (shaderTags != null && shaderTags.Length > 0)
            {
                foreach (var passName in shaderTags)
                    m_ShaderTagIdList.Add(new ShaderTagId(passName));
            }
            else
            {
                m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
                m_ShaderTagIdList.Add(new ShaderTagId("UniversalForward"));
                m_ShaderTagIdList.Add(new ShaderTagId("UniversalForwardOnly"));
                m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward"));
            }

            m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
        }

        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            SortingCriteria sortingCriteria = (renderQueueType == RenderQueueType.Transparent)
                ? SortingCriteria.CommonTransparent
                : renderingData.cameraData.defaultOpaqueSortFlags;

            DrawingSettings drawingSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, sortingCriteria);
            drawingSettings.overrideMaterial = overrideMaterial;
            drawingSettings.overrideMaterialPassIndex = overrideMaterialPassIndex;

            // NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
            // Currently there's an issue which results in mismatched markers.
            CommandBuffer cmd = CommandBufferPool.Get();
            using (new ProfilingScope(cmd, m_ProfilingSampler))
            {
                context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref m_FilteringSettings,
                    ref m_RenderStateBlock);
            }
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }
    }
}

ScriptableRendererFeature

次に、 MyTagパスタグが付いたシェーダーを実行し、_MyColorTarget というレンダーターゲットに対して描画を行うScriptableRendererFeatureを作成します。

URP内部のRenderObjectsを参考にして実装しました。

using UnityEngine;
using UnityEngine.Experimental.Rendering.Universal;
using UnityEngine.Rendering.Universal;

namespace MyRendering
{
    public class MyRenderObjects : ScriptableRendererFeature
    {
        MyRenderObjectsPass renderObjectsPass;

        public override void Create()
        {
            var lightModeTags = new string[]
            {
                "MyTag"
            };
            renderObjectsPass = new MyRenderObjectsPass(
                "Render MyTag",
                RenderPassEvent.AfterRendering,
                lightModeTags,
                RenderQueueType.Opaque,
                -1);
        }

        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            int colorTargetId = Shader.PropertyToID("_MyColorTarget");
            renderObjectsPass.Setup(colorTargetId);

            renderer.EnqueuePass(renderObjectsPass);
        }
    }
}

上記のRendererFeatureを Forward Renderer Data に登録すると、RendererFeatureが実行されます。

結果

FrameDebuggerを見ると、シェーダー2がアタッチされた赤いオブジェクトが、_MyColorTarget というレンダーターゲットに描画されることが確認できます。 検証1では赤いオブジェクトはZテストによって白いオブジェクトの裏に隠れていましたが、検証2では隠れていません。

検証3. デプスバッファを指定する

MyRenderObjectsPassにてデプスバッファを指定できるようにします。 MyRenderObjectsPass.cs を以下のように書き換えます。

        public void Setup(int colorTargetId, int depthTargetId)
        {
            _colorTargetId = colorTargetId;
            _depthTargetId = depthTargetId;
        }

        public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
        {
            base.Configure(cmd, cameraTextureDescriptor);
            cmd.GetTemporaryRT(_colorTargetId, cameraTextureDescriptor);
            
            ConfigureTarget(_colorTargetId, _depthTargetId);
        }

MyRenderObjects.cs は、以下のように書き換えます。 _CameraDepthTexture をレンダーターゲットのデプスバッファに指定しています。

        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            int colorTargetId = Shader.PropertyToID("_MyColorTarget");
            int depthTargetId = Shader.PropertyToID("_CameraDepthTexture");
            renderObjectsPass.Setup(colorTargetId, depthTargetId);

            renderer.EnqueuePass(renderObjectsPass);
        }

_CameraDepthTexture を有効化するため、Universal Render Pipeline Asset の Depth Texture を有効化しておきます。

結果

赤いオブジェクトが、手前のオブジェクトに遮蔽されるようになりました。

その他 : _CameraDepthTexture が書きこまれるパス

CopyDepth というパスが実行されたタイミングで、_CameraDepthTexture へデプス値が書きこまれます。

2023年振り返り

はじめに

2023年について振り返ろうと思います。

目次

書いた記事

2023年は記事を10件くらい書きました。グラフィックス周りの記事が多めです。

https://zenn.dev/r_ngtm/articles/urp14-custom-shadow https://zenn.dev/articles/urp-motion-blur
https://zenn.dev/articles/urp-vertex-shadow-volume
https://zenn.dev/r_ngtm/articles/urp-transparent-eyebrow
https://zenn.dev/r_ngtm/articles/novashader-ink-splatter
https://zenn.dev/r_ngtm/articles/shadergraph-tree-wind
https://zenn.dev/r_ngtm/articles/unity-mrt-urp10-urp14
https://zenn.dev/r_ngtm/articles/substance-water-flipbook
https://zenn.dev/r_ngtm/articles/urp-tessellation
https://zenn.dev/r_ngtm/articles/unity-z-fighting
https://zenn.dev/r_ngtm/articles/urp14-custom-shadow
https://zenn.dev/r_ngtm/articles/unity-urp10-shader-memo

転職

去年の12月1日に転職をし、渋谷のゲーム会社に中途入社しました。
職種はクライアントエンジニア (グラフィックスエンジニア) になります。

仕事はおもしろく、休日になったとしても 「早く仕事がしたい...」と思ってしまうほどに、
仕事は充実しています。

転職した理由

転職して1年くらいたち、何故転職したのかをちょっと忘れかけてきたので、 まとめてみます。

理由1. 転職先の会社のゲームが好きで、開発に関わりたかった (ユーザーとして1年くらいプレイしていました)
理由2. 実力のある人が多く在籍しており、技術的に得られるものが多そうだと感じた (成長できそうと感じた)
理由3. タイミング的に今しかない、と感じた
(関わりたいゲームが伸びている時期にあり、入社を後回しにすると好機を逃すと感じたため)
理由4. 開発の裁量が高そうで、意見を言いやすそうだと感じた (風通しが良さそう)
理由5. 給与も良さそうと感じた

働いてみてどうだったか

入社してから1年たったので、箇条書きでまとめてみます。

- 仕事やものづくりの熱量が高い人が多い
 - 良いものを作ることに貪欲な人がかなり多い印象を受けました
- グラフィックス開発の経験を業務レベルで得ることができている (と思う)
  - プロジェクトの要件に合わせたグラフィックスの機能開発
  - OSS のグラフィックスのライブラリを導入し、要件に合わせてカスタマイズ
  - 描画の不具合が起きたときに、プロファイラーなどを利用して不具合を調査
- 優秀な人が多い
- ゲーム・アニメなどのコンテンツが好きな人が多い
- 働く環境がかなり良く、快適に働ける
- ランチが美味しい
- 給与も上がった

仕事について

主に以下のような業務を行っています。
- グラフィックス周りの機能開発
- 空いた時間にツールを導入・開発してチームの開発効率の底上げ
- 社内ドキュメントの整備
など

運が良かったのか、6月にMVPにノミネートされました。

仕事の抱負

入社してから1年くらい経ち、会社にはかなり慣れました。
コンフォートゾーンにいる状態だと思います。
2024年はコンフォートゾーンを抜け出さねば、と思います。

プライベート

ここ1年を仕事ばかりに時間を使っていたため、プライベートはあまり充実していなかったと感じます。

来年はプライベートも充実させたほうが良いかも?と感じました。

来年は、仕事以外の夢中になれるものを見つけたい

面白かったゲーム

  • アーマードコア6 (エンディング全部観ました)
  • 崩壊スターレイル (リリースから1か月くらいは遊んでました)
  • 呪術廻戦ファントムパレード (現在進行形で遊んでます。呪術廻戦は好きなコンテンツなので、しばらくは遊び続けるかなと思います)
  • ゼルダの伝説ティアーズオブザキングダム (1か月くらい遊んでました)
  • ピクミン4 (体験版を全クリしました。製品版を買いましたが、1,2週間くらい遊んだ後、起動しなくなってしまいました)
  • FF16 (体験版を全クリしました)
  • DAVE THE DIVER (2日くらい遊んで、そのまま放置しています)
  • イカゲーム (2日くらい遊び、そのまま放置しています)
  • スーパーマリオワンダー(全クリしました)

時系列の振り返り

2月

渋谷区に引っ越しました。
会社から歩いて行けるくらいのところに引っ越しまして、通勤のストレスから解放されました。

5月

デザイナーズバイブルに寄稿させていただきました。 r-ngtm.hatenablog.com

6月

会社のMVPにノミネートされました。

2024年の抱負

  • 仕事のコンフォートゾーンを抜け出す
  • 仕事以外でも夢中になれる何かを見つける (プライベートを充実させる)

【宣伝】「Unityデザイナーズ・バイブル Reboot」 に寄稿しました 【Houdini / VAT / ShaderGraph】

はじめに

「Unityデザイナーズ・バイブル Reboot」にて、
「Houdiniを利用した手書きエフェクトのメッシュ化」について寄稿させていただきました。

書籍は 2023年6月上旬 発売です。


内容について

VATの作成 (前半)

チャプターの前半では、連番画像をVATと呼ばれるメッシュデータに変換し、UnityのParticleSystem上で再生する方法を紹介します。
メッシュデータはHoudini という3DCGソフトウェアを使用して作成しています。

連番の2Dエフェクト画像のサンプル
www.youtube.com

メッシュのサンプル動画
www.youtube.com

連番画像データや、VATデータは書籍付属のサンプルプロジェクトに同梱しています。

シェーダーのカスタマイズ (後半)

チャプターの後半では、作成したVATがエフェクトして映えるようにチューニングする方法を紹介します。
シェーダー(ShaderGraph)をParticle System向けにカスタマイズしています。

シェーダーを調整してリッチに見せる

画像をメッシュに変換する意味

画像は拡大すると粗くなってしまいます。
メッシュに変換することで、拡大しても滑らかに表示されます。

メッシュは拡大しても滑らか

動作環境について

  • Unity 2021.3.16f1
  • Universal RP 12.1.8
  • Houdini 19.5.435

書籍リンク

amazon https://amazon.co.jp/dp/4862465552

Twitter https://twitter.com/bd_publishing/status/1653217567593623553

●書籍 https://borndigital.co.jp/book/29947.html

●PDF書籍 https://borndigital.co.jp/book/29967.html

【ふりかえり】CyberAgent Graphics Academy に参加しました

はじめに

CyberAgentさんが主催する、Graphic Academy に参加させていただきました。

www.cyberagent.co.jp

今回は、ふりかえりの記事を書きたいと思います。

アカデミーの流れ

アカデミーの流れをざっくりまとめると、以下のような流れでした。

  • 書類選考
  • 面接
  • 講義 (2022/02/05 ~ 2022/04/09)
  • 制作物発表会 (2022/4/23)

アカデミー開始以前

書類選考 (2021年12月)

Googleフォーム上で質問に回答し、応募を行います。

質問の内容は以下のようなものでした

  • アカデミーを受講したいと思った理由
  • CyberAgentへの興味
  • 印象に残っている開発経験
  • 使用したことのあるフレームワークや開発言語
  • アピールポイント
  • 学歴

グラフィックスを学ぶ下地があるかどうかを判断されたのではないかと思います。

アカデミーのDay1では、カメラ変換周りの話や、行列演算まわりの講義があり、
多少の数学の知識が必要になりました。

面接 (1月)

  • 書類選考を通過すると、次は面接があります。
  • 面接の日程は希望を出すことができる形になっていました (Googleカレンダー上で選ぶ形でした)
  • 面接では、アカデミーの趣旨をCAの方が解説してくれました
  • 自分からは、グラフィックスを体系的に学びたいという意思や、グラフィックスエンジニアとしてのキャリアに興味がある、などといった話をしました。(プロセカを遊んでいるという話もしました)
  • ミスマッチを防ぐ場として、面接を行っているとの話でした

余談 : 面接の場にはCAの矢野 春樹さん(@harumak_11)がおられたので緊張しました。 LIGHT11にはいつもお世話になってます。

選考通過 (1月末)

  • 1月末あたりに、選考結果はメールで届き、Graphics AcademyのSlackに招待されました。
  • これ以降は、Slack上でCyberAgentの運営の方々や、ほかの受講生の方々とやり取りをしていくことになります。
  • 受講生は、私を含めて20名いました。(技術的につよつよな方々が集まっている印象でした)

グラフィックスに興味がある人が20人も集まるなんて、すごい!

教材が届く (2月)

  • グラフィックスアカデミーの講義資料(紙の冊子)
  • コンピューターグラフィックス (紙の書籍)
  • HLSLの魔導書

講義について

  • Zoom上で講義があり、グラフィックスに関わるあらゆることを叩き込まれます
  • 講師の方は、Unityの石井 勇一さん(@z_zabaglione)でした
  • 内容の一例
    • グラフィックスの理論寄りの話
      • 画像フォーマットの話
      • カメラ座標変換や、行列演算まわりの話
      • NPRやPBRの話
      • 影の描画の仕組み
      • ライティング など
    • NPRシェーダーやPBRシェーダーの作成
    • 複数Passによるシェーダー(.shader)の実装
    • ポストプロセスの話
      など

NRPシェーダーに関しては、記事にまとめてみました。
【Unity ShaderLab】NPRなライティングを実装してみる

講義の流れ

  • 毎週土曜日、朝の10:00から夜の19:00までZoom上で講義があります
    • 受講生20名がZoom上に集まり、講師(石井さん)の講義を受けます。
       - 1時間ごとに10分くらいの休憩がありました。
    • 13:00から14:00までお昼休み
    • 石井さんの講義があるのは 10:00 ~ 16:00あたりで、残りの時間は課題のレポート作成に時間を使う感じでした。
  • 毎週課題があり、次の講義までに提出する必要があります
    • 課題の形式はレポート形式で、自由なフォーマットで書くことができました。

受講生の方々の熱意が高く、欠席者はいませんでした (体調不良で欠席する人が1, 2名いた程度でした)

最終課題デモ

アカデミーの最終日には、制作物を発表するという最終課題があります。
Day5 のタイミングで、CAの方々が最終課題の一例(デモ)を見せてくれました。
- 株式会社Colorful Palette の山口 智也さん ( @togucchi )
- 株式会社グレンジ の山上 翔さん

山口さんの作品はニーアっぽい雰囲気の作品で、山上さんの作品はトゥーン系の表現でした。


使われていたテクニックの一例
- Deferredレンダリング
- G-Buffer(深度や法線)を利用した、アウトライン抽出
- グラデーションフォグ
- 距離・高さに依存するカスタムフォグ
- Cook-Torranceのスペキュラモデルを利用した金属表現
- Jump Floodを利用したアウトライン
- 色と場所を指定できるBloom (スキンブルーム)
などなど

山口さんはプロセカに関わっているということもあり、アーティストでも使いやすいようなカスタムエフェクトを作っているのが印象的でした。
参考 : 『プロセカ』の世界観はUnityシェーダで表現 空気感、柔らかさ、奥行きを作る4つのポストエフェクト - ログミーTech

懇親会 (2回)

  • CAの方々とZoom上で情報交換する機会がありました
  • 懇親会では nonpi の食事をふるまってくれました (費用はCyberAgentさんが全部持ってくれるという事で、太っ腹です)
  • こちらの質問にはどんなことでも答えてくれる感じでした。
    • Cyber Agentという組織について
    • グラフィックスエンジニアの業務について
    • グラフィックスエンジニアのキャリアについて
  • Cyber Agentの方が場を取り仕切ってくれたので、気持ちよく時間をすごせました。

井戸端

  • 受講生(希望者)が毎週木曜日・日曜日の22:00~23:00にあつまり、課題のわからないところを教えあうという取り組み(井戸端)がありました。
  • 井戸端は受講生の一人(丸山 純さん) のアイデアから始まった取り組みになります。

制作物発表会 (最終課題)

受講生の方々が、絵作りをテーマにして作品発表を行いました。
テーマはさまざまでした。

  • キャラクター表現をテーマにした作品
  • SFをテーマにした作品
  • ブラウン管シェーダー
  • 水槽をテーマにした作品
  • ハッチングを利用した手描き調の表現
  • 草原をテーマにした作品
  • 雪原をテーマにした作品
  • 光の使い方にこだわったライブステージ
  • ニーアっぽい絵作り
  • 海をテーマにした作品
  • アメコミをテーマにした作品
    などなど

人によって目指す方向性が違うのが面白かったです。
- 汎用性を意識している方
- パフォーマンスを意識した方
- 技術的な興味関心が高い方
- ルックをリッチにすることを意識した方

作品

私は 「リアル調と手描き調の両立」をテーマにした作品を制作しました。


ポストエフェクトで手書きっぽい感じに加工しています。

アセットは、CyberAgentゲーム事業部から提供いただいた素材をお借りしています。
(BLADE XLORD のアセットになります)

制作の流れは以下のような感じです。
- 火山のアセットを並べて、URP PostProcessingで加工
- 陽炎エフェクトを作成し、高熱を表現
- 地面マテリアルを調整し、キャラクターを目立たせる
- 距離・高さに依存するフォグの作成
- 深度に応じて彩度を落とすエフェクトの作成
- 近景を赤・遠景を青に寄せる (色彩遠近法)
- 深度を利用したアウトライン描画キャラだけを専用テクスチャに描画した後、アウトライン抽出
- Sobelフィルタで水彩画調に加工


作品をOSSとして公開している方もいました。(yunodaさん)

アカデミーで得たもの

グラフィックスの根幹にあるものへの理解が深まった

  • カメラ変換周り・ノーマルマッピング・影の描画・ライティングあたりの話は理解が浅かったのでとても勉強になりました
  • グラフィックスに関する疑問をSlackで投げると、講師やCAの運営の方々がすぐに回答してくれたのも良かったです。
  • 課題の中には、カメラのビューボリュームの投影を可視化するという課題があり、カメラ変換のイメージがつかめるようになりました。

絵作りの楽しさを知ることができた

  • 絵が美しいゲームを観察し、どうすればそれに近づけるのかをひたすら思考し、実装するという経験ができました。
  • 絵がそこそこのクオリティになるまでに2、3週間かかりました。
  • 数週間かけて、自分の絵を目標とする絵に近づけていくというもので、ある意味デッサンに近い行為だと感じました。
  • グラフィックスが美しいゲームは「自分には作れないかも」と今まで感じていたが、「自分でも作れるかもしれない」という認識に変わった

最終課題で得られるものが多かった

  • ゲームを観察し、どういう技術が使われているかを観察するトレーニングになった
  • 絵の見た目をリッチにしていく過程で、実装やアイデアに行き詰まることがあった
    • その際に、技術カンファレンスの資料や、論文にヒントが書かれてないか読んでヒントを探すということを行った
    • カンファレンス資料や論文などは、自分の道具として考えられるようにもなりました
    • アカデミーを受ける以前より、CEDECの資料を読むことへの抵抗が薄くなったと感じます
  • ほかの方の発表を見ることで勉強になった
    • 技術的に工夫したポイント
    • 苦労したポイント
    • 他の方の発表スライドと最終課題のUnityプロジェクト一式を見ることができる

グラフィックス技術資料を読むことへの心理的な抵抗が減った

  • 実装が上手くいかないとき、CEDECの資料を読んでヒントが無いか探した
  • 論文を読むこともあった

グラフィックスの引き出しが増えた

  • Slack上で、グラフィックス関連の情報交換が盛んにおこなわれていました
  • グラフィックスに関する情報が集約される感じで、グラフィックスの引き出しが増えたと感じました。

受講生とのつながりができた

  • グラフィックスアカデミーが終わった後も、Slackは残したままにしていただけるとのことでした
  • アカデミーが終わった後も、交流は続けていきたいと思っています。
  • 最終課題をブラッシュアップしたものに上げる、みたいな使い方をするのも良さそうな気がします。
  • 作品の途中経過を上げると、他の受講生の方がリアクションしてくれたり、為になる情報を共有してくれたりなど、 いろいろと勉強になりました。
  • 逆に他の受講生が詰まっているところがあったとき、自分からアイデアを提案するといったことも行いました。
  • 互いに教えあうという良好な関係を築くことができたと感じました。

FrameDebuggerと仲良くなった

  • 思った通りの結果にならない、と思ったときはFrameDebuggerとにらめっこしていました。
  • シェーダーへの入力・シェーダーの出力・レンダーターゲットといった情報が見れるので、描画のデバッグには欠かせない機能です

Slack上で話題に上がったシェーダーやツール

NiloCatさんが出しているToonシェーダー
- https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample
Microsoft PowerToys : https://docs.microsoft.com/ja-jp/windows/powertoys/
- PowerToys に入っているカラーピッカーを使うと、画面の色を数値で見ることができ、シェーダーをデバッグする際に役に立ちます。

Color Picker で色を表示

アカデミーを終えてみて

  • グラフィックスを学ぶ仲間がいたので最後までモチベーションを保ったままグラフィックスを学ぶことができた
    • 独学でグラフィックスを学んでいた時は、一緒に学ぶ仲間がいなかったり、発表の機会が無かったので挫折しやすかった
  • 毎週課題を提出する必要もあったので、緊張感を持って取り組むことができた
  • 作品を発表するという場もあったので、そこを目標として絵作りに取り組むこともできた
  • 作品に納得がいっていない部分があるので、アカデミーが終わった後もクオリティアップを続けていきたい。

CyberAgentさんについて

  • 費用を投じて、他社のエンジニアにノウハウを教えるというのはすごいと思いました。
    • OSS活動も行っていたり、CA.unityを開催するなど、技術投資や業界貢献に力を入れている会社さんだと感じました
  • 優秀な方々が多く集まっているという印象を受けました

【技術書】シェーダーグラフの書籍をリリースしました【ShaderGraph CookBook vol.1】

はじめに

2021/03/21 に Unity ShaderGraphの本をリリースしました。
今回は、この本の魅力を紹介したいと思います。
zenn.dev

本書の特徴

  • 20種以上のノードの解説
  • 30種以上の作例
  • サンプルプロジェクト付き


特徴① : ノードの解説

ノードについての解説や、ノードで作れる表現の作例を紹介しています。

f:id:r-ngtm:20210408043105p:plain

サンプルページ

🍎 Sineノード|Unity ShaderGraph CookBook vol.1【ShaderGraph 入門】

特徴② : 豊富な作例

書籍では、30種以上の作例を紹介しています。
ShaderGraph単体で完結するような表現に焦点を絞っています。
f:id:r-ngtm:20210408044005g:plain

作例を知りたい方は以下のページをご覧ください。
🍍 レシピ一覧|Unity ShaderGraph CookBook vol.1【ShaderGraph 入門】



特徴③ : サンプルプロジェクト

本書の作例の一部は、GitHubにて公開しています。 github.com

環境

Unity2020.3.0f1
Universal RP 10.3.2

【Houdini + Unity】点光源の影でグリッドを作って遊ぶ

はじめに

Unityのポイントライトの影でグリッドを出してみました。
今回はこの3Dモデルの作り方を紹介します。

f:id:r-ngtm:20210208013405p:plain
ポイントライトの影でグリッドを作ってみました
www.youtube.com

作り方

グリッドを球面へ投影したようなモデルを作ります。 このモデルと点光源が地面に映し出す影はグリッドの形になります。

f:id:r-ngtm:20210208010331p:plain
平面上の点を球面へ投影

投影点の座標の求め方

線分PAの長さをD, 線分PBの長さをd、球の半径をRとおいたとき、以下のようになります。
dが分かれば、投影点Bの座標が分かります。

f:id:r-ngtm:20210208005259p:plain
平面上の点を球面に投影

長さdの導出

三角形 HPA と 三角形BPHは相似なので、以下が成り立ちます。

 
\begin{align}
PB:PH &= PH : PA \\\
\\\
d:2R &= 2R : D \\\
\\\
d&= \frac{4R^2}{D}  
\end{align}

投影点Bの座標は以下のようになります。


\vec{B} = \vec{P} + d \cdot  \dfrac {\vec{PA}}{D}

Houdiniで実装する

投影点Bの座標が求まったので、次にこれをHoudiniで実装します。

Grid

Gridノードを使ってグリッドを作成します。

f:id:r-ngtm:20210208012423p:plain
グリッド作成

PolyExtrude

PolyExtrudeノードを使ってグリッドに穴をあけます。

f:id:r-ngtm:20210208012500p:plain
穴をあける

Attribute Wrangle

投影点の座標を計算して、グリッド点を球へ投影します。

f:id:r-ngtm:20210208012809p:plain
投影点の計算

vector origin = { 0, 1, 0 }; // 点Pの座標
vector p = (@P - origin); // 点Pから平面へ向かうベクトル
vector dir = normalize(p); // 正規化
float R = 0.5; // 球の半径
float D = length(p); // 点Pから平面上の点までの距離
float d = 4 * R * R / D; // 点Pから投影点までの距離

@P = origin + d * dir; // 投影点の座標で置き換える

投影したモデルは、ROP FBX Outputノードなどで3Dモデル化します。

完成

モデルをUnityへ取り込み、点光源を置くと、影がグリッドになります。

f:id:r-ngtm:20210208012942p:plain
モデルと点光源を配置

点光源の位置

f:id:r-ngtm:20210208013119p:plain
点光源の位置 (0, 1, 0)

モデルの位置

f:id:r-ngtm:20210208013143p:plain
モデル位置 (0, 0, 0)

関連書籍

www.amazon.co.jp

【シェーダーグラフメモ その58】XOR演算を利用した市松模様

はじめに

XOR演算を利用して、市松模様を作る方法を紹介します。

f:id:r-ngtm:20210130051822p:plain:w320
市松模様

今回はワールド座標のX,Z成分から市松模様を作ります。

目次

 

環境

Unity2020.2.0f1
Universal RP 10.2.2


市松模様の作り方

タテ方向、ヨコ方向のシマシマを作ります。

f:id:r-ngtm:20210130045131p:plain
シマシマ

二つのシマシマに関してXORを適用すると、市松模様が作れます

f:id:r-ngtm:20210130045207p:plain
XORを取ると市松模様になる

XORについて

xor(a,b)は以下のような値を取ります。

a b xor(a,b)
0 0 0
0 1 1
1 0 1
1 1 0

シェーダーグラフ実装

シマシマを作る

以下のようなシェーダーグラフを組みます。

f:id:r-ngtm:20210130050053p:plain
ワールド座標からシマシマを作る

結果

これを板ポリゴンに貼り付けると、以下のようになります。 f:id:r-ngtm:20210130050809p:plain:w480

色のX成分、Z成分はそれぞれ以下のようになっています。

f:id:r-ngtm:20210130051643p:plain
X,Z 成分はそれぞれ0, 1 の繰り返しになっている

次にXORを利用して市松模様を作ります。

f:id:r-ngtm:20210130051822p:plain:w320
市松模様

ShaderGraphにXOR演算は無い

ShaderGraphにはXOR演算は用意されておらず、自分で作る必要があります。 今回はXORの実装方法を3つ紹介します。

方法1 : Abs を利用したXOR

1つ目の方法はAbsを利用したXORの実装です。

abs(a-b)

X成分(横シマ)からZ成分(縦シマ)を減算すると、以下のようになります。

f:id:r-ngtm:20210130054431p:plain
二つのシマシマを引き算する

減算結果にAbs(絶対値)を取ると、0,1が交互に並びます。

f:id:r-ngtm:20210130054944p:plain
abs(絶対値)を適用

シェーダーグラフで実装すると以下のようになります。

f:id:r-ngtm:20210130055257p:plain
absを利用したXORの実装

方法2 : X・(1 - Z) + (1 - X)・Z

以下の計算式でXORを作ることができます。

x * (1 - z) + (1 - x) * z

f:id:r-ngtm:20210130060901p:plain
X(1-Z) + (1-X)Z

f:id:r-ngtm:20210130061329p:plain
X(1-Z) + (1-X)ZによるXOR実装

方法3 : Moduloを利用したXOR

3つ目の方法は、Moduloを使ったXORの実装です

fmod(a+b, 2.0)

X成分とZ成分を足し合わせると、以下のようになります。

f:id:r-ngtm:20210130053513p:plain
横のシマシマと縦のシマシマの加算

2で割った余りを計算すると0と1が交互に並びます。

f:id:r-ngtm:20210130053955p:plain
Addした結果にMod 2 を取る

シェーダーグラフで実装すると、以下のようになります。

f:id:r-ngtm:20210130052243p:plain
Moduloを使ったXOR実装

結果

f:id:r-ngtm:20210130051822p:plain:w320
市松模様