之前看到崩坏3的渲染,看起来效果很好,也看了网上一些对于崩坏3人物渲染的解析,不过并没有详细代码参考。今天就在这里分享下自己还原崩坏3的渲染,不能说100%还原,不过也达到80-90%,供大家参考。好了,不多说,上代码。
Shader "Custom/benghuai" { Properties { _MainTex("Texture", 2 D) = "white" {} _LightMapTex("LightMapTex", 2 D) = "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进行阈值控制,我个人在解释贴图的时候并没有去管顶点颜色,仅仅希望贴图就控制这三种颜色区分。来解释下光照阴影贴图:
如图:为一张阴影光照贴图
此贴图为角色使用shader的阴影光照贴图,以每个通道进行说明。
r通道:
用来控制高光颜色,整个的高光颜色值 = R通道值 * 高光颜色参数 * 高光强度系数;( 高光颜色参数 与 高光强度系数 在unity材质球可进行修改)下图为高光颜色为 纯白色 ,高光强度系数为0.5;
左图为单纯输出高光颜色值,右图为加上原有其他效果(高光颜色参数调成了紫色)。
通俗解释:R通道颜色值越接近1,最终高光颜色就是正常参数控制的高光颜色,R值通道颜色越接近0,最终高光颜色值会比较暗淡。也就是说,这张图黑色的地方高光会比较若,白色的地方高光会比较强。
G通道:
因为角色shader分为了3部分主要颜色,第一部分,diffuse的正常颜色,第二部分,可随光方向移动的第一层阴影颜色, 第三部分,固定的第二层阴影颜色。G通道算是一个diffuse,第一层阴影,第二层阴影的一个阈值。
从左到右依次为diffuse原有颜色, diffuse加第一层阴影, 第一层阴影加第二层阴影, diffuse加第一层阴影加第二层阴影(此图与第二张图类似,因为阈值关系,第二层阴影并未显示出来,这里的原因是我并未对模型顶点颜色进行修改,所以控制第二层阴影的只有B通道)
下图为修改了G通道后的渲染,第二层阴影颜色为蓝色,方便观察
此贴图对应的渲染,崩坏3中,可以看见,胸部下面,胯下,背后等有固定阴影的地方,其实就是G通道颜色深的地方,也就是第二层固定阴影。也就是如何旋转都会发现第二层阴影一直存在。
通俗解释:G通道,颜色值在0-0.5属于第二层固定阴影,颜色值在0.5-1之前属于第一层阴影,纯白色代表这个地方不会有阴影。
B通道:
B通道代表了高光范围,如图纯输出B通道 1,正常,0高光:
从左到右依次是,B通道全白,正常,B通道全黑图,很明显能看出来,B通道控制了高光的范围,哪个地方更容易出现高光。
通俗解释:B通道越接近0,代表这个地方不会出现高光,越接近1,代表这个地方有高光。
正常渲染加Bloom。
大家也可以参考下这篇文章,对于细节问题写的比较详细:https://blog.csdn.net/liumazi/article/details/78858811
暂无关于此日志的评论。