PowerPoint Presentation
Circle Modeling
Computer Graphics
Instructor: Sungkil Lee
More on State Setup
3
Vertex Ordering for a Triangle
• In general, triangles are not double-sided.
• Hence, we need to set the direction of a triangle face.
• In OpenGL, we use the order of vertices to distinguish front-facing vs.
back-facing triangles.
• Counter-clockwise encirclement of outward-pointing normal.
• The order {v1, v2, v7} and {v2, v7, v1} (front-facing) define the same
polygon with the same face direction, but the order {v1, v7, v2} (back-
facing) is different.
v1
v2
v7
4
Back-Face Culling
• By default, OpenGL will render back-facing triangles as well
as front-facing triangles.
• You need to explicitly command not to render back-facing triangles.
• This mechanism is called the back-face culling.
• You can query the current state of the face culling as follows.
glEnable( GL_CULL_FACE ); // enable face culling
glCullFace( GL_BACK ); // cull back faces
glFrontFace( GL_CCW ); // counterclockwise encirclement determine
// the direction of a face.
glIsEnabled( GL_CULL_FACE );
5
Wireframe mode rendering
• Wireframe mode (desktop only):
• To see how triangles are organized, we can turn on the wireframe mode.
• Set glPolygonMode as GL_LINE for wireframe mode or GL_FILL for solid
mode.
• You can change the line width using glLineWidth().
void keyboard( GLFWwindow* window, int key, int scancode, int action, int mods )
{
…
else if(key==GLFW_KEY_W)
{
bWireframe = !bWireframe;
glPolygonMode( GL_FRONT_AND_BACK, bWireframe ? GL_LINE:GL_FILL );
printf( “> using %s mode\n”, bWireframe ? “wireframe” : “solid” );
}
…
}
Definition of Geometry
7
Where you make a geometry in your code
• It actually does not matter.
• as long as OpenGL stuffs are initialized.
• However, it is clean and easy to do it in user_init().
• This is because we usually create geometric objects only once.
// user_init() is usually called after basics GL stuffs are initialized
void user_init()
{
… // create objects here …
}
8
Circle Definition
• Polygonal approximation of a circle
• Modern OpenGL supports only triangles as polygonal primitives.
• Implicit curves, such as a circle, are not supported.
• Hence, we need to approximate a circle using a finite set of triangles.
• As we increase the number of triangles, the shape becomes close to circle.
An octagonal approximation of a circle
9
Circle Definition
• Definition of vertex indices of vertices
• Polar coordinates of vertices
• k-th boundary vertex of N-gon of a radius one has the following polar
coordinates:
(𝑥, 𝑦) = ( cos
2𝜋
𝑁
× 𝑘, sin
2𝜋
𝑁
× 𝑘 )
2
1/9
3
4
5
6 8
7
0
10
Circle Definition
• Define arrays for vertices
• Be sure that the positions here are defined in LHS form of the canonical
view volume; z-axis goes farther from the eye.
• Actually, this circle is defined in 3D (z=0), but its normal vectors are (0,0,-1).
• When you model 3D objects, pay more attention to the z axis.
std::vector
void update_circle_vertices( uint N )
{
vertex_list.clear();
// define the position of four corner vertices
vertex_list.push_back( { vec3(0,0,0), vec3(0.0f,0.0f,-1.0f), vec2(0.5f)} ); // the origin
for( uint k=0; k <= N; k++ )
{
float t = PI*2.0f/float(N)*float(k);
float c=cos(t), s=sin(t);
vertex_list.push_back
({
vec3(c,s,0.0f), // vertex position
vec3(0.0f,0.0f,-1.0f), // normal vector facing your eye
vec2(c*0.5f+0.5f,s*0.5f+0.5f) // texture coordinate in ([0,1], [0,1])
});
}
}
11
Two Ways of Object Specification
• Simple vertex buffering
• Vertex buffer only
• Not using indices (i.e., topology is not separated).
• Index buffering: vertex buffer + index buffer
• Topology is separated from geometry by indexing scheme.
• In typical scenarios, the index buffering is faster than the vertex buffering.
This is because index buffering performs less vertex processing and the
vertex-only buffering has redundant/duplicate vertices in its definition.
Simple Vertex-Only Buffering
13
Simple Vertex Buffering
• For an N-gon, we need N×3 vertices.
• Pay attention to make out-facing triangles (counter-clockwise orders)
void update_vertex_buffer( uint N )
{
...
std::vector
for( uint k=0; k < N; k++ )
{
triangle_vertices.push_back(vertex_list.front()); // the origin
triangle_vertices.push_back(vertex_list[k+1]);
triangle_vertices.push_back(vertex_list[k+2]);
}
// generation of vertex buffer: use triangle_vertices instead of vertex_list
glGenBuffers( 1, &vertex_buffer );
glBindBuffer( GL_ARRAY_BUFFER, vertex_buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(vertex)*triangle_vertices.size(),
&triangle_vertices[0], GL_STATIC_DRAW);
}
14
Simple Vertex Buffering
• render()
• Render N×3 vertices instead of 3 vertices in the hello example.
void render()
{
...
// render vertices: trigger shader programs to process vertex data
glDrawArrays( GL_TRIANGLES, 0, NUM_TESS*3 ); // NUM_TESS = N
...
}
Index Buffering
16
Index Buffering
• Index definition
• We only indicate the topology for indices.
• Index buffer will indicate topology of vertices.
• Use the vertex buffer array as it is.
• We use N×3 indices unlike the simple vertex buffering with N×3 vertices.
void update_vertex_buffer( uint N )
{
...
index_list.clear();
for( uint k=0; k < N; k++ )
{
index_list.push_back(0); // the origin
index_list.push_back(k+1);
index_list.push_back(k+2);
}
}
17
Index Buffering
• Vertex/index buffer definition
• We need two buffers, vertex buffer and index buffer, simultaneously.
• The index buffer uses GL_ELEMENT_ARRAY_BUFFER as a buffer type.
• Vertex buffer will use the initial vertices directly (without connectivity).
void update_vertex_buffer( uint N )
{
...
// generation of vertex buffer: use vertex_list as it is
glGenBuffers( 1, &vertex_buffer );
glBindBuffer( GL_ARRAY_BUFFER, vertex_buffer );
glBufferData( GL_ARRAY_BUFFER, sizeof(vertex)*vertex_list.size(),
&vertex_list[0], GL_STATIC_DRAW);
// geneation of index buffer
glGenBuffers( 1, &index_buffer );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, index_buffer );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(uint)*index_list.size(),
&index_list[0], GL_STATIC_DRAW );
}
18
Index Buffering
• render()
• The setup for vertex buffer is the same.
• That means that binding of the vertex buffer is still necessary.
• Now, we need to the index buffer object, too.
• Render N×3 indices instead of N×3 vertices in the simple vertex buffering.
• Use glDrawElements() instead of glDrawArrays() to use the index buffering.
void render()
{
...
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, index_buffer );
glDrawElements( GL_TRIANGLES, index_list.size(), GL_UNSIGNED_INT, nullptr );
...
}
19
Your vertex shader
• One last stuff to do is the correction of the aspect ratio.
• Since we specify the vertex position in the default viewing volume, the
resulting shape in the horizontally/vertically wider screen will be distorted.
• Pass aspect_ratio = width/height to your shader as a uniform variable.
• In your shader, modify vertex positions as:
• Later, we will see how to apply this in general cases using projection matrix.
...
uniform float aspect_ratio; // correct a distortion of the shape
void main()
{
gl_Position = model_matrix * vec4( position*radius, 1 );
gl_Position.xy *= aspect_ratio>1 ? vec2(1/aspect_ratio,1) : vec2(1,aspect_ratio);
}
20
Your fragment shader
• Nearly the same as the hello example.
• Additionally, we visualize texture coordinates as color output.
// inputs from vertex shader
in vec2 tc; // used for texture coordinate visualization
…
void main()
{
fragColor = bUseSolidColor ? solid_color : vec4(tc.xy,0,1);
}
21
Results
• Octagonal approximation
• Color indicates the texture coordinates.
22
Results
• 64-gon approximation
• Now, it looks like a circle.
23
Results
• Wireframe-mode rendering (not supported in OpenGL ES)
• Now, you can see the triangular structure.