diff --git a/Assets/ConformalDecals/Shaders/Decal/DecalsCommon.cginc b/Assets/ConformalDecals/Shaders/Decal/DecalsCommon.cginc index 338ed46..3c0ffc0 100644 --- a/Assets/ConformalDecals/Shaders/Decal/DecalsCommon.cginc +++ b/Assets/ConformalDecals/Shaders/Decal/DecalsCommon.cginc @@ -3,7 +3,7 @@ #include "AutoLight.cginc" #include "Lighting.cginc" -#include "../LightingKSPDeferred.cginc" +#include "../LightingKSP.cginc" #define CLIP_MARGIN 0.05 #define EDGE_MARGIN 0.01 @@ -115,6 +115,10 @@ struct v2f #ifdef UNITY_PASS_FORWARDADD UNITY_LIGHTING_COORDS(5,6) #endif //UNITY_PASS_FORWARDADD + + #if UNITY_SHOULD_SAMPLE_SH + half3 sh : TEXCOORD7; // SH + #endif }; @@ -212,14 +216,14 @@ v2f vert(appdata_decal v) } -SurfaceOutput frag_common(v2f IN, out float3 viewDir, out UnityGI gi) { +SurfaceOutput frag_common(v2f IN, out float3 worldPos, out float3 worldViewDir, out float3 viewDir) { SurfaceOutput o; // setup world-space TBN vectors UNITY_EXTRACT_TBN(IN); - float3 worldPos = float3(IN.tSpace0.w, IN.tSpace1.w, IN.tSpace2.w); - float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos)); + worldPos = float3(IN.tSpace0.w, IN.tSpace1.w, IN.tSpace2.w); + worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos)); viewDir = _unity_tbn_0 * worldViewDir.x + _unity_tbn_1 * worldViewDir.y + _unity_tbn_2 * worldViewDir.z; #ifdef DECAL_PREVIEW @@ -290,31 +294,36 @@ SurfaceOutput frag_common(v2f IN, out float3 viewDir, out UnityGI gi) { worldN = normalize(worldN); o.Normal = worldN; + return o; +} + +fixed4 frag_forward(v2f IN) : SV_Target +{ + fixed4 c = 0; + + float3 worldPos; + float3 worldViewDir; + float3 viewDir; + SurfaceOutput o = frag_common(IN, worldPos, worldViewDir, viewDir); + // compute lighting & shadowing factor - #if UNITY_PASS_DEFERRED - fixed atten = 0; - #else UNITY_LIGHT_ATTENUATION(atten, IN, worldPos) - #endif - - // setup GI - UNITY_INITIALIZE_OUTPUT(UnityGI, gi); - gi.indirect.diffuse = 0; - gi.indirect.specular = 0; - gi.light.color = 0; - gi.light.dir = half3(0,1,0); - // setup light information in forward modes - #ifndef UNITY_PASS_DEFERRED #ifndef USING_DIRECTIONAL_LIGHT fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos)); #else fixed3 lightDir = _WorldSpaceLightPos0.xyz; #endif + + // Setup lighting environment + UnityGI gi; + UNITY_INITIALIZE_OUTPUT(UnityGI, gi); + gi.indirect.diffuse = 0; + gi.indirect.specular = 0; gi.light.color = _LightColor0.rgb; gi.light.dir = lightDir; - #endif + #ifdef UNITY_PASS_FORWARDBASE // Call GI (lightmaps/SH/reflections) lighting function UnityGIInput giInput; UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput); @@ -322,32 +331,24 @@ SurfaceOutput frag_common(v2f IN, out float3 viewDir, out UnityGI gi) { giInput.worldPos = worldPos; giInput.worldViewDir = worldViewDir; giInput.atten = atten; - giInput.ambient = 0.0; - - LightingBlinnPhongSmooth_GI(o, giInput, gi); - - #ifdef DECAL_PREVIEW - if (any(IN.uv_decal > 1) || any(IN.uv_decal < 0)) o.Alpha = 0; - - o.Albedo = lerp(_Background.rgb, o.Albedo, o.Alpha) * _Color.rgb; - o.Normal = lerp(float3(0,0,1), o.Normal, o.Alpha); - o.Specular = lerp(_Background.a, o.Specular, o.Alpha); - o.Emission = lerp(0, o.Emission, o.Alpha); - o.Alpha = _Opacity; - #endif //DECAL_PREVIEW - return o; -} + #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL + giInput.ambient = IN.sh; + #else + giInput.ambient.rgb = 0.0; + #endif -fixed4 frag_forward(v2f IN) : SV_Target -{ - fixed3 viewDir = 0; - UnityGI gi; + LightingBlinnPhongKSP_GI(o, giInput, gi); + #endif - SurfaceOutput o = frag_common(IN, viewDir, gi); + #ifdef UNITY_PASS_FORWARDADD + gi.light.color *= atten; + #endif //call modified KSP lighting function - return LightingBlinnPhongSmooth(o, viewDir, gi); + c += LightingBlinnPhongKSP(o, viewDir, gi); + c.rgb += o.Emission; + return c; } void frag_deferred (v2f IN, @@ -362,15 +363,41 @@ void frag_deferred (v2f IN, half4 outGBuffer2 = 0; // define dummy normal buffer when we're not writing to it #endif + float3 worldPos; + float3 worldViewDir; float3 viewDir; + SurfaceOutput o = frag_common(IN, worldPos, worldViewDir, viewDir); + + // Setup lighting environment UnityGI gi; - SurfaceOutput o = frag_common(IN, viewDir, gi); + UNITY_INITIALIZE_OUTPUT(UnityGI, gi); + gi.indirect.diffuse = 0; + gi.indirect.specular = 0; + gi.light.color = 0; + gi.light.dir = half3(0,1,0); - #ifdef DECAL_PREVIEW - o.Alpha = 1; + UnityGIInput giInput; + UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput); + giInput.light = gi.light; + giInput.worldPos = worldPos; + giInput.worldViewDir = worldViewDir; + giInput.atten = 1; + giInput.lightmapUV = 0.0; + + #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL + giInput.ambient = 0.0 * IN.sh; + #else + giInput.ambient.rgb = 0.0; #endif - outEmission = LightingBlinnPhongSmooth_Deferred(o, viewDir, gi, outGBuffer0, outGBuffer1, outGBuffer2); + giInput.probeHDR[0] = unity_SpecCube0_HDR; + giInput.probeHDR[1] = unity_SpecCube1_HDR; + + LightingBlinnPhongKSP_GI(o, giInput, gi); + + outEmission = LightingBlinnPhongKSP_Deferred(o, worldViewDir, gi, outGBuffer0, outGBuffer1, outGBuffer2); + + // outGBuffer0 = outEmission; #ifndef UNITY_HDR_ON outEmission.rgb = exp2(-outEmission.rgb); @@ -380,13 +407,14 @@ void frag_deferred (v2f IN, outGBuffer1 *= o.Alpha; outGBuffer2.a = o.Alpha; outEmission.a = o.Alpha; + } void frag_deferred_prepass(v2f IN, out half4 outGBuffer1: SV_Target1) { + float3 worldPos; + float3 worldViewDir; float3 viewDir; - UnityGI gi; - - SurfaceOutput o = frag_common(IN, viewDir, gi); + SurfaceOutput o = frag_common(IN, worldPos, worldViewDir, viewDir); outGBuffer1 = o.Alpha; } diff --git a/Assets/ConformalDecals/Shaders/Decal/StandardDecal.shader.template b/Assets/ConformalDecals/Shaders/Decal/StandardDecal.shader.template index 169326b..a9d34c5 100644 --- a/Assets/ConformalDecals/Shaders/Decal/StandardDecal.shader.template +++ b/Assets/ConformalDecals/Shaders/Decal/StandardDecal.shader.template @@ -26,7 +26,7 @@ {% endblock %} {% block body %} - #pragma multi_compile_local __ DECAL_BASE_NORMAL + #pragma multi_compile_local __ DECAL_BASE_NORMAL DECAL_BUMPMAP #pragma multi_compile_local __ DECAL_SPECMAP #pragma multi_compile_local __ DECAL_EMISSIVE #pragma multi_compile_local __ DECAL_SDF_ALPHA diff --git a/Assets/ConformalDecals/Shaders/DecalBack.shader b/Assets/ConformalDecals/Shaders/DecalBack.shader index 07936f7..decdb74 100644 --- a/Assets/ConformalDecals/Shaders/DecalBack.shader +++ b/Assets/ConformalDecals/Shaders/DecalBack.shader @@ -28,8 +28,8 @@ Shader "ConformalDecals/Decal Back" CGPROGRAM - #include "LightingKSPDeferred.cginc" - #pragma surface surf BlinnPhongSmooth vertex:vert + #include "LightingKSP.cginc" + #pragma surface surf BlinnPhongKSP vertex:vert #pragma target 3.0 sampler2D _MainTex; diff --git a/Assets/ConformalDecals/Shaders/LightingKSP.cginc b/Assets/ConformalDecals/Shaders/LightingKSP.cginc new file mode 100644 index 0000000..94903ff --- /dev/null +++ b/Assets/ConformalDecals/Shaders/LightingKSP.cginc @@ -0,0 +1,161 @@ +// WHAT IS THIS FILE? +// this file provides a replacement for the LightingKSP.cginc file that ships with part tools for writing custom shaders. +// This version enables support for the Deferred mod +// +// HOW DO I USE IT? +// Step 1) +// replace LightingKSP.cginc in your shader folder with this file, if present. If you aren't using LightingKSP.cginc +// in your shader, add the following below `CGPROGRAM`: +// `#include "../LightingKSP.cginc"` +// +// Step 2) +// add the following above `CGPROGRAM`: +// ``` +// Stencil +// { +// Ref 1 +// Comp Always +// Pass Replace +// } +// ``` +// +// Step 3) +// there should be a line in your shader that looks like this: +// `#pragma surface surf BlinnPhongSmooth keepalpha` +// Remove the `keepalpha` if it's there. the part after `surf` is the name of the lighting function your shader uses now. +// If the lighting function is `BlinnPhong` or `BlinnPhongSmooth`, change it to `BlinnPhongKSP` +// If the lighting function is `Standard`, change it to `StandardKSP` +// If the lighting function is `StandardSpecular`, change it to `StandardSpecularKSP` + +#ifndef LIGHTING_KSP_INCLUDED +#define LIGHTING_KSP_INCLUDED + +#include "UnityPBSLighting.cginc" + +#define blinnPhongShininessPower 0.215 + +// An exact conversion from blinn-phong to PBR is impossible, but the look can be approximated perceptually +// and by observing how blinn-phong looks and feels at various settings, although it can never be perfect +// 1) The specularColor can be used as is in the PBR specular flow, just needs to be divided by PI so it sums up to 1 over the hemisphere +// 2) Blinn-phong shininess doesn't stop feeling shiny unless at very low values, like below 0.04 +// while the PBR smoothness feels more linear -> map shininess to smoothness accordingly using a function +// that increases very quickly at first then slows down, I went with something like x^(1/4) or x^(1/6) then made the power configurable +// I tried various mappings from the literature but nothing really worked as well as this +// 3) Finally I noticed that some parts still looked very shiny like the AV-R8 winglet while in stock they looked rough thanks a low +// specularColor but high shininess and specularMap, so I multiplied the smoothness by the sqrt of the specularColor and that caps +// the smoothness when specularColor is low +void GetStandardSpecularPropertiesFromLegacy(float legacyShininess, float specularMap, out float3 specular, + out float smoothness) +{ + float3 legacySpecularColor = saturate(_SpecColor); + + smoothness = pow(legacyShininess, blinnPhongShininessPower) * specularMap; + smoothness *= sqrt(length(legacySpecularColor)); + + specular = legacySpecularColor * UNITY_INV_PI; +} + +float4 _Color; + +// LEGACY BLINN-PHONG LIGHTING FUNCTION FOR KSP WITH PBR CONVERSION FOR DEFERRED + +inline float4 LightingBlinnPhongKSP(SurfaceOutput s, half3 viewDir, UnityGI gi) +{ + return LightingBlinnPhong(s,viewDir, gi); +} + +inline float4 LightingBlinnPhongKSP_Deferred(SurfaceOutput s, float3 worldViewDir, UnityGI gi, + out float4 outDiffuseOcclusion, out float4 outSpecSmoothness, + out float4 outNormal) +{ + SurfaceOutputStandardSpecular ss; + ss.Albedo = s.Albedo; + ss.Normal = s.Normal; + ss.Emission = s.Emission; + ss.Occlusion = 1; + ss.Alpha = saturate(s.Alpha); + GetStandardSpecularPropertiesFromLegacy(s.Specular, s.Gloss, ss.Specular, ss.Smoothness); + + return LightingStandardSpecular_Deferred(ss, worldViewDir, gi, outDiffuseOcclusion, outSpecSmoothness, outNormal); +} + +inline void LightingBlinnPhongKSP_GI(inout SurfaceOutput s, UnityGIInput gi_input, inout UnityGI gi) +{ + #ifndef UNITY_PASS_DEFERRED + gi = UnityGlobalIllumination(gi_input, 1.0, s.Normal); + #endif +} + +// STANDARD UNITY LIGHTING FUNCTION FOR KSP + +inline float3 LightingStandardKSP(SurfaceOutputStandard s, float3 worldViewDir, UnityGI gi) +{ + return LightingStandard(s, worldViewDir, gi); // no change +} + +inline float4 LightingStandardKSP_Deferred(SurfaceOutputStandard s, float3 worldViewDir, UnityGI gi, + out float4 outDiffuseOcclusion, + out float4 outSpecSmoothness, out float4 outNormal) +{ + return LightingStandard_Deferred(s, worldViewDir, gi, outDiffuseOcclusion, outSpecSmoothness, outNormal); +} + +inline void LightingStandardKSP_GI(inout SurfaceOutputStandard s, UnityGIInput gi_input, inout UnityGI gi) +{ + #ifndef UNITY_PASS_DEFERRED + LightingStandard_GI(s, gi_input, gi); + #endif +} + +// STANDARD SPECULAR UNITY LIGHTING FUNCTION FOR KSP + +inline float3 LightingStandardSpecularKSP(SurfaceOutputStandardSpecular s, float3 worldViewDir, UnityGI gi) +{ + return LightingStandardSpecular(s, worldViewDir, gi); // no change +} + +inline float4 LightingStandardSpecularKSP_Deferred(SurfaceOutputStandardSpecular s, float3 worldViewDir, UnityGI gi, + out float4 outDiffuseOcclusion, + out float4 outSpecSmoothness, out float4 outNormal) +{ + return LightingStandardSpecular_Deferred(s, worldViewDir, gi, outDiffuseOcclusion, outSpecSmoothness, outNormal); +} + +inline void LightingStandardSpecularKSP_GI(inout SurfaceOutputStandardSpecular s, UnityGIInput gi_input, + inout UnityGI gi) +{ + #ifndef UNITY_PASS_DEFERRED + LightingStandardSpecular_GI(s, gi_input, gi); + #endif +} + +float4 _LocalCameraPos; +float4 _LocalCameraDir; +float4 _UnderwaterFogColor; +float _UnderwaterMinAlphaFogDistance; +float _UnderwaterMaxAlbedoFog; +float _UnderwaterMaxAlphaFog; +float _UnderwaterAlbedoDistanceScalar; +float _UnderwaterAlphaDistanceScalar; +float _UnderwaterFogFactor; + +float4 UnderwaterFog(float3 worldPos, float3 color) +{ + // skip fog in deferred mode + #ifdef UNITY_PASS_DEFERRED + return float4(color, 1); + #endif + + float3 toPixel = worldPos - _LocalCameraPos.xyz; + float toPixelLength = length(toPixel); ///< Comment out the math--looks better without it. + + float underwaterDetection = _UnderwaterFogFactor * _LocalCameraDir.w; ///< sign(1 - sign(_LocalCameraPos.w)); + float albedoLerpValue = underwaterDetection * (_UnderwaterMaxAlbedoFog * saturate( + toPixelLength * _UnderwaterAlbedoDistanceScalar)); + float alphaFactor = 1 - underwaterDetection * (_UnderwaterMaxAlphaFog * saturate( + (toPixelLength - _UnderwaterMinAlphaFogDistance) * _UnderwaterAlphaDistanceScalar)); + + return float4(lerp(color, _UnderwaterFogColor.rgb, albedoLerpValue), alphaFactor); +} + +#endif diff --git a/Assets/ConformalDecals/Shaders/LightingKSPDeferred.cginc b/Assets/ConformalDecals/Shaders/LightingKSPDeferred.cginc deleted file mode 100644 index fb4d41f..0000000 --- a/Assets/ConformalDecals/Shaders/LightingKSPDeferred.cginc +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef LIGHTING_KSP_INCLUDED -#define LIGHTING_KSP_INCLUDED - -#include "UnityPBSLighting.cginc" - - -#define blinnPhongShininessPower 0.215 - -// An exact conversion from blinn-phong to PBR is impossible, but the look can be approximated perceptually -// and by observing how blinn-phong looks and feels at various settings, although it can never be perfect -// 1) The specularColor can be used as is in the PBR specular flow, just needs to be divided by PI so it sums up to 1 over the hemisphere -// 2) Blinn-phong shininess doesn't stop feeling shiny unless at very low values, like below 0.04 -// while the PBR smoothness feels more linear -> map shininess to smoothness accordingly using a function -// that increases very quickly at first then slows down, I went with something like x^(1/4) or x^(1/6) then made the power configurable -// I tried various mappings from the literature but nothing really worked as well as this -// 3) Finally I noticed that some parts still looked very shiny like the AV-R8 winglet while in stock they looked rough thanks a low -// specularColor but high shininess and specularMap, so I multiplied the smoothness by the sqrt of the specularColor and that caps -// the smoothness when specularColor is low -void GetStandardSpecularPropertiesFromLegacy(float legacyShininess, float specularMap, out float3 specular, out float smoothness) -{ - float3 legacySpecularColor = saturate(_SpecColor); - - smoothness = pow(legacyShininess, blinnPhongShininessPower) * specularMap; - smoothness *= sqrt(length(legacySpecularColor)); - - specular = legacySpecularColor * UNITY_INV_PI; -} - -float4 _Color; - -fixed4 LightingBlinnPhongSmooth(SurfaceOutput s, half3 viewDir, UnityGI gi) -{ - fixed4 c = LightingBlinnPhong(s, viewDir, gi); - // #ifdef UNITY_PASS_FORWARDADD - // c.rgb *= c.a; - // #endif - return c; -} - -half4 LightingBlinnPhongSmooth_Deferred(SurfaceOutput s, half3 viewDir, UnityGI gi, - out half4 outDiffuseOcclusion, out half4 outSpecSmoothness, - out half4 outNormal) -{ - SurfaceOutputStandardSpecular ss; - ss.Albedo = s.Albedo; - ss.Normal = s.Normal; - ss.Emission = s.Emission; - ss.Occlusion = 1; - ss.Alpha = saturate(s.Alpha); - GetStandardSpecularPropertiesFromLegacy(s.Specular, s.Gloss, ss.Specular, ss.Smoothness); - - return LightingStandardSpecular_Deferred(ss, viewDir, gi, outDiffuseOcclusion, outSpecSmoothness, outNormal); -} - - -inline void LightingBlinnPhongSmooth_GI(inout SurfaceOutput s, UnityGIInput gi_input, inout UnityGI gi) -{ - gi = UnityGlobalIllumination(gi_input, 1.0, s.Normal); -} - -float4 _LocalCameraPos; -float4 _LocalCameraDir; -float4 _UnderwaterFogColor; -float _UnderwaterMinAlphaFogDistance; -float _UnderwaterMaxAlbedoFog; -float _UnderwaterMaxAlphaFog; -float _UnderwaterAlbedoDistanceScalar; -float _UnderwaterAlphaDistanceScalar; -float _UnderwaterFogFactor; - -float4 UnderwaterFog(float3 worldPos, float3 color) -{ - // skip fog in deferred mode - #ifdef UNITY_PASS_DEFERRED - return float4(color, 1); - #endif - - float3 toPixel = worldPos - _LocalCameraPos.xyz; - float toPixelLength = length(toPixel); ///< Comment out the math--looks better without it. - - float underwaterDetection = _UnderwaterFogFactor * _LocalCameraDir.w; ///< sign(1 - sign(_LocalCameraPos.w)); - float albedoLerpValue = underwaterDetection * (_UnderwaterMaxAlbedoFog * saturate( - toPixelLength * _UnderwaterAlbedoDistanceScalar)); - float alphaFactor = 1 - underwaterDetection * (_UnderwaterMaxAlphaFog * saturate( - (toPixelLength - _UnderwaterMinAlphaFogDistance) * _UnderwaterAlphaDistanceScalar)); - - return float4(lerp(color, _UnderwaterFogColor.rgb, albedoLerpValue), alphaFactor); -} - -#endif