mirror of
https://github.com/drewcassidy/KSP-Conformal-Decals.git
synced 2024-09-01 18:23:54 +00:00
Andrew Cassidy
cb98e78a3f
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.
422 lines
11 KiB
HLSL
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 |