@pmndrs/uikit
Version:
Build performant 3D user interfaces with Three.js and yoga.
78 lines (70 loc) • 3.16 kB
JavaScript
import { MeshBasicMaterial } from 'three';
export class InstancedGlyphMaterial extends MeshBasicMaterial {
constructor(font) {
super({
transparent: true,
depthWrite: false,
toneMapped: false,
});
this.onBeforeCompile = (parameters) => {
parameters.uniforms.fontPage = { value: font.page };
parameters.uniforms.pageSize = { value: [font.pageWidth, font.pageHeight] };
parameters.uniforms.distanceRange = { value: font.distanceRange };
parameters.vertexShader =
`attribute vec4 instanceUVOffset;
varying vec2 fontUv;
attribute vec4 instanceRGBA;
varying vec4 rgba;
attribute mat4 instanceClipping;
varying mat4 clipping;
varying vec3 localPosition;
` + parameters.vertexShader;
parameters.vertexShader = parameters.vertexShader.replace('#include <uv_vertex>', `
fontUv = instanceUVOffset.xy + uv * instanceUVOffset.zw;
rgba = instanceRGBA;
clipping = instanceClipping;
localPosition = (instanceMatrix * vec4(position, 1.0)).xyz;`);
parameters.fragmentShader =
`uniform sampler2D fontPage;
uniform vec2 pageSize;
uniform int distanceRange;
varying vec2 fontUv;
varying vec4 rgba;
varying mat4 clipping;
varying vec3 localPosition;
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
float getDistance() {
vec3 msdf = texture(fontPage, fontUv).rgb;
return median(msdf.r, msdf.g, msdf.b);
}
` + parameters.fragmentShader;
parameters.fragmentShader = parameters.fragmentShader.replace('#include <map_fragment>', `
vec4 plane;
float distanceToPlane, distanceGradient;
float clipOpacity = 1.0;
for(int i = 0; i < 4; i++) {
plane = clipping[ i ];
distanceToPlane = dot( localPosition, plane.xyz ) + plane.w;
distanceGradient = fwidth( distanceToPlane ) / 2.0;
clipOpacity *= smoothstep( - distanceGradient, distanceGradient, distanceToPlane );
if ( clipOpacity == 0.0 ) discard;
}
// Distance to the edge of the glyph in texels.
float dist = (getDistance() - 0.5) * float(distanceRange);
// Calculate the antialiasing distance based on the number of texels per screen pixel.
float aaDist = length(fwidth(fontUv * pageSize)) * 0.5;
// Clamp the antialiasing distance to avoid excessive blurring.
aaDist = clamp(aaDist, 0.0, float(distanceRange) * 0.5);
float alpha = smoothstep(-aaDist, aaDist, dist);
if (alpha <= 0.0) discard;
// Apply gamma correction to improve text appearance.
float gamma = 1.3;
alpha = pow(alpha, 1.0 / gamma);
diffuseColor.a *= clipOpacity * alpha;
diffuseColor *= rgba;
`);
};
}
}