Skip to content

Commit

Permalink
feat(troika-three-text): pack SDFs using all 4 color channels, to inc…
Browse files Browse the repository at this point in the history
…rease max glyphs in a texture
  • Loading branch information
lojjic committed Feb 8, 2021
1 parent b54fd6f commit d236caf
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 13 deletions.
18 changes: 11 additions & 7 deletions packages/troika-three-text/src/TextBuilder.js
@@ -1,4 +1,4 @@
import { Color, DataTexture, LinearFilter, LuminanceFormat } from 'three'
import { Color, DataTexture, LinearFilter, RGBAFormat } from 'three'
import { defineWorkerModule, ThenableWorkerModule } from 'troika-worker-utils'
import { createSDFGenerator } from './worker/SDFGenerator.js'
import { createFontProcessor } from './worker/FontProcessor.js'
Expand Down Expand Up @@ -144,10 +144,10 @@ function getTextRenderInfo(args, callback) {
if (!atlas) {
atlas = atlases[atlasKey] = {
sdfTexture: new DataTexture(
new Uint8Array(sdfGlyphSize * textureWidth),
new Uint8Array(sdfGlyphSize * textureWidth * 4),
textureWidth,
sdfGlyphSize,
LuminanceFormat,
RGBAFormat,
undefined,
undefined,
undefined,
Expand Down Expand Up @@ -175,14 +175,18 @@ function getTextRenderInfo(args, callback) {
}

// Insert the new glyph's data into the full texture image at the correct offsets
// Glyphs are packed sequentially into the R,G,B,A channels of a square, advancing
// to the next square every 4 glyphs.
const squareIndex = Math.floor(atlasIndex / 4)
const cols = texImg.width / sdfGlyphSize
const baseStartIndex = texImg.width * sdfGlyphSize * Math.floor(atlasIndex / cols) //full rows
+ (atlasIndex % cols) * sdfGlyphSize //partial row
const baseStartIndex = Math.floor(squareIndex / cols) * texImg.width * sdfGlyphSize * 4 //full rows
+ (squareIndex % cols) * sdfGlyphSize * 4 //partial row
+ (atlasIndex % 4) //color channel
for (let y = 0; y < sdfGlyphSize; y++) {
const srcStartIndex = y * sdfGlyphSize
const rowStartIndex = baseStartIndex + (y * texImg.width)
const rowStartIndex = baseStartIndex + (y * texImg.width * 4)
for (let x = 0; x < sdfGlyphSize; x++) {
texImg.data[rowStartIndex + x] = textureData[srcStartIndex + x]
texImg.data[rowStartIndex + x * 4] = textureData[srcStartIndex + x]
}
}
})
Expand Down
17 changes: 11 additions & 6 deletions packages/troika-three-text/src/TextDerivedMaterial.js
Expand Up @@ -17,6 +17,7 @@ attribute float aTroikaGlyphIndex;
attribute vec3 aTroikaGlyphColor;
varying vec2 vTroikaGlyphUV;
varying vec4 vTroikaTextureUVBounds;
varying float vTroikaTextureChannel;
varying vec3 vTroikaGlyphColor;
varying vec2 vTroikaGlyphDimensions;
`
Expand Down Expand Up @@ -53,12 +54,13 @@ ${''/* NOTE: it seems important to calculate the glyph's bounding texture UVs he
on some glyphs (those in the leftmost texture column) on some systems. The exact reason
isn't understood but doing this here, then mix()-ing in the fragment shader, seems to work. */}
float txCols = uTroikaSDFTextureSize.x / uTroikaSDFGlyphSize;
vec2 txUvPerGlyph = uTroikaSDFGlyphSize / uTroikaSDFTextureSize;
vec2 txStartUV = txUvPerGlyph * vec2(
mod(aTroikaGlyphIndex, txCols),
floor(aTroikaGlyphIndex / txCols)
vec2 txUvPerSquare = uTroikaSDFGlyphSize / uTroikaSDFTextureSize;
vec2 txStartUV = txUvPerSquare * vec2(
mod(floor(aTroikaGlyphIndex / 4.0), txCols),
floor(floor(aTroikaGlyphIndex / 4.0) / txCols)
);
vTroikaTextureUVBounds = vec4(txStartUV, vec2(txStartUV) + txUvPerGlyph);
vTroikaTextureUVBounds = vec4(txStartUV, vec2(txStartUV) + txUvPerSquare);
vTroikaTextureChannel = mod(aTroikaGlyphIndex, 4.0);
`

// language=GLSL
Expand All @@ -77,6 +79,7 @@ uniform float uTroikaStrokeOpacity;
uniform bool uTroikaSDFDebug;
varying vec2 vTroikaGlyphUV;
varying vec4 vTroikaTextureUVBounds;
varying float vTroikaTextureChannel;
varying vec2 vTroikaGlyphDimensions;
float troikaSdfValueToSignedDistance(float alpha) {
Expand All @@ -93,7 +96,9 @@ float troikaSdfValueToSignedDistance(float alpha) {
float troikaGlyphUvToSdfValue(vec2 glyphUV) {
vec2 textureUV = mix(vTroikaTextureUVBounds.xy, vTroikaTextureUVBounds.zw, glyphUV);
return texture2D(uTroikaSDFTexture, textureUV).r;
vec4 rgba = texture2D(uTroikaSDFTexture, textureUV);
float ch = round(vTroikaTextureChannel);
return ch == 0.0 ? rgba.r : ch == 1.0 ? rgba.g : ch == 2.0 ? rgba.b : rgba.a;
}
float troikaGlyphUvToDistance(vec2 uv) {
Expand Down

0 comments on commit d236caf

Please sign in to comment.