aframe
Version:
A web framework for building virtual reality experiences.
114 lines (96 loc) • 3.23 kB
JavaScript
import { registerShader } from '../core/shader.js';
import * as THREE from 'three';
var VERTEX_SHADER = [
'#include <common>',
'#include <fog_pars_vertex>',
'#include <logdepthbuf_pars_vertex>',
'out vec2 vUV;',
'void main(void) {',
' vUV = uv;',
' #include <begin_vertex>',
' #include <project_vertex>',
' #include <logdepthbuf_vertex>',
' #include <fog_vertex>',
'}'
].join('\n');
var FRAGMENT_SHADER = [
'#include <common>',
'#include <fog_pars_fragment>',
'#include <logdepthbuf_pars_fragment>',
'uniform float alphaTest;',
'uniform float opacity;',
'uniform sampler2D map;',
'uniform vec3 color;',
'in vec2 vUV;',
'float contour(float width, float value) {',
' return smoothstep(0.5 - value, 0.5 + value, width);',
'}',
// FIXME: Experimentally determined constants.
'#define BIG_ENOUGH 0.001',
'#define MODIFIED_ALPHATEST (0.02 * isBigEnough / BIG_ENOUGH)',
'void main() {',
' vec2 uv = vUV;',
' vec4 texColor = texture(map, uv);',
' float dist = texColor.a;',
' float width = fwidth(dist);',
' float alpha = contour(dist, width);',
' float dscale = 0.353505;',
' vec2 duv = dscale * (dFdx(uv) + dFdy(uv));',
' float isBigEnough = max(abs(duv.x), abs(duv.y));',
// When texel is too small, blend raw alpha value rather than supersampling.
// FIXME: experimentally determined constant
' if (isBigEnough > BIG_ENOUGH) {',
' float ratio = BIG_ENOUGH / isBigEnough;',
' alpha = ratio * alpha + (1.0 - ratio) * dist;',
' }',
// Otherwise do weighted supersampling.
// FIXME: why this weighting?
' if (isBigEnough <= BIG_ENOUGH) {',
' vec4 box = vec4 (uv - duv, uv + duv);',
' alpha = (alpha + 0.5 * (',
' contour(texture(map, box.xy).a, width)',
' + contour(texture(map, box.zw).a, width)',
' + contour(texture(map, box.xw).a, width)',
' + contour(texture(map, box.zy).a, width)',
' )) / 3.0;',
' }',
// Do modified alpha test.
' if (alpha < alphaTest * MODIFIED_ALPHATEST) { discard; return; }',
' gl_FragColor = vec4(color, opacity * alpha);',
' #include <logdepthbuf_fragment>',
' #include <tonemapping_fragment>',
' #include <colorspace_fragment>',
' #include <fog_fragment>',
'}'
].join('\n');
/**
* Signed distance field.
* Used by text component.
*/
export var Shader = registerShader('sdf', {
schema: {
alphaTest: {type: 'number', is: 'uniform', default: 0.5},
color: {type: 'color', is: 'uniform', default: 'white'},
map: {type: 'map', is: 'uniform'},
opacity: {type: 'number', is: 'uniform', default: 1.0}
},
vertexShader: VERTEX_SHADER,
fragmentShader: FRAGMENT_SHADER,
init: function () {
this.uniforms = this.initUniforms();
// When using the WebGPURenderer there is no UniformsLib or UniformsUtils.
if (THREE.UniformsUtils) {
this.uniforms = THREE.UniformsUtils.merge([
THREE.UniformsLib.fog,
this.uniforms
]);
}
this.material = new THREE.ShaderMaterial({
uniforms: this.uniforms,
vertexShader: this.vertexShader,
fragmentShader: this.fragmentShader,
fog: true
});
return this.material;
}
});