研究崩坏3人物渲染

之前看到崩坏3的渲染,看起来效果很好,也看了网上一些对于崩坏3人物渲染的解析,不过并没有详细代码参考。今天就在这里分享下自己还原崩坏3的渲染,不能说100%还原,不过也达到80-90%,供大家参考。好了,不多说,上代码。


 

Shader "Custom/benghuai"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_LightMapTex("LightMapTex", 2D) = "white" {}
		_MainColor("MainColor",color) = (1,1,1,1)
		_FirstShadowArea("FirstShadowArea",range(0,1)) = 0.5
		_FirstShadowColor("FirstShadowColor",color) = (0.5,0.5,0.5,1)
		_SecondShadow("SecondShadow", range(0,1)) = 0.2
		_SecondShadowColor("SecondShadowColor",color) = (0.5,0.5,0.5,1)
		_Shininess("Shininess",range(0,1)) = 0.2
		_SpecIntensity("SpecIntensity",range(0,1)) = 0.7
		_LightSpecColor("LightSpecColor",color) = (0.5,0.5,0.5,1)
		_DitherAlpha("DitherAlpha",float) = 0
		_Outline("Thick of Outline",range(0,0.1)) = 0.03
		_Factor("Factor",range(0,1)) = 0.5
		_OutColor("OutColor",color) = (0,0,0,0)
	}
	SubShader
	{
		pass 
		{//处理光照前的pass渲染
			Tags{ "LightMode" = "Always" }
			Cull Front
			ZWrite On
			CGPROGRAM
			#pragma multi_compile_fog
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			float _Outline;
			float _Factor;
			fixed4 _OutColor;

			struct v2f 
			{
				float4 pos:SV_POSITION;
				UNITY_FOG_COORDS(0)
			};

			v2f vert(appdata_full v) 
			{
				v2f o;
				float3 dir = normalize(v.vertex.xyz);
				float3 dir2 = v.normal;
				float D = dot(dir,dir2);
				dir = dir * sign(D);
				dir = dir * _Factor + dir2 * (1 - _Factor);
				v.vertex.xyz += dir * _Outline;
				o.pos = UnityObjectToClipPos(v.vertex);
				UNITY_TRANSFER_FOG(o, o.pos);
				return o;
			}

			float4 frag(v2f i) :COLOR
			{
				float4 c = _OutColor;
				UNITY_APPLY_FOG(i.fogCoord, c);
				return c;
			}
			ENDCG
		}


		Pass
		{
			Tags{ "RenderType" = "Opaque" }
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fog
			#include "lighting.cginc"
			#include "UnityCG.cginc"
			#include "AutoLight.cginc"


			sampler2D _MainTex;
			float4 _MainTex_ST;


			sampler2D _LightMapTex;
			//float4 _ProjectionParams;
			//float4 _WorldSpaceLightPos0;
			float _UsingDitherAlpha;
			float _DitherAlpha;
			float _SecondShadow;
			fixed4 _SecondShadowColor;
			fixed4 _FirstShadowColor;
			half _FirstShadowArea;
			float _Shininess;
			float _SpecIntensity;
			fixed4 _LightSpecColor;
			float _BloomFactor;
			fixed4 _MainColor;


			float4 imm[4];


			struct appdata
			{
				float4 vertex : POSITION;
				float3 normal: NORMAL;
				float4 uv : TEXCOORD0;
				float4 color : COLOR0;
			};


			struct v2f
			{
				float4 vertex : POSITION;
				float4 color : COLOR0;
				float2 uv : TEXCOORD0;
				float  color1 : COLOR1;
				float3 normal : TEXCOORD1;
				float3 viewDir : TEXCOORD2;
				float4 vstex3:  TEXCOORD3;
				UNITY_FOG_COORDS(4)
			};

			v2f vert (appdata v)
			{
				v2f o;
				float4 worldPos = UnityObjectToClipPos(v.vertex);
				o.viewDir = normalize(WorldSpaceViewDir(v.vertex));
				o.vertex = worldPos;
				o.color = v.color;
				o.uv = TRANSFORM_TEX(v.uv.xy, _MainTex);


				float4 worldNormal;
				worldNormal.xyz = normalize(UnityObjectToWorldNormal(v.normal));
				o.normal.xyz = worldNormal.xyz;

				//计算半lambert光照
				float lambert = dot(worldNormal.xyz, normalize(-_WorldSpaceLightPos0));
				lambert = lambert * 0.5 + 0.5;
				o.color1 = lambert;
				//目前vstex3再片源着色器里面使用是在opengles3.0中做一些特殊处理,目前不清楚这部分特殊处理是在做什么
				//worldPos.y = worldPos.y * _ProjectionParams.x;
				//worldNormal.xzw = worldPos.xwy * float3(0.5, 0.5, 0.5);
				//worldPos.xy = worldNormal.zz + worldNormal.xw;
				//o.vstex3.xyw = worldPos.xyw;//smoothstep(float3(0.0, 0.0, 0.0), worldPos.xyw, ises3);//mix(vec3(0.0, 0.0, 0.0), worldPos.xyw, vec3(bvec3(ises3)));
				//o.vstex3.z = _DitherAlpha;// ises3 ? _DitherAlpha : float(0.0);
				UNITY_TRANSFER_FOG(o,o.vertex);
				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
				imm[0] = float4(1.0, 0.0, 0.0, 0.0);
				imm[1] = float4(0.0, 1.0, 0.0, 0.0);
				imm[2] = float4(0.0, 0.0, 1.0, 0.0);
				imm[3] = float4(0.0, 0.0, 0.0, 1.0);
				float4 mainColor = float4(1,1,1,1);


				//opengles3.0里面做了一些特殊处理,目前不清楚处理了什么
				// bool ises3;
				//#ifdef UNITY_ADRENO_ES3
				// ises3 = !!(float4(0.0, 0.0, 0.0, 0.0) != float4(_UsingDitherAlpha, _UsingDitherAlpha, _UsingDitherAlpha, _UsingDitherAlpha));
				//#else
				// ises3 = float4(0.0, 0.0, 0.0, 0.0) != float4(_UsingDitherAlpha, _UsingDitherAlpha, _UsingDitherAlpha, _UsingDitherAlpha);
				//#endif
				// if (ises3) {
				//#ifdef UNITY_ADRENO_ES3
				// ises3 = !!(i.vstex3.z<0.949999988);
				//#else
				// ises3 = i.vstex3.z<0.949999988;
				//#endif
				// if (ises3) {
				//float2 temp = i.vstex3.yx / i.vstex3.ww;
				//temp.xy = temp.xy * _ScreenParams.yx;
				//temp.xy = temp.xy * float2(0.25, 0.25);
				//
				//float2 tempvec2 = float2(max(temp.x, -temp.x), max(temp.y, -temp.y));// greaterThanEqual(temp.xyxy, (-temp.xyxy)).xy;
				//temp.xy = float2(abs(temp.x) - floor(abs(temp.x)), abs(temp.y) - floor(abs(temp.y)));//fract(abs(temp.xy));
				//temp.x = (tempvec2.x) ? temp.x : (-temp.x);
				//temp.y = (tempvec2.y) ? temp.y : (-temp.y);
				//temp.xy = temp.xy * float2(4.0, 4.0);
				//float2 tempvec2_t = float2(temp.xy);


				/*mainColor.x = dot(hlslcc_mtx4x4_DITHERMATRIX[0], imm[int(tempvec2_t.y)]);
				mainColor.y = dot(hlslcc_mtx4x4_DITHERMATRIX[1], imm[int(tempvec2_t.y)]);
				mainColor.z = dot(hlslcc_mtx4x4_DITHERMATRIX[2], imm[int(tempvec2_t.y)]);
				mainColor.w = dot(hlslcc_mtx4x4_DITHERMATRIX[3], imm[int(tempvec2_t.y)]);*/




				/* temp.x = dot(mainColor, imm[int(tempvec2_t.x)]);
				temp.x = i.vstex3.z * 17.0 + (-temp.x);
				temp.x = temp.x + 0.99000001;
				temp.x = floor(temp.x);
				temp.x = max(temp.x, 0.0);
				int u_xlati0 = int(temp.x);
				if ((u_xlati0) == 0) { discard; }*/
				//}
				//}


				fixed3 lightMapColor = tex2D(_LightMapTex, i.uv.xy).xyz;
				mainColor.xyz = tex2D(_MainTex, i.uv.xy).xyz;
				//采样的图的g通道乘以顶点r值
				float vrCr = lightMapColor.g * i.color.r;


				//阈值,关于第二层暗面的颜色,如果lColorG = 0,就是用第二层暗面颜色,否则使用第一层暗面颜色
				float lColorG = max(floor((vrCr + i.color1)* 0.5 + (-_SecondShadow) + 1.0), 0.0);
				half3 secondShadowColor = mainColor.xyz * _SecondShadowColor.rgb;
				fixed3 firstShadowColor = mainColor.xyz * _FirstShadowColor.rgb;
				secondShadowColor.rgb = (int(lColorG) != 0) ? firstShadowColor.rgb : secondShadowColor.rgb;


				//阈值,关于普通颜色与第一层暗面的阈值,如果lColorGx = 0 就是用第一层暗面,随光的方向进行移动
				float lColorGx = max(floor((-i.color.r) * lightMapColor.g + 1.5), 0.0);
				float2 tempXY = vrCr.rr * float2(1.20000005, 1.25) + float2(-0.100000001, -0.125);
				vrCr = (lColorGx != 0) ? tempXY.y : tempXY.x;
				vrCr = max(floor((vrCr + i.color1) * 0.5 + (-_FirstShadowArea) + 1.0), 0);
				lColorGx = int(vrCr);
				firstShadowColor.rgb = (lColorGx != 0) ? mainColor.rgb : firstShadowColor.rgb;


				vrCr = lightMapColor.g * i.color.r;
				//阈值,关于第一层暗面与第二层暗面的阈值,如果lColorGz = 0 就使用第二层暗面,固定暗面
				float lColorGz = max(floor(vrCr + 0.909999967), 0.0);
				half3 color = (lColorGz != 0) ? firstShadowColor.rgb : secondShadowColor.rgb;

				//计算高光
				float3 normal = normalize(i.normal.xyz);
				float3 viewDirection = i.viewDir;
				float3 lightDirection = normalize(-_WorldSpaceLightPos0.xyz);
				float3 H = normalize(viewDirection + lightDirection);
				half spec = pow(max(dot(normal, H), 0.0), _Shininess);

				//阈值,关于高光颜色选择,如果lColorGy = 0, 使用高光颜色,否则,无高光
				float lColorGy = int(max(floor(2 -spec - lightMapColor.b), 0.0));
				float3 specColor = lightMapColor.r * _LightSpecColor *_SpecIntensity * spec;//  float3(_SpecIntensity * _LightSpecColor.x, _SpecIntensity * _LightSpecColor.y, _SpecIntensity * _LightSpecColor.z);
				specColor = (lColorGy != 0) ? float3(0.0, 0.0, 0.0) : specColor.rgb;
				color.rgb = color.rgb + specColor;
				color.rgb = color.rgb * _MainColor.rgb;


				fixed4 col;
				col.xyz = color.rgb;
				col.w = _BloomFactor;
				return col;
			}
			ENDCG
		}
	}
}

 

代码部分注释已经写的特别详细了,整个渲染颜色是3部分,diffuse+第一层暗面+第二层暗面,分别有阈值进行控制,是使用光照阴影贴图的G通道 * 顶点颜色R进行阈值控制,我个人在解释贴图的时候并没有去管顶点颜色,仅仅希望贴图就控制这三种颜色区分。来解释下光照阴影贴图:

 

如图:为一张阴影光照贴图

 研究崩坏3人物渲染_第1张图片

此贴图为角色使用shader的阴影光照贴图,以每个通道进行说明。

r通道:

 研究崩坏3人物渲染_第2张图片

用来控制高光颜色,整个的高光颜色值 = R通道值 * 高光颜色参数 * 高光强度系数;( 高光颜色参数 与 高光强度系数 在unity材质球可进行修改)下图为高光颜色为 纯白色 ,高光强度系数为0.5;

  

左图为单纯输出高光颜色值,右图为加上原有其他效果(高光颜色参数调成了紫色)。

研究崩坏3人物渲染_第3张图片   研究崩坏3人物渲染_第4张图片

通俗解释:R通道颜色值越接近1,最终高光颜色就是正常参数控制的高光颜色,R值通道颜色越接近0,最终高光颜色值会比较暗淡。也就是说,这张图黑色的地方高光会比较若,白色的地方高光会比较强。

 

G通道:

 研究崩坏3人物渲染_第5张图片

因为角色shader分为了3部分主要颜色,第一部分,diffuse的正常颜色,第二部分,可随光方向移动的第一层阴影颜色, 第三部分,固定的第二层阴影颜色。G通道算是一个diffuse,第一层阴影,第二层阴影的一个阈值。

 研究崩坏3人物渲染_第6张图片研究崩坏3人物渲染_第7张图片研究崩坏3人物渲染_第8张图片研究崩坏3人物渲染_第9张图片

从左到右依次为diffuse原有颜色, diffuse加第一层阴影, 第一层阴影加第二层阴影, diffuse加第一层阴影加第二层阴影(此图与第二张图类似,因为阈值关系,第二层阴影并未显示出来,这里的原因是我并未对模型顶点颜色进行修改,所以控制第二层阴影的只有B通道)

 

下图为修改了G通道后的渲染,第二层阴影颜色为蓝色,方便观察

     研究崩坏3人物渲染_第10张图片    研究崩坏3人物渲染_第11张图片

此贴图对应的渲染,崩坏3中,可以看见,胸部下面,胯下,背后等有固定阴影的地方,其实就是G通道颜色深的地方,也就是第二层固定阴影。也就是如何旋转都会发现第二层阴影一直存在。

 研究崩坏3人物渲染_第12张图片

通俗解释:G通道,颜色值在0-0.5属于第二层固定阴影,颜色值在0.5-1之前属于第一层阴影,纯白色代表这个地方不会有阴影。

 

B通道:

 研究崩坏3人物渲染_第13张图片

B通道代表了高光范围,如图纯输出B通道 1,正常,0高光:

   研究崩坏3人物渲染_第14张图片 研究崩坏3人物渲染_第15张图片 研究崩坏3人物渲染_第16张图片

从左到右依次是,B通道全白,正常,B通道全黑图,很明显能看出来,B通道控制了高光的范围,哪个地方更容易出现高光。

通俗解释:B通道越接近0,代表这个地方不会出现高光,越接近1,代表这个地方有高光。

 研究崩坏3人物渲染_第17张图片

正常渲染加Bloom。

 

大家也可以参考下这篇文章,对于细节问题写的比较详细:https://blog.csdn.net/liumazi/article/details/78858811

 

你可能感兴趣的:(shader)