このページは以下について説明します。
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)
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 コンポーネントには 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 has a built-in Mip Map Streaming debugging view mode. To access it, click the Scene view control drop-down and select Texture Streaming. This view mode tints GameObjects the following colours, depending on their status in the Mip Map Streaming system:
デバッグモードのマテリアルプロパティをアップロードするには、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 がミップマップデータをアンロードして再ロードするのを防ぎ、再生モードのワークフローが早くなります。
Add the following script to a GameObject in the Scene to display the Mip Map Streaming status. This is useful if you want to determine the correct memory budget to set.
Shader "Show Mip Map 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
// Common code used by most of the things below
#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 only supports textureSize for width, height, depth
// textureQueryLevels (GL_ARB_texture_query_levels) needs OpenGL 4.3 or above and doesn't compile in compute shaders
// tex.GetDimensions converted to textureQueryLevels
#define MIP_COUNT_SUPPORTED 1
#endif
// Metal doesn't support high enough OpenGL version
#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)
{
// alpha is amount to blend with source color (0.0 = use original, 1.0 = use new color)
// mipInfo :
// x = quality setings minStreamingMipLevel
// y = original mip count for texture
// z = desired on screen mip level
// w = loaded mip level
uint originalTextureMipCount = uint(mipInfo.y);
// If material/shader mip info (original mip level) has not been set it’s either not a streamed texture
// or no renderer is updating it
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)
{
// Can't calculate, use the passed value
mipCount = originalTextureMipCount - uint(mipInfo.w);
}
if (mipCount < mipCountDesired)
{
// red tones when not at the desired mip level (reduction due to budget). Brighter is further from original, alpha 0 when at desired
float ratioToDesired = float(mipCount) / float(mipCountDesired);
return float4(1.0, 0.0, 0.0, 1.0 - ratioToDesired);
}
else if (mipCount >= originalTextureMipCount)
{
// original color when at (or beyond) original mip count
return float4(1.0, 1.0, 1.0, 0.0);
}
else
{
// green tones when not at the original mip level. Brighter is closer to original, alpha 0 when at original
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
// As both normal opaque shaders and terrain splat shaders
// have "Opaque" render type, we need to do some voodoo
// to make both work.
#pragma vertex vertWTerrain
#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);
// assume it's not a terrain if _Splat0_TexelSize is not set up.
float nonterrain = _Splat0_TexelSize.z==0.0 ? 1:0;
// collapse/don't draw terrain's add pass in this mode, since it looks really bad if first pass
// and add pass blink depending on which gets drawn first with this replacement shader
// TODO: make it display mips properly even for two-pass terrains.
o.pos *= _MainTex_TexelSize.z==0.0 && _Splat0_TexelSize.z!=0.0 ? 0 : 1;
// normal texture UV
o.uvnormal = TRANSFORM_TEX(v.texcoord,_MainTex);
// terrain splat UVs
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
{
// sample regular texture
fixed4 colnormal = UNITY_SAMPLE_TEX2D(_MainTex, i.uvnormal);
// sample splatmaps
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 between normal and splatmaps
half3 col = lerp(splat_color, colnormal.rgb, (half)i.nonterrain);
half4 res;
// TODO: Take splat mips into account
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
}