欢迎光临散文网 会员登陆 & 注册

Unity-3D 纹理

2021-03-28 12:01 作者:unity_某某师_高锦锦  | 我要投稿

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

在 Unity 项目中,Unity Editor 将 3D 纹理表示为纹理资源。要配置纹理资源的导入设置,可选择该纹理资源并使用 Inspector,或者编写一个使用 TextureImporter API 的脚本。

在 Unity 引擎中,Unity 使用 Texture3D 类来表示 3D 纹理。使用此类可以在 C# 脚本中与 3D 纹理进行交互。

3D 纹理大小

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

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

创建 3D 纹理

要在项目中创建 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 纹理

下面是一个使用 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)
            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 纹理,则结果将如下所示:


Unity-3D 纹理的评论 (共 条)

分享到微博请遵守国家法律