3D 纹理是位图图像,其中包含三维信息,而不是标准的二维信息。3D 纹理通常用于仿真诸如雾或烟的体积效果,模拟体积 3D 网格,或存储动画纹理并在这些动画纹理之间平滑混合。
在 Unity 项目中,Unity Editor 将 3D 纹理表示为纹理资源。要配置纹理资源的导入设置,可选择该纹理资源并使用 Inspector,或者编写一个使用 TextureImporter API 的脚本。
在 Unity 引擎中,Unity 使用 Texture3D 类来表示 3D 纹理。使用此类可以在 C# 脚本中与 3D 纹理进行交互。
3D 纹理的最大分辨率为 2048 x 2048 x 2048。
请注意,随着分辨率的提高,3D 纹理在内存中和磁盘上的大小会迅速增加。一个没有 Mipmap 且分辨率为 16 x 16 x 16 的 RGBA32 3D 纹理具有 128KB 的大小,而分辨率为 256 x 256 x 256 时则具有 512MB 的大小。
要在项目中创建 3D 纹理,必须使用脚本。
下面的示例是一个 Editor 脚本,该脚本创建一个 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;
// 创建 3 维数组以存储颜色数据
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);
// 将更改应用到纹理,然后将更新的纹理上传到 GPU
texture.Apply();
// 将纹理保存到 Unity 项目
AssetDatabase.CreateAsset(texture, "Assets/Example3DTexture.asset");
}
}
下面是一个使用 3D 纹理来可视化体积的简单光线追踪 (Raymarching) 着色器示例。
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 纹理,则结果将如下所示:
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.