To LUGNET HomepageTo LUGNET News HomepageTo LUGNET Guide Homepage
 Help on Searching
 
Post new message to lugnet.cad.devOpen lugnet.cad.dev in your NNTP NewsreaderTo LUGNET News Traffic PageSign In (Members)
 CAD / Development / 11104
11103  |  11105
Subject: 
Re: Bricksmith 2.4: Faster. Much Faster.
Newsgroups: 
lugnet.cad.dev
Date: 
Wed, 16 Jun 2010 21:24:33 GMT
Highlighted: 
(details)
Viewed: 
38812 times
  
In lugnet.cad.dev, Don Heyse wrote:
Ah ha! Yet another reason to read that vertex-shader book.  So, are you
suggesting running this algorithm on the graphics card (with counters for
the vertices instead counting pixels in the stencil buffer) or perhaps
some other algorithm?


Actually, it's even simpler!  This is the code for the geometry-shader:

            #version 120
            #extension GL_NV_geometry_shader4 : enable

            varying out vec4 gl_TexCoord[];

            float determinant(mat2 m)
            {
                return m[0][0] * m[1][1] - m[0][1] * m[1][0];
            }

            void main(void)
            {
                vec4 c1 = gl_PositionIn[0];
                vec4 c2 = gl_PositionIn[3];
                vec4 v1 = gl_PositionIn[1];
                vec4 v2 = gl_PositionIn[2];

                c1 /= c1.w;
                c2 /= c2.w;
                v1 /= v1.w;
                v2 /= v2.w;

                c1 -= v1;
                c2 -= v1;
                v2 -= v1;

                if (sign(determinant(mat2(v2.x, v2.y, c1.x, c1.y))) ==
                    sign(determinant(mat2(v2.x, v2.y, c2.x, c2.y))))
                {
                    gl_Position = gl_PositionIn[1];
                    EmitVertex();
                    gl_Position = gl_PositionIn[2];
                    EmitVertex();
                    EndPrimitive();
                }
            }


gl_Position[0] and gl_Position[3] are the control-points, [1] and [2] are the
vertices of the line.  Using it is simply a matter of configuring the
shader-program to accept GL_LINES_ADJACENCY as input and return GL_LINES as
output, and then you do this to draw the line:

       glUseProgram(myProgram);
       glBegin(GL_LINES_ADJACENCY);
       glVertex(control1);
       glVertex(vertex1);
       glVertex(vertex2);
       glVertex(control2);
       glEnd();
       glUseProgram(0);


Obviously you can stick as many sets of controls/vertices in there as you like -
the shader will be called once for each set.

Performance is excellent :-)  The above has one small drawback, though: geometry
shaders require one of the extensions "EXT_geometry_shader4",
"ARB_geometry_shader4" or "NV_geometry_shader4".  This pretty much means you
need a fairly recent card - OpenGL3.2 or better.  Fortunately, this is not the
only way to do the trick :-)

After a bit of head-scratching, I worked out how to pull off the same stunt with
a vertex-shader.  It's slightly less efficient, since it isn't possible to pass
more than one vertex at a time to the shader (unlike the geometry-shader, which
can take multiple inputs) and so the calculation ends up having to be done twice
- once for each end of the line.

The control-points - along with both vertices - have to be passed to the shader
via some of the ancillary vertex-attributes, and since it also isn't possible
for a vertex-shader to /not/ emit a vertex when it leaves, if the algorithm
decides that the vertex should not be drawn it simply moves it to the origin.
OpenGL is smart enough not to draw a dot when both ends of the line are at the
same location :-)

I won't post the code for this one here, as it's misbehaving on one of my test
machines and I haven't had time to debug it.  I'm pretty sure it's a driver bug,
but still :-(

Vertex-shaders require OpenGL 2 or better, so we have a third approach :-)  It
is in fact possible to do this with the ancient ARB vertex-program, which is
available from IIRC OpenGL 1.2 and up.  It's basically the same algorithm,
complicated ever so slightly by the facts that a) vertex-programs are in
assembler, and b) the language has no flow-control instructions...

As before, you pass the vertices and control-points in via four spare
vertex-attribs (11, 12, 13 and 14 in the code below) and the program is executed
for each input vertex.

          !!ARBvp1.0

          TEMP c1;
          TEMP c2;
          TEMP v1;
          TEMP v2;
          TEMP c1w;
          TEMP c2w;
          TEMP v1w;
          TEMP v2w;
          TEMP det1;
          TEMP det2;
          TEMP floor;
          TEMP sign;
          TEMP factor;

          DP4 c1.x, state.matrix.mvp.row[0], vertex.attrib[11];
          DP4 c1.y, state.matrix.mvp.row[1], vertex.attrib[11];
          DP4 c1.w, state.matrix.mvp.row[3], vertex.attrib[11];
          RCP c1w, c1.w;
          MUL c1, c1, c1w;

          DP4 c2.x, state.matrix.mvp.row[0], vertex.attrib[12];
          DP4 c2.y, state.matrix.mvp.row[1], vertex.attrib[12];
          DP4 c2.w, state.matrix.mvp.row[3], vertex.attrib[12];
          RCP c2w, c2.w;
          MUL c2, c2, c2w;

          DP4 v1.x, state.matrix.mvp.row[0], vertex.attrib[13];
          DP4 v1.y, state.matrix.mvp.row[1], vertex.attrib[13];
          DP4 v1.w, state.matrix.mvp.row[3], vertex.attrib[13];
          RCP v1w, v1.w;
          MUL v1, v1, v1w;

          DP4 v2.x, state.matrix.mvp.row[0], vertex.attrib[14];
          DP4 v2.y, state.matrix.mvp.row[1], vertex.attrib[14];
          DP4 v2.w, state.matrix.mvp.row[3], vertex.attrib[14];
          RCP v2w, v2.w;
          MUL v2, v2, v2w;

          SUB c2, c2, v1;
          SUB c1, c1, v1;
          SUB v2, v2, v1;

          XPD det1, v2, c1;
          XPD det2, v2, c2;

          MUL sign, det1.z, det2.z;
          SGE factor, sign, 0.0;

          TEMP vertexClip;
          DP4 vertexClip.x, state.matrix.mvp.row[0], vertex.position;
          DP4 vertexClip.y, state.matrix.mvp.row[1], vertex.position;
          DP4 vertexClip.z, state.matrix.mvp.row[2], vertex.position;
          DP4 vertexClip.w, state.matrix.mvp.row[3], vertex.position;
          MUL result.position, vertexClip, factor;
          END

This requires the "ARB_vertex_program" extension.  Usage here is:

       glEnable(GL_VERTEX_PROGRAM_ARB);
       glBegin(GL_LINES);
       glVertexAttrib(11, control1);
       glVertexAttrib(12, control2);
       glVertexAttrib(13, vertex1);
       glVertexAttrib(14, vertex2);
       glVertex(vertex1);
       glVertex(vertex2);
       glEnd();
       glDisable(GL_VERTEX_PROGRAM_ARB);


(Note that in both cases in the interests of clarity I've deliberately omitted
the code which passes through things like normals, colours and texcoords.)


If anyone wants to use either of the above bits of code, please feel free :-)
And if you have questions, I'll be happy to answer them either here or via
email.


Alex



Message has 2 Replies:
  Re: Bricksmith 2.4: Faster. Much Faster.
 
(...) ... Wow, that is simple. It looks just like the description of the algorithm in the LDRAW specs. I haven't felt this old since manipulating the colormap for animation went outta style. :^) Thanks for sharing, Don (14 years ago, 16-Jun-10, to lugnet.cad.dev)
  Re: Bricksmith 2.4: Faster. Much Faster.
 
(...) <snip> (...) Wow, this is amazing. I had no idea you could do these kinds of things with shaders. Like Don I too think it's time for me to get a good book/document on using shaders in OpenGL. Thanks for the revelation. Roland (14 years ago, 16-Jun-10, to lugnet.cad.dev)

Message is in Reply To:
  Re: Bricksmith 2.4: Faster. Much Faster.
 
(...) Ah ha! Yet another reason to read that vertex-shader book. So, are you suggesting running this algorithm on the graphics card (with counters for the vertices instead counting pixels in the stencil buffer) or perhaps some other algorithm? (...) (14 years ago, 16-Jun-10, to lugnet.cad.dev, FTX)

21 Messages in This Thread:





Entire Thread on One Page:
Nested:  All | Brief | Compact | Dots
Linear:  All | Brief | Compact

This Message and its Replies on One Page:
Nested:  All | Brief | Compact | Dots
Linear:  All | Brief | Compact
    

Custom Search

©2005 LUGNET. All rights reserved. - hosted by steinbruch.info GbR