diff --git a/data/ofxGPUFont/shaders/DEBUG_ES3/background.frag b/data/ofxGPUFont/shaders/DEBUG_ES3/background.frag new file mode 100644 index 0000000..6d8baa7 --- /dev/null +++ b/data/ofxGPUFont/shaders/DEBUG_ES3/background.frag @@ -0,0 +1,12 @@ +#version 330 core + +in vec2 position; + +out vec3 color; + +void main() { + float t = (position.y + 1.0) / 2.0; + vec3 bottom = vec3(75.0, 55.0, 201.0) / 255.0; + vec3 top = vec3(0.0, 12.0, 0.0) / 255.0; + color = mix(bottom, top, t); +} diff --git a/data/ofxGPUFont/shaders/DEBUG_ES3/background.vert b/data/ofxGPUFont/shaders/DEBUG_ES3/background.vert new file mode 100644 index 0000000..c6b01be --- /dev/null +++ b/data/ofxGPUFont/shaders/DEBUG_ES3/background.vert @@ -0,0 +1,15 @@ +#version 330 core + +const vec2 vertices[4] = vec2[4]( + vec2(-1.0, -1.0), + vec2( 1.0, -1.0), + vec2(-1.0, 1.0), + vec2( 1.0, 1.0) +); + +out vec2 position; + +void main() { + position = vertices[gl_VertexID]; + gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); +} diff --git a/data/ofxGPUFont/shaders/DEBUG_ES3/font.frag b/data/ofxGPUFont/shaders/DEBUG_ES3/font.frag new file mode 100644 index 0000000..bb956ef --- /dev/null +++ b/data/ofxGPUFont/shaders/DEBUG_ES3/font.frag @@ -0,0 +1,182 @@ +#version 300 es +precision highp float; +precision highp isampler2D; +precision highp sampler2D; + +// Based on: http://wdobbie.com/post/gpu-text-rendering-with-vector-textures/ + +struct Glyph { + int start, count; +}; + +struct Curve { + vec2 p0, p1, p2; +}; + +uniform isampler2D glyphs; +uniform sampler2D curves; +uniform sampler2D iChannel0; +uniform vec4 color; + +// Controls for debugging and exploring: + +// Size of the window (in pixels) used for 1-dimensional anti-aliasing along each rays. +// 0 - no anti-aliasing +// 1 - normal anti-aliasing +// >=2 - exaggerated effect +uniform float antiAliasingWindowSize; + +// Enable a second ray along the y-axis to achieve 2-dimensional anti-aliasing. +uniform bool enableSuperSamplingAntiAliasing; + +// Draw control points for debugging (green - on curve, magenta - off curve). +uniform bool enableControlPointsVisualization; + + +in vec2 uv; +flat in int bufferIndex; + +out vec4 result; + +Glyph loadGlyph(int index) { + Glyph result; + ivec2 data = texelFetch(glyphs, ivec2(index, 0), 0).xy; + result.start = data.x; + result.count = data.y; + return result; +} + +Curve loadCurve(int index) { + Curve result; + result.p0 = texelFetch(curves, ivec2(3*index+0, 0), 0).xy; + result.p1 = texelFetch(curves, ivec2(3*index+1, 0), 0).xy; + result.p2 = texelFetch(curves, ivec2(3*index+2, 0), 0).xy; + return result; +} + +float computeCoverage(float inverseDiameter, vec2 p0, vec2 p1, vec2 p2) { + if (p0.y > 0.0 && p1.y > 0.0 && p2.y > 0.0) return 0.0; + if (p0.y < 0.0 && p1.y < 0.0 && p2.y < 0.0) return 0.0; + + // Note: Simplified from abc formula by extracting a factor of (-2) from b. + vec2 a = p0 - 2.0*p1 + p2; + vec2 b = p0 - p1; + vec2 c = p0; + + float t0, t1; + if (abs(a.y) >= 1e-5) { + // Quadratic segment, solve abc formula to find roots. + float radicand = b.y*b.y - a.y*c.y; + if (radicand <= 0.0) return 0.0; + + float s = sqrt(radicand); + t0 = (b.y - s) / a.y; + t1 = (b.y + s) / a.y; + } else { + // Linear segment, avoid division by a.y, which is near zero. + // There is only one root, so we have to decide which variable to + // assign it to based on the direction of the segment, to ensure that + // the ray always exits the shape at t0 and enters at t1. For a + // quadratic segment this works 'automatically', see readme. + float t = p0.y / (p0.y - p2.y); + if (p0.y < p2.y) { + t0 = -1.0; + t1 = t; + } else { + t0 = t; + t1 = -1.0; + } + } + + float alpha = 0.0; + + if (t0 >= 0.0 && t0 < 1.0) { + float x = (a.x*t0 - 2.0*b.x)*t0 + c.x; + alpha += clamp(x * inverseDiameter + 0.5, 0.0, 1.0); + } + + if (t1 >= 0.0 && t1 < 1.0) { + float x = (a.x*t1 - 2.0*b.x)*t1 + c.x; + alpha -= clamp(x * inverseDiameter + 0.5, 0.0, 1.0); + } + + return alpha; +} + +vec2 rotate(vec2 v) { + return vec2(v.y, -v.x); +} + +void main() { + //vec4 debug = texture(curves, vec2(uv.x, 0.5)); + //ivec4 debug = texelFetch(glyphs, ivec2(uv.x * float(textureSize(glyphs, 0).x), 0), 0); + //result = vec4(debug.rgb, 1.0); + + //Glyph gly = loadGlyph(bufferIndex); + //result = vec4((float(gly.start) / 1883.0), (float(gly.count) / 42.0), 0.0, 1.0); + + // verify bufferIndex [x] + //result = vec4((float(bufferIndex) / 100.0), 0.0, 0.0, 1.0); + //return; + + float alpha = 0.0; + + // Inverse of the diameter of a pixel in uv units for anti-aliasing. + vec2 inverseDiameter = 1.0 / (antiAliasingWindowSize * fwidth(uv)); + + Glyph glyph = loadGlyph(bufferIndex); + for (int i = 0; i < glyph.count; i++) { + Curve curve = loadCurve(glyph.start + i); + + vec2 p0 = curve.p0 - uv; + vec2 p1 = curve.p1 - uv; + vec2 p2 = curve.p2 - uv; + + alpha += computeCoverage(inverseDiameter.x, p0, p1, p2); + if (enableSuperSamplingAntiAliasing) { + alpha += computeCoverage(inverseDiameter.y, rotate(p0), rotate(p1), rotate(p2)); + } + } + // DEBUG + //if (uv.x > 0.5) { + //ivec2 ts = textureSize(curves,0); + //float w = float(ts.x); + //float h = float(ts.y); + ////float green = (float(glyph.count) / 27.0); + //float green = float(w) / (3.0 * 27.0); + //result = vec4(0.0,h * 0.5,0.0,1.0); + //} else { + //result = vec4(0.0,0.5,0.0,1.0); + //} + //return; + + if (enableSuperSamplingAntiAliasing) { + alpha *= 0.5; + } + + alpha = clamp(alpha, 0.0, 1.0); + result = color * alpha; + + if (enableControlPointsVisualization) { + // Visualize control points. + vec2 fw = fwidth(uv); + float r = 4.0 * 0.5 * (fw.x + fw.y); + for (int i = 0; i < glyph.count; i++) { + Curve curve = loadCurve(glyph.start + i); + + vec2 p0 = curve.p0 - uv; + vec2 p1 = curve.p1 - uv; + vec2 p2 = curve.p2 - uv; + + if (dot(p0, p0) < r*r || dot(p2, p2) < r*r) { + result = vec4(0.0, 1.0, 0.0, 1.0); + return; + } + + if (dot(p1, p1) < r*r) { + result = vec4(1.0, 0.0, 1.0, 1.0); + return; + } + } + } +} diff --git a/data/ofxGPUFont/shaders/DEBUG_ES3/font.vert b/data/ofxGPUFont/shaders/DEBUG_ES3/font.vert new file mode 100644 index 0000000..96fc5ae --- /dev/null +++ b/data/ofxGPUFont/shaders/DEBUG_ES3/font.vert @@ -0,0 +1,21 @@ +#version 300 es +precision highp float; +precision highp isampler2D; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; +uniform float z; + +layout (location = 0) in vec2 vertexPosition; +layout (location = 1) in vec2 vertexUV; +layout (location = 2) in int vertexIndex; + +out vec2 uv; +flat out int bufferIndex; + +void main() { + gl_Position = projection * view * model * vec4(vertexPosition, z, 1.0); + uv = vertexUV; + bufferIndex = vertexIndex; +} diff --git a/data/ofxGPUFont/shaders/ES3/background.frag b/data/ofxGPUFont/shaders/ES3/background.frag new file mode 100644 index 0000000..6d8baa7 --- /dev/null +++ b/data/ofxGPUFont/shaders/ES3/background.frag @@ -0,0 +1,12 @@ +#version 330 core + +in vec2 position; + +out vec3 color; + +void main() { + float t = (position.y + 1.0) / 2.0; + vec3 bottom = vec3(75.0, 55.0, 201.0) / 255.0; + vec3 top = vec3(0.0, 12.0, 0.0) / 255.0; + color = mix(bottom, top, t); +} diff --git a/data/ofxGPUFont/shaders/ES3/background.vert b/data/ofxGPUFont/shaders/ES3/background.vert new file mode 100644 index 0000000..c6b01be --- /dev/null +++ b/data/ofxGPUFont/shaders/ES3/background.vert @@ -0,0 +1,15 @@ +#version 330 core + +const vec2 vertices[4] = vec2[4]( + vec2(-1.0, -1.0), + vec2( 1.0, -1.0), + vec2(-1.0, 1.0), + vec2( 1.0, 1.0) +); + +out vec2 position; + +void main() { + position = vertices[gl_VertexID]; + gl_Position = vec4(vertices[gl_VertexID], 0.0, 1.0); +} diff --git a/data/ofxGPUFont/shaders/ES3/font.frag b/data/ofxGPUFont/shaders/ES3/font.frag new file mode 100644 index 0000000..4a3eee0 --- /dev/null +++ b/data/ofxGPUFont/shaders/ES3/font.frag @@ -0,0 +1,171 @@ +#version 300 es +precision highp float; +precision highp isampler2D; +precision highp sampler2D; + +// Based on: http://wdobbie.com/post/gpu-text-rendering-with-vector-textures/ + +struct Glyph { + int start, count; +}; + +struct Curve { + vec2 p0, p1, p2; +}; + +uniform isampler2D glyphs; +uniform sampler2D curves; +uniform vec4 color; + + +// Controls for debugging and exploring: + +// Size of the window (in pixels) used for 1-dimensional anti-aliasing along each rays. +// 0 - no anti-aliasing +// 1 - normal anti-aliasing +// >=2 - exaggerated effect +uniform float antiAliasingWindowSize; + +// Enable a second ray along the y-axis to achieve 2-dimensional anti-aliasing. +uniform bool enableSuperSamplingAntiAliasing; + +// Draw control points for debugging (green - on curve, magenta - off curve). +uniform bool enableControlPointsVisualization; + + +in vec2 uv; +flat in int bufferIndex; + +out vec4 result; + +Glyph loadGlyph(int index) { + Glyph result; + ivec2 data = texelFetch(glyphs, ivec2(index, 0), 0).xy; + result.start = data.x; + result.count = data.y; + return result; +} + +Curve loadCurve(int index) { + Curve result; + result.p0 = texelFetch(curves, ivec2(3*index+0, 0), 0).xy; + result.p1 = texelFetch(curves, ivec2(3*index+1, 0), 0).xy; + result.p2 = texelFetch(curves, ivec2(3*index+2, 0), 0).xy; + return result; +} + +float computeCoverage(float inverseDiameter, vec2 p0, vec2 p1, vec2 p2) { + if (p0.y > 0.0 && p1.y > 0.0 && p2.y > 0.0) return 0.0; + if (p0.y < 0.0 && p1.y < 0.0 && p2.y < 0.0) return 0.0; + + // Note: Simplified from abc formula by extracting a factor of (-2) from b. + vec2 a = p0 - 2.0*p1 + p2; + vec2 b = p0 - p1; + vec2 c = p0; + + float t0, t1; + if (abs(a.y) >= 1e-5) { + // Quadratic segment, solve abc formula to find roots. + float radicand = b.y*b.y - a.y*c.y; + if (radicand <= 0.0) return 0.0; + + float s = sqrt(radicand); + t0 = (b.y - s) / a.y; + t1 = (b.y + s) / a.y; + } else { + // Linear segment, avoid division by a.y, which is near zero. + // There is only one root, so we have to decide which variable to + // assign it to based on the direction of the segment, to ensure that + // the ray always exits the shape at t0 and enters at t1. For a + // quadratic segment this works 'automatically', see readme. + float t = p0.y / (p0.y - p2.y); + if (p0.y < p2.y) { + t0 = -1.0; + t1 = t; + } else { + t0 = t; + t1 = -1.0; + } + } + + float alpha = 0.0; + + if (t0 >= 0.0 && t0 < 1.0) { + float x = (a.x*t0 - 2.0*b.x)*t0 + c.x; + alpha += clamp(x * inverseDiameter + 0.5, 0.0, 1.0); + } + + if (t1 >= 0.0 && t1 < 1.0) { + float x = (a.x*t1 - 2.0*b.x)*t1 + c.x; + alpha -= clamp(x * inverseDiameter + 0.5, 0.0, 1.0); + } + + return alpha; +} + +vec2 rotate(vec2 v) { + return vec2(v.y, -v.x); +} + +void main() { + //vec4 debug = texture(curves, vec2(uv.x, 0.5)); + //ivec4 debug = texture(glyphs, vec2(uv.x, 0.5)); + ////ivec4 debug = texelFetch(glyphs, ivec2(uv.x * float(textureSize(glyphs, 0).x), 0), 0); + //result = vec4(debug.rgb, 1.0); + + Glyph gly = loadGlyph(bufferIndex); + result = vec4((float(gly.start) / 1883.0), (float(gly.count) / 42.0), 0.0, 1.0); + + // verify bufferIndex [x] + //result = vec4((float(bufferIndex) / 100.0), 0.0, 0.0, 1.0); + return; + + float alpha = 0.0; + + // Inverse of the diameter of a pixel in uv units for anti-aliasing. + vec2 inverseDiameter = 1.0 / (antiAliasingWindowSize * fwidth(uv)); + + Glyph glyph = loadGlyph(bufferIndex); + for (int i = 0; i < glyph.count; i++) { + Curve curve = loadCurve(glyph.start + i); + + vec2 p0 = curve.p0 - uv; + vec2 p1 = curve.p1 - uv; + vec2 p2 = curve.p2 - uv; + + alpha += computeCoverage(inverseDiameter.x, p0, p1, p2); + if (enableSuperSamplingAntiAliasing) { + alpha += computeCoverage(inverseDiameter.y, rotate(p0), rotate(p1), rotate(p2)); + } + } + + if (enableSuperSamplingAntiAliasing) { + alpha *= 0.5; + } + + alpha = clamp(alpha, 0.0, 1.0); + result = color * alpha; + + if (enableControlPointsVisualization) { + // Visualize control points. + vec2 fw = fwidth(uv); + float r = 4.0 * 0.5 * (fw.x + fw.y); + for (int i = 0; i < glyph.count; i++) { + Curve curve = loadCurve(glyph.start + i); + + vec2 p0 = curve.p0 - uv; + vec2 p1 = curve.p1 - uv; + vec2 p2 = curve.p2 - uv; + + if (dot(p0, p0) < r*r || dot(p2, p2) < r*r) { + result = vec4(0.0, 1.0, 0.0, 1.0); + return; + } + + if (dot(p1, p1) < r*r) { + result = vec4(1.0, 0.0, 1.0, 1.0); + return; + } + } + } +} diff --git a/data/ofxGPUFont/shaders/ES3/font.vert b/data/ofxGPUFont/shaders/ES3/font.vert new file mode 100644 index 0000000..96fc5ae --- /dev/null +++ b/data/ofxGPUFont/shaders/ES3/font.vert @@ -0,0 +1,21 @@ +#version 300 es +precision highp float; +precision highp isampler2D; + +uniform mat4 projection; +uniform mat4 view; +uniform mat4 model; +uniform float z; + +layout (location = 0) in vec2 vertexPosition; +layout (location = 1) in vec2 vertexUV; +layout (location = 2) in int vertexIndex; + +out vec2 uv; +flat out int bufferIndex; + +void main() { + gl_Position = projection * view * model * vec4(vertexPosition, z, 1.0); + uv = vertexUV; + bufferIndex = vertexIndex; +}