116 lines
4.4 KiB
C++
116 lines
4.4 KiB
C++
#include <plot/render/shaders.hpp>
|
|
|
|
// Both pipelines are `#version 330 core` and stay untouched by the upload
|
|
// strategy: the 4.3+ path changes how vertex data reaches the GPU, never the
|
|
// GLSL, so one program pair serves every context the library supports. The
|
|
// declarations here are the contract the render layer's attribute layouts and
|
|
// uniform setters mirror (see program.hpp, vertex_array.hpp, shaders.hpp).
|
|
|
|
namespace plot::render {
|
|
|
|
const char* const kLineVS = R"(#version 330 core
|
|
layout(location = 0) in vec2 a_p0; // per-instance segment start, data space
|
|
layout(location = 1) in vec2 a_p1; // per-instance segment end
|
|
|
|
uniform vec2 u_scale; // data -> pixels
|
|
uniform vec2 u_offset; // pixels
|
|
uniform vec2 u_viewport; // render target size, pixels
|
|
uniform float u_width; // stroke width, pixels
|
|
|
|
out float v_dist; // signed distance to the segment axis, pixels
|
|
out float v_along; // distance travelled from a_p0, pixels
|
|
|
|
void main() {
|
|
vec2 p0 = a_p0 * u_scale + u_offset;
|
|
vec2 p1 = a_p1 * u_scale + u_offset;
|
|
vec2 d = p1 - p0;
|
|
float len = length(d);
|
|
float half_w = u_width * 0.5 + 1.0; // + one pixel of AA fringe
|
|
// A degenerate segment has no direction: pin the normal, and give the quad
|
|
// area by stretching its two ends apart along x. Collapsing all four corners
|
|
// onto p0 would rasterise nothing, and a flat trace staged as p == p (the
|
|
// dense path's min == max buckets) would then silently vanish. When
|
|
// len > 1e-6 the two ends are p0 and p1 exactly as before.
|
|
vec2 n = len > 1e-6 ? vec2(-d.y, d.x) / len : vec2(0.0, 1.0);
|
|
vec2 e0 = len > 1e-6 ? p0 : p0 - vec2(half_w, 0.0);
|
|
vec2 e1 = len > 1e-6 ? p1 : p0 + vec2(half_w, 0.0);
|
|
|
|
// gl_VertexID picks the corner: 0,1 are the sides of the segment's start and
|
|
// 2,3 of its end, which is the order GL_TRIANGLE_STRIP expects.
|
|
float side = (gl_VertexID & 1) == 0 ? -1.0 : 1.0;
|
|
vec2 base = (gl_VertexID & 2) == 0 ? e0 : e1;
|
|
vec2 px = base + n * (side * half_w);
|
|
|
|
// Rect is top-left origin, GL is bottom-left: flip y once, here, and never
|
|
// again in the fragment shader or in a caller.
|
|
vec2 ndc = vec2(px.x / u_viewport.x * 2.0 - 1.0, 1.0 - px.y / u_viewport.y * 2.0);
|
|
gl_Position = vec4(ndc, 0.0, 1.0);
|
|
|
|
v_dist = side * half_w;
|
|
v_along = (gl_VertexID & 2) == 0 ? 0.0 : len;
|
|
}
|
|
)";
|
|
|
|
const char* const kLineFS = R"(#version 330 core
|
|
in float v_dist; // signed distance to the segment axis, pixels
|
|
|
|
uniform float u_width; // stroke width, pixels
|
|
uniform vec4 u_color;
|
|
|
|
out vec4 o_color;
|
|
|
|
void main() {
|
|
float half_w = u_width * 0.5;
|
|
float d = abs(v_dist);
|
|
if (d > half_w + 1.0) {
|
|
discard; // outside the fringe; nothing to blend
|
|
}
|
|
// Ramp over the last pixel of the half width so a thin stroke keeps its
|
|
// weight instead of aliasing into a dashed line.
|
|
float alpha = 1.0 - smoothstep(half_w - 0.5, half_w + 0.5, d);
|
|
o_color = vec4(u_color.rgb, u_color.a * alpha);
|
|
}
|
|
)";
|
|
|
|
const char* const kTextVS = R"(#version 330 core
|
|
layout(location = 0) in vec2 a_pos; // per-instance glyph quad top-left, pixels
|
|
layout(location = 1) in vec2 a_uv; // per-instance glyph atlas texel origin
|
|
|
|
uniform vec2 u_viewport; // render target size, pixels
|
|
uniform vec2 u_px_range; // pixels per glyph cell on screen
|
|
|
|
out vec2 v_texel; // atlas texel coordinate of this fragment
|
|
|
|
const vec2 kGlyphTexels = vec2(8.0); // BitmapFont::kGlyphPx
|
|
|
|
void main() {
|
|
vec2 corner = vec2(float(gl_VertexID & 1), float((gl_VertexID >> 1) & 1));
|
|
vec2 px = a_pos + corner * u_px_range;
|
|
// Rect is top-left origin, GL is bottom-left: the one and only y flip.
|
|
vec2 ndc = vec2(px.x / u_viewport.x * 2.0 - 1.0, 1.0 - px.y / u_viewport.y * 2.0);
|
|
gl_Position = vec4(ndc, 0.0, 1.0);
|
|
v_texel = a_uv + corner * kGlyphTexels;
|
|
}
|
|
)";
|
|
|
|
const char* const kTextFS = R"(#version 330 core
|
|
in vec2 v_texel; // atlas texel coordinate of this fragment
|
|
|
|
uniform sampler2D u_atlas; // GL_R8 glyph atlas, GL_NEAREST
|
|
uniform vec2 u_tex_size; // atlas size in texels
|
|
uniform vec4 u_color;
|
|
|
|
out vec4 o_color;
|
|
|
|
void main() {
|
|
// Sample the centre of the texel the fragment lands in. GL_NEAREST alone
|
|
// would tie at a texel boundary, where the driver may pick the neighbouring
|
|
// glyph cell; rounding to a centre removes the tie by construction.
|
|
vec2 texel = clamp(floor(v_texel), vec2(0.0), u_tex_size - vec2(1.0));
|
|
float coverage = texture(u_atlas, (texel + vec2(0.5)) / u_tex_size).r;
|
|
o_color = vec4(u_color.rgb, u_color.a * coverage);
|
|
}
|
|
)";
|
|
|
|
} // namespace plot::render
|