3D テクスチャは、標準の 2 次元ではなく 3 次元の情報を含むビットマップ画像です。3D テクスチャは、フォグや煙などのボリュメトリック効果をシミュレートしたり、ボリュメトリックな 3D メッシュを近似したり、アニメーション化されたテクスチャを格納してそれらを滑らかにブレンドしたりするためによく使用されます。
3D テクスチャの最大解像度は 2048 x 2048 x 2048 です。
メモリとディスク上の 3D テクスチャのサイズは、解像度が上がるとすぐに大きくなることに注意してください。ミップマップがなく、解像度が 16 x 16 x 16 の RGBA32 3D テクスチャのサイズは 128KB ですが、解像度が 256 x 256 x 256 の場合、サイズは 512MB です。
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");
}
}
Unity エディターには、3D テクスチャのプレビューに使用できる 3 つの異なる可視化モードがあります。
3D テクスチャは、Inspector でプレビューすることもできますし、Handles API を使ってシーンビューでプレビューするスクリプトを書くこともできます。Inspector の使用は手早く便利ですが、カスタムグラデーションを使用することはできません。Handles API を使用すると、必要に応じてプレビューを設定でき、カスタムグラデーションを使用できます。
Inspector ウィンドウで 3D テクスチャをプレビューするには、以下を行います。
この可視化モードは、3D テクスチャを半透明の立方体として描画します。
ツールバーには以下が用意されています。
コントロール | 機能 |
---|---|
Ramp | カラーランプの視覚化を有効/無効にします。画像に微妙なディテールが多く含まれている場合、 Ramp を有効にすると、それらのディテールが見やすくなります。 |
Quality | テクスチャピクセルごとのサンプル数を設定します。値が大きいほど、高品質なレンダリングになります。 |
Alpha | 可視化の不透明度をコントロールします。値が 1 の場合は完全に不透明で、値が 0 の場合は完全に透明です。内部のピクセルを見るために調整します。 |
この可視化モードは、3D テクスチャの各軸の面のスライスを描画します。
ツールバーには以下が用意されています。
コントロール | 機能 |
---|---|
Ramp | カラーランプの視覚化を有効/無効にします。画像に微妙なディテールが多く含まれている場合、 Ramp を有効にすると、それらのディテールが見やすくなります。 |
X | スライス位置を X 軸上にテクスチャピクセル単位で設定します。特定のスライスを見るために調整します。 |
Y | スライス位置を Y 軸上にテクスチャピクセル単位で設定します。特定のスライスを見るために調整します。 |
Z | スライス位置を Z 軸上にテクスチャピクセル単位で設定します。特定のスライスを見るために調整します。 |
この可視化モードでは、Unity は 3D 空間で符号付き距離フィールド (SDF) レンダリングモードを使用して、3D テクスチャを描画します。 なお、この可視化モードは、非指向性の符号付き距離フィールドのみをサポートしています。
ツールバーには以下が用意されています。
コントロール | 機能 |
---|---|
Scale | レイのステップサイズを乗じる数値です。レイのステップサイズは、隣接する 2 つのピクセル間の距離です。 可視化の遠くの部分がカットされてる場合は、この数値を増加してください。まったく描画されない場合は、これを減らしてみてください。 |
Offset | サーフェスが描画されるピクセルの強度です。この値が正の場合、Unity は描画されるサーフェスを拡大します。この値が負の場合、何もない空間をサーフェスとして、また、サーフェスを何もない空間として描画します。 |
Handles API を使った 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 テクスチャでこのシェーダーを使用すると、 結果は以下のようになります。