Version: 2020.2
电影纹理 (Movie Textures)
纹理数组

3D 纹理

3D 纹理是位图图像,其中包含三维信息,而不是标准的二维信息。3D 纹理通常用于仿真诸如雾或烟的体积效果,模拟体积 3D 网格,或存储动画纹理并在这些动画纹理之间平滑混合。

3D 纹理大小

3D 纹理的最大分辨率为 2048 x 2048 x 2048。

请注意,随着分辨率的提高,3D 纹理在内存中和磁盘上的大小会迅速增加。一个没有 Mipmap 且分辨率为 16 x 16 x 16 的 RGBA32 3D 纹理具有 128KB 的大小,而分辨率为 256 x 256 x 256 时则具有 512MB 的大小。

Importing 3D textures

You can import 3D textures from source texture files that are divided into cells. These are called flipbook textures. To do this:

  1. Import the source texture into your Unity Project.
  2. In your Project view, select the resulting Texture Asset. Unity displays the Texture import settings in the Inspector.
  3. In the Inspector, set Texture Shape to 3D. Unity displays the Columns and Rows properties.
  4. Set Columns and Rows to the appropriate values for your flipbook texture.
  5. Click Apply.

For more information, see Texture import settings.

Flipbook image as a 3D shape
Flipbook image as a 3D shape

Creating a 3D texture from a script

Unity uses the Texture3D class to represent 3D textures. Use this class to interact with 3D textures in C# scripts.

The following example is an Editor script that creates an instance of the Texture3D class, populates it with color data, and then saves it to your Project as a serialized asset file.

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");
    }
}

Previewing a 3D texture

The Unity Editor has three different visualization modes you can use to preview a 3D texture:

  • Volumetric visualization mode renders the 3D texture as a translucent cube
  • Slice visualization mode renders a single slice of each of the three axes of the 3D texture
  • SDF visualization mode renders the texture as a Signed Distance Field in 3D space

You can preview the 3D texture in the Inspector, or you can write a script to preview it in the Scene view using the Handles API. Using the Inspector is quick and convenient, but does not allow the use of custom gradients. The Handles API lets you configure the preview to your exact requirements, and allows the use of custom gradients.

Using the Inspector

To preview the 3d texture in the Inspector window:

  1. In your Project window, select the Texture Asset. The Texture Asset Importer for this Texture Asset is now visible in the Inspector, and Unity renders a preview of the 3D texture at the bottom of the Inspector.
  2. Navigate to the toolbar above the preview of the 3D texture.
  3. Use the buttons on the right-hand side of the toolbar to choose between Volumetric, Slice, and SDF visualization modes. The preview image and the buttons on the toolbar change depending on the preview mode.

Volumetric

In this visualization mode, Unity renders the 3D texture as a translucent cube.

The following controls are available in the toolbar:

Control: 功能:
Ramp Enables and disables color ramp visualization. If the image contains a lot of subtle details, enable Ramp to make those details easier to see.
Quality Sets the sample per texture pixel count. Higher values result in a higher quality render.
Alpha Controls the opacity of the visualization. A value of 1 is fully opaque and a value of 0 is fully transparent. Adjust to view the inner pixels.

Slice

In this visualization mode, Unity renders slices of each axis plane of the 3D texture.

The following controls are available in the toolbar:

Control: 功能:
Ramp Enables and disables color ramp visualization. If the image contains a lot of subtle details, enable Ramp to make those details easier to see.
X Sets the slicing position on the x axis, in texture pixels. Adjust to view a specific slice.
Y Sets the slicing position on the y axis, in texture pixels. Adjust to view a specific slice.
Z Sets the slicing position on the z axis, in texture pixels. Adjust to view a specific slice.

SDF

In this visualization mode, Unity uses Signed Distance Field rendering mode in 3D space to render the 3D texture. Note that this visualization mode supports only non-directional Signed Distance Fields.

The following controls are available in the toolbar:

Control: 功能:
Scale The number by which to multiply the ray step size. The ray step size is the distance between 2 neighboring pixels.

If distant parts of the visualization are cut off, try increasing this. If the visualization is not rendered at all, try decreasing this.
Offset The intensity of the pixels at which the surface is rendered. When this value is positive, Unity will expand the rendered surface. When this value is negative, Unity will render empty space as a surface, and a surface as empty space.

Using the Handles API

For information on previewing a 3D texture using the Handles API and code samples, see the following documentation:

在着色器中使用 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 纹理,则结果将如下所示:

电影纹理 (Movie Textures)
纹理数组