BR流程,贴图mask只有PBR的roughness、metallic、occlusion,不像战双那种PBRMask和ILM贴图全上。模型和法线做的很细,拆成两张图来存保证精度。
FragmentOutput frag(v2f i){// Tex SamplemainTexpbrMaskbumpilmMask// VectorPreparelightDirWScamDirWSnormalWSNdotL...// Property prepareemissionmetallicsmoothness...// Remap NdotL ShadowAreashadowArea calculateNdotLRemap = 1 - shadowArea;// TODO: Remap NdotV modify fresnelNdotV// shadowRampshadowRamp.rgb = Sample(_RampTex, 1 - shadowArea);// Remap ShadowRamp , ILM SecondShadowshadowRamp.rgb = lerp(_SecShadowColor.rgb, shadowRamp.rgb, ilm_AO);// Direct PBRNDF, G, F// NDF GGX/Anisotropy specArea remapNDF = NDF * ilm_SpecMask;// SpecRampspecRamp.rgb = Sample(_RampTex, specArea);// ComposedirectLightReuslt = (directDiffCol * shadowRamp + directSpecCol * specRamp) * mainlight.color * shadow * directOcclusion;// Indirect PBR// diffuse lerp Normal and Updir, lerp SHColor and self_envColorindirDiff sampleSH RemapSHNormalindirDiff * indirKd * albedo * occlusion// specular use reflectionProbe or Cubemap or MatcapindirSpec sampleCubeindirSpec * indirSpeFactor// Other Emission, Rimlight, additional light}
// Property preparehalf emission = 1 - mainTex.a;half metallic = lerp(0, _Metallic, pbrMask.r);half smoothness = lerp(0, _Smoothness, pbrMask.g);half occlusion = lerp(1 - _Occlusion, 1, pbrMask.b);half directOcclusion = lerp(1 - _DirectOcclusion, 1, pbrMask.b);half3 albedo = mainTex.rgb * _BaseColor.rgb;// NPR diffusefloat shadowArea = sigmoid(1 - halfLambert, _ShadowOffset, _ShadowSmooth * 10) * _ShadowStrength;half3 shadowRamp = lerp(1, _ShadowColor.rgb, shadowArea);//Remap NdotL for PBR Spechalf NdotLRemap = 1 - shadowArea;#if _SHADOW_RAMPshadowRamp = SampleDirectShadowRamp(TEXTURE2D_ARGS(_ShadowRampTex, sampler_ShadowRampTex), NdotLRemap);#endif// NdotV modify fresnel// ilmShadowshadowRamp.rgb = lerp(_SecShadowColor.rgb, shadowRamp.rgb, ilmAO);
// Directfloat3 directDiffColor = albedo.rgb;float perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(smoothness);float roughness = max(PerceptualRoughnessToRoughness(perceptualRoughness), HALF_MIN_SQRT);float roughnessSquare = max(roughness * roughness, HALF_MIN);float3 F0 = lerp(0.04, albedo, metallic);float NDF = DistributionGGX(NdotH, roughnessSquare);float G = GeometrySmith(NdotLRemap, NdotV, pow(roughness + 1.0, 2.0) / 8.0);float3 F = fresnelSchlick(HdotV, F0);// GGX specArea remapNDF = NDF * ilmSpecMask;float3 kSpec = F;// LightUpDiff: (1.0 - F) => (1.0 - F) * 0.5 + 0.5float3 kDiff = ((1.0 - F) * 0.5 + 0.5) * (1.0 - metallic);float3 nom = NDF * G * F;float3 denom = 4.0 * NdotV * NdotLRemap + 0.0001;float3 BRDFSpec = nom / denom;directDiffColor = kDiff * albedo;float3 directSpecColor = BRDFSpec * PI;#if _SHADOW_RAMPfloat specRange= saturate(NDF * G / denom.x);half4 specRampCol = SampleDirectSpecularRamp(TEXTURE2D_ARGS(_ShadowRampTex, sampler_ShadowRampTex), specRange);directSpecColor = clamp(specRampCol.rgb * 3 + BRDFSpec * PI / F, 0, 10) * F * shadowRamp;#endif// Compose direct lightingfloat3 directLightResult = (directDiffColor * shadowRamp + directSpecColor * NdotLRemap)* mainLight.color * mainLight.shadowAttenuation * directOcclusion;
// Hair Specfloat anisotropicOffsetV = - viewDirWS.y * _AnisotropicSlide + _AnisotropicOffset;half3 hairSpecTex = SAMPLE_TEXTURE2D(_HairSpecTex, sampler_LinearClamp, float2(UV1.x, UV1.y + anisotropicOffsetV));float hairSpecStrength = _SpecMinimum + pow(NdotH, _BlinnPhongPow) * NdotLRemap;half3 hairSpecColor = hairSpecTex * _SpecColor * hairSpecStrength;
// 本文中所用到的StencilUsage//MaterialMask = 0b_1110_0000,//...//MaterialCharacterLit = 0b_0110_0000,//MaterialCharFeatureMask = 0b_0110_0011,//MaterialFringeShadow = 0b_0110_0001,//MaterialEyelash = 0b_0110_0010,Name "FringeShadowCaster"Tags{"LightMode" = "GBufferFringeShadowCaster"}Stencil{Ref[_FriStencil]//MaterialCharacterLitComp[_FriStencilComp]//EqualPass[_FriStencilOp]//IncrementSaturateReadMask[_FriStencilReadMask]//MaterialFringeShadowWriteMask[_FriStencilWriteMask]//MaterialFringeShadow}Cull BackZWrite OffColorMask [_FriColorMask]...FringeShadowCaster_v2f FringeShadowCasterVert(FringeShadowCaster_a2v v){FringeShadowCaster_v2f o;Light mainLight = GetMainLight();float3 lightDirWS = normalize(mainLight.direction);float3 lightDirVS = normalize(TransformWorldToViewDir(lightDirWS));// Cam is Upward: let shadow close to face.float3 camDirOS = normalize(TransformWorldToObject(GetCameraPositionWS()));float camDirFactor = 1 - smoothstep(0.1, 0.9, camDirOS.y);float3 positionVS = TransformWorldToView(TransformObjectToWorld(v.vertex));positionVS.x -= 0.004 * lightDirVS.x * _ScreenOffsetScaleX;positionVS.y -= 0.007 * _ScreenOffsetScaleY * camDirFactor;o.positionHCS = TransformWViewToHClip(positionVS);return o;}
Name "FringeShadowReceiver"Tags{"LightMode" = "GBufferFringeShadowReceiver"}Stencil{Ref[_FriStencil]//MaterialCharacterLitComp[_FriStencilComp]//EqualPass[_FriStencilOp]//KeepReadMask[_FriStencilReadMask]//MaterialFringeShadowWriteMask[_FriStencilWriteMask]//MaterialFringeShadow}Cull BackZWrite OffColorMask [_FriColorMask]
// vert// Face lightmap dot valueLight mainLight = GetMainLight();float3 lightDirWS = mainLight.direction;lightDirWS.xz = normalize(lightDirWS.xz);_FaceRightDirWS.xz = normalize(_FaceRightDirWS.xz);o.faceLightDot.x = dot(lightDirWS.xz, _FaceRightDirWS.xz);o.faceLightDot.y = saturate(dot(-lightDirWS.xz, _FaceFrontDirWS.xz) * 0.5 + _ShadowOffset);// frag// FaceLightMapfloat2 faceLightMapUV = UV1;faceLightMapUV.x = 1 - faceLightMapUV.x;faceLightMapUV.x = i.faceLightDot.x < 0 ? 1 - faceLightMapUV.x : faceLightMapUV.x;half4 faceLightMap = SAMPLE_TEXTURE2D(_FaceLightMap, sampler_FaceLightMap, faceLightMapUV);half faceSDF = faceLightMap.r;half faceShadowArea = faceLightMap.a;float faceMapShadow = sigmoid(faceSDF, i.faceLightDot.y, _ShadowSmooth * 10) * faceShadowArea;shadowArea = (1 - faceMapShadow) * _ShadowStrength;
// Nose Specfloat faceSpecStep = clamp(i.faceLightDot.y, 0.001, 0.999);faceLightMapUV.x = 1 - faceLightMapUV.x;faceLightMap = SAMPLE_TEXTURE2D(_FaceLightMap, sampler_FaceLightMap, faceLightMapUV);float noseSpecArea1 = step(faceSpecStep, faceLightMap.g);float noseSpecArea2 = step(1 - faceSpecStep, faceLightMap.b);float noseSpecArea = noseSpecArea1 * noseSpecArea2;// alternative: noseSpecArea *= smoothstep(_NoseSpecMin, _NoseSpecMax, 1 - i.faceLightDot.y)
// Eye// Parallaxfloat3 viewDirOS = TransformWorldToObjectDir(viewDirWS);viewDirOS = normalize(viewDirOS);float2 parallaxOffset = viewDirOS.xy;parallaxOffset.y *= -1;float2 parallaxUV = i.uv + _ParallaxScale * parallaxOffset;// parallaxMaskfloat2 centerVec = i.uv - float2(0.5, 0.5);half centerDist = dot(centerVec, centerVec);half parallaxMask = smoothstep(_ParallaxMaskEdge, _ParallaxMaskEdge + _ParallaxMaskEdgeOffset, 1 - centerDist);// Tex Samplehalf4 mainTex = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, lerp(UV, parallaxUV, parallaxMask));...// Eye spec addBlendOp [_BlendOp]// AddBlend [_BlendSrc] [_BlendDst]// SrcAlpha One// Eye shadow blendBlendOp [_BlendOp]// AddBlend [_BlendSrc] [_BlendDst]// SrcColor Zero
v2f vert (a2v v){...float offsetDist = _MULTIPASS_PARAMS.z * _FurLength;float3 offsetPositionWS = TransformObjectToWorld(v.vertex);float3 normalWS = TransformObjectToWorldNormal(v.normal);offsetPositionWS += offsetDist * normalWS;o.positionHCS = TransformWorldToHClip(offsetPositionWS);o.positionWS = offsetPositionWS;...o.clipThreshold = _FurCLipMin + (_FurCLipMax - _FurCLipMin) * pow(_MULTIPASS_PARAMS.z, _FurPowShape);return o;}FragmentOutput frag(v2f i){UNITY_SETUP_INSTANCE_ID(i);// Fur Cliphalf furNoise = SAMPLE_TEXTURE2D(_FurNoise, sampler_FurNoise, i.uv.zw);clip(furNoise - i.clipThreshold);...}
// Outline if ((data.materialFlags & kCharacterMaterialFlagOutline) != 0) { float lightMask = 1 - smoothstep(0.5, 0.8, abs(lightDirVS.z)); float outLineLightResult = step(0.8, NdotL * NdotL) * alpha * lightMask; #if defined(_DIRECTIONAL) return half4(unityLight.color * outLineLightResult, 1); #else outLineLightResult = step(0.8, pow(NdotL, 3)) * alpha; return half4(unityLight.color * outLineLightResult, 1); #endif }