Page 181 - Computer Graphics Handout
P. 181
glEnableVertexAttribArray(loc);
glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, 0,
BUFFER_OFFSET(0));
glGenBuffers(1, buffers);
glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(triangles), triangles,
GL_STATIC_DRAW);
and display it as
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawArrays(GL_TRIANGLES, 0, 6*N*M);
If we integrate this code with our previous example using line strips, the output will look almost identical. Although we have designed
a surface, by choosing to display only the edges by using a polygon mode of GL_LINE, we do not generate any fragments
corresponding to the inside of the polygon, and thus we see the edges of polygons that would be hidden if the mode were GL_FILL.
We can fix this problem by rendering the data twice, first as a filled white surface and second as black lines. Because the data are
already on the GPU, we do not have to send any vertex data to the GPU for the second rendering.We can specify two colors in the
initialization that we will send to the fragment shader
color4 white = vec4(1.0, 1.0, 1.0, 1.0);
color4 black = vec4(0.0, 0.0, 0.0, 1.0);
color_loc = glGetUniformLocation(program, “fcolor”);
and then modify to the display callback to have the code
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glUniform4fv(color_loc, 1, white);
glDrawArrays(GL_TRIANGLES, 0, 6*N*M);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glUniform4fv(color_loc, 1, black);
glDrawArrays(GL_TRIANGLES, 0, 6*N*M);
The modified fragment shader is
uniform vec4 fcolor;
void main()
{
gl_FragColor = fcolor;
}
4.9.2 Polygon Offset
There are interesting aspects to this OpenGL program, and we can make various modifications. First, if we use all the data, the
resulting plot may contain many small polygons. The resulting density of lines in the display may be annoying and can contain moir´e
patterns. Hence, we might prefer to subsample the data either by using every kth point for some k or by averaging groups of data
points to obtain a new set of samples with smaller N and M.
There is one additional trick that we used in the display of Figure 4.44. If we draw both a polygon and a line loop with the code in
the previous section, then each triangle is rendered twice in the same plane, once filled and once by its edges. Even though the
second rendering of just the edges is done with filled rendering, numerical inaccuracies in the renderer often cause parts of second
rendering to lie behind the corresponding fragments in the first rendering. We can avoid this problem by enabling the polygon offset
mode and setting the offset parameters using glPolygonOffset.
Polygon fill offset moves fragments slightly away from the viewer, so all the desired lines should be visible. In initialization, we can
set up polygon offset by
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0, 1.0);
The two parameters in PolygonOffset are combined with the slope of the polygon and an implementation-dependent constant.
Consequently, you may have to do a little experimentation to find the best values. Perhaps the greatest weakness of our code is
that we are sending too much data to the GPU and not using the most efficient rendering method. Consider a mesh consisting of a
single row of N quadrilaterals. If we render it as 2N triangles using
point4 vertices[6*N];
glDrawArrays(GL_TRIANGLES, 0, 6*N);
we send 6N vertices to the GPU. If we instead set our vertices as a triangle strip, then these two lines of code become
point4 vertices[2*N-2];
glDrawArrays(GL_TRIANGLE_STRIP, 0, 2*N-2);
181

