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");
}
}
Unity 编辑器具有三种不同的可视化模式,可用于预览 3D 纹理:
您可以在 Inspector 中预览 3D 纹理,或者编写脚本以使用 Handles API 在 Scene 视图中预览。使用 Inspector 既快捷又方便,但不允许使用自定义渐变。Handles API 允许您根据您的确切需求来配置预览,并允许使用自定义渐变。
要在 Inspector 窗口中预览 3D 纹理:
在此可视化模式中,Unity 将 3D 纹理渲染为半透明立方体。
工具栏中会显示以下控件:
控件: | 功能: |
---|---|
Ramp | 启用和禁用颜色渐变可视化。如果图像包含很多细微的细节,请启用 Ramp 使这些细节更容易看到。 |
Quality | 设置每纹理像素样本数。该值越高渲染质量越好。 |
Alpha | 控制可视化的不透明度。值为 1 表示完全不透明,值为 0 表示完全透明。调整以查看内部像素。 |
在这种可视化模式下,Unity 渲染 3D 纹理的每个轴平面的切片。
工具栏中会显示以下控件:
控件: | 功能: |
---|---|
Ramp | 启用和禁用颜色渐变可视化。如果图像包含很多细微的细节,请启用 Ramp 使这些细节更容易看到。 |
X | 设置 x 轴上的切片位置,以纹理像素为单位。调整以查看特定切片。 |
Y | 设置 y 轴上的切片位置,以纹理像素为单位。调整以查看特定切片。 |
Z | 设置 z 轴上的切片位置,以纹理像素为单位。调整以查看特定切片。 |
在这种可视化模式下,Unity 在 3D 空间中使用有向距离场渲染模式来渲染 3D 纹理。 请注意,此可视化模式仅支持非定向的有向距离场。
工具栏中会显示以下控件:
控件: | 功能: |
---|---|
Scale | 与射线步长相乘的数字。射线步长是两个相邻像素之间的距离。 如果可视化的远处部分被截断,请尝试增加该值。如果根本没有渲染可视化,请尝试减少该值。 |
Offset | 渲染表面的像素强度。当此值为正时,Unity 将扩展渲染表面。当此值为负时,Unity 会将空白空间渲染为曲面,将曲面渲染为空白空间。 |
有关使用 Handles API 预览 3D 纹理及代码示例的信息,请参阅以下文档:
下面是一个使用 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.