ofSetLineWidth() doesn't work with ofGLProgrammableRenderer::TYPE

Hi,

ofSetLineWidth() seems not to work when I changed the current renderer to ofGLProgrammableRenderer. Is this a bug? or, do I have to tell some more to renderer…?

main.cpp

  
int main(){  
	ofSetCurrentRenderer(ofGLProgrammableRenderer::TYPE); // << changing current renderer   
	ofSetupOpenGL(1024, 768, OF_WINDOW);  
	ofRunApp(new ofApp());  
}  

testApp.cpp

  
void testApp::setup()  
{  
  
}  
  
void testApp::update()  
{  
	  
}  
  
void testApp::draw()  
{  
	ofSetLineWidth(10);  
	ofLine(0, 0, 100, 100); // << draws 1px width line...  
}  

Thanks,
Motoi

![](http://forum.openframeworks.cc/uploads/default/3094/Screen Shot 2013-11-11 at ??4.16.24.png)

With the programmable renderer you’ll want to just make an ofVboMesh of your lines and draw that, the line width stuff is very old OpenGL specific.

Thanks joshua, really helped me out!

Sorry to open up this rather old post, but I am struggling to get this to work. (OF 08.00.00 on Ubuntu studio 13.10 64 bits).

I load vertices into an ofVboMesh, then draw that, but the lines always stay 1 pixel in width. I tried to get this to work, and this, but both are really beyond my comprehension of shaders and GLSL at this time.

Also, I wonder how to get a filled polygon; tesselation (with or without tesselation shaders) seems like a really advance technique… Or is it?

When using a for loop with ofBeginShape(), ofVertex() and ofEndShape(), at least ofFill() does what is expected. But then the degree of control of coloring is much less than with shaders…

Update function (excerpt):

    for(int i = 0; i < contourFinder.nBlobs; ++i)
    {
        for(int j = 0; j < (contourFinder.blobs[i].nPts); j++)
        {
            contourMesh[i]->addVertex(ofVec3f(contourFinder.blobs[i].pts[j].x,
                                             contourFinder.blobs[i].pts[j].y,
                                             1.0));
            contourMesh[i]->addColor(ofColor(ofRandom(255),ofRandom(255),ofRandom(255),255));
        }
    // last vertex (closing the deal)
        contourMesh[i]->addVertex(ofVec3f(contourFinder.blobs[i].pts[0].x,
                                             contourFinder.blobs[i].pts[0].y,
                                             1.0));
    // optionally add indices...
        /*for(int j = 0; j < contourMesh[i]->getNumVertices() - 1; ++j)
        {
            contourMesh[i]->addIndex(i);
            contourMesh[i]->addIndex(i+1);
        }*/
    }

Then drawing:

    // using a shader (which I would like to)
    // - I do not know how to fill
    // - line width stay 1 px (also without shaders)
    //shader.begin();
    ofSetLineWidth(10); // no effect
    ofFill(); // no effect

    ofSetColor(155, 200, 20, 255);

    for(int i = 0; i < numContours; ++i)
    {
        contourMesh[i]->draw();
    }
    //shader.end();

Edit: using

glLineWidth(10);

works fine, but lines are very unsmooth, on the connecting places between lines there is the problem described on the Cinder forum page. Maybe I should try using ofBezier()? Though that would involve a lot of calculations for coordinates, and I guess it is fixed pipeline…

I don’t know these are exact answers to what you’re looking for but there are couple examples comes with openframeworks that might show you ways of achieving this with different approaches:

  • examples/gl/cameraRibbonExample creates lines with different widths with mouse movement
  • examples/gl/geometryShaderExample uses shaders to create lines with different thickness in 3d

I also created a 2d and simplified shader version of line thickness stuff based on geometryShaderExample here, might be helpful, you can download it here: https://github.com/IxDS/ShaderExamples-of

2 Likes

Thanks @muharrem_yildirim! That’s really helpful. I have been struggling to get this to work for many hours now. The versions of GLSL really can get confusing… (So far I was using #version 420 on an nvidia GT 540m, using optirun on Ubuntu).

With thick lines, especially in combination with transparency, there are still pieces 'missing´, or overlapping, in the corners, but with this code I can continue working on that. If I find a way to correct it like proposed on the cinder forum I will post the result here.

The code is indeed very similar to the geometry shader example, with which I experimented a lot. The result when using it with an opencv contourfinder is as seen below; there are a lot of very small lines. But even when skipping some points, the resulting drawn contour is really not smooth, more like a dashed line. Also, maybe because of updating so many points for every frame, the fps drops a lot. However my first concern is finding a way to draw nice contours.
[edit: framerate goes up to ~48 when I do not update empty meshes, which caused a lot of console output]

As for filling, I found the ofTesselator class very easy to implement.

Finally, solved it! Now, using a combination of the code @muharrem_yildirim wrote based on the geometryShaderExample, and the code found via the cinder forum, the geometry shader nicely makes thicker lines when it receives GL_LINES_ADJACENCY input. Framerate is excellent, ~60 fps.

Here is the current code for the geometry shader (note that this will only work well for 2d). It uses screen coordinates to calculate vertices.
If desired I could create an oF example with this, also incorporating tesselation.
Thanks a lot for the help!

#version 150

// geometry shader based on code by Paul Houx:
// https://forum.libcinder.org/topic/smooth-thick-lines-using-geometry-shader
// http://www.cowlumbus.nl/forum/GeometryShader.zip


uniform float thickness;

layout(lines_adjacency) in;
layout(triangle_strip, max_vertices = 7) out;

vec3 screen_space(vec4 vertex)
{
    return vec3( vertex.xy / vertex.w, 0 ) * vec3(1024, 768, 1.0);
}

void main() {
  // this could also be a uniform
  vec3 WIN_SCALE = vec3(1024, 768, 1.0);
  float MITER_LIMIT = 1.0f;

  vec3 p0 = screen_space(gl_in[0].gl_Position);
  vec3 p1 = screen_space(gl_in[1].gl_Position);
  vec3 p2 = screen_space(gl_in[2].gl_Position);
  vec3 p3 = screen_space(gl_in[3].gl_Position);

  // perform naive culling
  vec3 area = WIN_SCALE * 1.2;
  if( p1.x < -area.x || p1.x > area.x ) return;
  if( p1.y < -area.y || p1.y > area.y ) return;
  if( p2.x < -area.x || p2.x > area.x ) return;
  if( p2.y < -area.y || p2.y > area.y ) return;

  // determine the direction of each of the 3 segments (previous, current, next)
  vec3 v0 = normalize(p1-p0);
  vec3 v1 = normalize(p2-p1);
  vec3 v2 = normalize(p3-p2);

  // determine the normal of each of the 3 segments (previous, current, next)
  vec3 n0 = vec3(-v0.y, v0.x, 0);
  vec3 n1 = vec3(-v1.y, v1.x, 0);
  vec3 n2 = vec3(-v2.y, v2.x, 0);

    // determine miter lines by averaging the normals of the 2 segments
  vec3 miter_a = normalize(n0 + n1);    // miter at start of current segment
  vec3 miter_b = normalize(n1 + n2);    // miter at end of current segment

  // determine the length of the miter by projecting it onto normal and then inverse it
  float length_a = thickness / dot(miter_a, n1);
  float length_b = thickness / dot(miter_b, n1);

  vec3 up = vec3(0, 0, 1);  // arbitrary up vector

  vec3 dir = normalize(p1 - p0);      // normalized direction vector from p0 to p1
  vec3 right = normalize(cross(dir, up)); // right vector
  vec3 norm = cross(right, dir);

  right *= thickness;

  // prevent excessively long miters at sharp corners
  if( dot(v0,v1) < -MITER_LIMIT ) {
    miter_a = n1;
    length_a = thickness;

    // close the gap
    if( dot(v0,n1) > 0 ) {
        gl_Position = vec4( (p1 + thickness * n0) / WIN_SCALE, 1.0 );
        EmitVertex();
        gl_Position = vec4( (p1 + thickness * n1) / WIN_SCALE, 1.0 );
        EmitVertex();
        gl_Position = vec4( p1 / WIN_SCALE, 1.0 );
        EmitVertex();
        EndPrimitive();
    }
    else {
        gl_Position = vec4( (p1 - thickness * n1) / WIN_SCALE, 1.0 );
        EmitVertex();
        gl_Position = vec4( (p1 - thickness * n0) / WIN_SCALE, 1.0 );
        EmitVertex();
        gl_Position = vec4( p1 / WIN_SCALE, 1.0 );
        EmitVertex();
        EndPrimitive();
    }
  }

  if( dot(v1,v2) < -MITER_LIMIT ) {
    miter_b = n1;
    length_b = thickness;
  }

  // generate the triangle strip
  gl_Position = vec4( (p1 + length_a * miter_a) / WIN_SCALE, 1.0 );
  EmitVertex();
  gl_Position = vec4( (p1 - length_a * miter_a) / WIN_SCALE, 1.0 );
  EmitVertex();
  gl_Position = vec4( (p2 + length_b * miter_b) / WIN_SCALE, 1.0 );
  EmitVertex();
  gl_Position = vec4( (p2 - length_b * miter_b) / WIN_SCALE, 1.0 );
  EmitVertex();

  EndPrimitive();
}
2 Likes

I wasn’t able to make it work. Do you still have an example for that?

Thanks in advanced!

Hi Imanolgo,

can you be more specific about what problems you are facing? ie. What are trying to accomplish, what have you tried and what is going wrong?

The code in the posts should work, but of course you need to create your own vertex and pixel shaders, and connect it all in the right way. Please note using geometry shaders is a contested practice…

Hi mennowo,

You are totally right; you have to be very careful setting up shaders and that was my problem.

In the end, I adapted @muharrem_yildirim example, used GL_LINE_STRIP_ADJACENCY to draw them and voilà, the shader was drawing thick lines!

Anyway, I really appreciate the quick response.

ofPath has width, you can use it to draw a line:

void ofApp::draw()
{
  ofPath line;
  line.setFilled(false);
  line.setStrokeColor(ofColor::teal);
  line.setStrokeWidth(20.0f);

  line.lineTo(0, 0);
  line.lineTo(100, 100);

  ofPushMatrix();
  ofTranslate((ofGetWidth()/2.0f)-50, (ofGetHeight()/2.0f)-50);
    line.draw();
  ofPopMatrix();
}

Although, its ends will be aligned incorrectly:

And if your line path is crazy like this, by default it will render choppy too:

void ofApp::draw()
{
  ofPath line;
  line.setFilled(false);
  line.setStrokeColor(ofColor::teal);
  line.setStrokeWidth(20.0f);

  for (int i = 0; i < 20; i++)
  {
      float s = 150*1.5;
      float t = (i*100)/PI;
      ofPoint p = ofPoint(cos(t)*(sin(t*(cos(t)/PI))*s), sin(t)*(cos(t*(sin(t)/PI))*s));

      if (i == 0) line.moveTo(p);
      else line.curveTo(p);
  }

  ofPushMatrix();
  ofTranslate(ofGetWidth()/2.0f, ofGetHeight()/2.0f);
    line.draw();
  ofPopMatrix();
}

To fix these issues you can use ofxShivaVG, vector shape API implemented in OpenGL. Just add #include "ofxShivaVGRenderer.h" to ofApp.h and set its renderer in ofApp::setup to render shapes properly:

void ofApp::setup()
{
  ofSetCurrentRenderer(shared_ptr<ofxShivaVGRenderer>(new ofxShivaVGRenderer));
}

Regarding ofSetLineWidth I’ve opened an issue on Github: https://github.com/openframeworks/openFrameworks/issues/3460

2 Likes

Is it possible to use GL_LINE_STRIP_ADJACENCY with ofPolyline so it could use the shader?

you need to put all the vertices from the polyline in an ofMesh, like:

mesh.addVertices(polyline.getVertices());
mesh.setMode(OF_PRIMITIVE_LINE_STRIP_ADJACENCY);

btw that will only work on OF 0.9 or using the nightly build