#ifndef BEAUTIFY_PPSOUTLINE #define BEAUTIFY_PPSOUTLINE // Copyright 2016-2021 Ramiro Oliva (Kronnect) - All Rights Reserved. #include "BeautifyCommon.hlsl" TEXTURE2D_X(_MainTex); float4 _MainTex_TexelSize; float4 _MainTex_ST; TEXTURE2D_X_FLOAT(_OutlineDepth); TEXTURE2D_X_FLOAT(_OutlineObjectId); float4 _Outline; #define OUTLINE_EDGE_THRESHOLD _Outline.a float4 _OutlineData; #define OUTLINE_INTENSITY_MULTIPLIER _OutlineData.x #define OUTLINE_DISTANCE_FADE _OutlineData.y #define OUTLINE_MIN_DEPTH_THRESHOLD _OutlineData.z #define OUTLINE_MIN_SATURATION_THRESHOLD _OutlineData.w half _BlurScale; #define OUTLINE_MIN_SEPARATION _OutlineData.z struct VaryingsOutline { float4 positionCS : SV_POSITION; float2 uv: TEXCOORD0; UNITY_VERTEX_OUTPUT_STEREO }; struct VaryingsCross { float4 positionCS : SV_POSITION; float2 uv: TEXCOORD0; BEAUTIFY_VERTEX_CROSS_UV_DATA UNITY_VERTEX_OUTPUT_STEREO }; VaryingsOutline VertOutline(AttributesSimple input) { VaryingsOutline output; UNITY_SETUP_INSTANCE_ID(input); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); output.positionCS = input.positionOS; output.positionCS.y *= _ProjectionParams.x * _FlipY; output.uv = input.uv; return output; } float OutlinePass(VaryingsOutline i) { float3 uvInc = float3(_MainTex_TexelSize.x, _MainTex_TexelSize.y, 0); #if BEAUTIFY_DEPTH_FADE || !BEAUTIFY_OUTLINE_SOBEL #if BEAUTIFY_OUTLINE_CUSTOM_DEPTH || BEAUTIFY_OUTLINE_OBJECT_ID float depth = BEAUTIFY_GET_CUSTOM_DEPTH_01(_OutlineDepth, i.uv); float sceneDepth = BEAUTIFY_GET_SCENE_DEPTH_01(i.uv); if (sceneDepth < depth * 0.999) return 0; #else float depth = BEAUTIFY_GET_SCENE_DEPTH_01(i.uv); if (depth >= 1.0) return 0; #endif #endif float outline = 0; #if BEAUTIFY_OUTLINE_OBJECT_ID && !BEAUTIFY_OUTLINE_SOBEL float objS = SAMPLE_TEXTURE2D_X(_OutlineObjectId, sampler_PointClamp, i.uv - uvInc.zy).x; float objN = SAMPLE_TEXTURE2D_X(_OutlineObjectId, sampler_PointClamp, i.uv + uvInc.zy).x; float objW = SAMPLE_TEXTURE2D_X(_OutlineObjectId, sampler_PointClamp, i.uv - uvInc.xz).x; float objE = SAMPLE_TEXTURE2D_X(_OutlineObjectId, sampler_PointClamp, i.uv + uvInc.xz).x; float maxObj = max( max(objS, objN), max(objW, objE) ); float minObj = min( min(objS, objN), min(objW, objE) ); float objDiff = maxObj - minObj; outline = objDiff > 0.01; #if BEAUTIFY_OUTLINE_OUTER_ONLY float depthN = BEAUTIFY_GET_CUSTOM_DEPTH_01(_OutlineDepth, i.uv + uvInc.zy); float depthS = BEAUTIFY_GET_CUSTOM_DEPTH_01(_OutlineDepth, i.uv - uvInc.zy); float depthW = BEAUTIFY_GET_CUSTOM_DEPTH_01(_OutlineDepth, i.uv - uvInc.xz); float depthE = BEAUTIFY_GET_CUSTOM_DEPTH_01(_OutlineDepth, i.uv + uvInc.xz); float objM = SAMPLE_TEXTURE2D_X(_OutlineObjectId, sampler_PointClamp, i.uv).x; if ( (depth < depthN && abs(objN - objM) > 0.01) || (depth < depthS && abs(objS - objM) > 0.01) || (depth < depthW && abs(objW - objM) > 0.01) || (depth < depthE && abs(objE - objM) > 0.01)) outline = 0; #endif // check object is big enough #if BEAUTIFY_OUTLINE_MIN_SEPARATION uvInc *= OUTLINE_MIN_SEPARATION; float objS2 = SAMPLE_TEXTURE2D_X(_OutlineObjectId, sampler_PointClamp, i.uv - uvInc.zy).x; float objN2 = SAMPLE_TEXTURE2D_X(_OutlineObjectId, sampler_PointClamp, i.uv + uvInc.zy).x; if (abs(objN2 - objN) > 0.01 && abs(objS2 - objS) > 0.01) outline *= 0.25 / OUTLINE_INTENSITY_MULTIPLIER; float objW2 = SAMPLE_TEXTURE2D_X(_OutlineObjectId, sampler_PointClamp, i.uv - uvInc.xz).x; float objE2 = SAMPLE_TEXTURE2D_X(_OutlineObjectId, sampler_PointClamp, i.uv + uvInc.xz).x; if (abs(objE2 - objE) > 0.01 && abs(objW2 - objW) > 0.01) outline *= 0.25 / OUTLINE_INTENSITY_MULTIPLIER; #endif #else float3 rgbS = SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, i.uv - uvInc.zy).rgb; float3 rgbN = SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, i.uv + uvInc.zy).rgb; float3 rgbW = SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, i.uv - uvInc.xz).rgb; float3 rgbE = SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, i.uv + uvInc.xz).rgb; #if BEAUTIFY_OUTLINE_CUSTOM_DEPTH || !BEAUTIFY_OUTLINE_SOBEL #if BEAUTIFY_OUTLINE_CUSTOM_DEPTH float depthS = BEAUTIFY_GET_CUSTOM_DEPTH_01(_OutlineDepth, i.uv - uvInc.zy); float depthW = BEAUTIFY_GET_CUSTOM_DEPTH_01(_OutlineDepth, i.uv - uvInc.xz); float depthE = BEAUTIFY_GET_CUSTOM_DEPTH_01(_OutlineDepth, i.uv + uvInc.xz); float depthN = BEAUTIFY_GET_CUSTOM_DEPTH_01(_OutlineDepth, i.uv + uvInc.zy); #else float depthS = BEAUTIFY_GET_SCENE_DEPTH_01(i.uv - uvInc.zy); float depthW = BEAUTIFY_GET_SCENE_DEPTH_01(i.uv - uvInc.xz); float depthE = BEAUTIFY_GET_SCENE_DEPTH_01(i.uv + uvInc.xz); float depthN = BEAUTIFY_GET_SCENE_DEPTH_01(i.uv + uvInc.zy); #endif float maxDepth = max(depth, max(max(depthS, depthN), max(depthW, depthE))); float minDepth = min(depth, min(min(depthS, depthN), min(depthW, depthE))); float depthDiff = maxDepth - minDepth; float3 normalNW = getNormal(depth, depthN, depthW, uvInc.zy, float2(-uvInc.x, -uvInc.z)); float3 normalSE = getNormal(depth, depthS, depthE, -uvInc.zy, uvInc.xz); float dnorm = dot(normalNW, normalSE); float satN = getSaturation(rgbN); float satS = getSaturation(rgbS); float satW = getSaturation(rgbW); float satE = getSaturation(rgbE); float maxSat = max( max(satN, satS), max(satW, satE) ); float minSat = min( min(satN, satS), min(satW, satE) ); float dSat = maxSat - minSat; outline = dSat > OUTLINE_MIN_SATURATION_THRESHOLD && dnorm < OUTLINE_EDGE_THRESHOLD && depthDiff > OUTLINE_MIN_DEPTH_THRESHOLD; #else float3 rgbSW = SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, i.uv - uvInc.xy).rgb; float3 rgbNE = SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, i.uv + uvInc.xy).rgb; float3 rgbSE = SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, i.uv + float2( uvInc.x, -uvInc.y)).rgb; float3 rgbNW = SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, i.uv + float2(-uvInc.x, uvInc.y)).rgb; float3 gx = rgbSW * -1.0; gx += rgbSE * 1.0; gx += rgbW * -2.0; gx += rgbE * 2.0; gx += rgbNW * -1.0; gx += rgbNE * 1.0; float3 gy = rgbSW * -1.0; gy += rgbS * -2.0; gy += rgbSE * -1.0; gy += rgbNW * 1.0; gy += rgbN * 2.0; gy += rgbNE * 1.0; outline = (length(gx * gx + gy * gy) - _Outline.a) > 0.0; #endif #endif #if BEAUTIFY_DEPTH_FADE float factor = max(0, (OUTLINE_DISTANCE_FADE - depth) / OUTLINE_DISTANCE_FADE); outline *= factor; #endif return outline; } float4 fragOutline (VaryingsOutline i) : SV_Target { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); i.uv = UnityStereoTransformScreenSpaceTex(i.uv); float outline = OutlinePass(i); return outline; } VaryingsCross VertBlur(AttributesSimple v) { VaryingsCross o; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); o.positionCS = v.positionOS; o.positionCS.y *= _ProjectionParams.x * _FlipY; o.uv = v.uv; BEAUTIFY_VERTEX_OUTPUT_GAUSSIAN_UV(o) return o; } half4 FragBlur (VaryingsCross i): SV_Target { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); i.uv = UnityStereoTransformScreenSpaceTex(i.uv); BEAUTIFY_FRAG_SETUP_GAUSSIAN_UV(i) half4 pixel = SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, i.uv) * 0.2270270270 + (SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, uv1) + SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, uv2)) * 0.3162162162 + (SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, uv3) + SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, uv4)) * 0.0702702703; return pixel; } half4 FragCopy (VaryingsSimple i): SV_Target { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i); i.uv = UnityStereoTransformScreenSpaceTex(i.uv); half outline = SAMPLE_TEXTURE2D_X(_MainTex, sampler_LinearClamp, i.uv).r; half4 color = half4(_Outline.rgb, _Outline.a * outline); color *= OUTLINE_INTENSITY_MULTIPLIER; color.a = saturate(color.a); return color; } #endif