本日はShader学習枠です。
〇ShaderLODとは?
ShaderLODはShaderLab側で提供されているShaderのパフォーマンスにかかわる機能です。
UnityのShaderLabではSubShaderというブロックの中にPassという二つのブロックがあり、どちらも複数定義することができます。
Passは複数定義した場合複数回処理が実行されますがSubShaderは排他的で、一度に1つのSubShaderしか実行されません。
ShaderLODはどのSubShaderを実行するのかを指定するパラメータになります。
ShaderではFallbackというパラメータを指定することもでき、このシェーダーが実行できない場合こちらのシェーダーを実行するという使い方ができます。
しかしながら実行できるが著しくパフォーマンスに影響を及ぼすといった場合は描画されてしまいます。
ShaderLODを使用することでパフォーマンスに影響が出る場合に一つ軽量な処理のシェーダーを実行するといった細かい使用方法が可能です。
〇ShaderLODの使い方
ShaderLODはSubShaderブロック内にLODとして値を定義します。
今回は次のようにShaderを構築してみました。
SubShader { Tags { "RenderType"="Opaque" } LOD 200 ・・・ライトを受けるシェーダー } SubShader { Tags { "RenderType"="Opaque" } LOD 100 ・・・ライトを受けない軽量なシェーダー }
LODが2つあり、200の方はライトの処理を挟んだシェーダー、100の方はライトの処理を受けないシェーダーを記述します。
特に指定しない限りSubShaderは一番最初に記述されているものが実行されます。
ここでC#スクリプト側でShader.globalMaximumLODを定義することでLODを使用することができます。
Shader.globalMaximumLOD = 100;
例えば上記の例ではLODが設定されている場合100までのLODが優先して実行され200などのLODは実行されません。
今回はUpdate関数でキー入力によってLODを変えるテストを作っていきます。
if (Input.GetKeyDown(KeyCode.Y)) { Shader.globalMaximumLOD = 200; } if(Input.GetKeyDown((KeyCode.T))) { Shader.globalMaximumLOD = 100; }
Yキーを押した場合は200までのLODの中で高いSubShaderが実行されます。ここではライトを受けるSubShaderが実行されます。
実行するとLit、Unlitが切り替わることがわかりました。
例えばFPSが低下する場面にLODを切り替えることで負荷を軽減させるなどの使用法でより良いパフォーマンスを発揮できそうです。
〇コード
using UnityEngine; public class ShaderLODtest : MonoBehaviour { // Start is called before the first frame update void Start() { Shader.globalMaximumLOD = 100; } // Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.Y)) { Shader.globalMaximumLOD = 200; } if(Input.GetKeyDown((KeyCode.T))) { Shader.globalMaximumLOD = 100; } } }
Shader "Unlit/TutorialShader" { Properties { // _MainTex ("Texture", 2D) = "white" {} _MainColor("Color" ,color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 200 Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile _ _MAIN_LIGHT_SHADOWS #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" struct appdata { float4 vertex : POSITION; half3 normal: NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 normalWS : TEXCOORD1; float4 shadowCoord : TEXCOORD3; }; float4 _MainColor; v2f vert (appdata v) { v2f o; // o.vertex = TransformObjectToHClip(v.vertex); //面の法線を取得、ライトの当たる向きを計算 VertexNormalInputs normal = GetVertexNormalInputs(v.normal); o.normalWS = normal.normalWS; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz); o.vertex = vertexInput.positionCS; o.shadowCoord = GetShadowCoord(vertexInput); return o; } float4 frag (v2f i) : SV_Target { float4 col = _MainColor; //Light.hlslで提供されるUnityのライトを取得する関数 Light lt = GetMainLight(i.shadowCoord); //ライトの向きを計算 float strength = dot(lt.direction, i.normalWS); float4 lightColor = float4(lt.color, 1)*(lt.distanceAttenuation * lt.shadowAttenuation); return col* lightColor*strength; } ENDHLSL } } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" struct appdata { float4 vertex : POSITION; half3 normal: NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 normalWS : TEXCOORD1; float4 shadowCoord : TEXCOORD3; }; float4 _MainColor; v2f vert (appdata v) { v2f o; // o.vertex = TransformObjectToHClip(v.vertex); //面の法線を取得、ライトの当たる向きを計算 VertexNormalInputs normal = GetVertexNormalInputs(v.normal); o.normalWS = normal.normalWS; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz); o.vertex = vertexInput.positionCS; o.shadowCoord = GetShadowCoord(vertexInput); return o; } float4 frag (v2f i) : SV_Target { float4 col = _MainColor; return col; } ENDHLSL } } }