//Since this is shared, and the output structs/input structs are all slightly differently named in each shader template, just handle them all here. float4 CustomStandardLightingBRDF( #if defined(GEOMETRY) g2f i #elif defined(TESSELLATION) vertexOutput i #else v2f i #endif ) { //LIGHTING PARAMS UNITY_LIGHT_ATTENUATION(attenuation, i, i.worldPos.xyz); float3 worldPos = i.worldPos; //NORMAL float3 unmodifiedWorldNormal = normalize(i.btn[2]); float3 unmodifiedTangent = i.btn[1]; float3 unmodifiedBitangent = i.btn[0]; float4 normalMap = texTP(_BumpMap, _BumpMap_ST, i.worldPos, i.objPos, i.btn[2], i.objNormal, _TriplanarFalloff, i.uv); float3 worldNormal = i.btn[2]; float3 tangent = i.btn[1]; float3 bitangent = i.btn[0]; initBumpedNormalTangentBitangent(normalMap, bitangent, tangent, worldNormal); //---- //FurNoise float layer = i.layer; float occlusionFalloff = smoothstep(_OcclusionFalloffMin, _OcclusionFalloffMax, layer / _LayerCount); float4 lengthtex = texTP(_FurLengthMask, _FurLengthMask_ST, i.worldPos, i.objPos, i.btn[2], i.objNormal, _TriplanarFalloff, i.uv); float lengthMultiplier = lengthtex.x; float2 furUV = i.uv * _NoiseTexture_ST.xy + _NoiseTexture_ST.zw; furUV.x += _CombX * 0.1 * layer; furUV.y += _CombY * 0.1 * layer; float4 noise = tex2D(_NoiseTexture, furUV); float4 furOcclusion = lerp(1, occlusionFalloff, _FurOcclusion); //---- //DIFFUSE float colorFalloffBlend = smoothstep(_ColorFalloffMin, _ColorFalloffMax, layer / _LayerCount); float4 furAlbedo = texTP(_FurTexture, _FurTexture_ST, i.worldPos, i.objPos, i.btn[2], i.objNormal, _TriplanarFalloff, i.uv) * lerp(_BottomColor, _Color, colorFalloffBlend); float4 albedo = texTP(_MainTex, _MainTex_ST, i.worldPos, i.objPos, i.btn[2], i.objNormal, _TriplanarFalloff, i.uv); albedo = lerp(furAlbedo, albedo, step(layer, 1)); float4 diffuse = albedo; float alpha; doAlpha(alpha, float4(noise.r, lengthMultiplier.r, 0, diffuse.a), i.screenPos, layer); //---- //METALLIC SMOOTHNESS float4 metallicGlossMap = texTP(_MetallicGlossMap, _MetallicGlossMap_ST, i.worldPos, i.objPos, i.btn[2], i.objNormal, _TriplanarFalloff, i.uv); float4 metallicSmoothness = getMetallicSmoothness(metallicGlossMap); float metallic = metallicSmoothness.r; float reflectance = _Reflectance; float roughness = metallicSmoothness.a; albedo.rgb *= (1-metallic); //---- //OCCLUSION float4 occlusionMap = texTP(_OcclusionMap, _OcclusionMap_ST, i.worldPos, i.objPos, i.btn[2], i.objNormal, _TriplanarFalloff, i.uv); float4 occlusion = lerp(_OcclusionColor, 1, occlusionMap); //---- //EMISSION float4 emission = 0; #if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_META) // Emissions should only happen in the forward base pass (and meta pass) float4 emissionMap = texTP(_EmissionMap, _EmissionMap_ST, i.worldPos, i.objPos, i.btn[2], i.objNormal, _TriplanarFalloff, i.uv); float emissionFalloff = smoothstep(_EmissionFalloffMin, _EmissionFalloffMax, layer / _LayerCount); emission = lerp(0, emissionMap * _EmissionColor, emissionFalloff); #endif //---- //CLEARCOAT MAP float4 clearcoatMap = texTP(_ClearcoatMap, _ClearcoatMap_ST, i.worldPos, i.objPos, i.btn[2], i.objNormal, _TriplanarFalloff, i.uv); float4 clearcoatReflectivitySmoothness = getClearcoatSmoothness(clearcoatMap); float clearcoatReflectivity = clearcoatReflectivitySmoothness.r; float clearcoatRoughness = clearcoatReflectivitySmoothness.a; //---- //LIGHTING VECTORS bool lightEnv = any(_WorldSpaceLightPos0.xyz); float3 lightDir = getLightDir(i.worldPos); float4 lightCol = _LightColor0; float3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos); float3 halfVector = normalize(lightDir + viewDir); float3 reflViewDir = getAnisotropicReflectionVector(viewDir, bitangent, tangent, worldNormal, roughness, _Anisotropy); float3 reflLightDir = reflect(lightDir, worldNormal); //---- //DOT PRODUCTS FOR LIGHTING float ndl = saturate(dot(lightDir, worldNormal)); float vdn = abs(dot(viewDir, worldNormal)); float vdh = saturate(dot(viewDir, halfVector)); float rdv = saturate(dot(reflLightDir, float4(-viewDir, 0))); float ldh = saturate(dot(lightDir, halfVector)); float ndh = saturate(dot(worldNormal, halfVector)); //---- //LIGHTING //Diffuse BRDF #if defined(LIGHTMAP_ON) float3 indirectDiffuse = 0; float3 directDiffuse = albedo * getLightmap(i.uv1, worldNormal, i.worldPos); #if defined(DYNAMICLIGHTMAP_ON) float3 realtimeLM = getRealtimeLightmap(i.uv2, worldNormal); directDiffuse += realtimeLM; #endif #else //Gather up non-important lights float3 vertexLightData = 0; #if defined(VERTEXLIGHT_ON) VertexLightInformation vLight = (VertexLightInformation)0; float4 vertexLightAtten = float4(0,0,0,0); float3 vertexLightColor = get4VertexLightsColFalloff(vLight, worldPos, worldNormal, vertexLightAtten); float3 vertexLightDir = getVertexLightsDir(vLight, worldPos, vertexLightAtten); for(int i = 0; i < 4; i++) { vertexLightData += saturate(dot(vLight.Direction[i], worldNormal)) * vLight.ColorFalloff[i]; } #endif float3 indirectDiffuse = getIndirectDiffuse(worldNormal) + vertexLightData; float3 atten = (attenuation * ndl * lightCol) + indirectDiffuse; float3 directDiffuse = (albedo * atten); #endif //---- //Specular BRDF // This is a pretty big hack of a specular brdf but I didn't like other implementations entirely. This is my own, mixed with some other stuff from other places. // This probably means it breaks energy conservation, fails the furnace test, etc, but, in my opinion, it looks better. float3 specularLightCol = getLightCol(lightEnv, indirectDiffuse); // This makes things look a little bit better in baked lighting by forcing a "direct" specular highlight to always be visible by getting the dominant light probe direction and color. float3 f0 = 0.16 * reflectance * reflectance * (1.0 - metallic) + diffuse * metallic; float3 fresnel = lerp(F_Schlick(vdn, f0), f0, metallic); //Kill fresnel on metallics, it looks bad. float3 directSpecular = getDirectSpecular(roughness, ndh, vdn, ndl, ldh, f0, halfVector, tangent, bitangent, _Anisotropy) * attenuation * ndl * specularLightCol; float3 indirectSpecular = getIndirectSpecular(metallic, roughness, reflViewDir, worldPos, directDiffuse, worldNormal); //Lightmap is stored in directDiffuse and used for specular lightmap occlusion float3 vertexLightSpec = 0; float3 vertexLightClearcoatSpec = 0; #if defined(VERTEXLIGHT_ON) && !defined(LIGHTMAP_ON) [UNROLL(4)] for(int i = 0; i < 4; i++) { // All of these need to be recalculated for each individual light to treat them how we want to treat them. float3 vHalfVector = normalize(vLight.Direction[i] + viewDir); float vNDL = saturate(dot(vLight.Direction[i], worldNormal)); float vLDH = saturate(dot(vLight.Direction[i], vHalfVector)); float vNDH = saturate(dot(worldNormal, vHalfVector)); float vCndl = saturate(dot(vLight.Direction[i], unmodifiedWorldNormal)); float vCvdn = abs(dot(viewDir, unmodifiedWorldNormal)); float vCndh = saturate(dot(unmodifiedWorldNormal, vHalfVector)); float3 vLspec = getDirectSpecular(roughness, vNDH, vdn, vNDL, vLDH, f0, vHalfVector, tangent, bitangent, _Anisotropy) * vNDL; float3 vLspecCC = getDirectSpecular(clearcoatRoughness, vCndh, vCvdn, vCndl, vLDH, f0, vHalfVector, unmodifiedTangent, unmodifiedBitangent, _ClearcoatAnisotropy) * vNDL; vertexLightSpec += vLspec * vLight.ColorFalloff[i]; vertexLightClearcoatSpec += vLspecCC * vLight.ColorFalloff[i]; } #endif float3 specular = max(0, (indirectSpecular + directSpecular + vertexLightSpec) * lerp(fresnel, f0, roughness)); //---- //Clearcoat BRDF float3 creflViewDir = getAnisotropicReflectionVector(viewDir, unmodifiedBitangent, unmodifiedTangent, unmodifiedWorldNormal, roughness, _ClearcoatAnisotropy); float cndl = saturate(dot(lightDir, unmodifiedWorldNormal)); float cvdn = abs(dot(viewDir, unmodifiedWorldNormal)); float cndh = saturate(dot(unmodifiedWorldNormal, halfVector)); float3 clearcoatf0 = 0.16 * clearcoatReflectivity * clearcoatReflectivity; float3 clearcoatFresnel = F_Schlick(cvdn, clearcoatf0); float3 clearcoatDirectSpecular = getDirectSpecular(clearcoatRoughness, cndh, cvdn, cndl, ldh, clearcoatf0, halfVector, unmodifiedTangent, unmodifiedBitangent, _ClearcoatAnisotropy) * attenuation * cndl * lightCol; float3 clearcoatIndirectSpecular = getIndirectSpecular(0, clearcoatRoughness, creflViewDir, worldPos, directDiffuse, unmodifiedWorldNormal); float3 clearcoat = (clearcoatDirectSpecular + clearcoatIndirectSpecular + vertexLightClearcoatSpec) * clearcoatReflectivity * clearcoatFresnel; //---- //TODO: Implement subsurface scattering float3 litPixel = ((directDiffuse + specular + clearcoat) * occlusion * furOcclusion) + emission; return float4(litPixel, alpha); }