/** * Copyright (c) 2019-2026 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Áron Samuel Kovács * @author Alexander Rose * @author Gianluca Tomasello * @author Sebastian Bittrich */ export declare const outlines_frag = "\nprecision highp float;\nprecision highp int;\nprecision highp sampler2D;\n\nuniform sampler2D tDepthOpaque;\nuniform sampler2D tDepthTransparent;\nuniform vec2 uTexSize;\n\nuniform float uNear;\nuniform float uFar;\nuniform mat4 uInvProjection;\n\nuniform float uOutlineThreshold;\n\n#include common\n\nfloat getViewZ(const in float depth) {\n #if dOrthographic == 1\n return orthographicDepthToViewZ(depth, uNear, uFar);\n #else\n return perspectiveDepthToViewZ(depth, uNear, uFar);\n #endif\n}\n\nfloat getDepthOpaque(const in vec2 coords) {\n #ifdef depthTextureSupport\n return texture2D(tDepthOpaque, coords).r;\n #else\n return unpackRGBAToDepth(texture2D(tDepthOpaque, coords));\n #endif\n}\n\nvec2 getDepthTransparentWithAlpha(const in vec2 coords) {\n #ifdef dTransparentOutline\n return unpackRGBAToDepthWithAlpha(texture2D(tDepthTransparent, coords));\n #else\n return vec2(1.0, 0.0);\n #endif\n}\n\nbool isBackground(const in float depth) {\n return depth == 1.0;\n}\n\nfloat getPixelSize(const in vec2 coords, const in float depth) {\n vec3 viewPos0 = screenSpaceToViewSpace(vec3(coords, depth), uInvProjection);\n vec3 viewPos1 = screenSpaceToViewSpace(vec3(coords + vec2(1.0, 0.0) / uTexSize, depth), uInvProjection);\n return distance(viewPos0, viewPos1);\n}\n\nvoid main(void) {\n float backgroundViewZ = 2.0 * uFar;\n\n vec2 coords = gl_FragCoord.xy / uTexSize;\n vec2 invTexSize = 1.0 / uTexSize;\n\n float selfDepthOpaque = getDepthOpaque(coords);\n float selfViewZOpaque = isBackground(selfDepthOpaque) ? backgroundViewZ : getViewZ(selfDepthOpaque);\n float pixelSizeOpaque = getPixelSize(coords, selfDepthOpaque) * uOutlineThreshold;\n\n vec2 selfDepthTransparentWithAlpha = getDepthTransparentWithAlpha(coords);\n float selfDepthTransparent = selfDepthTransparentWithAlpha.x;\n float selfViewZTransparent = isBackground(selfDepthTransparent) ? backgroundViewZ : getViewZ(selfDepthTransparent);\n float pixelSizeTransparent = getPixelSize(coords, selfDepthTransparent) * uOutlineThreshold;\n\n float bestOpaqueDepth = 1.0;\n float bestTransparentDepth = 1.0;\n float bestTransparentAlpha = 0.0;\n\n float opaqueOutlineFlag = 0.0;\n float transparentOutlineFlag = 0.0;\n\n for (int y = -1; y <= 1; y++) {\n for (int x = -1; x <= 1; x++) {\n vec2 sampleCoords = coords + vec2(float(x), float(y)) * invTexSize;\n\n // Opaque\n float sampleDepthOpaque = getDepthOpaque(sampleCoords);\n float sampleViewZOpaque = isBackground(sampleDepthOpaque) ? backgroundViewZ : getViewZ(sampleDepthOpaque);\n if (abs(selfViewZOpaque - sampleViewZOpaque) > pixelSizeOpaque && selfDepthOpaque > sampleDepthOpaque && sampleDepthOpaque <= bestOpaqueDepth) {\n bestOpaqueDepth = sampleDepthOpaque;\n opaqueOutlineFlag = 1.0;\n }\n\n // Transparent\n vec2 sampleDepthTransparentWithAlpha = getDepthTransparentWithAlpha(sampleCoords);\n float sampleDepthTransparent = sampleDepthTransparentWithAlpha.x;\n float sampleAlphaTransparent = sampleDepthTransparentWithAlpha.y;\n float sampleViewZTransparent = isBackground(sampleDepthTransparent) ? backgroundViewZ : getViewZ(sampleDepthTransparent);\n if (abs(selfViewZTransparent - sampleViewZTransparent) > pixelSizeTransparent && selfDepthTransparent > sampleDepthTransparent && sampleDepthTransparent <= bestTransparentDepth) {\n bestTransparentDepth = sampleDepthTransparent;\n bestTransparentAlpha = sampleAlphaTransparent;\n transparentOutlineFlag = 1.0;\n }\n }\n }\n\n if (transparentOutlineFlag > 0.0 && abs(selfViewZOpaque - getViewZ(bestTransparentDepth)) < pixelSizeOpaque) { // Disable transparent outline close to opaque elements\n transparentOutlineFlag = 0.0;\n }\n\n // lazy curvature veto: only compute if an outline was found and center is not background\n const float kCurvatureGate = 0.75;\n vec2 dx = vec2(invTexSize.x, 0.0);\n vec2 dy = vec2(0.0, invTexSize.y);\n\n if (opaqueOutlineFlag > 0.0 && !isBackground(selfDepthOpaque)) {\n float dL = getDepthOpaque(coords - dx);\n float dR = getDepthOpaque(coords + dx);\n float dU = getDepthOpaque(coords + dy);\n float dD = getDepthOpaque(coords - dy);\n\n float vzL = isBackground(dL) ? backgroundViewZ : getViewZ(dL);\n float vzR = isBackground(dR) ? backgroundViewZ : getViewZ(dR);\n float vzU = isBackground(dU) ? backgroundViewZ : getViewZ(dU);\n float vzD = isBackground(dD) ? backgroundViewZ : getViewZ(dD);\n\n float ddx = abs(vzL + vzR - 2.0 * selfViewZOpaque);\n float ddy = abs(vzU + vzD - 2.0 * selfViewZOpaque);\n float curvOpaque = max(ddx, ddy);\n\n if (curvOpaque < pixelSizeOpaque * kCurvatureGate) {\n opaqueOutlineFlag = 0.0;\n bestOpaqueDepth = 1.0;\n }\n }\n\n if (transparentOutlineFlag > 0.0 && !isBackground(selfDepthTransparent)) {\n vec2 daL = getDepthTransparentWithAlpha(coords - dx);\n vec2 daR = getDepthTransparentWithAlpha(coords + dx);\n vec2 daU = getDepthTransparentWithAlpha(coords + dy);\n vec2 daD = getDepthTransparentWithAlpha(coords - dy);\n\n float dL = daL.x;\n float dR = daR.x;\n float dU = daU.x;\n float dD = daD.x;\n\n float vzL = isBackground(dL) ? backgroundViewZ : getViewZ(dL);\n float vzR = isBackground(dR) ? backgroundViewZ : getViewZ(dR);\n float vzU = isBackground(dU) ? backgroundViewZ : getViewZ(dU);\n float vzD = isBackground(dD) ? backgroundViewZ : getViewZ(dD);\n\n float ddx = abs(vzL + vzR - 2.0 * selfViewZTransparent);\n float ddy = abs(vzU + vzD - 2.0 * selfViewZTransparent);\n float curvTransparent = max(ddx, ddy);\n\n if (curvTransparent < pixelSizeTransparent * kCurvatureGate) {\n transparentOutlineFlag = 0.0;\n bestTransparentDepth = 1.0;\n bestTransparentAlpha = 0.0;\n }\n }\n\n vec2 depthPacked; // Pack depth in G/B channels\n float outlineTypeFlag = 0.0;\n if (opaqueOutlineFlag > 0.0 && transparentOutlineFlag > 0.0) {\n outlineTypeFlag = 0.75; // Both\n depthPacked = packUnitIntervalToRG(bestOpaqueDepth);\n } else if (transparentOutlineFlag > 0.0) {\n outlineTypeFlag = 0.5; // Transparent only\n depthPacked = packUnitIntervalToRG(bestTransparentDepth);\n } else if (opaqueOutlineFlag > 0.0) {\n outlineTypeFlag = 0.25; // Opaque only\n depthPacked = packUnitIntervalToRG(bestOpaqueDepth);\n }\n\n float alpha = clamp(bestTransparentAlpha, 0.0, 0.5) * 2.0; // limiting to range [0.0, 0.5] to improve alpha precision since we don't need a wider range\n float packedFlagWithAlpha = pack2x4(vec2(outlineTypeFlag, alpha)); // pack outlineType with alpha\n gl_FragColor = vec4(packedFlagWithAlpha, depthPacked.x, depthPacked.y, bestTransparentDepth);\n}\n";