#ifndef CHARACTER_CORE_INCLUDED #define CHARACTER_CORE_INCLUDED #include "CharacterPBR_Inputs.hlsl" ////INPUTS//////////////////////////////////////////////////////////// /////////////////////////////////// #if defined(_EMISSION_ON) || defined(_ANISO_GGX_ON) || defined(ANISO_KAJIYA_KAY) TEXTURE2D(_MaskMap2); SAMPLER(sampler_MaskMap2); #endif CBUFFER_START(UnityPerMaterial) half4 _MainTex_ST; half4 _NormalMap_ST; half _CutoffThres; half _HighLightLerp; half4 _HighLightLerpColor; half _OriColorAdd; half4 _Color; half _NormalMapScale;half _ColorLerpMask; half4 _LerpMaskColor; half4 _AmbientColor; half4 _MaskMap_ST; half4 _MaskMap2_ST; half4 _EmissionColor; half _EmissionStrength; half _SmoothnessScale; half _MetallicScale; half _Occlusion; half _DiffuseWrap; half _CameraLightIntensityMin; half _CameraLightIntensityMax; half _SkinNormalBias; half _SkinBlurStrength; half _SkinCurvature; half _SkinAmbientIntensity; half4 _SSSLut_ST; half _ReflectionCubeLod; half _ReflectionYOffset; #if defined(TRANSLUCENCY_ON) half3 _SkinTranslucencyColor; #endif #if defined(_ANISO_GGX_ON) half _AnisoSmoothnessScale; half _AnisoMetallicScale; half _AnisoSpecAlbedoScale; half _AmbientReflectionStrength; #endif CBUFFER_END ///////////////////////////////////////////////////////////////////////////////////////////////////////// TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex); TEXTURE2D(_MaskMap); SAMPLER(sampler_MaskMap); TEXTURE2D(_SSSLut); SAMPLER(sampler_SSSLut); TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap); TEXTURECUBE(_ReflectionCube); SAMPLER(sampler_ReflectionCube); struct SurfaceOutputStandardCustom { half3 Albedo; half3 Normal; half3 Emission; half Metallic; half Smoothness; half Occlusion; half Alpha; half IsSkin; half3 SkinNormal; half SkinCurvature; half3 TranslucencyColor; half Anisotropy; half3 WorldTangent; half3 WorldBitangent; half3 HighlightColor; half3 AnisoSpecColor; half MainShift; half MainExponent; half SubShift; half SubExponent; }; struct VertexInput { half4 vertex : POSITION; half4 normal : NORMAL; half4 tangent: TANGENT; half2 texcoord : TEXCOORD0; half2 lightmapUV : TEXCOORD1; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct VertexOutput { half4 pos : SV_POSITION; half2 texcoord : TEXCOORD0; half4 tSpace0 : TEXCOORD1; half4 tSpace1 : TEXCOORD2; half4 tSpace2 : TEXCOORD3; half3 normal : TEXCOORD4; float4 shadowCoord : TEXCOORD5; UNITY_VERTEX_INPUT_INSTANCE_ID UNITY_VERTEX_OUTPUT_STEREO }; #define WorldNormalVector(i, normal) normalize(half3(dot(i.tSpace0.xyz,normal), dot(i.tSpace1.xyz,normal), dot(i.tSpace2.xyz,normal))) ////BRDF///////////////////////////////////////////////////////////////////////////////////////////////// half3 Unity_GlossyEnvironment (half4 hdr, Unity_GlossyEnvironmentData glossIn) { half perceptualRoughness = glossIn.roughness /* perceptualRoughness */ ; #if 0 float m = PerceptualRoughnessToRoughness(perceptualRoughness); const float fEps = 1.192092896e-07F; float n = (2.0/max(fEps, m*m))-2.0; n /= 4; perceptualRoughness = pow( 2/(n+2), 0.25); #else perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness); #endif half3 R = glossIn.reflUVW; half4 rgbm = SAMPLE_TEXTURECUBE_LOD(_ReflectionCube, sampler_ReflectionCube, R, 3); return DecodeHDR(rgbm, hdr); } inline half GGXSpecularTerm(half roughness, half nh, half lh) { half a = roughness; float a2 = a * a; float d = nh * nh * (a2 - 1.f) + 1.00001f; float specularTerm = a2 / (max(0.1f, lh * lh) * (roughness + 0.5f) * (d * d) * 4); #if defined (SHADER_API_MOBILE) specularTerm = clamp(specularTerm - 1e-4f, 0.0, 100.0); // Prevent FP16 overflow on mobiles #endif return specularTerm; } #if defined(_SKIN_ON) inline half3 SkinDiffuseTerm(half3 normal, half3 lightDir, half skinCurvature) { half NoL = dot(normal, lightDir); NoL = NoL * 0.5 + 0.5; half3 diffuseTerm = SAMPLE_TEXTURE2D(_SSSLut, sampler_SSSLut, half2(NoL, skinCurvature)); return diffuseTerm; } #endif #if defined(_ANISO_GGX_ON) inline half D_GGXAniso_SGame(half TdotH, half BdotH, half mt, half mb, half nh) { half d = TdotH * TdotH / (mt * mt) + BdotH * BdotH / (mb * mb) + nh * nh; return (1.0 / (3.14 * mt * mb * d * d)); } inline half V_SmithJointGGXAniso_SGame(half TdotV, half BdotV, half NdotV, half TdotL, half BdotL, half NdotL, half roughnessT, half roughnessB) { half lambdaV = NdotL * sqrt(roughnessT * TdotV * TdotV + roughnessB * BdotV * BdotV + NdotV * NdotV); half lambdaL = NdotV * sqrt(roughnessT * TdotL * TdotL + roughnessB * BdotL * BdotL + NdotL * NdotL); return 0.5 / max(1e-5f, (lambdaV + lambdaL)); } inline half GGXSpecularTermAniso_SGame(half3 worldTangent, half3 worldBiTangent, half roughnessT, half roughnessB, half3 halfDir, half3 lightDir, half3 viewDir , half nl, half nh, half nv) { half mt = roughnessT; half mb = roughnessB; half TdotH = dot(worldTangent, halfDir); half BdotH = dot(worldBiTangent, halfDir); half TdotV = dot(worldTangent, viewDir); half BdotV = dot(worldBiTangent, viewDir); half TdotL = dot(worldTangent, lightDir); half BdotL = dot(worldBiTangent, lightDir); half D = D_GGXAniso_SGame(TdotH, BdotH, mt, mb, nh); half V = V_SmithJointGGXAniso_SGame(TdotV, BdotV, nv, TdotL, BdotL, nl, mt, mb); half specularTerm = (V * D) * 3.14; return clamp(specularTerm, 0, 5); } #endif half3 DirectDiffuse(SurfaceOutputStandardCustom s, half nl, half3 lightDir, half3 viewDir) { half3 diffColor = s.Albedo; half3 diffuseTerm = half3(nl, nl, nl); #if defined(_SKIN_ON) half3 skinDiffuseTerm = SkinDiffuseTerm(s.SkinNormal, lightDir, s.SkinCurvature); diffuseTerm = lerp(diffuseTerm, skinDiffuseTerm, s.IsSkin); #endif return diffColor * diffuseTerm; } half3 DirectSpecular(SurfaceOutputStandardCustom s, half nl, half3 lightDir, half smoothness, half roughness, half3 specColor, half3 viewDir) { half3 normal = s.Normal; #if defined(_ANISO_GGX_ON) half3 worldTangent = s.WorldTangent; half3 worldBitangent = s.WorldBitangent; #endif half3 halfDir = normalize(lightDir + viewDir); half nh = saturate(dot(normal, halfDir)); half nv = abs(dot(normal, viewDir)); half lh = saturate(dot(lightDir, halfDir)); half specularTerm = GGXSpecularTerm(roughness, nh, lh); #if defined(_ANISO_GGX_ON) half roughnessT = roughness; half roughnessB = 1.0; half anisoSpecularTerm = GGXSpecularTermAniso_SGame(worldTangent, worldBitangent, roughnessT, roughnessB, halfDir, lightDir, viewDir, nl, nh, nv); specularTerm = lerp(specularTerm, anisoSpecularTerm, s.Anisotropy); #endif specularTerm = max(0, specularTerm * nl); return specularTerm * specColor; } half3 DirectSpecular(SurfaceOutputStandardCustom s, half nl, half3 lightDir, half3 specColor, half3 viewDir) { half smoothness = s.Smoothness; half perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(smoothness); half roughness = PerceptualRoughnessToRoughness(perceptualRoughness); return DirectSpecular(s, nl, lightDir, smoothness, roughness, specColor, viewDir); } inline half4 Character_BRDF2_PBS(SurfaceOutputStandardCustom s, half3 specColor, half oneMinusReflectivity, half3 viewDir, UnityLight light) { half3 normal = s.Normal; half nl = saturate((dot(normal, light.dir) + _DiffuseWrap) / ((1 + _DiffuseWrap) * (1 + _DiffuseWrap))); half3 directDiffuse = DirectDiffuse(s, nl, light.dir, viewDir); half3 directSpecular = DirectSpecular(s, nl, light.dir, specColor, viewDir); half3 color = directDiffuse * light.color + directSpecular * light.color; return half4(color, 1); } half3 IndirectSpecular(half roughness, half3 specColor) { half surfaceReduction; surfaceReduction = 1.0 / (roughness * roughness + 1.0); return surfaceReduction * specColor; } inline half4 Character_BRDF2_PBS(SurfaceOutputStandardCustom s, half3 specColor, half oneMinusReflectivity, half3 viewDir, UnityLight light, UnityIndirect gi) { half3 diffColor = s.Albedo; half3 normal = s.Normal; half smoothness = s.Smoothness; half perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(smoothness); half roughness = PerceptualRoughnessToRoughness(perceptualRoughness); half nl = saturate((dot(normal, light.dir) + _DiffuseWrap) / ((1 + _DiffuseWrap) * (1 + _DiffuseWrap))); half3 directDiffuse = DirectDiffuse(s, nl, light.dir, viewDir); half3 directSpecular = DirectSpecular(s, nl, light.dir, smoothness, roughness, specColor, viewDir); half3 directColor = directDiffuse * light.color + directSpecular * light.color; half3 indirectSpecular = IndirectSpecular(roughness, specColor) * gi.specular; half3 indirectColor = diffColor * gi.diffuse + indirectSpecular; half3 finalColor = directColor + indirectColor; return half4(finalColor, 1); } ///////////////////////////////////////////////////////////////////////////////////////////////////////// /////LIGHTING//////////////////////////////////////////////////////////////////////////////////////////// inline half4 LightingCharacter(SurfaceOutputStandardCustom s, half3 viewDir, UnityGI gi) { half3 mainLightDir = gi.light.dir; half3 normalizeLightDir = normalize(half3(mainLightDir.x, 0.0, mainLightDir.z)); half3 normalizeViewDir = normalize(half3(viewDir.x, 0.0, viewDir.z)); half3 dirFactor = dot(normalizeLightDir, normalizeViewDir) * 0.5 + 0.5; half cameraLightAtten = lerp(_CameraLightIntensityMax, _CameraLightIntensityMin, dirFactor); #if defined(TRANSLUCENCY_ON) half translucencyAtten = lerp(_CameraLightIntensityMin, _CameraLightIntensityMax, dirFactor); s.Albedo += s.TranslucencyColor * (saturate(dot(s.Normal, -mainLightDir)) + saturate(dot(s.Normal, viewDir)) * translucencyAtten); #endif half oneMinusReflectivity; half3 specColor; half outputAlpha; half3 albedo = DiffuseAndSpecularFromMetallic(s.Albedo, s.Metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity); #if defined(_ANISO_GGX_ON) specColor = lerp(specColor, lerp(half4(0.04, 0.04, 0.04, 0.06), s.Albedo, _AnisoSpecAlbedoScale), s.Anisotropy); #endif half nv = dot(s.Normal, viewDir); // s.Albedo = PreMultiplyAlpha(albedo, s.Alpha, oneMinusReflectivity, /*out*/ outputAlpha); half4 c = Character_BRDF2_PBS(s, specColor, oneMinusReflectivity, viewDir, gi.light, gi.indirect); UnityGI gi2 = gi; // HSV Color Alter half3 hsvLightCol = RgbToHsv_SGame(GetMainLight().color.rgb); hsvLightCol.b = 1; hsvLightCol = HsvToRgb_SGame(hsvLightCol); gi2.light.color = hsvLightCol * cameraLightAtten; gi2.light.dir = viewDir; c.rgb += Character_BRDF2_PBS(s, specColor, oneMinusReflectivity, viewDir, gi2.light); c.a = outputAlpha; return c; } inline half3 CharacterGI_IndirectSpecular(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn) { half4 hdr = data.probeHDR[0]; hdr.x = 1; glossIn.reflUVW = RotationY(glossIn.reflUVW, _ReflectionYOffset); half3 env0 = Unity_GlossyEnvironment(hdr, glossIn); return env0 * occlusion; } inline UnityGI CharacterGI_Base(UnityGIInput data, half occlusion, half3 normalWorld) { UnityGI o_gi; ResetUnityGI(o_gi); o_gi.light = data.light; half3 hsvLightCol = RgbToHsv_SGame(GetMainLight().color.rgb); hsvLightCol.b = 1; hsvLightCol = HsvToRgb_SGame(hsvLightCol); o_gi.light.color = hsvLightCol * data.atten; o_gi.indirect.diffuse = data.ambient; o_gi.indirect.diffuse *= occlusion; return o_gi; } inline UnityGI CharacterGlobalIllumination(UnityGIInput data, half occlusion, half3 normalWorld, Unity_GlossyEnvironmentData glossIn) { UnityGI o_gi = CharacterGI_Base(data, occlusion, normalWorld); o_gi.indirect.specular = CharacterGI_IndirectSpecular(data, occlusion, glossIn); return o_gi; } inline void LightingCharacter_GI(SurfaceOutputStandardCustom s, UnityGIInput data, inout UnityGI gi) { // Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(_ReflectionCubeLod, data.worldViewDir, s.Normal, half3(0, 0, 0)); Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(s.Smoothness, data.worldViewDir, s.Normal, half3(0, 0, 0)); gi = CharacterGlobalIllumination(data, s.Occlusion, s.Normal, g); #if defined(_SKIN_ON) gi.indirect.diffuse *= lerp(1, _SkinAmbientIntensity, s.IsSkin); #endif #if defined(_ANISO_GGX_ON) gi.indirect.specular *= lerp(1, _AmbientReflectionStrength, s.Anisotropy); #endif } ///////////////////////////////////////////////////////////////////////////////////////////////////////// VertexOutput VertBaseCore(VertexInput v) { VertexOutput o; VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz); VertexNormalInputs normalInput = GetVertexNormalInputs(v.normal, v.tangent); o.pos = vertexInput.positionCS; half3 worldPos = vertexInput.positionWS; float3 worldNormal = normalInput.normalWS; half3 worldViewDir = GetCameraPositionWS() - worldPos; o.tSpace0 = half4(normalInput.tangentWS.x, normalInput.bitangentWS.x, normalInput.normalWS.x, worldPos.x); o.tSpace1 = half4(normalInput.tangentWS.y, normalInput.bitangentWS.y, normalInput.normalWS.y, worldPos.y); o.tSpace2 = half4(normalInput.tangentWS.z, normalInput.bitangentWS.z, normalInput.normalWS.z, worldPos.z); o.texcoord = v.texcoord.xy; o.normal = worldNormal; o.shadowCoord = GetShadowCoord(vertexInput); return o; } half4 FragBaseCore(VertexOutput i, half3 worldPos, half3 worldViewDir, half3 worldTangent = half3(0, 0, 0)) { half2 uv_MainTex = TRANSFORM_TEX(i.texcoord, _MainTex); half2 uv_NrmTex = TRANSFORM_TEX(i.texcoord, _NormalMap); half2 uv_mskTex = TRANSFORM_TEX(i.texcoord, _MaskMap); half4 albedoColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv_MainTex); half4 normalColor = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, uv_NrmTex); half4 mask = SAMPLE_TEXTURE2D(_MaskMap, sampler_MaskMap, uv_mskTex); #if defined(_CUTOFF_ON) clip(albedoColor.a - _CutoffThres / 255); #endif SurfaceOutputStandardCustom o = (SurfaceOutputStandardCustom)0; o.Occlusion = 1.0; half3 colorAddLerp = lerp(_Color.rgb * _OriColorAdd, _LerpMaskColor.rgb * 0.8, _ColorLerpMask * albedoColor.a); #if defined(_ANISO_GGX_ON) half4 mask2 = SAMPLE_TEXTURE2D(_MaskMap2, sampler_MaskMap2, TRANSFORM_TEX(i.texcoord, _MaskMap2)); o.Albedo = albedoColor.rgb * colorAddLerp * step(0.5, mask2.g) + albedoColor.rgb * step(mask2.g, 0.5); #else o.Albedo = albedoColor.rgb * colorAddLerp; #endif o.Alpha = albedoColor.a * _Color.a; half4 c = 0; #if defined(_USENORMALMAP_OFF) o.Normal = i.normal; #else o.Normal = lerp(half3(0, 0, 1), normalColor.rgb * 2 - 1, _NormalMapScale); #endif o.Normal = WorldNormalVector(i, o.Normal); o.Metallic = mask.r * (_MetallicScale + 1); o.Emission = normalColor.a * _EmissionColor.rgb * _EmissionStrength; o.Occlusion = lerp(mask.b, 1, _Occlusion); o.Smoothness = (1 - mask.a) * (_SmoothnessScale + 1); #if defined(_ANISO_GGX_ON) o.Anisotropy = mask2.g; half anisoMetallic = _AnisoMetallicScale; half anisoSmoothness = _AnisoSmoothnessScale; o.Metallic = lerp(o.Metallic, anisoMetallic, o.Anisotropy); o.Smoothness = lerp(o.Smoothness, anisoSmoothness, o.Anisotropy); o.WorldTangent = worldTangent; o.WorldBitangent = cross(o.Normal, o.WorldTangent); o.WorldTangent = cross(o.WorldBitangent, o.Normal); #endif o.IsSkin = 0; #if defined(_SKIN_ON) o.IsSkin = mask.g; half3 skinNormal = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, uv_NrmTex)); o.SkinNormal = normalize(lerp(o.Normal, skinNormal, _SkinBlurStrength)); o.SkinCurvature = _SkinCurvature; o.SkinNormal = WorldNormalVector(i, o.SkinNormal); #endif half atten = 1; UnityGI gi = (UnityGI)0; gi.indirect.diffuse = 0; gi.indirect.specular = 0; gi.light.color = GetMainLight().color.rgb; gi.light.dir = GetMainLight().direction; UnityGIInput giInput = (UnityGIInput)0; giInput.light = gi.light; giInput.worldPos = worldPos; giInput.worldViewDir = worldViewDir; giInput.atten = atten; giInput.ambient.rgb = _AmbientColor; giInput.probeHDR[0] = unity_SpecCube0_HDR; LightingCharacter_GI(o, giInput, gi); c += LightingCharacter(o, worldViewDir, gi); c.rgb += o.Emission; c.rgb = clamp(c.rgb, 0, 3); c.rgb = lerp(c.rgb, c.rgb + pow(1 - saturate(dot(o.Normal, worldViewDir)), 2.5) * _HighLightLerpColor * 4, _HighLightLerp); c.a = albedoColor.a; return c; } half4 FragBaseCore(VertexOutput i) { half3 worldPos = half3(i.tSpace0.w, i.tSpace1.w, i.tSpace2.w); half3 worldViewDir = normalize(GetCameraPositionWS() - worldPos); #if defined(_ANISO_GGX_ON) half3 worldTangent = normalize(half3(i.tSpace0.x, i.tSpace1.x, i.tSpace2.x)); #else half3 worldTangent = half3(0, 0, 0); #endif return FragBaseCore(i, worldPos, worldViewDir, worldTangent); } VertexOutput VertBase(VertexInput v) { return VertBaseCore(v); } half4 FragBase(VertexOutput i) : SV_Target { half4 finalColor = FragBaseCore(i); return finalColor; } #endif