3D テクスチャは、標準の 2 次元ではなく 3 次元の情報を含むビットマップ画像です。3D テクスチャは、フォグや煙などのボリュメトリック効果をシミュレートしたり、ボリュメトリックな 3D メッシュを近似したり、アニメーション化されたテクスチャを格納してそれらを滑らかにブレンドしたりするためによく使用されます。
Unity プロジェクトでは、Unity エディターは 3D テクスチャをテクスチャアセットとして表します。テクスチャアセットのインポート設定を行うには、テクスチャアセットを選択して インスペクター を使用したり、TextureImporter API を使用するスクリプトを作成します。
Unity エンジンでは、Unity は Texture3D クラスを使用して 3D テクスチャを表現します。このクラスを使用して、C# スクリプトで 3D テクスチャを操作します。
3D テクスチャの最大解像度は 2048 x 2048 x 2048 です。
メモリとディスク上の 3D テクスチャのサイズは、解像度が上がるとすぐに大きくなることに注意してください。ミップマップがなく、解像度が 16 x 16 x 16 の RGBA32 3D テクスチャのサイズは 128KB ですが、解像度が 256 x 256 x 256 の場合、サイズは 512MB です。
プロジェクトで 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 テクスチャを使用してボリュームを視覚化する簡単なレイマーチングシェーダーの例です。
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 テクスチャでこのシェーダーを使用すると、 結果は以下のようになります。