游戏开发Unity渲染场景光照性能优化 ShaderLOD

 

LODGroup VS ShaderLOD

https://gameinstitute.qq.com/community/detail/110113

前言
       LOD(Level Of Detais)多细节层次。在游戏中,根据摄像机与模型的距离,来决定显示哪一个模型,往往离得近显示高模,离得远显示低模。LOD技术在大场景的应用非常普遍,在展示远景作用非常大。
网络上关于UnityLOD技术大多是关于LODGroup,这里我会简单介绍,而实际上shader上的LOD功能对性能优化也是非常有用的。

LOD Group
        首先创建一个Cube并为其添加LOD Group组件


游戏开发Unity渲染场景光照性能优化 ShaderLOD

为Cube创建作为低模显示的子物体,这里使用一个球体和一个胶囊体。


游戏开发Unity渲染场景光照性能优化 ShaderLOD

设置LOD Group


游戏开发Unity渲染场景光照性能优化 ShaderLOD游戏开发Unity渲染场景光照性能优化 ShaderLOD游戏开发Unity渲染场景光照性能优化 ShaderLOD

点Add为每个lod等级设置显示模型,设置完毕后拖动相机距离就能查看效果了,简单实用。

Shader LOD 
       然而使用LOD Group必须为使用这个技术的模型再另外制作一套低精度模型,工作量为此会增加不少。
       着色器中的LOD技术则是渲染等级抉择,同类型的模型都可以使用。
       好,进入正题,首先创建一个Shader,写一个最简单的带高光的单张纹理着色器。代码如下

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 Shader "Custom/LODShader" {     Properties {         _Color ("Color", Color) = (1,1,1,1)         _MainTex ("Main Tex", 2D) = "white" {}         _Specular("Specular",Color) = (1,1,1,1)         _Gloss("Gloss",Range(8.0,256)) = 20     }     SubShader {
        LOD 300 //设置该SubShader的LOD等级为300         Pass{             Tags {"LightMode" = "ForwardBase"}             CGPROGRAM             #pragma vertex vert             #pragma fragment frag             #include "Lighting.cginc"             fixed4 _Color;             sampler2D _MainTex;             float4 _MainTex_ST;             fixed4 _Specular;             float _Gloss;               struct a2v{                 float4 vertex : POSITION;                 float3 normal : NORMAL;                 float4 texcoord : TEXCOORD0;             };             struct v2f{                 float4 pos:SV_POSITION;                 float3 worldNormal:TEXCOORD0;                 float3 worldPos:TEXCOORD1;                 float2 uv : TEXCOORD2;             };               v2f vert(a2v v){                 v2f o;                 o.pos = mul(UNITY_MATRIX_MVP,v.vertex);                 o.worldNormal = UnityObjectToWorldNormal(v.normal);                 o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;                 o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;                 return o;             }               fixed4 frag(v2f i) : SV_Target{                 fixed3 worldNormal = normalize(i.worldNormal);                 fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));                 fixed3 albedo = tex2D(_MainTex,i.uv).rgb * _Color.rgb;                 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;                 fixed3 diffuse = _LightColor0.rgb * albedo * max(0,dot(worldNormal,worldLightDir));                 fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));                 fixed3 halfDir = normalize(worldLightDir + viewDir);                 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);                 return fixed4(ambient + diffuse + specular ,1.0);             }               ENDCG         }     } }
      第九行设置了LOD = 300。
      实际上Unity默认的LOD最大值是无限的,这意味着只要显卡支持这个shader就可以被使用。
      我们可以修改LOD的最大值来选择使用的shader。
       再写一个控制ShaderLOD最大值的脚本。
1 2 3 4 5 6 7 8 9 10 11 using UnityEngine; using System.Collections;   public class ChangeLOD : MonoBehaviour {       public int lodlevel;       void Update () {         Shader.globalMaximumLOD = lodlevel;     } }
        关键就是一句Shader.globalMaximumLOD = lodlevel;
        场景测试效果如下。
游戏开发Unity渲染场景光照性能优化 ShaderLOD游戏开发Unity渲染场景光照性能优化 ShaderLOD

       结果很明显,shader中的LOD大于Shader.globalMaximumLOD就不会被显示。所以为了不同的硬件需求我们可以写多个subshader来应对。
  我们继续为shader添加两个subshader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 SubShader {         LOD 200         Pass{             Tags {"LightMode" = "ForwardBase"}             CGPROGRAM             #pragma vertex vert             #pragma fragment frag             #include "Lighting.cginc"             fixed4 _Color;             sampler2D _MainTex;             float4 _MainTex_ST;             fixed4 _Specular;             float _Gloss;               struct a2v{                 float4 vertex : POSITION;                 float3 normal : NORMAL;             };             struct v2f{                 float4 pos:SV_POSITION;                 float3 worldNormal:TEXCOORD0;                 float3 worldPos:TEXCOORD1;             };               v2f vert(a2v v){                 v2f o;                 o.pos = mul(UNITY_MATRIX_MVP,v.vertex);                 o.worldNormal = UnityObjectToWorldNormal(v.normal);                 o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;                 return o;             }               fixed4 frag(v2f i) : SV_Target{                 fixed3 worldNormal = normalize(i.worldNormal);                 fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));                 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;                 fixed3 diffuse = _LightColor0.rgb * max(0,dot(worldNormal,worldLightDir));                 fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));                 fixed3 halfDir = normalize(worldLightDir + viewDir);                 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss);                 return fixed4(ambient + diffuse + specular ,1.0);             }               ENDCG         }     }     SubShader {         Lod 100         Pass{             Tags {"LightMode" = "ForwardBase"}             CGPROGRAM             #pragma vertex vert             #pragma fragment frag             #include "Lighting.cginc"             fixed4 _Specular;             float _Gloss;             fixed4 _Color;;             struct a2v{                 float4 vertex : POSITION;                 float4 normal : NORMAL;             };             struct v2f{                 float4 pos : SV_POSITION;                 fixed3 color : COLOR;             };               v2f vert(a2v v){                 v2f o;                 o.pos = mul(UNITY_MATRIX_MVP,v.vertex);                 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;                 fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));                 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);                 fixed3 diffuse = _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal,worldLightDir));                 fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));                 fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);                 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir,viewDir)),_Gloss);                 o.color = ambient + diffuse + specular;                   return o;             }               fixed4 frag (v2f i) : SV_Target{                 return fixed4(i.color,1.0);             }             ENDCG         }     }

       第二个subshader和第一个相比只是去除了对贴图渲染的部分,第三个subshader则是将所有的光照计算放到了顶点函数中。
       注意第二个subshader LOD为200,第三个subshader LOD为100。

结果如下
游戏开发Unity渲染场景光照性能优化 ShaderLOD游戏开发Unity渲染场景光照性能优化 ShaderLOD游戏开发Unity渲染场景光照性能优化 ShaderLOD游戏开发Unity渲染场景光照性能优化 ShaderLOD

用这种方式可以动态的剔除复杂的Shader渲染,比如在低端的手机平台上,当检测到FPS低于一定数值可以考虑替换带有高度映射,法线贴图等功能的Shader,甚至可以降低贴图采样密度,停止UV动画。

 

游戏渠道心流游戏类型游戏引擎画面表现风格游戏视觉开放源码JavaS
上一篇:Unity shader入门精要笔记(六)


下一篇:【Unity Shader】渲染纹理(镜子与玻璃)