packages feed

keid-render-basic-0.1.12.0: src/Render/Font/Msdf/Code.hs

module Render.Font.Msdf.Code
  ( vert
  , frag
  ) where

import RIO

import Render.Code (Code, glsl)
import Render.DescSets.Set0.Code (set0binding0, set0binding1, set0binding2)

vert :: Code
vert = fromString
  [glsl|
    #version 450

    ${set0binding0}

    layout(location = 0) in  vec4 iVert;
    layout(location = 1) in  vec4 iFrag;
    layout(location = 2) in ivec2 iTextureIds;
    layout(location = 3) in  vec2 iSdfDist;

    layout(location = 4) in  vec4 iColor;
    layout(location = 5) in  vec4 iOutlineColor;
    layout(location = 6) in  vec2 iSdf;

    layout(location = 0)      out  vec2 fTexCoord;
    layout(location = 1) flat out  vec4 fColor;
    layout(location = 2) flat out  vec4 fOutlineColor;
    layout(location = 3) flat out  vec4 fSdf;
    layout(location = 4) flat out ivec2 fTextureIds;

    vec2 positions[6] = vec2[](
      vec2(-0.5, 0.5),
      vec2(0.5, 0.5),
      vec2(-0.5, -0.5),

      vec2(0.5, -0.5),
      vec2(-0.5, -0.5),
      vec2(0.5, 0.5)
    );

    vec2 texCoords[6] = vec2[](
      vec2(0.0, 1),
      vec2(1.0, 1),
      vec2(0.0, 0),

      vec2(1.0, 0),
      vec2(0.0, 0),
      vec2(1.0, 1)
    );

    void main() {
      vec2 pos = positions[gl_VertexIndex];

      gl_Position
        = scene.projection
        * scene.view
      // TODO: * iModel
        * vec4(iVert.xy + pos * iVert.zw, 0, 1.0);

      vec2 uv = texCoords[gl_VertexIndex];
      fTexCoord = iFrag.xy + uv * iFrag.zw;

      fColor = iColor;
      fOutlineColor = iOutlineColor;
      fTextureIds = iTextureIds;
      fSdf = vec4(iSdfDist, iSdf);
    }
  |]

frag :: Code
frag = fromString
  [glsl|
    #version 450
    #extension GL_EXT_nonuniform_qualifier : enable

    ${set0binding1}
    ${set0binding2}

    layout(location = 0)      in  vec2 fTexCoord;
    layout(location = 1) flat in  vec4 fColor;
    layout(location = 2) flat in  vec4 fOutlineColor;
    layout(location = 3) flat in  vec4 fSdf;
    layout(location = 4) flat in ivec2 fTextureIds;

    layout(location = 0) out vec4 outColor;

    #define HARDMASK 0
    #define SOFTMASK 1
    #define SDF 2
    #define PSDF 3
    #define MSDF 4
    #define MTSDF 5

    layout(constant_id = 0) const uint type = MTSDF;
    layout(constant_id = 1) const bool srgb = false;

    float median3(float a, float b, float c) {
      return max(min(a, b), min(max(a, b), c));
    }

    void main() {
      float hard; // text
      float soft; // effects

      switch (type) {
        // single-channel
        case HARDMASK: // actually, bit-packed
        case SOFTMASK: // r8, antialiased greyscale
        case SDF: // r8, actually distance
        case PSDF: // r8, dunno
          hard = texture(
            sampler2D(
              textures[nonuniformEXT(fTextureIds[1])],
              samplers[nonuniformEXT(fTextureIds[0])]
            ),
            fTexCoord
          ).r;
          if (srgb) hard = pow(hard, 1/2.2);
          // No outlines for non-SDF types, just apply tint
          outColor = fColor * vec4(hard);
          return;
        // 3-channel
        case MSDF:
          vec3 msdf = texture(
            sampler2D(
              textures[nonuniformEXT(fTextureIds[1])],
              samplers[nonuniformEXT(fTextureIds[0])]
            ),
            fTexCoord
          ).rgb;
          hard = median3(msdf.r, msdf.g, msdf.b);
          if (srgb) hard = pow(hard, 1/2.2);
          soft = hard; // no dedicated channel
          break;
        // 4-channel
        case MTSDF:
          vec4 mtsdf = texture(
            sampler2D(
              textures[nonuniformEXT(fTextureIds[1])],
              samplers[nonuniformEXT(fTextureIds[0])]
            ),
            fTexCoord
          ).rgba;
          hard = median3(mtsdf.r, mtsdf.g, mtsdf.b);
          if (srgb) hard = pow(hard, 1/2.2);
          soft = mtsdf.a; // dedicated channel for soft effects
          break;
      }

      float sdfLower = fSdf[0];
      float sdfRange = fSdf[1];
      float sdfSmoothing = fSdf[2]; // fallback
      float sdfOutline = fSdf[3];

      float hardPx = sdfLower + (hard * sdfRange);
      float hardWidth = max(fwidth(hard) * sdfRange, sdfSmoothing);
      float hardCoverage = smoothstep(-0.5, 0.5, hardPx / hardWidth);

     	float softPx = sdfLower + (soft * sdfRange);
     	float softWidth = max(fwidth(soft) * sdfRange, sdfSmoothing);
     	float softCoverage = clamp(smoothstep(-sdfOutline / softWidth, 0, softPx / softWidth), 0, 1);

     	outColor = mix(fOutlineColor, fColor, hardCoverage) * softCoverage;
    }
  |]