Version: 2019.3
言語: 日本語
Streaming Controller
グラフィックスチュートリアル

テクスチャストリーミング API

このページは以下について説明します。

概要

API を使用して、Unity がテクスチャをストリーミングする方法をより細かく制御できます。テクスチャストリーミングシステムが自動的にすべてのテクスチャを管理する一方で、特定のテクスチャに対してロードするミップマップレベルをオーバーライドすることができます。特定のテクスチャを完全にロードする必要があるとわかっている特定のゲームプレイシナリオがあるかもしれません。例えば、大きな距離を素早く動かしたり、瞬間的なカメラカットを使用すると、テクスチャストリーミングシステムがミップマップをディスクからメモリにストリーミングするときに、テクスチャの質に著しい変化を引き起こす可能性があります。この問題を軽減するために、API を使用して新しいカメラ位置でミップマップをプリロードすることができます。

テクスチャのテクスチャストリーミングを有効にして制御するには、以下のプロパティを使用します。

テクスチャストリーミングは、テクスチャのサイズをテクスチャストリーミングの Memory Budget (メモリバジェット) に収まるように自動的に縮小します。テクスチャの Mip Map Priority (ミップマップ優先順位) の数字はおおむね Memory Budget のミップマップオフセットに当たります。例えば、優先順位が 2 の場合、テクスチャストリーミングシステムは、優先順位が 0 の他のテクスチャよりミップレベルが 2 つ高いミップマップを使用しようとします。負の値も有効です。これを行うことができない場合は、Memory Budget に合うように低いミップレベルを使用します。

テクスチャストリーミングシステムの制御

These properties are read-only at run time:

ランタイムに発生する処理を制御するには、以下の静的プロパティを使用します。

To control the way Unity caches unused mip levels via script, use Texture2D.streamingTextureDiscardUnusedMips. It’s useful to set this to true for initial testing to check metrics, and to set a Memory Budget (set in Quality Settings when Texture Streaming is enabled, or via QualitySettings.streamingMipmapsMemoryBudget), otherwise Unity doesn’t drop any mips. The Memory Budget is set to 512MB by default.

テクスチャストリーミングのカメラの制御

Quality Settings (Edit > Project Settings > Quality) で Add All Cameras を使用して、プロジェクト内のすべてのカメラのテクスチャストリーミングを計算するかどうかを指定します。これはデフォルトで有効になっています。

どのカメラをアクティブにするかを細かく制御するには、Camera コンポーネントがアタッチされているゲームオブジェクトの Streaming Controller コンポーネントを使用します。これにより、Camera コンポーネントから位置やカメラ設定 (Field of View など) を直接取得します。

カメラが無効になっている場合、Streaming Controller が有効でプリロード状態でない限り、Unity はテクスチャストリーミングを計算しません。Streaming Controller と、その紐付けられたカメラが有効になっている場合、または Streaming Controller がプリロード状態の場合、Unity はこのカメラのテクスチャストリーミングを計算します。Streaming Controller が無効の場合は、Unity はこのカメラのテクスチャストリーミングを計算しません。

Streaming Controller コンポーネント
Streaming Controller コンポーネント

Streaming Controller コンポーネントには Mip Map Bias 設定が含まれています。これを API で制御するには、StreamingController.streamingMipmapBias を使用します。

テクスチャストリームシステムがテクスチャに対して選択したミップマップレベルより小さい、または大きいミップマップレベルを強制的に読み込ませるには、この設定を使用します。数値フィールドを使用して、Unity がミップマップレベルに適用するオフセットを設定します。このオフセットは、このカメラから見えるすべてのテクスチャに加えられます。

カメラカット

ある場所のカットから別の場所のカットに切り替えるとき、テクスチャストリーミングシステムは必要なテクスチャを Unity にストリーミングする時間が必要です。無効にしたターゲットカメラの位置でプリロードをトリガーするには、ターゲットのカメラの Streaming Controller コンポーネントの StreamingController.SetPreloading を呼び出します。タイムアウトを指定して、プリロードのフェーズを終了できます。プリロードフェーズの終了時にカメラを自動的にアクティブにするには、スクリプトの activateCameraOnTimeout フラグを true に設定します。カメラを新しいカメラに切り替えた後に非アクティブにするには、そのカメラに disableCameraCuttingFrom パラメーターを渡します。

void StreamingController.SetPreloading(float timeoutSeconds=0.0f, bool activateCameraOnTimeout=false, Camera disableCameraCuttingFrom=null)

プリロード状態をキャンセルまたはクエリするには、以下のメソッドを使用します。

テクスチャストリーミングシステムが依然としてテクスチャをロードしているかどうかを判断するには、以下のプロパティをクエリできます。

カメラをアクティブにしてから、プロパティが 0 以外の値になるまでに時間がかかることに注意してください。この遅延は、テクスチャストリーミングシステムがタイムスライス処理を使用してミップマップを計算するためです。このため、カメラカットの最中には、カットする前に最小限の時間待つ必要があります。テクスチャのバジェットとシーンの動きは連続的なテクスチャストリーミングを引き起こす場合があります。そのため、カット前の最大時間も設定する必要があります。

特定のミップマップのロード

特定テクスチャのミップレベルの計算をオーバーライドするには、Texture2D.requestedMipmapLevel を使用します。これは、その特定テクスチャの 0 から最大の範囲内で設定できる正確なミップレベルです。または、それより低い場合は Max Level Reduction 値を使用します。0 が最高解像度ミップレベルです。

要求されたミップレベルがロードされているかどうかを確認するには、Texture2D.IsRequestedMipmapLevelLoaded を使用します。

リクエストしたミップレベルでのオーバーライドを止め、システムにミップマップレベルを計算させたい場合は、Texture2D.ClearRequestedMipmapLevel を使用して値をリセットします。

メッシュの UV 密度の推定値を取得するには、以下を使用します。

float Mesh.GetUVDistributionMetric(int uvSetIndex)

UV 分布メトリックを使用すると、カメラの位置に基づいて、必要なミップマップレベルを計算することができます。コードサンプルは Mesh.GetUVDistributionMetric を参照してください。

システムをオーバーライドしてすべてのミップを強制的にロードするには、Texture.streamingTextureForceLoadAll を使用します。

関連 API メソッド

マテリアルに割り当てられたテクスチャを取得および設定するには、以下を使用します。

マテリアルのすべてのテクスチャプロパティを取得するには、以下を使用します。

テクスチャストリーミングのデバッグ

Unity にはビルトインのテクスチャストリーミングデバッグビューモードがあります。それにアクセスするには、シーンビューの描画モードのドロップダウン をクリックし、Texture Streaming を選択します。このビューモードでは、Texture Streaming システム内でのゲームオブジェクトの状態に応じて、ゲームオブジェクトに以下の色を設定します。

  • Green - テクスチャストリーミングシステムによってミップマップが減少したテクスチャ。
  • Red - テクスチャストリーミングシステムにすべてのミップマップをロードするのに十分なリソースがないため、ミップマップがほどんどないテクスチャ。
  • Blue - ストリーミングに設定されていないテクスチャ、またはミップレベルを計算するレンダラーがない場合。

スクリプトを使ったデバッグ

デバッグモードのマテリアルプロパティをアップロードするには、Texture.SetStreamingTextureMaterialDebugProperties を使用します。

デバッグモードのマテリアルプロパティをアップロードするには、以下のプロパティを使用します。

テクスチャストリームシステムが相互作用するテクスチャやレンダラーの数に関する情報を取得するには、以下のプロパティを使用します。

ミップマップレベルに関する情報を取得するには、以下のプロパティを使用します。

ライトマップ

テクスチャストリーミングシステムを使用してライトマップをストリーミングできます。テクスチャ設定は直接編集できますが、Unity がライトマップを再生成するときにデフォルト値にリセットされます。Player 設定 (Edit > Project Settings > Player) には、生成されたライトマップのストリーミングと優先度を制御できる 2 つの設定があります。Lightmap Streaming EnabledStreaming Priority です。

Edit > Project Settings > Player > Other Settings
Edit > Project Settings > Player > Other Settings

再生モード

テクスチャストリーミングは、デフォルトでは再生モードでアクティブになっています。ただし、再生モードで実行している場合、エディターのオーバーヘッドは統計にバイアスを生じさせます。正確な数値を取得するには、ターゲットデバイスでアプリケーションをテストします。

When Texture Streaming is enabled in Play mode, but not enabled in Edit mode (or the other way around), toggling in and out of Play mode takes slightly longer. To disable Texture Streaming in Play mode, go to the Editor Settings (Edit > Project Settings > Editor), navigate to Streaming Settings and disable Enabled Texture Streaming in Play Mode. This prevents Unity from unloading and reloading mipmap data, and should speed up the Play mode workflow.

Edit > Project Settings > Editor > Streaming Settings
Edit > Project Settings > Editor > Streaming Settings

テクスチャストリーミングの状態のデバッグスクリプト

テクスチャストリーミングの状態を表示するには、シーンのゲームオブジェクトに以下のスクリプトを追加します。これは、正しいメモリバジェットを設定したい場合に便利です。

Shader "Show Texture Streaming" {
    Properties {
        _MainTex( ""、2D)= "white" {}
        _Control( "Control(RGBA)"、2D)= "red" {}
        _Splat3( "Layer 3(A)"、2D)= "white" {}
        _Splat2( "Layer 2(B)"、2D)= "white" {}
        _Splat1( "Layer 1(G)"、2D)= "white" {}
        _Splat0( "Layer 0(R)"、2D)= "white" {}
        _BaseMap( ""、2D)= "white" {}
        _Cutoff( "Cutoff"、float)= 0.5
    }


CGINCLUDE
//以下のほとんどのものが使用する共通コード
#include "UnityCG.cginc"
struct v2f {
    float4 pos:SV_POSITION;
    float2 uv:TEXCOORD0;
};
uniform float4 _MainTex_ST;
uniform float4 _MainTex_TexelSize;
uniform float4 _MainTex_MipInfo;

UNITY_DECLARE_TEX2D(_MainTex);
UNITY_DECLARE_TEX2D(_SceneViewMipcolorsTexture);

uint GetMipCount(Texture2D tex)
{
# if defined(SHADER_API_D3D11) || defined(SHADER_API_D3D12) || defined(SHADER_API_D3D11_9X) || defined(SHADER_API_XBOXONE) || defined(SHADER_API_PSSL)
    #define MIP_COUNT_SUPPORTED 1
# endif
# if (defined(SHADER_API_OPENGL) || defined(SHADER_API_VULKAN)) && !defined(SHADER_STAGE_COMPUTE)
   //OpenGL は width、height、depth の textureSize のみをサポートします
   //textureQueryLevels(GL_ARB_texture_query_levels) は OpenGL 4.3以降を必要とし、コンピュートシェーダーでコンパイルしません
   //tex.GetDimensions を textureQueryLevelsに変換
    #define MIP_COUNT_SUPPORTED 1
#endif
   //Metal は比較的高い OpenGL のバージョンをサポートしません

# if defined(MIP_COUNT_SUPPORTED)
    uint mipLevel、width、height、mipCount;
    mipLevel = width = height = mipCount = 0;
    tex.GetDimensions(mipLevel、width、height、mipCount);
    return mipCount;
#else
    return 0;
#endif
}

float4 GetStreamingMipColor(uint mipCount、float4 mipInfo)
{
   //アルファはソースカラーとブレンドする量です(0.0 =オリジナルを使用、1.0 =新しいカラーを使用)。

   //mipInfo:
   //x = quality 設定の minStreamingMipLevel
   //y = テクスチャの元のミップ数
   //z = 希望する screen mip level
   //w = ロードされたミップレベル 
    uint originalTextureMipCount = uint(mipInfo.y);

   //マテリアル/シェーダーミップ情報 (元のミップレベル) が設定されていない場合は、
   //ストリームテクスチャではないか、どのレンダラーもそれを更新していないかのいずれかです
    if(originalTextureMipCount == 0)
        return float4(0.0、0.0、1.0、0.5);

    uint desiredMipLevel = uint(mipInfo.z);
    uint mipCountDesired = uint(originalTextureMipCount) -uint(desiredMipLevel);
    if(mipCount == 0)
    {
       //計算できません。渡された値を使用します。
        mipCount = originalTextureMipCount  -  uint(mipInfo.w);
    }

    if(mipCount <mipCountDesired)
    {
       //希望のミップレベルでない場合(バジェットによる削減)は赤のトーン。明るい場合は、オリジナルのアルファ0からさらに遠くなります
        float ratioToDesired = float(mipCount)/float(mipCountDesired);
        return float4(1.0、0.0、0.0、1.0  -  ratioToDesired);
    }
    else if(mipCount> = originalTextureMipCount)
    {
       //元のミップカウント時の元の色
        return float4(1.0、1.0、1.0、0.0);
    }
    else
    {
       //元のミップレベルでない場合は緑のトーン。明るい方はオリジナルの色に近く、オリジナルの場合はアルファ0
        float ratioToOriginal = float(mipCount)/float(originalTextureMipCount);
        return float4(0.0、1.0、0.0、1.0  -  ratioToOriginal);
    }
}

float3 GetDebugStreamingMipColorBlended(float3 originalColor、Texture2D tex、float4 mipInfo)
{
    uint mipCount = GetMipCount(tex);
    float4 mipColor = GetStreamingMipColor(mipCount、mipInfo);
    return lerp(originalColor、mipColor.rgb、mipColor.a);
}


v2f vert(appdata_base v){
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.texcoord、_MainTex);
    
    return o;
}

fixed4 frag(v2f i) : COLOR
{
    fixed4 col = UNITY_SAMPLE_TEX2D(_MainTex, i.uv);
    half4 res;
    res.rgb = GetDebugStreamingMipColorBlended(col.rgb, _MainTex, _MainTex_MipInfo);
    res.a = col.a;
    return res;
}

struct v2fGrass {
    float4 pos : SV_POSITION;
    fixed4 color : COLOR;
    float2 uv : TEXCOORD0;
};

fixed4 fragGrass(v2fGrass i) : COLOR
{
    fixed4 col = UNITY_SAMPLE_TEX2D(_MainTex, i.uv);
    half4 res;
    res.rgb = GetDebugStreamingMipColorBlended(col.rgb, _MainTex, _MainTex_MipInfo);
    res.a = col.a * i.color.a;
    return res;
}
ENDCG

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="Opaque" }
    Pass {

CGPROGRAM
//通常の不透明シェーダーとテレインスプラットシェーダーの両方が 
//"Opaque" レンダリングタイプを持つため、
//両方を動作可能にするため、いくつかの voodoo を行う必要があります。

#pragma vertex vertRTerrain
#pragma fragment fragWTerrain
#pragma target 2.0
#pragma exclude_renderers gles

struct v2fterr {
    float4 pos:SV_POSITION;
    float2 uvnormal:TEXCOORD0;
    float4 uv [3]:TEXCOORD2;
    float nonterrain:TEXCOORD5;
};

uniform float4 _Splat0_ST、_Splat1_ST、_Splat2_ST、_Splat3_ST、_Splat4_ST;
uniform float4 _Splat0_TexelSize、_Splat1_TexelSize、_Splat2_TexelSize、_Splat3_TexelSize、_Splat4_TexelSize;
uniform float4 _BaseMap_TexelSize;

v2fterr vertWTerrain(appdata_base v){
    v2fterr o;
    o.pos = UnityObjectToClipPos(v.vertex);
   //_Splat0_TexelSize が設定されていない場合はテレインではないと仮定します。
    float nonterrain = _Splat0_TexelSize.z == 0.0? 1:0;
    // このモードでは、テレインの追加パスを折りたたむ/描画しないでください。
    // この置換シェーダーで最初に描画されるパスに応じて、最初のパスと追加パスの点滅が非常に悪いように見えるためです。
   // TODO: 2 パスの Terrain でも適切に ミップを表示させます。
    o.pos * = _MainTex_TexelSize.z == 0.0 && _Splat0_TexelSize.z!= 0.0? 0:1;
   //通常のテクスチャ UV
    o.uvnormal = TRANSFORM_TEX(v.texcoord、_MainTex);
   //Terrain スプラットUV
    float2 baseUV = v.texcoord.xy;
    o.uv [0] .xy = baseUV;
    o.uv [0] .zw = half2(0,0);
    o.uv [1] .xy = TRANSFORM_TEX(baseUV、_Splat0);
    o.uv [1] .zw = TRANSFORM_TEX(baseUV、_Splat1);
    o.uv [2] .xy = TRANSFORM_TEX(baseUV、_Splat2);
    o.uv [2] .zw = TRANSFORM_TEX(baseUV、_Splat3);
    
    o.nonterrain = nonterrain;
    return o;
}
UNITY_DECLARE_TEX2D(_Control);
UNITY_DECLARE_TEX2D(_Splat0);
UNITY_DECLARE_TEX2D(_Splat1);
UNITY_DECLARE_TEX2D(_Splat2);
UNITY_DECLARE_TEX2D(_Splat3);
UNITY_DECLARE_TEX2D(_BaseMap);
fixed4 fragWTerrain(v2fterr i):COLOR
{
   //通常のテクスチャをサンプリング
    fixed4 colnormal = UNITY_SAMPLE_TEX2D(_MainTex、i.uvnormal);
    
   //スプラットマップをサンプリング
    half4 splat_control = UNITY_SAMPLE_TEX2D(_Control、i.uv [0] .xy);
    half3 splat_color = splat_control.r * UNITY_SAMPLE_TEX2D(_Splat0、i.uv [1] .xy).rgb;
    splat_color + = splat_control.g * UNITY_SAMPLE_TEX2D(_Splat1、i.uv [1] .zw).rgb;
    splat_color + = splat_control.b * UNITY_SAMPLE_TEX2D(_Splat2、i.uv [2] .xy).rgb;
    splat_color + = splat_control.a * UNITY_SAMPLE_TEX2D(_Splat3、i.uv [2] .zw).rgb;
    
   //通常とスプラットマップ間の線形補間 ( lerp )
    half3 col = lerp(splat_color、colnormal.rgb、(half)i.nonterrain);

    half4 res;
   //TODO: スプラットミップを考慮に入れます
    res.rgb = GetDebugStreamingMipColorBlended(col.rgb、_MainTex、_MainTex_MipInfo);
    res.a = colnormal.a;
    
    return res;
}
ENDCG
    }
}

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="Transparent" }
    Pass {
        Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
ENDCG
    }
}

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="TransparentCutout" }
    Pass {
        AlphaTest Greater [_Cutoff]
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
ENDCG
    }
}

SubShader {
   Tags { "ForceSupported" = "True" "RenderType"="TreeBark" }
   Pass {
CGPROGRAM
#pragma vertex vertTreeBark
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "UnityCG.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
v2f vertTreeBark(appdata_full v){
    v2f o;
    TreeVertBark(v);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
    }
}

SubShader {
    Tags {"ForceSupported" = "True" "RenderType" = "TreeLeaf"}
    Pass {
CGPROGRAM
#pragma vertex vertTreeLeaf
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "UnityCG.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
v2f vertTreeLeaf(appdata_full v){
    v2f o;
    TreeVertLeaf(v);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest GEqual [_Cutoff]
    }
}

SubShader {
    Tags {"ForceSupported" = "True" "RenderType" = "TreeOpaque"}
    Pass {
CGPROGRAM
#pragma vertex vertTree
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
struct appdata {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
v2f vertTree(appdata v){
    v2f o;
    TerrainAnimateTree(v.vertex、v.color.w);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
    }
}

SubShader {
    Tags {"ForceSupported" = "True" "RenderType" = "TreeTransparentCutout"}
    Pass {
        Cull Off
CGPROGRAM
#pragma vertex vertTree
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
struct appdata {
float4 vertex : POSITION;
fixed4 color : COLOR;
float4 texcoord : TEXCOORD0;
};
v2f vertTree(appdata v){
    v2f o;
    TerrainAnimateTree(v.vertex、v.color.w);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest GEqual [_Cutoff]
    }
}

SubShader {
    Tags {"ForceSupported" = "True" "RenderType" = "TreeBillboard"}
    Pass {
        Cull Off
        ZWrite Off
CGPROGRAM
#pragma vertex vertTree
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
v2f vertTree(appdata_tree_billboard v){
    v2f o;
    TerrainBillboardTree(v.vertex、v.texcoord1.xy、v.texcoord.y);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv.x = v.texcoord.x;
    o.uv.y = v.texcoord.y&gt; 0;
    return o;
}
ENDCG
        
        SetTexture [_MainTex] {combine primary、texture}
    }
}

SubShader {
    Tags{"ForceSupported" = "True" "RenderType" = "GrassBillboard"}
    Pass{
        Cull Off
CGPROGRAM
#pragma vertex vertGrass
#pragma fragment fragGrass
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
v2fGrass vertGrass(appdata_full v){
    v2fGrass o;
    WavingGrassBillboardVert(v);
    o.color = v.color;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest Greater [_Cutoff]
    }
}

SubShader {
    Tags{"ForceSupported" = "True" "RenderType" = "Grass"}
    Pass{
        Cull Off 
CGPROGRAM
#pragma vertex vertGrass
#pragma fragment fragGrass
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
v2fGrass vertGrass(appdata_full v){
    v2fGrass o;
    WavingGrassVert(v);
    o.color = v.color;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest Greater [_Cutoff]
    }
}

Fallback Off
}
Streaming Controller
グラフィックスチュートリアル