Version: 2023.1
言語: 日本語
ムービーテクスチャ
テクスチャ配列

3D テクスチャ

3D テクスチャは、標準の 2 次元ではなく 3 次元の情報を含むビットマップ画像です。3D テクスチャは、フォグや煙などのボリュメトリック効果をシミュレートしたり、ボリュメトリックな 3D メッシュを近似したり、アニメーション化されたテクスチャを格納してそれらを滑らかにブレンドしたりするためによく使用されます。

3D テクスチャサイズ

3D テクスチャの最大解像度は 2048 x 2048 x 2048 です。

メモリとディスク上の 3D テクスチャのサイズは、解像度が上がるとすぐに大きくなることに注意してください。ミップマップがなく、解像度が 16 x 16 x 16 の RGBA32 3D テクスチャのサイズは 128KB ですが、解像度が 256 x 256 x 256 の場合、サイズは 512MB です。

3D テクスチャのインポート

3D テクスチャは、セルに分割されたソーステクスチャファイルからインポートできます。これをフリップブックテクスチャと呼びます。以下のように行います。

  1. ソーステクスチャを Unity プロジェクトにインポートします。
  2. Project ビューで、出来上がったテクスチャアセットを選択します。Unity は Inspector にテクスチャのインポート設定を表示します。
  3. Inspector で、 Texture Shape3D に設定します。Unity は Columns と Rows のプロパティを表示します。
  4. Columns (列) と Rows (行) を、フリップブックのテクスチャに適切な値に設定します。
  5. Apply をクリックします。

詳しくは、テクスチャのインポート設定 を参照してください。

フリップブックの画像を 3D 形状として
フリップブックの画像を 3D 形状として

スクリプトから 3D テクスチャを作成

Unity は Texture3D クラスを使用して 3D テクスチャを表現します。このクラスを使用して、C# スクリプトで 3D テクスチャを操作します。

以下の例は、Texture3D クラスのインスタンスを作成し、それにカラーデータを入力し、それをシリアル化したアセットとしてプロジェクトに保存するエディタースクリプトです。

using UnityEditor;
using UnityEngine;

public class ExampleEditorScript : MonoBehaviour
{
    [MenuItem("CreateExamples/3DTexture")]
    static void CreateTexture3D()
    {
        // テクスチャを設定
        int size = 32;
        TextureFormat format = TextureFormat.RGBA32;
        TextureWrapMode wrapMode =  TextureWrapMode.Clamp;

        // テクスチャを作成して設定を適用 
        Texture3D texture = new Texture3D(size, size, size, format, false);
        texture.wrapMode = wrapMode;

        // 3D 配列を作成し、カラーデータを保存
        Color[] colors = new Color[size * size * size];

        // 配列に入力。テクスチャの x, y, z 値が赤、青、緑にマッピングされます。 
        float inverseResolution = 1.0f / (size - 1.0f);
        for (int z = 0; z < size; z++)
        {
            int zOffset = z * size * size;
            for (int y = 0; y < size; y++)
            {
                int yOffset = y * size;
                for (int x = 0; x < size; x++)
                {
                    colors[x + yOffset + zOffset] = new Color(x * inverseResolution,
                        y * inverseResolution, z * inverseResolution, 1.0f);
                }
            }
        }

        // テクスチャにカラー値をコピー
        texture.SetPixels(colors);

        // Apply the changes to the texture and upload the updated texture to the GPU
        texture.Apply();        

        // テクスチャを Unity プロジェクトに保存
        AssetDatabase.CreateAsset(texture, "Assets/Example3DTexture.asset");
    }
}

3D テクスチャのプレビュー

Unity エディターには、3D テクスチャのプレビューに使用できる 3 つの異なる可視化モードがあります。

  • Volumetric 可視化モードは、3D テクスチャを半透明の立方体としてレンダリングします。
  • Slice 可視化モードは、3D テクスチャの 3 つの軸のそれぞれを 1 つのスライスとして描画します。
  • SDF 可視化モードでは、テクスチャを 3D 空間の符号付き距離フィールド (Signed Distance Field) として描画します。

3D テクスチャは、Inspector でプレビューすることもできますし、Handles API を使ってシーンビューでプレビューするスクリプトを書くこともできます。Inspector の使用は手早く便利ですが、カスタムグラデーションを使用することはできません。Handles API を使用すると、必要に応じてプレビューを設定でき、カスタムグラデーションを使用できます。

Inspector の使用

Inspector ウィンドウで 3D テクスチャをプレビューするには、以下を行います。

  1. Project ウィンドウで、テクスチャアセットを選択します。このテクスチャアセットのインポーターが Inspector に表示され、Inspector の下部に 3D テクスチャのプレビューが表示されます。
  2. 3D テクスチャのプレビューの上にあるツールバーに移動します。
  3. ツールバーの右側にあるボタンを使って、Volumetric、Slice、SDF の各可視化モードを選択します。プレビューモードに応じて、プレビュー画像やツールバーのボタンが変わります。

Volumetric

この可視化モードは、3D テクスチャを半透明の立方体として描画します。

ツールバーには以下が用意されています。

コントロール 機能
Ramp カラーランプの視覚化を有効/無効にします。画像に微妙なディテールが多く含まれている場合、 Ramp を有効にすると、それらのディテールが見やすくなります。
Quality テクスチャピクセルごとのサンプル数を設定します。値が大きいほど、高品質なレンダリングになります。
Alpha 可視化の不透明度をコントロールします。値が 1 の場合は完全に不透明で、値が 0 の場合は完全に透明です。内部のピクセルを見るために調整します。

Slice

この可視化モードは、3D テクスチャの各軸の面のスライスを描画します。

ツールバーには以下が用意されています。

コントロール 機能
Ramp カラーランプの視覚化を有効/無効にします。画像に微妙なディテールが多く含まれている場合、 Ramp を有効にすると、それらのディテールが見やすくなります。
X スライス位置を X 軸上にテクスチャピクセル単位で設定します。特定のスライスを見るために調整します。
Y スライス位置を Y 軸上にテクスチャピクセル単位で設定します。特定のスライスを見るために調整します。
Z スライス位置を Z 軸上にテクスチャピクセル単位で設定します。特定のスライスを見るために調整します。

SDF

この可視化モードでは、Unity は 3D 空間で符号付き距離フィールド (SDF) レンダリングモードを使用して、3D テクスチャを描画します。 なお、この可視化モードは、非指向性の符号付き距離フィールドのみをサポートしています。

ツールバーには以下が用意されています。

コントロール 機能
Scale レイのステップサイズを乗じる数値です。レイのステップサイズは、隣接する 2 つのピクセル間の距離です。

可視化の遠くの部分がカットされてる場合は、この数値を増加してください。まったく描画されない場合は、これを減らしてみてください。
Offset サーフェスが描画されるピクセルの強度です。この値が正の場合、Unity は描画されるサーフェスを拡大します。この値が負の場合、何もない空間をサーフェスとして、また、サーフェスを何もない空間として描画します。

Handles API の使用

Handles API を使った 3D テクスチャのプレビューやコード例については、以下のドキュメントを参照してください。

シェーダーで 3D テクスチャを使用

以下は、3D テクスチャを使用してボリュームを視覚化する簡単なレイマーチングシェーダーの例です。

Shader "Unlit/VolumeShader"
{
    Properties
    {
        _MainTex ("Texture", 3D) = "white" {}
        _Alpha ("Alpha", float) = 0.02
        _StepSize ("Step Size", float) = 0.01
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
        Blend One OneMinusSrcAlpha
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            //レイマーチングサンプルの最大数 
            #define MAX_STEP_COUNT 128

            // 可能な浮動小数点の不正確性 
            #define EPSILON 0.00001f

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float3 objectVertex : TEXCOORD0;
                float3 vectorToSurface : TEXCOORD1;
            };

            sampler3D _MainTex;
            float4 _MainTex_ST;
            float _Alpha;
            float _StepSize;

            v2f vert (appdata v)
            {
                v2f o;

                // オブジェクト空間の頂点。これがレイマーチングの開始点になります
                o.objectVertex = v.vertex;

                // ワールド空間でカメラから頂点までのベクトルを計算します
                float3 worldVertex = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.vectorToSurface = worldVertex - _WorldSpaceCameraPos;

                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            float4 BlendUnder(float4 color, float4 newColor)
            {
                color.rgb += (1.0 - color.a) * newColor.a * newColor.rgb;
                color.a += (1.0 - color.a) * newColor.a;
                return color;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                // オブジェクトの前面でレイマーチングを開始します
                float3 rayOrigin = i.objectVertex;

                // カメラからオブジェクトサーフェスへのベクトルを使用してレイの方向を取得
                float3 rayDirection = mul(unity_WorldToObject, float4(normalize(i.vectorToSurface), 1));

                float4 color = float4(0, 0, 0, 0);
                float3 samplePosition = rayOrigin;

                // オブジェクト全体にレイマーチ
                for (int i = 0; i < MAX_STEP_COUNT; i++)
                {
                    // 単位立方体の範囲内でのみ色を累積します
                    if(max(abs(samplePosition.x), max(abs(samplePosition.y), abs(samplePosition.z))) < 0.5f + EPSILON)
                    {
                        float4 sampledColor = tex3D(_MainTex, samplePosition + float3(0.5f, 0.5f, 0.5f));
                        sampledColor.a *= _Alpha;
                        color = BlendUnder(color, sampledColor);
                        samplePosition += rayDirection * _StepSize;
                    }
                }

                return color;
            }
            ENDCG
        }
    }
}

ページ上の例で作成した 3D テクスチャでこのシェーダーを使用すると、 結果は以下のようになります。

Texture3D

ムービーテクスチャ
テクスチャ配列