Lib Sampler - Shader API


lib-sampler.glsl

Public Functions: getAO getShadowFactor getGlossiness getRoughness getMetallic getAnisotropyLevel getAnisotropyAngle getOpacity getHeight getDisplacement getSpecularLevel getBaseColor getDiffuse getSpecularColor getScattering generateAnisotropicRoughness generateDiffuseColor generateSpecularColor

Import from library

import lib-defines.glsl
import lib-sparse.glsl

Default background colors when there is no data in channel (alpha is 0)

const vec3  DEFAULT_BASE_COLOR       = vec3(0.5);
const float DEFAULT_ROUGHNESS        = 0.3;
const float DEFAULT_METALLIC         = 0.0;
const float DEFAULT_ANISOTROPY_LEVEL = 0.0;
const float DEFAULT_ANISOTROPY_ANGLE = 0.0;
const float DEFAULT_OPACITY          = 1.0;
const float DEFAULT_AO               = 1.0;
const float DEFAULT_SPECULAR_LEVEL   = 0.5;
const float DEFAULT_HEIGHT           = 0.0;
const float DEFAULT_DISPLACEMENT     = 0.0;
const float DEFAULT_SCATTERING       = 0.0;

AO map.

//: param auto ao_blending_mode
uniform int ao_blending_mode;
//: param auto texture_ao
uniform SamplerSparse base_ao_tex;
//: param auto channel_ao
uniform SamplerSparse ao_tex;

A value used to tweak the Ambient Occlusion intensity.

//: param custom {
//:   "default": 0.75,
//:   "label": "AO Intensity",
//:   "min": 0.00,
//:   "max": 1.0,
//:   "group": "Common Parameters"
//: }
uniform float ao_intensity;

Shadowmask.

//: param auto shadow_mask_enable
uniform bool sm_enable;
//: param auto shadow_mask_opacity
uniform float sm_opacity;
//: param auto shadow_mask
uniform sampler2D sm_tex;
//: param auto screen_size
uniform vec4 screen_size;

Return sampled glossiness or a default value

float getGlossiness(vec4 sampledValue)
{
  return sampledValue.r + (1.0 - DEFAULT_ROUGHNESS) * (1.0 - sampledValue.g);
}

float getGlossiness(SamplerSparse sampler, SparseCoord coord)
{
  return getGlossiness(textureSparse(sampler, coord));
}

Return sampled roughness or a default value

float getRoughness(vec4 sampledValue)
{
  return sampledValue.r + DEFAULT_ROUGHNESS * (1.0 - sampledValue.g);
}

float getRoughness(SamplerSparse sampler, SparseCoord coord)
{
  return getRoughness(textureSparse(sampler, coord));
}

Return sampled metallic or a default value

float getMetallic(vec4 sampledValue)
{
  return sampledValue.r + DEFAULT_METALLIC * (1.0 - sampledValue.g);
}

float getMetallic(SamplerSparse sampler, SparseCoord coord)
{
  return getMetallic(textureSparse(sampler, coord));
}

Return sampled anisotropy level or a default value

float getAnisotropyLevel(vec4 sampledValue)
{
  return sampledValue.r + DEFAULT_ANISOTROPY_LEVEL * (1.0 - sampledValue.g);
}

float getAnisotropyLevel(SamplerSparse sampler, SparseCoord coord)
{
  return getAnisotropyLevel(textureSparse(sampler, coord));
}

Return sampled anisotropy angle or a default value

float getAnisotropyAngle(vec4 sampledValue)
{
  return M_2PI * (sampledValue.r + DEFAULT_ANISOTROPY_ANGLE * (1.0 - sampledValue.g));
}

float getAnisotropyAngle(SamplerSparse sampler, SparseCoord coord)
{
  // Manual trilinear filtering
  float level = max(0.0, textureSparseQueryLod(sampler, coord) + uvtile_lod_bias);
  int level0 = int(level);
  int level1 = level0 + 1;

  ivec2 texSize0 = ivec2(sampler.size.xy) >> level0;
  ivec2 texSize1 = texSize0 >> 1;
  ivec2 itex_coord0 = ivec2(coord.tex_coord * vec2(texSize0));
  ivec2 itex_coord1 = ivec2(coord.tex_coord * vec2(texSize1));

  // Assuming tex sizes are pow of 2, we can do the fast modulo
  ivec2 texSizeMask0 = texSize0 - ivec2(1);
  ivec2 texSizeMask1 = texSize1 - ivec2(1);

  // Fetch the 8 samples needed
  float a000 = getAnisotropyAngle(texelFetch(sampler.tex,  itex_coord0                & texSizeMask0, level0));
  float a001 = getAnisotropyAngle(texelFetch(sampler.tex, (itex_coord0 + ivec2(1, 0)) & texSizeMask0, level0)) - a000;
  float a010 = getAnisotropyAngle(texelFetch(sampler.tex, (itex_coord0 + ivec2(0, 1)) & texSizeMask0, level0)) - a000;
  float a011 = getAnisotropyAngle(texelFetch(sampler.tex, (itex_coord0 + ivec2(1, 1)) & texSizeMask0, level0)) - a000;
  float a100 = getAnisotropyAngle(texelFetch(sampler.tex,  itex_coord1                & texSizeMask1, level1)) - a000;
  float a101 = getAnisotropyAngle(texelFetch(sampler.tex, (itex_coord1 + ivec2(1, 0)) & texSizeMask1, level1)) - a000;
  float a110 = getAnisotropyAngle(texelFetch(sampler.tex, (itex_coord1 + ivec2(0, 1)) & texSizeMask1, level1)) - a000;
  float a111 = getAnisotropyAngle(texelFetch(sampler.tex, (itex_coord1 + ivec2(1, 1)) & texSizeMask1, level1)) - a000;

  // Detect if the angle warps inside the filtering footprint, and fix it
  a001 += abs(a001) > M_PI ? sign(a001) * -M_2PI + a000 : a000;
  a010 += abs(a010) > M_PI ? sign(a010) * -M_2PI + a000 : a000;
  a011 += abs(a011) > M_PI ? sign(a011) * -M_2PI + a000 : a000;
  a100 += abs(a100) > M_PI ? sign(a100) * -M_2PI + a000 : a000;
  a101 += abs(a101) > M_PI ? sign(a101) * -M_2PI + a000 : a000;
  a110 += abs(a110) > M_PI ? sign(a110) * -M_2PI + a000 : a000;
  a111 += abs(a111) > M_PI ? sign(a111) * -M_2PI + a000 : a000;

  // Trilinear blending of the samples
  vec2 t0 = coord.tex_coord * vec2(texSize0) - vec2(itex_coord0);
  vec2 t1 = coord.tex_coord * vec2(texSize1) - vec2(itex_coord1);
  return mix(
    mix(mix(a000, a001, t0.x), mix(a010, a011, t0.x), t0.y),
    mix(mix(a100, a101, t1.x), mix(a110, a111, t1.x), t1.y),
    level - float(level0));
}

Return sampled opacity or a default value

float getOpacity(vec4 sampledValue)
{
  return sampledValue.r + DEFAULT_OPACITY * (1.0 - sampledValue.g);
}

float getOpacity(SamplerSparse sampler, SparseCoord coord)
{
  return getOpacity(textureSparse(sampler, coord));
}

Return sampled height or a default value

float getHeight(vec4 sampledValue)
{
  return sampledValue.r + DEFAULT_HEIGHT * (1.0 - sampledValue.g);
}

float getHeight(SamplerSparse sampler, SparseCoord coord)
{
  return getHeight(textureSparse(sampler, coord));
}

Return sampled displacement or a default value

float getDisplacement(vec4 sampledValue)
{
  return sampledValue.r + DEFAULT_DISPLACEMENT * (1.0 - sampledValue.g);
}

float getDisplacement(SamplerSparse sampler, SparseCoord coord)
{
  return getDisplacement(textureSparse(sampler, coord));
}

Return ambient occlusion

float getAO(SparseCoord coord, bool is_premult)
{
  vec2 ao_lookup = textureSparse(base_ao_tex, coord).ra;
  float ao = ao_lookup.x + DEFAULT_AO * (1.0 - ao_lookup.y);

  if (ao_tex.is_set) {
    ao_lookup = textureSparse(ao_tex, coord).rg;
    if (!is_premult) ao_lookup.x *= ao_lookup.y;
    float channel_ao = ao_lookup.x + DEFAULT_AO * (1.0 - ao_lookup.y);
    if (ao_blending_mode == BlendingMode_Replace) {
      ao = channel_ao;
    } else if (ao_blending_mode == BlendingMode_Multiply) {
      ao *= channel_ao;
    }
  }

  // Modulate AO value by AO_intensity
  return mix(1.0, ao, ao_intensity);
}

Helper to get ambient occlusion for shading

float getAO(SparseCoord coord)
{
  return getAO(coord, true);
}

Return specular level

float getSpecularLevel(vec4 sampledValue)
{
  return sampledValue.r + DEFAULT_SPECULAR_LEVEL * (1.0 - sampledValue.g);
}

float getSpecularLevel(SamplerSparse sampler, SparseCoord coord)
{
  return getSpecularLevel(textureSparse(sampler, coord));
}

Fetch the shadowing factor (screen-space)

float getShadowFactor()
{
  float shadowFactor = 1.0;

  if (sm_enable) {
    vec2 screenCoord = (gl_FragCoord.xy * vec2(screen_size.z, screen_size.w));
    vec2 shadowSample = texture(sm_tex, screenCoord).xy;
    // shadowSample.x / shadowSample.y is the normalized shadow factor.
    // shadowSample.x may already be normalized, shadowSample.y contains 0.0 in this case.
    shadowFactor = shadowSample.y == 0.0 ? shadowSample.x : shadowSample.x / shadowSample.y;
  }

  return mix(1.0, shadowFactor, sm_opacity);
}

Return sampled base color or a default value

vec3 getBaseColor(vec4 sampledValue)
{
  return sampledValue.rgb + DEFAULT_BASE_COLOR * (1.0 - sampledValue.a);
}

vec3 getBaseColor(SamplerSparse sampler, SparseCoord coord)
{
  return getBaseColor(textureSparse(sampler, coord));
}

Return sampled diffuse color or a default value

vec3 getDiffuse(vec4 sampledValue)
{
  return getBaseColor(sampledValue);
}

vec3 getDiffuse(SamplerSparse sampler, SparseCoord coord)
{
  return getDiffuse(textureSparse(sampler, coord));
}

Return sampled specular color or a default value

vec3 getSpecularColor(vec4 sampledValue)
{
  vec3 specColor = sampledValue.rgb + DEFAULT_BASE_COLOR * (1.0 - sampledValue.a);
  vec3 defaultF0 = mix(vec3(0.04), specColor, DEFAULT_METALLIC);
  return mix(specColor, defaultF0, (1.0 - sampledValue.a));
}

vec3 getSpecularColor(SamplerSparse sampler, SparseCoord coord)
{
  return getSpecularColor(textureSparse(sampler, coord));
}

Generate anisotropic roughness from roughness and anisotropy level

vec2 generateAnisotropicRoughness(float roughness, float anisoLevel)
{
  return vec2(roughness, roughness / sqrt(max(1e-8, 1.0 - anisoLevel)));
}

Generate diffuse color from base color and metallic factor

vec3 generateDiffuseColor(vec3 baseColor, float metallic)
{
  return baseColor * (1.0 - metallic);
}

Generate specular color from dielectric specular level, base color and metallic factor

vec3 generateSpecularColor(float specularLevel, vec3 baseColor, float metallic)
{
  return mix(vec3(0.08 * specularLevel), baseColor, metallic);
}

Generate specular color from base color and metallic factor, using default specular level (0.04) for dielectrics

vec3 generateSpecularColor(vec3 baseColor, float metallic)
{
  return mix(vec3(0.04), baseColor, metallic);
}

Return sampled scattering value or a default value

float getScattering(vec4 sampledValue)
{
  return sampledValue.r + DEFAULT_SCATTERING * (1.0 - sampledValue.g);
}

float getScattering(SamplerSparse sampler, SparseCoord coord)
{
  return getScattering(textureSparse(sampler, coord));
}