What is the best way to draw a variable width line without using glLineWidth? Just draw a rectangle? Various parallel lines? None of the above?
7 Answers
You can draw two triangles:
// Draws a line between (x1,y1) - (x2,y2) with a start thickness of t1 and
// end thickness t2.
void DrawLine(float x1, float y1, float x2, float y2, float t1, float t2)
{
float angle = atan2(y2 - y1, x2 - x1);
float t2sina1 = t1 / 2 * sin(angle);
float t2cosa1 = t1 / 2 * cos(angle);
float t2sina2 = t2 / 2 * sin(angle);
float t2cosa2 = t2 / 2 * cos(angle);
glBegin(GL_TRIANGLES);
glVertex2f(x1 + t2sina1, y1 - t2cosa1);
glVertex2f(x2 + t2sina2, y2 - t2cosa2);
glVertex2f(x2 - t2sina2, y2 + t2cosa2);
glVertex2f(x2 - t2sina2, y2 + t2cosa2);
glVertex2f(x1 - t2sina1, y1 + t2cosa1);
glVertex2f(x1 + t2sina1, y1 - t2cosa1);
glEnd();
}

- 10,409
- 8
- 46
- 56
-
4This is way too complicated! Too much trigonometry where it's not needed too. – PierreBdR Jun 04 '09 at 09:35
-
It turned out this is not what the OP wanted (see his answer below). But I'd love to see a simpler algorithm, if you would care to share it. – Ozgur Ozcitak Jun 05 '09 at 23:39
-
I added a trig free version here now – bobobobo Jul 22 '10 at 23:43
Ok, how about this: (Ozgar)
A / \ / \ . p1 \ / \ / D B - .p2 - - - C
So AB is width1
and CD is width2
.
Then,
// find line between p1 and p2
Vector p1p2 = p2 - p1 ;
// find a perpendicular
Vector perp = p1p2.perpendicular().normalize()
// Walk from p1 to A
Vector A = p1 + perp*(width1/2)
Vector B = p1 - perp*(width1/2)
Vector C = p2 - perp*(width2/2)
Vector D = p2 - perp*(width2/2)
// wind triangles
Triangle( A, B, D )
Triangle( B, D, C )
Note there's potentially a CW/CCW winding problem with this algorithm -- if perp is computed as (-y, x) in the above diagram then it will be CCW winding, if (y, -x) then it will be a CW winding.

- 64,917
- 62
- 258
- 363
I've had to do the same thing earlier today.
For creating a line that spans (x1,y1) -> (x2,y2)
of a given width
, a very easy method is to transform a simple unit-sized square spanning (0., -0.5) -> (1., 0.5)
using:
glTranslatef(...)
to move it to your desired(x1,y1)
location;glScalef(...)
to scale it to the rightlength
and desiredwidth
: uselength = sqrt( (x2-x1)^2 + (y2-y1)^2 )
or any other low-complexity approximation;glRotatef(...)
toangle
it to the right orientation: useangle = atan2(y2-y1, x2-x1)
.
The unit-square is very simply created from a two-triangle strip GL_TRIANGLE_STRIP
, that turns into your solid line after the above transformations.
The burden here is placed primarily on OpenGL (and your graphics hardware) rather than your application code. The procedure above is turned very easily into a generic function by surrounding glPushMatrix()
and glPopMatrix()
calls.

- 924
- 11
- 12
For those coming looking for a good solution to this, this code is written using LWJGL, but can easily be adapted to any implementation of OpenGL.
import java.awt.Color;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Vector2f;
public static void DrawThickLine(int startScreenX, int startScreenY, int endScreenX, int endScreenY, Color color, float alpha, float width) {
Vector2f start = new Vector2f(startScreenX, startScreenY);
Vector2f end = new Vector2f(endScreenX, endScreenY);
float dx = startScreenX - endScreenX;
float dy = startScreenY - endScreenY;
Vector2f rightSide = new Vector2f(dy, -dx);
if (rightSide.length() > 0) {
rightSide.normalise();
rightSide.scale(width / 2);
}
Vector2f leftSide = new Vector2f(-dy, dx);
if (leftSide.length() > 0) {
leftSide.normalise();
leftSide.scale(width / 2);
}
Vector2f one = new Vector2f();
Vector2f.add(leftSide, start, one);
Vector2f two = new Vector2f();
Vector2f.add(rightSide, start, two);
Vector2f three = new Vector2f();
Vector2f.add(rightSide, end, three);
Vector2f four = new Vector2f();
Vector2f.add(leftSide, end, four);
GL11.glBegin(GL11.GL_QUADS);
GL11.glColor4f(color.getRed(), color.getGreen(), color.getBlue(), alpha);
GL11.glVertex3f(one.x, one.y, 0);
GL11.glVertex3f(two.x, two.y, 0);
GL11.glVertex3f(three.x, three.y, 0);
GL11.glVertex3f(four.x, four.y, 0);
GL11.glColor4f(1, 1, 1, 1);
GL11.glEnd();
}

- 3,356
- 2
- 25
- 29
A rectangle (i.e. GL_QUAD or two GL_TRIANGLES) sounds like your best bet by the sounds of it, not sure I can think of any other way.

- 470
- 4
- 15
Assume your original points are (x1,y1) -> (x2,y2). Use the following points (x1-width/2, y1), (x1+width/2,y1), (x2-width/2, y2), (x2+width/2,y2) to construct a rectangle and then use quads/tris to draw it. This the simple naive way. Note that for large line widths you'll get weird endpoint behavior. What you really want to do then is some smart parallel line calculations (which shouldn't be that bad) using vector math. For some reason dot/cross product and vector projection come to mind.

- 29,624
- 9
- 57
- 79
-
@Ozgur `s answer is much better. Consider adding the vector math you are talking about. – Valdrinium Sep 10 '14 at 09:42
-
Another way to do this, if you are writing a software rasterizer by chance, is to use barycentric coordinates in your pixel coloration stage and color pixels when one of the barycentric coordinates is near 0. The more of an allowance you make, the thicker the lines will be.

- 64,917
- 62
- 258
- 363