KSP-Conformal-Decals/Assets/ConformalDecals/Shaders/Decal/DecalsCommon.cginc
Andrew Cassidy cb98e78a3f New LightingKSP to solve some issues with Deferred
Deferred moves the ambient calculation to the lighting pass for very good reasons, but Unity shaders still try to do it in the base pass. Easy solution is make the GI function a no-op in deferred mode.
2024-07-30 22:40:23 -07:00

422 lines
11 KiB
HLSL

#ifndef DECALS_COMMON_INCLUDED
#define DECALS_COMMON_INCLUDED
#include "AutoLight.cginc"
#include "Lighting.cginc"
#include "../LightingKSP.cginc"
#define CLIP_MARGIN 0.05
#define EDGE_MARGIN 0.01
// UNIFORM VARIABLES
// Projection matrix, normal, and tangent vectors
float4x4 _ProjectionMatrix;
float3 _DecalNormal;
float3 _DecalTangent;
// Common Shading Paramaters
float _Cutoff;
float _DecalOpacity;
float4 _Background;
sampler2D _Decal;
float4 _Decal_ST;
// Variant Shading Parameters
#ifdef DECAL_BASE_NORMAL
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _EdgeWearStrength;
float _EdgeWearOffset;
#endif //DECAL_BASE_NORMAL
#ifdef DECAL_BUMPMAP
sampler2D _BumpMap;
float4 _BumpMap_ST;
#endif //DECAL_BUMPMAP
#ifdef DECAL_SPECMAP
sampler2D _SpecMap;
float4 _SpecMap_ST;
// specular color is declared in a unity CGINC for some reason??
#endif //DECAL_SPECMAP
fixed _Shininess;
#ifdef DECAL_EMISSIVE
sampler2D _Emissive;
float4 _Emissive_ST;
fixed4 _Emissive_Color;
#endif //DECAL_EMISSIVE
// KSP EFFECTS
// opacity and color
float _Opacity;
float _RimFalloff;
float4 _RimColor;
// SURFACE INPUT STRUCT
struct DecalSurfaceInput
{
float3 uv;
float2 uv_decal;
#ifdef DECAL_BUMPMAP
float2 uv_bumpmap;
#endif //DECAL_BUMPMAP
#ifdef DECAL_SPECMAP
float2 uv_specmap;
#endif //DECAL_SPECMAP
#ifdef DECAL_EMISSIVE
float2 uv_emissive;
#endif //DECAL_EMISSIVE
#ifdef DECAL_BASE_NORMAL
float3 normal;
#endif //DECAL_BASE_NORMAL
float3 vertex_normal;
float3 viewDir;
float3 worldPosition;
};
struct appdata_decal
{
float4 vertex : POSITION;
float3 normal : NORMAL;
#if defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
#endif
};
struct v2f
{
UNITY_POSITION(pos);
float3 normal : NORMAL;
float4 uv_decal : TEXCOORD0;
#ifdef DECAL_BASE_NORMAL
float2 uv_base : TEXCOORD1;
#endif //DECAL_BASE_NORMAL
float4 tSpace0 : TEXCOORD2;
float4 tSpace1 : TEXCOORD3;
float4 tSpace2 : TEXCOORD4;
#ifdef UNITY_PASS_FORWARDBASE
fixed3 vlight : TEXCOORD5;
UNITY_SHADOW_COORDS(6)
#endif //UNITY_PASS_FORWARDBASE
#ifdef UNITY_PASS_FORWARDADD
UNITY_LIGHTING_COORDS(5,6)
#endif //UNITY_PASS_FORWARDADD
#if UNITY_SHOULD_SAMPLE_SH
half3 sh : TEXCOORD7; // SH
#endif
};
inline void decalClipAlpha(float alpha) {
#ifndef DECAL_PREVIEW
clip(alpha - 0.001);
#endif
}
inline float CalcMipLevel(float2 texture_coord) {
float2 dx = ddx(texture_coord);
float2 dy = ddy(texture_coord);
float delta_max_sqr = max(dot(dx, dx), dot(dy, dy));
return 0.5 * log2(delta_max_sqr);
}
// Decal bounds distance function
// takes in a world position, world normal, and projector normal and outputs a unitless signed distance from the
inline float BoundsDist(float3 p, float3 normal, float3 projNormal) {
float3 q = abs(p - 0.5) - 0.5; // 1x1 square/cube centered at (0.5,0.5)
//float dist = length(max(q,0)) + min(max(q.x,max(q.y,q.z)),0.0); // true SDF
#ifdef DECAL_PREVIEW
return 10 * max(q.x, q.y); // 2D pseudo SDF
#else
float dist = max(max(q.x, q.y), q.z); // pseudo SDF
float ndist = EDGE_MARGIN - dot(normal, projNormal); // SDF to normal
return 10 * max(dist, ndist); // return intersection
#endif //DECAL_PREVIEW
}
// declare surf function,
// this must be defined in any shader using this cginc
void surf (DecalSurfaceInput IN, inout SurfaceOutput o);
v2f vert(appdata_decal v)
{
v2f o;
UNITY_INITIALIZE_OUTPUT(v2f,o);
o.pos = UnityObjectToClipPos(v.vertex);
o.normal = v.normal;
#ifdef DECAL_PREVIEW
o.uv_decal = v.texcoord;
#else
o.uv_decal = mul (_ProjectionMatrix, v.vertex);
#endif //DECAL_PREVIEW
#ifdef DECAL_BASE_NORMAL
o.uv_base = TRANSFORM_TEX(v.texcoord, _BumpMap);
#endif //DECAL_BASE_NORMAL
float3 worldPosition = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
#if defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
// use tangent of base geometry
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
fixed3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
#else
// use tangent of projector
fixed3 decalTangent = UnityObjectToWorldDir(_DecalTangent);
fixed3 worldBinormal = cross(decalTangent, worldNormal);
fixed3 worldTangent = cross(worldNormal, worldBinormal);
#endif //defined(DECAL_BASE_NORMAL) || defined(DECAL_PREVIEW)
o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPosition.x);
o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPosition.y);
o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPosition.z);
// forward base pass specific lighting code
#ifdef UNITY_PASS_FORWARDBASE
// SH/ambient light
#if UNITY_SHOULD_SAMPLE_SH
float3 shlight = ShadeSH9 (float4(worldNormal,1.0));
o.vlight = shlight;
#else
o.vlight = 0.0;
#endif // UNITY_SHOULD_SAMPLE_SH
// vertex light
#ifdef VERTEXLIGHT_ON
o.vlight += Shade4PointLights (
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
unity_4LightAtten0, worldPosition, worldNormal );
#endif // VERTEXLIGHT_ON
#endif // UNITY_PASS_FORWARDBASE
// pass shadow and, possibly, light cookie coordinates to pixel shader
UNITY_TRANSFER_LIGHTING(o, 0.0);
return o;
}
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);
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
fixed4 uv_projected = IN.uv_decal;
#else
// perform decal projection
fixed4 uv_projected = UNITY_PROJ_COORD(IN.uv_decal);
clip(uv_projected.xyz + CLIP_MARGIN);
clip(CLIP_MARGIN + (1-uv_projected.xyz));
#endif //DECAL_PREVIEW
// declare data
DecalSurfaceInput i;
// initialize surface input
UNITY_INITIALIZE_OUTPUT(DecalSurfaceInput, i)
i.uv_decal = TRANSFORM_TEX(uv_projected, _Decal);
i.uv = uv_projected;
#ifdef DECAL_BUMPMAP
i.uv_bumpmap = TRANSFORM_TEX(uv_projected, _BumpMap);
#endif //DECAL_BUMPMAP
#ifdef DECAL_SPECMAP
i.uv_specmap = TRANSFORM_TEX(uv_projected, _SpecMap);
#endif //DECAL_SPECMAP
#ifdef DECAL_EMISSIVE
i.uv_emissive = TRANSFORM_TEX(uv_projected, _Emissive);
#endif //DECAL_EMISSIVE
#ifdef DECAL_BASE_NORMAL
#ifdef DECAL_PREVIEW
i.normal = fixed3(0,0,1);
#else
i.normal = UnpackNormalDXT5nm(tex2D(_BumpMap, IN.uv_base));
#endif //DECAL_PREVIEW
#endif //DECAL_BASE_NORMAL
i.vertex_normal = IN.normal;
i.viewDir = viewDir;
i.worldPosition = worldPos;
// initialize surface output
o.Albedo = 0.0;
o.Emission = 0.0;
o.Specular = 0.4;
o.Alpha = _DecalOpacity;
o.Gloss = 0.0;
o.Normal = fixed3(0,0,1);
// call surface function
surf(i, o);
// apply KSP fog. In the deferred pass this is a no-op
o.Albedo = UnderwaterFog(i.worldPosition, o.Albedo).rgb;
// apply KSP rim lighting
half rim = 1.0 - saturate(dot(normalize(i.viewDir), o.Normal));
o.Emission += o.Emission = (_RimColor.rgb * pow(rim, _RimFalloff)) * _RimColor.a;
// compute world normal
float3 worldN;
worldN.x = dot(_unity_tbn_0, o.Normal);
worldN.y = dot(_unity_tbn_1, o.Normal);
worldN.z = dot(_unity_tbn_2, o.Normal);
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
UNITY_LIGHT_ATTENUATION(atten, IN, worldPos)
#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;
#ifdef UNITY_PASS_FORWARDBASE
// Call GI (lightmaps/SH/reflections) lighting function
UnityGIInput giInput;
UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
giInput.light = gi.light;
giInput.worldPos = worldPos;
giInput.worldViewDir = worldViewDir;
giInput.atten = atten;
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
giInput.ambient = IN.sh;
#else
giInput.ambient.rgb = 0.0;
#endif
LightingBlinnPhongKSP_GI(o, giInput, gi);
#endif
#ifdef UNITY_PASS_FORWARDADD
gi.light.color *= atten;
#endif
//call modified KSP lighting function
c += LightingBlinnPhongKSP(o, viewDir, gi);
c.rgb += o.Emission;
return c;
}
void frag_deferred (v2f IN,
out half4 outGBuffer0 : SV_Target0,
out half4 outGBuffer1 : SV_Target1,
#if defined(DECAL_BUMPMAP) || defined(DECAL_PREVIEW)
out half4 outGBuffer2 : SV_Target2,
#endif
out half4 outEmission : SV_Target3)
{
#if !(defined(DECAL_BUMPMAP) || defined(DECAL_PREVIEW))
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;
UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
gi.indirect.diffuse = 0;
gi.indirect.specular = 0;
gi.light.color = 0;
gi.light.dir = half3(0,1,0);
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
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);
#endif
outGBuffer0.a = o.Alpha;
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;
SurfaceOutput o = frag_common(IN, worldPos, worldViewDir, viewDir);
outGBuffer1 = o.Alpha;
}
#endif