1
votes

I would like to draw a large number of quads, each representing a small segment of a curve, so that the curve appears smooth but is really made up of just linear pieces. I also want to be able to set both the thickness of the line and a thickness for a blended part of the line, such that in the blended region the line linearly loses its alpha component. Ideally these lines would be able to be oriented in any way in a 3D scene, though they will most often be in a plane facing the camera.

The problem is that it is that it's hard to blend the line with the background without having the overlapping segments interact with each other, becoming brighter where many overlap. I only want 1 fragment of the line to blend with the background, but it's hard to know which fragment is the correct one if you render each quad separately.

Is there an efficient way to do something like this? Or do I have to do some kind of pre-processing to turn the line into 1 mesh instead of rendering it as a bunch of quads?

[Here is a picture of the problem with the lines intersecting each other & not blending correctly]

http://i.imgur.com/8cYm1zK.png

Here is a related problem, but it's different because I want to blend the quads so that they fall off linearly. If you implement the solution from this thread, then the blending will look incorrect.

OpenGL blending function to elminate primitive overlap but maintain overall opacity

3

3 Answers

3
votes

It looks like you are rendering your lines as simple overlapping rectangles, like this:

lines as simple overlapping rectangles

For perfect results, you need to render an corner cap like this, with nothing overlapping:

rounded corner cap

Think about how you'd construct those orange points. You can approximate the circular cap with a triangle fan. You can also make sharp or flat corners depending on your preference.

In response to @GuyRT's answer: GL_MAX blend mode would look like this:

intersection of feathered lines in MAX blend mode

2
votes

You could draw your lines into an FBO using glBlendEquation( GL_MAX ), then combine the result with your background using glBlendEquation( GL_FUNC_ADD ) (the default blending equation).

A better option (in light of japreiss's and the OP's comments), would be to draw just the bright part of the lines into an FBO, then blur the result (firstly horizontally into another buffer, then again vertically into the first buffer), before compositing onto the background.

0
votes

Pseudo-code

fn draw_segments(a: Vec2, b: Vec2, c: Vec2, color: Color, thickness: f32) {
    // goal: draw segment AB and segment BC using triangle primitive.
    let k = vec2(0.5 * thickness, 0.5 * thickness);

    // proj half thickness \\
    let p_ab = (b - a).normalize() * k;
    let p_bc = (c - b).normalize() * k;
    let p_ac = (c - a).normalize() * k;
    // rej half thickness _|_
    let r_ab = p_ab.perp();
    let r_bc = p_bc.perp();
    let r_ac = p_ac.perp();
    // b_s is the shared point south from b used as join
    let b_s = b - r_ac;
    let mid_b_s_from_ab = (b + r_ab - p_ab).lerp(b_s, 0.5);
    let mid_b_s_from_bc = (b + r_bc + p_bc).lerp(b_s, 0.5);

    // main ab quad
    add_triangle(a + r_ab - p_ab, b + r_ab - p_ab, a - r_ab - p_ab, Color::RED);
    add_triangle(b + r_ab - p_ab, a - r_ab - p_ab, b_s, Color::WHITE);
    // corner ab quad
    add_triangle(b + r_ab - p_ab, b + r_ab, mid_b_s_from_ab, Color::GREEN);
    add_triangle(b + r_ab, mid_b_s_from_ab, b, Color::BLUE);
    // corner ab triangle
    add_triangle(mid_b_s_from_ab, b, b_s, Color::RED);
    // corner 'arc'
    add_triangle(b + r_ab, b, b + r_bc, Color::WHITE);
    // corner bc triangle
    add_triangle(mid_b_s_from_bc, b, b_s, Color::WHITE);
    // corner bc quad
    add_triangle(b + r_bc, b + r_bc + p_bc, b, Color::BLUE);
    add_triangle(b + r_bc + p_bc, b, mid_b_s_from_bc, Color::GREEN);
    // main bc quad
    add_triangle(b + r_bc + p_bc, c + r_bc + p_bc, b_s, Color::RED);
    add_triangle(c + r_bc + p_bc, b_s, c - r_bc + p_bc, Color::WHITE);
}

Output