Created
August 5, 2019 23:46
-
-
Save lyuma/c552d926815c4f9eeb590e439a642349 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (C) 2019 Lyuma (Lyuma#0781) ([email protected]) | |
// MIT License | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a copy of | |
// this software and associated documentation files (the "Software"), to deal in | |
// the Software without restriction, including without limitation the rights to | |
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |
// of the Software, and to permit persons to whom the Software is furnished to do | |
// so, subject to the following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included in all | |
// copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
// SOFTWARE. | |
Shader "LyumaShader/ThiccnessCombined" { | |
// CAVEAT: This will not work if any other objects in the scene use the same shader. | |
// To fix, this must be split up into three materials/queues, with the GrabPass | |
// combined with rendering in the last queue. | |
Properties | |
{ | |
} | |
SubShader | |
{ | |
Tags { "RenderType"="Transparent" "Queue"="Transparent+30"} | |
LOD 100 | |
Lighting Off | |
CGINCLUDE | |
#pragma target 4.5 | |
#include "UnityCG.cginc" | |
struct v2f { | |
float4 pos : SV_Position; | |
float3 viewNormal : TEXCOORD0; | |
float viewDepthFromCenter : TEXCOORD1; | |
float4 screenPos : TEXCOORD2; | |
}; | |
#define ANGLE_STAGES 13 | |
#define EPSILON 0.0078125 | |
#define HALF_MINUS_EPSILON 0.4921875 | |
#define THICKNESS_SCALE 4.0 | |
v2f vert(appdata_full v) { | |
v2f o = (v2f)0; | |
o.pos = UnityObjectToClipPos(v.vertex); | |
//o.worldNormal = mul((float3x3)unity_ObjectToWorld, v.normal.xyz); | |
o.viewNormal = mul((float3x3)UNITY_MATRIX_V, mul((float3x3)unity_ObjectToWorld, v.normal.xyz)); | |
float3 viewPos = UnityObjectToViewPos(v.vertex).xyz; | |
float3 centerViewPos = UnityObjectToViewPos(float4(0,0,0,1)).xyz; | |
o.viewDepthFromCenter = centerViewPos.z - viewPos.z; | |
o.screenPos = ComputeGrabScreenPos(o.pos); | |
return o; | |
} | |
// Normal encoding as power of two (exponent portion) | |
// ALU noise in Next-gen post processing in COD:AW | |
float InterleavedGradientNoise( float2 screenUV ) | |
{ | |
float3 magic = { 0.06711056, 0.00583715, 52.9829189 }; | |
return frac( magic.z * frac( dot( screenUV, magic.xy ) ) ); | |
} | |
float encodeBackfaceNormal(float3 viewNormal, float2 ditherUV) { | |
float2 normalXY = normalize(viewNormal.xy); | |
float angle = atan2(normalXY.y, normalXY.x); | |
float angleToQuantize = (angle * ANGLE_STAGES / UNITY_TWO_PI + 127); | |
float noise01 = InterleavedGradientNoise(ditherUV); | |
float quantizedAngle = (noise01 < frac(angleToQuantize) ? 1.0 : 0.0) + floor(angleToQuantize); | |
precise uint exponentBits = (((uint)quantizedAngle) << 23); | |
precise float powOfTwo = asfloat(exponentBits); | |
return powOfTwo; | |
} | |
float3 decodeBackfaceNormal(float alphaChannel) { | |
precise uint bits = asuint(alphaChannel); | |
float exponent = float((bits >> 23) & 0xff) - 127; | |
float quantizedAngle = exponent.r * UNITY_TWO_PI / ANGLE_STAGES; | |
float3 normal = normalize(float3(cos(quantizedAngle), sin(quantizedAngle), -1.0)); | |
return normal; | |
} | |
static float objectScaledThickness = THICKNESS_SCALE;// * length(unity_ObjectToWorld._11_12_13); | |
// Thickness encoding as fractional part: | |
float encodeViewDistance(float viewDepthFromCenter) { | |
return 0.25 * clamp(viewDepthFromCenter / objectScaledThickness, -0.99, 0.99); | |
} | |
float decodeThickness(float alphaChannel) { | |
precise uint bits = asuint(alphaChannel); | |
precise uint exponentBits = (((bits >> 23) & 0xff) << 23); | |
float powOfTwo = asfloat(exponentBits); | |
float thickness = (frac(abs(alphaChannel) / powOfTwo) - EPSILON) * objectScaledThickness; | |
return thickness; | |
} | |
ENDCG | |
Pass { | |
// Backface Depth Prepass: we can only afford to subtract depth once | |
ZWrite On | |
ColorMask RA | |
Cull Front | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
float4 frag(v2f i) : SV_Target { | |
return float4(1,0,0,0); | |
} | |
ENDCG | |
} | |
Pass { | |
// Compute frontface depth in alpha mantissa channel | |
ZWrite Off | |
ZTest LEqual | |
Cull Back | |
ColorMask RA | |
Blend One One | |
BlendOp Min | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
float4 frag(v2f i) : SV_Target { | |
return float4(1,0,0, -1.5 + encodeViewDistance(i.viewDepthFromCenter)); | |
} | |
ENDCG | |
} | |
Pass { | |
// Subtract backface depth in alpha mantissa channel | |
ZWrite Off | |
ZTest Equal | |
Cull Front | |
ColorMask RA | |
Blend One One | |
BlendOp Add | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
float4 frag(v2f i) : SV_Target { | |
float HalfMinusEpsilon = 0.4921875; | |
return float4(1,0,0, HALF_MINUS_EPSILON - encodeViewDistance(i.viewDepthFromCenter)); | |
} | |
ENDCG | |
} | |
Pass { | |
// Store backface normal direction in alpha exponent channel | |
ZWrite Off | |
ZTest Equal | |
Cull Front | |
ColorMask RA | |
Blend DstAlpha Zero | |
BlendOp Add | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
float4 frag(v2f i) : SV_Target { | |
float2 ditherUV = _ScreenParams.xy * (i.screenPos.xy / i.screenPos.w); | |
return float4(1, 0, 0, encodeBackfaceNormal(i.viewNormal, ditherUV)); | |
} | |
ENDCG | |
} | |
GrabPass { | |
"_ColorNormalThickness" | |
// Grab screen | |
} | |
Pass { | |
// FInal Render | |
ZWrite Off | |
ZTest LEqual | |
Cull Back | |
ColorMask RGBA | |
Blend One Zero | |
BlendOp Add | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
sampler2D _ColorNormalThickness; | |
float4 frag(v2f i) : SV_Target { | |
float4 texRead = tex2Dproj(_ColorNormalThickness, i.screenPos); | |
if (0) {//texRead.a >= 0) { | |
// We did not write any data. | |
return texRead; | |
} else { | |
float3 backfaceNormal = decodeBackfaceNormal(texRead.a); | |
float thickness = decodeThickness(texRead.a); | |
// Show all parts visually. | |
return float4(texRead.rgb * 0 + 1 * ((thickness / objectScaledThickness) * (.5 + .5 * (i.viewNormal.xyz - backfaceNormal.xyz))), 1.0); | |
} | |
} | |
ENDCG | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment