<?xml version="1.0"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <title>Drawing - ClanLib SDK</title> <link rel="stylesheet" media="screen" type="text/css" href="clanlib.css"/> <link rel="icon" href="gfx/favicon.png" type="image/png"/> </head> <body> <div id="content"> <h1><a href="."><img src="gfx/clanlib.png" alt="ClanLib SDK" /></a></h1> <!-- <div style="float: right;"> <a href="download.html">Download</a> : <a href="docs.html">Documentation</a> : <a href="development.html">Development</a> : <a href="donations.html">Donations</a> : <a href="http://www.rtsoft.com/clanlib">Forum</a> : <a href="contributions.html">Contributions</a> </div> --> <h2> <img src="gfx/overview.png"/>Drawing </h2> <p>Drawing graphics in ClanLib is done by first acquiring an instance of the <span class="code">CL_GraphicContext</span> interface. Graphical contexts can be provided by different sources, with <span class="code">CL_DisplayWindow</span> being the common source.</p> <p>A simple example of obtaining a graphic-context:</p> <pre> CL_DisplayWindow window(0, 0, 640, 480, "Hello World"); CL_GraphicContext gc = window.get_gc(); </pre> <p>A graphic context is an interface which you can use to draw graphics and create graphical objects like textures, shader objects and so on. Each graphic context has a number of states that determine how each drawing command is to be executed.</p> <p>If the graphic context comes from a <span class="code">CL_DisplayWindow</span> object, it is normally written to an off-screen buffer in order to avoid the user seeing your changes as they are being made. In order to change the content being drawn onto the screen, you need to call <span class="code">CL_DisplayWindow::flip()</span> or <span class="code">CL_DisplayWindow::update(rect)</span>. The following example shows how to make a ClanLib application drawing some graphics onto the screen:</p> <pre> CL_SetupCore setup_core; CL_SetupDisplay setup_display; CL_SetupGL setup_gl; CL_DisplayWindow window(640, 480, "Hello World"); CL_GraphicContext gc = window.get_gc(); CL_InputContext ic = window.get_ic(); CL_Font font_tahoma(gc, "Tahoma", 16); CL_BlendMode blend_transparent; blend_transparent.enable_blending(true); while (ic.get_keyboard().get_keycode(CL_KEY_ESCAPE) == false) { gc.clear(CL_Colord::smokewhite); gc.set_map_mode(cl_map_2d_upper_left); gc.set_blend_mode(blend_transparent); font_tahoma.draw_text(gc, 10, 10, "Hello There", CL_Colord::lemonchiffon); window.flip(); CL_KeepAlive::process(); } </pre> <h3>High-level Rendering</h3> <p>ClanLib provides a set of higher level classes to draw simple 2D graphics easilly:</p> <ul> <li>CL_Draw</li> <li>CL_Image</li> <li>CL_Sprite</li> <li>CL_Font</li> <li>CL_SpanLayout</li> </ul> <p>Using these classes are fairly straight forward. In the following loop we draw some text, show a simple image and render a gradient:</p> <pre> CL_Image image(gc, "image.png"); CL_Font font(gc, "Tahoma", 16); while (ic.get_keyboard().get_keycode(CL_KEY_ESCAPE) == false) { gc.clear(CL_Colord::smokewhite); CL_Gradient color(CL_Colorf::mediumspringgreen, CL_Colorf::honeydew); CL_Draw::gradient_fill(gc, 0.0f, 0.0f, 100.0f, 100.0f, color); image.draw(gc, 10.0f, 10.0f); font.draw_text(gc, 50.0f, 50.0f, "High level 2D drawing", CL_Colord::navy); window.flip(); CL_KeepAlive::process(); } </pre> <p>For further information on each of these classes, see the overview documentation dedicated to them or check the reference documentation.</p> <h3>Low-level Rendering</h3> <p>The graphic context class itself draws primitives, with each primitive being a point, line segment, polygon or a rectangle of pixels. The primitives are defined by a group of one or more vertices. A vertex defines a point, an endpoint of an edge, or a corner of a polygon where two edges meet. Additional data is associated with each vertex, such as colors, normals and texture coordinates.</p> <p>For example, a single line drawn is described as one line-segment primitive using two vertices. The first vertex may have position 10,10 using a red color, and the second vertex may have position 100,100 using a green color. The line drawn will then go from 10,10 to 100,100 and changing color from red to green along the line.</p> <p>Data associated with a vertex is called a vertex attribute. In our line drawing example the two attributes are <i>position</i> and <i>color</i> and are assigned index 0 and 1 respectively. To draw the line we use the class <span class="code">CL_PrimitivesArray</span> to describe our vertex data and then we call <span class="code">draw_primitives</span> to render the line:</p> <pre> CL_Vec4f red_color(1.0f, 0.0f, 0.0f, 1.0f); CL_Vec4f green_color(0.0f, 1.0f, 0.0f, 1.0f); CL_Vec2i positions[] = { CL_Vec2i(10,10), CL_Vec2i(100,100) }; CL_Vec4f colors[] = { red_color, green_color }; CL_PrimitivesArray vertices(gc); vertices.set_attribute(0, positions); vertices.set_attribute(1, colors); gc.set_program(cl_program_color_only); gc.draw_primitives(cl_lines, 2, vertices); </pre> <p>The actual look and position of the line is determined by a <i>shader program</i>. A shader program consists of two individual shaders called a vertex shader and a fragment shader. Each shader is run at a different stage when rendering the line. Consider the following pseudo-code for how a line is rendered:</p> <pre> struct Vertex { VertexAttribute attributes[max_attributes]; }; struct ShadedVertex { Vec2 position; VaryingVariable varyings[max_varyings]; }; void draw_line(Vertex v1, Vertex v2) { ShadedVertex sv1 = vertex_shader(v1); ShadedVertex sv2 = vertex_shader(v2); foreach pixel covered by line (sv1.position,sv2.position) { VaryingVariable varyings[] = interpolate(pixel position, sv1, sv2); Color shaded_pixel = fragment_shader(varyings); framebuffer[pixel position] = blend_function(framebuffer_pixel, shaded_pixel); } } </pre> <p>To do: Describe the process of rendering a primitive a lot better :)</p> <!-- <p>The details of the first two steps can be either described by writing a shader program, or by using the fixed function pipeline. A shader program is a small piece of code (handled by <span class="code">CL_ProgramObject</span>) that is compiled and uploaded to the graphics card's processing unit (GPU), which then executes the details of the above steps.</p> <h3>Rendering Primitives</h3> <p>To draw one or more primitives, you have to first construct some vertex data, then describe your vertex data to ClanLib and finally tell the graphic context how many vertices you got and what type of primitive you want to render. The following example does this to draw a rectangle:</p> <pre> void draw_rectangle(CL_GraphicContext &gc, const CL_Rect &rect, const CL_Colord &color) { CL_Vec2i positions[] = { CL_Vec2i(rect.left, rect.top), CL_Vec2i(rect.right, rect.top), CL_Vec2i(rect.right, rect.bottom), CL_Vec2i(rect.left, rect.bottom) } CL_PrimitivesArray vertex_data(gc); vertex_data.set_positions(positions); vertex_data.set_primary_color(color); gc.draw_primitives(cl_polygon, 4, vertex_data); } </pre> <p>As mentioned earlier, each vertex defines a point and can have additional data associated with it. In traditional (fixed function pipeline) rendering, the vertex data attributes are:</p> <div style="margin-left: 32px;"> <table> <tr><th width=180 align=left>Attribute</th><th width=180 align=left>Shader Variable</th><th width=180 align=left>Attribute Bind Index</th></tr> <tr><td>Position</li></td><td>gl_Vertex</td><td>0</td></tr> <tr><td>Normal</td><td>gl_Normal</td><td>2</td></tr> <tr><td>Primary color</td><td>gl_Color</td><td>3</td></tr> <tr><td>Secondary color</td><td>gl_SecondaryColor</td><td>4</td></tr> <tr><td>Fog coordinate</td><td>gl_FogCoord</td><td>5</td></tr> <tr><td>Texture coordinates</td><td>gl_MultiTexCoord0-7</td><td>8 to 15</td></tr> </table> </div> <p>You can either specify a vertex attribute by supplying an array of values, or you can specify one single value to be applied to all vertices. In the above example, <span class="code">set_positions</span> specifies an array of <span class="code">CL_Vec2i</span> values, while <span class="code">set_primary_color</span> specifies a single value to be used for all vertices.</p> <p>Vertex attributes, however, are not limited to only the above listed types. If you write your own shader programs, the shaders can define new attributes which you supply using <span class="code">CL_PrimitivesArray::set_attributes</span>. The officially defined attributes above basically just cover the types of data which the fixed function pipeline uses - you do not have to use any of them if your vertex shader does not.</p> <p>The following example only uses our own special vertex attribute:</p> <pre> // vertex.glsl: attribute vec1 angle; void main() { gl_Position = vec2(200+cos(radians(angle))*100.0, 200+sin(radians(angle)*100.0); gl_TexCoord0 = gl_Position.xy; } // fragment.glsl: uniform shader2D texture; void main() { gl_FragColor = texture2D(texture, gl_TexCoord0.st); } // ClanLib code: void draw_circle(CL_GraphicContext &gc, CL_Texture &texture) { CL_ProgramObject program = CL_ProgramObject::load( gc, "vertex.glsl", "fragment.glsl"); int angle_index = 0; program.bind_attribute_location(angle_index, "angle"); program.link(); int texture_index = 0; program.set_uniform("texture", texture_index); CL_Vec2i angles[360]; for (int i = 0; i < 360; i++) angles[i].x = i; CL_PrimitivesArray vertex_data(gc); vertex_data.set_attributes(angle_index, angles); gc.set_program_object(program); gc.set_texture(texture_index, texture); gc.draw_primitives(cl_polygon, 360, vertex_data); gc.reset_texture(texture_index); gc.reset_program_object(); } </pre> <p>If you mix your own attributes with built-in attributes, make sure that you do not bind the attributes to indexes already used by the built-in attributes you use. For example, you cannot bind <span class="code">angle</span> in the above example to index 0 if you also use <span class="code">gl_Vertex</span>, since gl_Vertex always uses index 0.</p> <h3>Buffer Objects</h3> <p>To do: introduce CL_VertexArrayBuffer and other methods to store data in GPU memory.</p> <h3>The Fixed Function Pipeline</h3> <p>The fixed function pipeline can be considered a pre-written shader program that is configured by a number of states you can set on <span class="code">CL_GraphicContext</span> using functions such as <span class="code">set_modelview</span>, <span class="code">set_texture_unit</span>, <span class="code">set_light</span>, etc.</p> <h4>Vertex Shading</h4> <p>The vertex transformation sequence of the fixed function pipeline is as follows:</p> <ul> <li>Object coordinates -> Model-view matrix -> Eye coordinates</li> <li>Eye coordinates -> Projection matrix -> Clip coordinates</li> <li>Clip coordinates -> Perspective division -> Normalized (-1.0 to 1.0) coordinates</li> <li>Normalized coordinates -> Viewport transformation -> Window coordinates</li> </ul> <p>The vertex coordinates passed along with the line-segment primitive command are first multiplied with the model-view matrix. This produces what is called eye coordinates. This set of coordinates are then multiplied with the projection matrix, which creates clip coordinates. A perspective division is performed and finally the coordinates are scaled up to fit the size of the window, i.e. (0.0,0.0) in normalized coordinates maps to the center of the window.</p> <p>The above transformation sequence is mostly useful for drawing 3D and can be somewhat complicated to work with when doing 2D drawing operations. ClanLib therefore offers a function on the graphic context, <span class="code">CL_GraphicContext::set_map_mode</span>, which preconfigures certain parts of the sequence. If the mapping mode is set to <span class="code">cl_map_2d_upper_left</span> then the projection matrix and viewport transformation will be configured to let eye coordinates map to window coordinates. So eye coordinates of (0,0) will map to the upper left corner, and (100,100) will map to 100 pixels down and 100 pixels to the right. Likewise, <span class="code">cl_map_2d_lower_left</span> will do the same but with (0,0) being lower left corner and the y-axis going upwards.</p> <p>Each vertex have two colors, the primary and secondary color. If lighting is enabled in the pipeline, the primary and secondary color is calculated by applying all active <span class="code">CL_LightSource</span> objects. The colors are set by using the currently active <span class="code">CL_Material</span> object, the position of the light sources and the position of the vertex itself. For further detail on how light is applied, see the OpenGL specification.</p> <h4>Fragment Shading</h4> <p>After the vertices have been processed, the primitive being drawn (a line in our example) is realized by generating fragments for each pixel the line covers. For example, a fragment for (10,10), one for (11,11), another for (12,12), etc.</p> <p>Each of these fragments go through the following steps which determine what color the fragment gets:</p> <ul> <li>Texturing</li> <li>Color Sum</li> <li>Fog</li> </ul> <p>Texturing maps a portion of one or more specified images onto each primitive for which texturing is enabled. The mapping is accomplished by using the color of an image at the location indicated by texture coordinates. Each vertex carries multiple sets of texture coordinates, one for each active texture unit. If we take the line segment example again, the first vertex at (10,10) may have a texture coordinate for texture unit 1 saying (0.0,0.0) and at second vertex at (100,100) it might say (1.0,1.0). The fragment located at (50,50) will then have the texture coordinate of (0.5,0.5). Likewise it might have another set for texture unit 2, going from (1.0,1.0) to (0.0,0.0)</p> <p>The color in the image located at the texture coordinate position is called a texel. The texel and the primary color of the fragment is blended by the texturing unit based on the currently active texture function. When using several texture units, the result of the first texturing unit is passed into the next texture unit. A texture unit's texture function can be described as a function with the following syntax:</p> <pre> color = texture_function( fragment_primary_color, texel_color, result_color_from_previous_unit); </pre> <p>The result returned by the last of the active texture units will be the new primary color of the fragment.</p> <p>After the texturing step, a color sum between the fragment primary color and secondary color is applied. The resulting color of the fragment will then be <span class="code">normalize(primary_color+secondary_color)</span>. Finally fog coloring is applied to the fragment color, if enabled.</p> <h4>Blending Fragments with the Frame Buffer</h4> <p>The last step of rendering a primitive is to apply the fragments to the frame buffer. This is done by running a few tests on the fragment and then a blending operation with the pixel in the frame buffer. The steps are as follows:</p> <ul> <li>Scissor (Clipping) Test</li> <li>Alpha Test</li> <li>Stencil Test</li> <li>Depth Buffer Test</li> <li>Blending</li> <li>Dithering</li> <li>Logical Operation</li> </ul> <p>The scissor test checks if the fragment is inside the clipping rectangle of the graphic context. The alpha test, if enabled, checks if the alpha part of the fragment color is above, equal or below a specified value. The stencil test discards a fragment based on the outcome of a comparison between the value in the stencil buffer and the fragment. Likewise, the depth buffer test compares the fragment to the value in the depth buffer. All these tests are configured by the <span class="code">CL_BufferControl</span> states in the graphic context. If the fragment fails any of these tests, the fragment is dropped.</p> <p>If blending is enabled (<span class="code">CL_BlendMode</span>), the color of the fragment will be changed to a value calculated by the blending function. The blending functions use the color of the fragment and the current color of the pixel in the frame buffer beneath the fragment. If blending is not enabled, the fragment color will always become the new color in the frame buffer, no matter what value the alpha part of the color is.</p> <p>Finally, a logical operation is applied between the incoming fragment's color and the color in the frame buffer. The result of this operation becomes the new color in the frame buffer.</p> --> </div> </body> </html>