Created
August 6, 2019 03:21
-
-
Save lyuma/8ce4d44ffc0ef1225c2c9bdf21e4afa7 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) (xn.lyuma@gmail.com) | |
// 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/Thiccnessv2Combined" { | |
// This differs from Thiccness v1 by flipping which channel stores thickness and normal data. | |
// See the previous version here: | |
// https://gist.github.com/lyuma/c552d926815c4f9eeb590e439a642349 | |
// 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 thickness_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 2.0 | |
// Half precision has: | |
// 1 bit sign | |
// 5 bits exponent (cannot be 0:denorm or 31:infinity/NaN) | |
// 10 bits mantissa (10 bits split into angle and length) | |
#define NORMAL_ANGLE_BITS 4 // 256 | |
#define LENGTH_ANGLE_BITS (10 - NORMAL_ANGLE_BITS) // 4 | |
#define NORMAL_ANGLE_MULT (1 << (NORMAL_ANGLE_BITS - 1)) // 256 | |
#define LENGTH_ANGLE_MULT (1 << (LENGTH_ANGLE_BITS - 1)) // 4 | |
thickness_v2f vert(appdata_full v) { | |
thickness_v2f o = (thickness_v2f)0; | |
o.pos = UnityObjectToClipPos(v.vertex); | |
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 leng = length(viewNormal.xy); | |
float anglePortion = clamp((angle + UNITY_PI) * NORMAL_ANGLE_MULT / UNITY_TWO_PI, 0, (NORMAL_ANGLE_MULT)); | |
float lengthPortion = clamp(abs(leng) * LENGTH_ANGLE_MULT, 0, (LENGTH_ANGLE_MULT - 1)); | |
float noise01 = InterleavedGradientNoise(ditherUV); | |
precise uint angleUint = uint((noise01 < frac(anglePortion) ? 1.0 : 0.0) + floor(anglePortion)) & (NORMAL_ANGLE_MULT - 1); | |
precise uint lengthUint = uint((noise01 < frac(lengthPortion) ? 1.0 : 0.0) + floor(lengthPortion)); | |
precise uint bits = 0xbf800000 | ((angleUint << (13 + LENGTH_ANGLE_BITS)) | (lengthUint << 13)); | |
// -1.angle|length | |
return asfloat(bits); | |
} | |
float3 decodeBackfaceNormal(float alphaChannel) { | |
precise uint bits = asuint(alphaChannel); | |
precise uint angleUint = (bits >> (13 + LENGTH_ANGLE_BITS)) & (NORMAL_ANGLE_MULT - 1); | |
precise uint lengthUint = (bits >> 13) & (LENGTH_ANGLE_MULT - 1); | |
float angle = float(angleUint) * UNITY_TWO_PI / NORMAL_ANGLE_MULT; | |
float leng = float(lengthUint) / LENGTH_ANGLE_MULT; | |
float2 sc; | |
sincos(angle, sc.x, sc.y); | |
float3 normal = normalize(float3(sc.yx * leng, -sqrt(1 - leng * leng))); | |
//float3 normal = normalize(float3(sc.yx, -1.0)); | |
return normal; | |
} | |
// Thickness encoding as fractional part: | |
float encodeViewDistance(float viewDepthFromCenter, float2 ditherUV) { | |
float dist = 127 + ANGLE_STAGES * clamp(viewDepthFromCenter / THICKNESS_SCALE, -0.99, 0.99); | |
float noise01 = InterleavedGradientNoise(ditherUV); | |
float quantizedDist = (noise01 < frac(dist) ? 1.0 : 0.0) + floor(dist); | |
precise uint exponentBits = (((uint)quantizedDist) << 23); | |
precise float powOfTwo = asfloat(exponentBits); | |
return powOfTwo; | |
} | |
float decodeThickness(float alphaChannel) { | |
precise uint bits = asuint(alphaChannel); | |
float exponent = float((bits >> 23) & 0xff) - 128; | |
float quantizedThickness = exponent / ANGLE_STAGES; | |
return quantizedThickness * THICKNESS_SCALE; | |
} | |
ENDCG | |
Pass { | |
// Backface Stencil prepass | |
ZWrite On | |
ColorMask A | |
Cull Front | |
Stencil { Ref 167 Comp Always Pass Replace } | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
float4 frag(thickness_v2f i) : SV_Target { | |
return float4(1,0,0,0); | |
} | |
ENDCG | |
} | |
Pass { | |
// Backface Depth Prepass: we can only afford to subtract depth once | |
ZWrite On | |
ColorMask 0 | |
ZTest Greater | |
Cull Front | |
Stencil { Ref 167 Comp Equal Pass Keep } | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
float4 frag(thickness_v2f i) : SV_Target { | |
return float4(1,1,1,0); | |
} | |
ENDCG | |
} | |
Pass { | |
// Compute frontface depth in alpha exponent channel | |
ZWrite Off | |
ZTest LEqual | |
Cull Back | |
ColorMask A | |
Blend One One | |
BlendOp Min | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
float4 frag(thickness_v2f i) : SV_Target { | |
float2 ditherUV = _ScreenParams.xy * (i.screenPos.xy / i.screenPos.w); | |
return float4(1,0,0, -encodeViewDistance(-i.viewDepthFromCenter, ditherUV * 0.9173)); | |
} | |
ENDCG | |
} | |
Pass { | |
// Divide backface depth in alpha exponent channel | |
// Store backface normal direction in alpha mantissa channel | |
ZWrite Off | |
ZTest Equal | |
Cull Front | |
ColorMask A | |
Stencil { Ref 167 Comp Always Pass Zero } | |
Blend DstAlpha Zero | |
BlendOp Add | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
float4 frag(thickness_v2f i) : SV_Target { | |
float2 ditherUV = _ScreenParams.xy * (i.screenPos.xy / i.screenPos.w); | |
return float4(1,0,0, encodeViewDistance(i.viewDepthFromCenter, ditherUV * 0.9622) * encodeBackfaceNormal(i.viewNormal, ditherUV)); | |
} | |
ENDCG | |
} | |
GrabPass { | |
"_ColorNormalThickness" | |
// Grab screen | |
} | |
Pass { | |
// Frontface Depth prepass. Just for fun. | |
ZWrite On | |
ColorMask 0 | |
Cull Back | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
float4 frag(thickness_v2f i) : SV_Target { | |
return float4(1,0,0,0); | |
} | |
ENDCG | |
} | |
Pass { | |
// FInal Render | |
ZWrite Off | |
ZTest Equal | |
Cull Back | |
ColorMask RGBA | |
Blend One Zero | |
BlendOp Add | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
sampler2D _ColorNormalThickness; | |
float4 frag(thickness_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 dot(i.viewNormal.y, float3(-.86,.86,0)) * .5 + .5 * float4(.5 - .5 * backfaceNormal.xyz, 1.0); | |
//return float4(thickness.rrr / THICKNESS_SCALE, 1.0); | |
return float4(texRead.rgb * 0 + 1 * ((thickness / THICKNESS_SCALE) * (.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