王者荣耀资源浅析

简单分析场景贴图制作

Posted by Bob on October 18, 2018

王者荣耀国内移动游戏收入榜的第一名,其季度流水超过40亿元。虽然我不爱玩,但是可以分析学习其制作方式。分析文章将形成一个系列:地图、角色、特效、UI等。

1.地图

  • 地表草沙石头alpha融合
  • 河流、河岸波浪、瀑布
  • 粒子特效
  • 出生地建筑模型、pbr使用
  • 地表物件:草的运动(Hair Groom系统),旗帜飘动

2.角色

  • 模型分级low middle high battle show
  • 模型动作
  • 模型shader

开篇简单分析一下场景石头贴图处理方式

贴图

红方(PGD_M_20RockGroupRed_01_D)和蓝方(PGD_M_20RockGroupBlue_01_D)均未512px的贴图(无alpha) image

草丛(PGD_M_20Foliage_01_D_rgb)贴图带alpha image

模型

石头块小摆件做了静态合批 image

草丛 image

shader

无alpha的贴图处理,特别之处就是对贴图亮度使用lerp(c1, c2, w)处理。 当w为0时返回c1,为1时返回c2,在0~1之间时,以比重w将c1-c2进行线性插值计算。 image

如下shader是反向编译后shader,分析人工重写的,可能与原始游戏有些差异。

Shader "S_Game_Scene/Texture"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}

		//原版的如下三个参数定义的是全局变量,这里为了方便调试修改为可视化属性
		_Global_SceneTintColor("GlobalSceneTintColor",Color)= (1,1,1)
		_Global_SceneBrightness("GlobalSceneBrightness",Range(1,10))=1
		_Global_SceneTintIntensity("GlobalSceneTintIntensity",Range(0,1))=1
	}
	SubShader
	{
		Tags { "QUEUE" = "Geometry+3" "RenderType" = "Opaque" }
		LOD 100
		ColorMask RGB -1
		
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			// make fog work
			#pragma multi_compile_fog
			
		
 
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				UNITY_FOG_COORDS(1)
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				UNITY_TRANSFER_FOG(o,o.vertex);
				return o;
			}

			fixed3 _Global_SceneTintColor;
			float _Global_SceneTintIntensity;
			float _Global_SceneBrightness;
			
			fixed4 frag (v2f i) : SV_Target
			{
				//Operation for brightnes
				fixed4 col = tex2D(_MainTex, i.uv);

				float3 brtColor = col.rgb * _Global_SceneBrightness;
				float3 newColor = lerp (brtColor, brtColor * _Global_SceneTintColor,_Global_SceneTintIntensity);
				col.rgb = newColor;
				// apply fog
				UNITY_APPLY_FOG(i.fogCoord, col);
				return col;
			}
			ENDCG
		}
	}
}

无光照渲染石块如下图 image

下面是alpha测试的shader,使用clip来做裁剪。多用在植被上面。

透明度裁剪原理: if ((col.a - _Cutoff) < 0.0){ discard; } 如果结果小于0,将片元舍弃 clip(col.a - _Cutoff);

image

Shader "S_Game_Scene/Texture_Alpha"
{
	Properties
	{
		_MainTex ("Base (RGBA)", 2D) = "white" {}
		_CutOff ("AlphaTest", Range(0, 1.01)) = 0.5
		//原始的如下三个参数定义的是全局变量,这里为了方便调试修改为可视化属性
		_Global_SceneTintColor("GlobalSceneTintColor",Color)= (1,1,1)
		_Global_SceneBrightness("GlobalSceneBrightness",Range(1,10))=1
		_Global_SceneTintIntensity("GlobalSceneTintIntensity",Range(0,1))=1
	}
	SubShader
	{
		Tags { "QUEUE" = "Geometry+3" "RenderType" = "Opaque" }
		LOD 100
		Blend SrcAlpha OneMinusSrcAlpha

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			// make fog work
			#pragma multi_compile_fog
			
		
 
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				UNITY_FOG_COORDS(1)
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				UNITY_TRANSFER_FOG(o,o.vertex);
				return o;
			}

			fixed3 _Global_SceneTintColor;
			float _Global_SceneTintIntensity;
			float _Global_SceneBrightness;
			float _Cutoff;
			
			fixed4 frag (v2f i) : SV_Target
			{
				//Operation for brightnes
				fixed4 col = tex2D(_MainTex, i.uv);
				clip(col.a - _Cutoff);
				float3 brtColor = col.rgb * _Global_SceneBrightness;
				float3 newColor = lerp (brtColor, brtColor * _Global_SceneTintColor,_Global_SceneTintIntensity);
				col.rgb = newColor;
				// apply fog
				UNITY_APPLY_FOG(i.fogCoord, col);
				return col;
			}
			ENDCG
		}
	}
}

无光照渲染草如下图 image