diff --git a/Editor/DecalProjectorTest.cs b/Editor/DecalProjectorTest.cs new file mode 100644 index 0000000..f365c78 --- /dev/null +++ b/Editor/DecalProjectorTest.cs @@ -0,0 +1,47 @@ +using UnityEngine; + +[ExecuteInEditMode] +public class DecalProjectorTest : MonoBehaviour +{ + public GameObject target = null; + public Material targetMaterial = null; + public MeshRenderer targetRenderer; + + + public float aspectRatio = 1.0f; + public float size = 1.0f; + public float factor = 1.0f; + + private Matrix4x4 _projectionMatrix; + private Matrix4x4 _OrthoMatrix; + + private int _matrixID; + private int _normalID; + public int _tangentID; + + // Start is called before the first frame update + void Awake() + { + _projectionMatrix = Matrix4x4.identity; + _matrixID = Shader.PropertyToID("_ProjectionMatrix"); + _normalID = Shader.PropertyToID("_DecalNormal"); + _tangentID= Shader.PropertyToID("_DecalTangent"); + targetRenderer = target.GetComponent(); + } + + // Update is called once per frame + void Update() + { + Vector3 pos =new Vector3( 0.5f ,0.5f, 0); + Vector3 scale = new Vector3(1 / size, 1 / (aspectRatio * size), 1); + _OrthoMatrix.SetTRS(pos, Quaternion.identity, scale); + //Debug.Log(_OrthoMatrix); + var targetToProjector = transform.worldToLocalMatrix * targetRenderer.localToWorldMatrix; + var projectorToTarget = targetRenderer.worldToLocalMatrix * transform.localToWorldMatrix; + _projectionMatrix = _OrthoMatrix * targetToProjector; + + targetMaterial.SetMatrix(_matrixID, _projectionMatrix); + targetMaterial.SetVector(_normalID, projectorToTarget.MultiplyVector(Vector3.back).normalized); + targetMaterial.SetVector(_tangentID, projectorToTarget.MultiplyVector(Vector3.right).normalized); + } +} diff --git a/Shaders/DecalNormal.shader b/Shaders/DecalNormal.shader new file mode 100644 index 0000000..696ecf9 --- /dev/null +++ b/Shaders/DecalNormal.shader @@ -0,0 +1,70 @@ +Shader "ConformalDecals/Feature/Bumped" +{ + Properties + { + _Decal ("Decal Texture", 2D) = "gray" {} + _DecalBumpMap("Decal Normal Map", 2D) = "bump" {} + + _Cutoff ("Alpha cutoff", Range(0,1)) = 0.5 + _Opacity("_Opacity", Range(0,1) ) = 1 + } + + SubShader + { + Tags { "Queue" = "Transparent" } + + ZWrite Off + ZTest LEqual + Blend SrcAlpha OneMinusSrcAlpha + + CGPROGRAM + + #pragma surface surf Lambert alpha vertex:vert + #pragma target 4.0 + + float4x4 _ProjectionMatrix; + float3 _DecalNormal; + float3 _DecalBiNormal; + + sampler2D _Decal; + sampler2D _DecalBumpMap; + + float _Cutoff; + float _Opacity; + + struct Input + { + float4 pos_decal : TEXCOORD0; + float3 normal : NORMAL; + }; + + void vert (inout appdata_full v, out Input o) { + o.pos_decal = mul (_ProjectionMatrix, v.vertex); + o.normal = v.normal; + float3 localTangent = normalize(cross(v.normal, _DecalBiNormal)); + } + + void surf (Input IN, inout SurfaceOutput o) + { + fixed4 projUV = UNITY_PROJ_COORD(IN.pos_decal); + + // since I cant easily affect the clamping mode in KSP, do it here + clip(projUV.xyz); + clip(1-projUV.xyz); + + // clip backsides + clip(dot(_DecalNormal, IN.normal)); + + float4 color = tex2D(_Decal, projUV); + float3 normal = UnpackNormal(tex2D(_DecalBumpMap, projUV)); + + clip (color.a - _Cutoff); + + o.Albedo = color.rgb; + o.Normal = normal; + o.Alpha = color.a * _Opacity; + } + + ENDCG + } +} \ No newline at end of file diff --git a/Shaders/DecalNormalVert.shader b/Shaders/DecalNormalVert.shader new file mode 100644 index 0000000..e903edf --- /dev/null +++ b/Shaders/DecalNormalVert.shader @@ -0,0 +1,67 @@ +Shader "ConformalDecals/Feature/BumpedVert" +{ + Properties + { + _Decal("Decal Texture", 2D) = "gray" {} + _DecalBumpMap("Decal Normal Map", 2D) = "bump" {} + + _Cutoff ("Alpha cutoff", Range(0,1)) = 0.5 + _Opacity("_Opacity", Range(0,1) ) = 1 + } + SubShader + { + ZWrite Off + ZTest LEqual + + Pass + { + Name "FORWARD" + Tags { "LightMode" = "ForwardBase" } + Blend SrcAlpha OneMinusSrcAlpha + + CGPROGRAM + #pragma vertex vert_forward_base + #pragma fragment frag_forward_base + + #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight + + #include "UnityCG.cginc" + #include "Lighting.cginc" + #include "AutoLight.cginc" + #include "DecalsCommon.cginc" + + sampler2D _Decal; + sampler2D _DecalBumpMap; + + float _Cutoff; + float _Opacity; + + void surf (DecalSurfaceInput IN, inout SurfaceOutput o) + { + fixed4 projUV = UNITY_PROJ_COORD(IN.uv_decal); + + // since I cant easily affect the clamping mode in KSP, do it here + clip(projUV.xyz); + clip(1-projUV.xyz); + + // clip backsides + clip(dot(_DecalNormal, IN.normal)); + + float4 color = tex2D(_Decal, projUV); + float3 normal = UnpackNormal(tex2D(_DecalBumpMap, projUV)); + //clip(color.a - _Cutoff); + + o.Normal = normal; + o.Albedo = 1;//normal;//color.rgb; + o.Alpha = 1;//color.a * _Opacity; + } + + #include "DecalsSurface.cginc" + + ENDCG + } + + // shadow casting support + UsePass "Legacy Shaders/VertexLit/SHADOWCASTER" + } +} \ No newline at end of file diff --git a/Shaders/DecalPaint.shader b/Shaders/DecalPaint.shader new file mode 100644 index 0000000..9fac776 --- /dev/null +++ b/Shaders/DecalPaint.shader @@ -0,0 +1,80 @@ +Shader "ConformalDecals/Paint/Diffuse" +{ + Properties + { + _Decal ("Cookie", 2D) = "gray" {} + _BumpMap("_BumpMap", 2D) = "bump" {} + + _Cutoff ("Alpha cutoff", Range(0,1)) = 0.5 + _Opacity("_Opacity", Range(0,1) ) = 1 + _NormalWear("_NormalWear", Range(0,100)) = 50 + } + + SubShader + { + Tags { "Queue" = "Geometry" } + + ZWrite On + ZTest LEqual + Blend SrcAlpha OneMinusSrcAlpha + + CGPROGRAM + + #pragma surface surf Lambert alpha vertex:vert + #pragma target 4.0 + + float4x4 _ProjectionMatrix; + float3 _DecalNormal; + float3 _DecalBiNormal; + + sampler2D _Decal; + sampler2D _DecalBumpMap; + sampler2D _BumpMap; + + float _Cutoff; + float _Opacity; + float _NormalWear; + + struct Input + { + float4 decal : TEXCOORD0; + float2 uv_BumpMap : TEXCOORD1; + float4 position : SV_POSITION; + float3 normal : NORMAL; + }; + + void vert (inout appdata_full v, out Input o) { + o.decal = mul (_ProjectionMatrix, v.vertex); + o.uv_BumpMap = v.texcoord.xy; + o.position = UnityObjectToClipPos(v.vertex); + o.normal = v.normal; + } + + void surf (Input IN, inout SurfaceOutput o) + { + fixed4 projUV = UNITY_PROJ_COORD(IN.decal); + + // since I cant easily affect the clamping mode in KSP, do it here + clip(projUV.xyz); + clip(1-projUV.xyz); + + // clip backsides + clip(dot(_DecalNormal, IN.normal)); + + float4 color = tex2D(_Decal, projUV); + float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)); + + color.a *= (1 - (_NormalWear * (1 - dot(normal, fixed3(0,0,1))))); + clip (color.a - _Cutoff); + + fixed2 normalGradient = fixed2(ddx(normal.z), ddy(normal.z)); + + o.Albedo = color.rgb; + //o.Albedo = projUV; + o.Normal = normal; + o.Alpha = color.a * _Opacity; + } + + ENDCG + } +} \ No newline at end of file diff --git a/Shaders/DecalsCommon.cginc b/Shaders/DecalsCommon.cginc new file mode 100644 index 0000000..815d525 --- /dev/null +++ b/Shaders/DecalsCommon.cginc @@ -0,0 +1,44 @@ +#ifndef DECALS_COMMON_INCLUDED +#define DECALS_COMMON_INCLUDED + +struct DecalSurfaceInput +{ + float4 uv_decal; + #ifdef DECAL_BASE_NORMAL + float2 uv_base; + #endif + float3 normal; + float3 viewDir; +}; + +struct appdata_decal +{ + float4 vertex : POSITION; + float3 normal : NORMAL; + #ifdef DECAL_BASE_NORMAL + float4 texcoord : TEXCOORD0; + #endif +}; + +struct v2f +{ + UNITY_POSITION(pos); + float3 normal : NORMAL; + float4 uv_decal : TEXCOORD0; + #ifdef DECAL_BASE_NORMAL + float2 uv_base : TEXCOORD1; + #endif + float4 tSpace0 : TEXCOORD2; + float4 tSpace1 : TEXCOORD3; + float4 tSpace2 : TEXCOORD4; + #if UNITY_SHOULD_SAMPLE_SH + half3 sh : TEXCOORD5; // SH + #endif + UNITY_SHADOW_COORDS(6) +}; + +float4x4 _ProjectionMatrix; +float3 _DecalNormal; +float3 _DecalTangent; + +#endif diff --git a/Shaders/DecalsSurface.cginc b/Shaders/DecalsSurface.cginc new file mode 100644 index 0000000..d3e3832 --- /dev/null +++ b/Shaders/DecalsSurface.cginc @@ -0,0 +1,130 @@ +#ifndef DECALS_SURFACE_INCLUDED +#define DECALS_SURFACE_INCLUDED + +#include "UnityCG.cginc" +#include "Lighting.cginc" +#include "AutoLight.cginc" +#include "LightingKSP.cginc" +#include "DecalsCommon.cginc" + +v2f vert_forward_base(appdata_decal v) +{ + v2f o; + UNITY_INITIALIZE_OUTPUT(v2f,o); + + o.pos = UnityObjectToClipPos(v.vertex); + o.normal = v.normal; + o.uv_decal = mul (_ProjectionMatrix, v.vertex); + + #ifdef DECAL_BASE_NORMAL + o.uv_base = TRANSFORM_TEX(v.texcoord, _BumpMap); + #endif + + float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; + float3 worldNormal = UnityObjectToWorldNormal(v.normal); + //fixed3 worldTangent = fixed3(0,0,0);//UnityObjectToWorldDir(v.tangent.xyz); + fixed3 worldTangent = UnityObjectToWorldDir(_DecalTangent); + fixed3 worldBinormal = cross(worldTangent, worldNormal); + worldTangent = cross(worldNormal, worldBinormal); + o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x); + o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y); + o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z); + + #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL + o.sh = 0; + // Approximated illumination from non-important point lights + #ifdef VERTEXLIGHT_ON + o.sh += 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, worldPos, worldNormal); + #endif + o.sh = ShadeSHPerVertex (worldNormal, o.sh); + #endif + + UNITY_TRANSFER_LIGHTING(o, 0.0); // pass shadow and, possibly, light cookie coordinates to pixel shader + + return o; +} + +fixed4 frag_forward_base(v2f IN) : SV_Target +{ + DecalSurfaceInput i; + SurfaceOutput o; + fixed4 c = 0; + + UNITY_EXTRACT_TBN(IN); + + float3 worldPos = float3(IN.tSpace0.w, IN.tSpace1.w, IN.tSpace2.w); + + float3 worldTan = float3(IN.tSpace0.x, IN.tSpace1.x, IN.tSpace2.x); + + #ifndef USING_DIRECTIONAL_LIGHT + fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos)); + #else + fixed3 lightDir = _WorldSpaceLightPos0.xyz; + #endif + float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos)); + float3 viewDir = _unity_tbn_0 * worldViewDir.x + _unity_tbn_1 * worldViewDir.y + _unity_tbn_2 * worldViewDir.z; + + UNITY_INITIALIZE_OUTPUT(DecalSurfaceInput, i) + i.uv_decal = IN.uv_decal; + #ifdef DECAL_BASE_NORMAL + i.uv_base = IN.uv_base; + #endif + i.normal = IN.normal; + i.viewDir = viewDir; + + o.Albedo = 0.0; + o.Emission = 0.0; + o.Specular = 0.0; + o.Alpha = 0.0; + o.Gloss = 0.0; + o.Normal = fixed3(0,0,1); + fixed3 normalWorldVertex = fixed3(0,0,1); + + // call surface function + surf(i, o); + + // compute lighting & shadowing factor + UNITY_LIGHT_ATTENUATION(atten, IN, worldPos) + 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; + + // 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; + + // 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; + giInput.lightmapUV = 0.0; + #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL + giInput.ambient = IN.sh; + #else + giInput.ambient.rgb = 0.0; + #endif + + LightingBlinnPhong_GI(o, giInput, gi); + + //KSP lighting function + //c += LightingBlinnPhongSmooth(o, lightDir, viewDir, atten); + c += LightingBlinnPhong(o, worldViewDir, gi); + c.rgb += o.Emission; + //c.xyz = (worldTan * 0.5) + 0.5; + return c; +} + +#endif \ No newline at end of file diff --git a/Shaders/LightingKSP.cginc b/Shaders/LightingKSP.cginc new file mode 100644 index 0000000..752771c --- /dev/null +++ b/Shaders/LightingKSP.cginc @@ -0,0 +1,102 @@ +#ifndef LIGHTING_KSP_INCLUDED +#define LIGHTING_KSP_INCLUDED + +inline fixed4 LightingBlinnPhongSmooth(SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten) +{ + s.Normal = normalize(s.Normal); + half3 h = normalize(lightDir + viewDir); + + fixed diff = max(0, dot(s.Normal, lightDir)); + + float nh = max(0, dot(s.Normal, h)); + float spec = pow(nh, s.Specular*128.0) * s.Gloss; + + fixed4 c; + c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * _SpecColor.rgb * spec) * (atten); + c.a = s.Alpha + _LightColor0.a * _SpecColor.a * spec * atten; + return c; +} + + + + +inline half4 LightingUnlit(SurfaceOutput s, half3 lightDir, half atten) +{ + // half diff = max (0, dot (s.Normal, lightDir)); + + half4 c; + c.rgb = s.Albedo; + c.a = s.Alpha; + return c; +} + + + + +inline half4 LightingUnlit_PrePass(SurfaceOutput s, half4 light) +{ + half4 c; + c.rgb = s.Albedo; + c.a = s.Alpha; + return c; +} + + + + +fixed4 LightingNoLighting(SurfaceOutput s, fixed3 lightDir, fixed atten) { return fixed4(0, 0, 0, 0); } + + + + + +float4 _Color; +half _LightBoost; +half4 LightingLightWrapped(SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) { + float3 w = _Color.rgb*0.5; + half3 NdotL = dot(s.Normal, lightDir); + + //Specular term + half3 h = normalize(lightDir + viewDir); + s.Normal = normalize(s.Normal); + float NdotH = dot(s.Normal, h); + float spec = pow(max(NdotH, 0), s.Specular * 128.0) * s.Gloss; + fixed3 specColor = _SpecColor.rgb * _LightColor0.rgb; + + half3 diff = NdotL * (1 - w) + w; + half4 c; + c.rgb = ((s.Albedo * _LightColor0.rgb * diff) + (specColor * spec)) * (atten * _LightBoost); + c.a = s.Alpha + (_LightColor0.a * _SpecColor.a * spec * atten); + return c; +} + + + +float4 _LocalCameraPos; +float4 _LocalCameraDir; +float4 _UnderwaterFogColor; +float _UnderwaterMinAlphaFogDistance; +float _UnderwaterMaxAlbedoFog; +float _UnderwaterMaxAlphaFog; +float _UnderwaterAlbedoDistanceScalar; +float _UnderwaterAlphaDistanceScalar; +float _UnderwaterFogFactor; + +float4 UnderwaterFog(float3 worldPos, float3 color) +{ + float3 toPixel = worldPos - _LocalCameraPos.xyz; + float toPixelLength = length(toPixel); ///< Comment out the math--looks better without it. + //float angleDot = dot(_LocalCameraDir.xyz, toPixel / toPixelLength); + //angleDot = lerp(0.00000001, angleDot, saturate(sign(angleDot))); + //float waterDist = -_LocalCameraPos.w / angleDot; + //float dist = min(toPixelLength, waterDist); + + + 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 \ No newline at end of file