User Guide Cancel

Lib Sparse - Shader API | Substance 3D Painter

Lib Sparse - Shader API

lib-sparse.glsl

This file provides useful functions to ensure sparse textures sampling correctness (ARB_sparse_texture). Allows to sample only part of textures really present in video memory.

Public Functions: getSparseCoord getSparseCoordLod0 textureSparseQueryLod textureSparse

Public Structures: SamplerSparse SparseCoord

The FEATURE_SPARSE_TEXTURE macro is defined only if the sparse virtual texture extension is enabled.

If enabled, process additional texture lookup checks to climb up mipmap pyramid if texels are missing.

#ifdef FEATURE_SPARSE_TEXTURE
//: param auto material_lod_check_needed
uniform bool material_lod_check_needed = false;
//: param auto material_lod_mask
uniform usampler2D material_lod_mask;
#endif // FEATURE_SPARSE_TEXTURE
//: param auto uvtile_reference_sampler
uniform sampler2D uvtile_reference_sampler;
//: param auto uvtile_size
uniform vec2 uvtile_size;
//: param auto uvtile_inverse_size
uniform vec2 uvtile_inverse_size;
//: param auto uvtile_lod_bias
uniform float uvtile_lod_bias;
#ifdef FEATURE_SPARSE_TEXTURE //: param auto material_lod_check_needed uniform bool material_lod_check_needed = false; //: param auto material_lod_mask uniform usampler2D material_lod_mask; #endif // FEATURE_SPARSE_TEXTURE //: param auto uvtile_reference_sampler uniform sampler2D uvtile_reference_sampler; //: param auto uvtile_size uniform vec2 uvtile_size; //: param auto uvtile_inverse_size uniform vec2 uvtile_inverse_size; //: param auto uvtile_lod_bias uniform float uvtile_lod_bias;
#ifdef FEATURE_SPARSE_TEXTURE 
//: param auto material_lod_check_needed 
uniform bool material_lod_check_needed = false; 
//: param auto material_lod_mask 
uniform usampler2D material_lod_mask; 
#endif // FEATURE_SPARSE_TEXTURE 
//: param auto uvtile_reference_sampler 
uniform sampler2D uvtile_reference_sampler; 
//: param auto uvtile_size 
uniform vec2 uvtile_size; 
//: param auto uvtile_inverse_size 
uniform vec2 uvtile_inverse_size; 
//: param auto uvtile_lod_bias 
uniform float uvtile_lod_bias;

Sampler and sparse texture information structure

Used to query all sampler related uniforms with a single auto binding

struct SamplerSparse {
sampler2D tex;
vec4 size; // width, height, 1/width, 1/height
bool is_set; // a boolean indicating whether the texture is in the texture set or not
uvec3 lod_mask_select; // masking operations description allowing to retrieve loaded mipmaps information
};
struct SamplerSparse { sampler2D tex; vec4 size; // width, height, 1/width, 1/height bool is_set; // a boolean indicating whether the texture is in the texture set or not uvec3 lod_mask_select; // masking operations description allowing to retrieve loaded mipmaps information };
struct SamplerSparse { 
  sampler2D tex; 
  vec4 size; // width, height, 1/width, 1/height 
  bool is_set; // a boolean indicating whether the texture is in the texture set or not 
  uvec3 lod_mask_select; // masking operations description allowing to retrieve loaded mipmaps information 
};

Sparse sampling coordinates

Store the UV coordinates & material-wise sparse LoD mask

struct SparseCoord {
vec2 tex_coord;
vec2 dfdx;
vec2 dfdy;
float lod;
uint material_lod_mask;
};
#if defined(SHADER_FRAGMENT)
struct SparseCoord { vec2 tex_coord; vec2 dfdx; vec2 dfdy; float lod; uint material_lod_mask; }; #if defined(SHADER_FRAGMENT)
struct SparseCoord { 
  vec2 tex_coord; 
  vec2 dfdx; 
  vec2 dfdy; 
  float lod; 
  uint material_lod_mask; 
}; 
 
 
#if defined(SHADER_FRAGMENT)

Build texture coordinates structure used by textureSparse() sampling function (must be called from fragment shader)

Example: SparseCoord uv1coord = getSparseCoord(inputs.multi_tex_coord[1]);

SparseCoord getSparseCoord(vec2 tex_coord) {
SparseCoord res;
res.tex_coord = tex_coord;
res.dfdx = dFdx(tex_coord);
res.dfdy = dFdy(tex_coord);
#ifdef FEATURE_SPARSE_TEXTURE
res.material_lod_mask = material_lod_check_needed ?
textureLod(material_lod_mask,tex_coord,0.0).r :
0u;
res.lod = getLodFromReferenceSampler(tex_coord);
#endif // FEATURE_SPARSE_TEXTURE
return res;
}
#endif
SparseCoord getSparseCoord(vec2 tex_coord) { SparseCoord res; res.tex_coord = tex_coord; res.dfdx = dFdx(tex_coord); res.dfdy = dFdy(tex_coord); #ifdef FEATURE_SPARSE_TEXTURE res.material_lod_mask = material_lod_check_needed ? textureLod(material_lod_mask,tex_coord,0.0).r : 0u; res.lod = getLodFromReferenceSampler(tex_coord); #endif // FEATURE_SPARSE_TEXTURE return res; } #endif
SparseCoord getSparseCoord(vec2 tex_coord) { 
  SparseCoord res; 
  res.tex_coord = tex_coord; 
  res.dfdx = dFdx(tex_coord); 
  res.dfdy = dFdy(tex_coord); 
#ifdef FEATURE_SPARSE_TEXTURE 
  res.material_lod_mask = material_lod_check_needed ? 
    textureLod(material_lod_mask,tex_coord,0.0).r : 
    0u; 
  res.lod = getLodFromReferenceSampler(tex_coord); 
#endif // FEATURE_SPARSE_TEXTURE 
  return res; 
} 
#endif

Build texture coordinates structure used by textureSparse() sampling function Base level sampling version (can be used if outside fragment shader)

SparseCoord getSparseCoordLod0(vec2 tex_coord) {
SparseCoord res;
res.tex_coord = tex_coord;
res.dfdx = vec2(0.0);
res.dfdy = vec2(0.0);
#ifdef FEATURE_SPARSE_TEXTURE
res.material_lod_mask = material_lod_check_needed ?
textureLod(material_lod_mask,tex_coord,0.0).r :
0u;
res.lod = 0.0;
#endif // FEATURE_SPARSE_TEXTURE
return res;
}
#if defined(SHADER_FRAGMENT)
SparseCoord getSparseCoordLod0(vec2 tex_coord) { SparseCoord res; res.tex_coord = tex_coord; res.dfdx = vec2(0.0); res.dfdy = vec2(0.0); #ifdef FEATURE_SPARSE_TEXTURE res.material_lod_mask = material_lod_check_needed ? textureLod(material_lod_mask,tex_coord,0.0).r : 0u; res.lod = 0.0; #endif // FEATURE_SPARSE_TEXTURE return res; } #if defined(SHADER_FRAGMENT)
SparseCoord getSparseCoordLod0(vec2 tex_coord) { 
  SparseCoord res; 
  res.tex_coord = tex_coord; 
  res.dfdx = vec2(0.0); 
  res.dfdy = vec2(0.0); 
#ifdef FEATURE_SPARSE_TEXTURE 
  res.material_lod_mask = material_lod_check_needed ? 
    textureLod(material_lod_mask,tex_coord,0.0).r : 
    0u; 
  res.lod = 0.0; 
#endif // FEATURE_SPARSE_TEXTURE 
  return res; 
} 
 
#if defined(SHADER_FRAGMENT)

Compute the level-of-detail that would be used to sample from a sparse texture

Climb up mipmap pyramid if texels are missing Returns LoD BEFORE LoD bias applied

float textureSparseQueryLod(SamplerSparse sampler, SparseCoord coord) {
#ifdef FEATURE_SPARSE_TEXTURE
float lodfix = coord.lod;
if (material_lod_check_needed) {
lodfix = getFixedSparseLod(getTextureLodMask(sampler.lod_mask_select, coord.material_lod_mask), lodfix);
}
return lodfix-uvtile_lod_bias;
#else // FEATURE_SPARSE_TEXTURE
return textureQueryLod(sampler.tex, coord.tex_coord).y-uvtile_lod_bias;
#endif // FEATURE_SPARSE_TEXTURE
}
#endif // SHADER_FRAGMENT
float textureSparseQueryLod(SamplerSparse sampler, SparseCoord coord) { #ifdef FEATURE_SPARSE_TEXTURE float lodfix = coord.lod; if (material_lod_check_needed) { lodfix = getFixedSparseLod(getTextureLodMask(sampler.lod_mask_select, coord.material_lod_mask), lodfix); } return lodfix-uvtile_lod_bias; #else // FEATURE_SPARSE_TEXTURE return textureQueryLod(sampler.tex, coord.tex_coord).y-uvtile_lod_bias; #endif // FEATURE_SPARSE_TEXTURE } #endif // SHADER_FRAGMENT
float textureSparseQueryLod(SamplerSparse sampler, SparseCoord coord) { 
#ifdef FEATURE_SPARSE_TEXTURE 
  float lodfix = coord.lod; 
  if (material_lod_check_needed) { 
    lodfix = getFixedSparseLod(getTextureLodMask(sampler.lod_mask_select, coord.material_lod_mask), lodfix); 
  } 
  return lodfix-uvtile_lod_bias; 
#else // FEATURE_SPARSE_TEXTURE 
  return textureQueryLod(sampler.tex, coord.tex_coord).y-uvtile_lod_bias; 
#endif // FEATURE_SPARSE_TEXTURE 
} 
#endif // SHADER_FRAGMENT

Compute the derivatives that would be used to sample from a sparse texture

Climb up mipmap pyramid if texels are missing

void textureSparseQueryGrad(out vec2 dfdx, out vec2 dfdy, SamplerSparse sampler, SparseCoord coord) {
#ifdef FEATURE_SPARSE_TEXTURE
if (material_lod_check_needed) {
float lodfix = getFixedSparseLod(getTextureLodMask(sampler.lod_mask_select, coord.material_lod_mask), coord.lod);
if (coord.lod!=lodfix) {
// Fix dfdx dfdy, take account offset, no more anisotropy
vec2 ddfix = exp2(lodfix-uvtile_lod_bias) * uvtile_inverse_size;
dfdx = vec2(ddfix.x,0.0);
dfdy = vec2(0.0,ddfix.y);
return;
}
}
#endif // FEATURE_SPARSE_TEXTURE
dfdx = coord.dfdx;
dfdy = coord.dfdy;
}
void textureSparseQueryGrad(out vec2 dfdx, out vec2 dfdy, SamplerSparse sampler, SparseCoord coord) { #ifdef FEATURE_SPARSE_TEXTURE if (material_lod_check_needed) { float lodfix = getFixedSparseLod(getTextureLodMask(sampler.lod_mask_select, coord.material_lod_mask), coord.lod); if (coord.lod!=lodfix) { // Fix dfdx dfdy, take account offset, no more anisotropy vec2 ddfix = exp2(lodfix-uvtile_lod_bias) * uvtile_inverse_size; dfdx = vec2(ddfix.x,0.0); dfdy = vec2(0.0,ddfix.y); return; } } #endif // FEATURE_SPARSE_TEXTURE dfdx = coord.dfdx; dfdy = coord.dfdy; }
void textureSparseQueryGrad(out vec2 dfdx, out vec2 dfdy, SamplerSparse sampler, SparseCoord coord) { 
#ifdef FEATURE_SPARSE_TEXTURE 
  if (material_lod_check_needed) { 
    float lodfix = getFixedSparseLod(getTextureLodMask(sampler.lod_mask_select, coord.material_lod_mask), coord.lod); 
    if (coord.lod!=lodfix) { 
      // Fix dfdx dfdy, take account offset, no more anisotropy 
      vec2 ddfix = exp2(lodfix-uvtile_lod_bias) * uvtile_inverse_size; 
      dfdx = vec2(ddfix.x,0.0); 
      dfdy = vec2(0.0,ddfix.y); 
      return; 
    } 
  } 
#endif // FEATURE_SPARSE_TEXTURE 
  dfdx = coord.dfdx; 
  dfdy = coord.dfdy; 
}

Performs a texture lookup on a sparse texture, go up the mipmap levels if necessary

This function replaces the standard texture(sampler2D, vec2) to retrieve texels from a sparse texture

vec4 textureSparse(SamplerSparse sampler, SparseCoord coord) {
vec2 dfdx,dfdy;
textureSparseQueryGrad(dfdx, dfdy, sampler, coord);
return textureGrad(sampler.tex, coord.tex_coord, dfdx, dfdy);
}
vec4 textureSparse(SamplerSparse sampler, SparseCoord coord) { vec2 dfdx,dfdy; textureSparseQueryGrad(dfdx, dfdy, sampler, coord); return textureGrad(sampler.tex, coord.tex_coord, dfdx, dfdy); }
vec4 textureSparse(SamplerSparse sampler, SparseCoord coord) { 
  vec2 dfdx,dfdy; 
  textureSparseQueryGrad(dfdx, dfdy, sampler, coord); 
  return textureGrad(sampler.tex, coord.tex_coord, dfdx, dfdy); 
}

Given a texture, performs an optimized multiple texture lookups with small offsets

We are providing alternatives versions of this helper for up to N=4

void textureSparseOffsets(SamplerSparse sampler, SparseCoord coord, vec2 offsets[N], out vec4 results[N]) {
vec2 dfdx,dfdy;
textureSparseQueryGrad(dfdx, dfdy, sampler, coord);
for(int i = 0; i < N; ++i) {
results[i] = textureGrad(sampler.tex, coord.tex_coord + offsets[i], dfdx, dfdy);
}
}
void textureSparseOffsets(SamplerSparse sampler, SparseCoord coord, vec2 offsets[N], out vec4 results[N]) { vec2 dfdx,dfdy; textureSparseQueryGrad(dfdx, dfdy, sampler, coord); for(int i = 0; i < N; ++i) { results[i] = textureGrad(sampler.tex, coord.tex_coord + offsets[i], dfdx, dfdy); } }
void textureSparseOffsets(SamplerSparse sampler, SparseCoord coord, vec2 offsets[N], out vec4 results[N]) { 
  vec2 dfdx,dfdy; 
  textureSparseQueryGrad(dfdx, dfdy, sampler, coord); 
  for(int i = 0; i < N; ++i) { 
    results[i] = textureGrad(sampler.tex, coord.tex_coord + offsets[i], dfdx, dfdy); 
  } 
} 
 

Get help faster and easier

New user?