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 随笔 第1张

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

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。


游戏开发Unity渲染场景光照性能优化 ShaderLOD 随笔 第2张

设置LOD Group


游戏开发Unity渲染场景光照性能优化 ShaderLOD 随笔 第3张游戏开发Unity渲染场景光照性能优化 ShaderLOD 随笔 第4张游戏开发Unity渲染场景光照性能优化 ShaderLOD 随笔 第5张

点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 随笔 第6张 游戏开发Unity渲染场景光照性能优化 ShaderLOD 随笔 第7张

       结果很明显, 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 随笔 第8张游戏开发Unity渲染场景光照性能优化 ShaderLOD 随笔 第9张游戏开发Unity渲染场景光照性能优化 ShaderLOD 随笔 第10张游戏开发Unity渲染场景光照性能优化 ShaderLOD 随笔 第11张

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

 

游戏渠道 心流 游戏类型 游戏引擎画面表现风格 游戏视觉 开放源码 JavaS
扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄