このページは以下について説明します。
API を使用して、Unity がテクスチャをストリーミングする方法をより細かく制御できます。テクスチャストリーミングシステムが自動的にすべてのテクスチャを管理する一方で、特定のテクスチャに対してロードするミップマップレベルをオーバーライドすることができます。特定のテクスチャを完全にロードする必要があるとわかっている特定のゲームプレイシナリオがあるかもしれません。例えば、大きな距離を素早く動かしたり、瞬間的なカメラカットを使用すると、テクスチャストリーミングシステムがミップマップをディスクからメモリにストリーミングするときに、テクスチャの質に著しい変化を引き起こす可能性があります。この問題を軽減するために、API を使用して新しいカメラ位置でミップマップをプリロードすることができます。
テクスチャのテクスチャストリーミングを有効にして制御するには、以下のプロパティを使用します。
テクスチャストリーミングは、テクスチャのサイズをテクスチャストリーミングの Memory Budget (メモリバジェット) に収まるように自動的に縮小します。テクスチャの Mip Map Priority (ミップマップ優先順位) の数字はおおむね Memory Budget のミップマップオフセットに当たります。例えば、優先順位が 2 の場合、テクスチャストリーミングシステムは、優先順位が 0 の他のテクスチャよりミップレベルが 2 つ高いミップマップを使用しようとします。負の値も有効です。これを行うことができない場合は、Memory Budget に合うように低いミップレベルを使用します。
これらはランタイムに読み取り専用です。
ランタイムに発生する処理を制御するには、以下の静的プロパティを使用します。
QualitySettings.streamingMipmapsMemoryBudget (デフォルトは 512MB)
QualitySettings.streamingMipmapsRenderersPerFrame (デフォルトは 512MB)
QualitySettings.streamingMipmapsMaxLevelReduction (デフォルトは 2MB)
QualitySettings.streamingMipmapsMaxFileIORequests (デフォルトは 1024MB)
Unity がスクリプトを通して未使用のミップレベルをキャッシュする方法を制御するには、Texture2D.streamingTextureDiscardUnusedMips を使用します。指標を確認するため、または、Memory Budget を設定 (Texture Streaming が有効になっている場合は、Quality Settings で設定、または、QualitySettings.streamingMipmapsMemoryBudget を使用して設定) するために、最初のテストでこれを false にすると便利です。それ以外の場合は、Unity はどのミップレベルも無効にしません。Memory Budget はデフォルトで 512MB に設定されています。
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 コンポーネントには 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 を使用します。
マテリアルに割り当てられたテクスチャを取得および設定するには、以下を使用します。
マテリアルのすべてのテクスチャプロパティを取得するには、以下を使用します。
Unity にはビルトインのテクスチャストリーミングデバッグビューモードがあります。それにアクセスするには、シーンビューの描画モードのドロップダウン をクリックし、Texture Streaming を選択します。このビューモードでは、Texture Streaming システム内でのゲームオブジェクトの状態に応じて、ゲームオブジェクトに以下の色を設定します。
デバッグモードのマテリアルプロパティをアップロードするには、Texture.SetStreamingTextureMaterialDebugProperties を使用します。
デバッグモードのマテリアルプロパティをアップロードするには、以下のプロパティを使用します。
テクスチャストリームシステムが相互作用するテクスチャやレンダラーの数に関する情報を取得するには、以下のプロパティを使用します。
ミップマップレベルに関する情報を取得するには、以下のプロパティを使用します。
テクスチャストリーミングシステムを使用してライトマップをストリーミングできます。テクスチャ設定は直接編集できますが、Unity がライトマップを再生成するときにデフォルト値にリセットされます。Player 設定 (Edit > Project Settings > Player) には、生成されたライトマップのストリーミングと優先度を制御できる 2 つの設定があります。Lightmap Streaming Enabled と Streaming Priority です。
テクスチャストリーミングは、デフォルトでは再生モードでアクティブになっています。ただし、再生モードで実行している場合、エディターのオーバーヘッドは統計にバイアスを生じさせます。正確な数値を取得するには、ターゲットデバイスでアプリケーションをテストします。
テクスチャストリーミングでは、再生モードの入/切にかかる時間が若干長くなります。余分な情報やより多くの計算が必要だからです。再生モードでテクスチャストリーミングの使用を止めるには、エディター設定 (Edit > Project Settings > Editor) に移動し、Streaming Settings に移動し、Enabled Texture Streaming in Play Mode を無効にします。こうすることにより、Unity がミップマップデータをアンロードして再ロードするのを防ぎ、再生モードのワークフローが早くなります。
テクスチャストリーミングの状態を表示するには、シーンのゲームオブジェクトに以下のスクリプトを追加します。これは、正しいメモリバジェットを設定したい場合に便利です。
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> 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
}