Skip to content Skip to sidebar Skip to footer

Draw Half Circle in Opengl

First created on 2016-03-19
Concluding updated on 2019-02-03

This commodity builds on code and data that was provided in a number of other articles on this site. See Related Articles, at the bottom of this article, for the full listing.

The only shapes that OpenGL can draw are points, lines, and triangles,then how practise y'all depict circles and other shapes? A reasonable approximation of a circle tin be drawn using many, many triangles with 1 vertex at the centre of the circumvolve and the other 2 vertices sharing the same location equally ane of the vertices of the triangles next to information technology on the outer border of the circle. Triangles of course have straight sides, but the more triangles you use to define the circle, and therefore the closer the vertices on the border of the circumvolve are, the closer they represent a rounded edge. Simply that is a lot of piece of work, and there is a better way: define a square whose sides are equal to the bore of the circle you desire to draw and take the fragment shader determine whether the point it is drawing is inside or outside the circle. That is what nosotros will do in this post.

The programme that we will create in this post will simply draw a light-green circle at the centre of the view, but nosotros volition be modifying the programme over time to display circles and other objects at various locations in the view and rotate them around the heart of the view. New techniques will be described as they are used.

Drawing A Circle

Kickoff past creating a new empty project in Visual Studio called
CirclesAndRotators, then add iii new classes to it called CirclesAndRotatorsApp, CirclesAndRotatorsFrame, and CirclesAndRotatorsCanvas. Follow the steps outlined in:

  1. Creating wxWidgets Programs with Visual Studio 2017 – Part 1
  2. Visual Studio, wxWidgets and OpenGL
  3. How-do-you-do Triangle: OpenGL with wxWidgets, and
  4. OpenGL Shaders.

replacing class names as appropriate.

A number of the methods in the classes remain the same, and so they will not be repeated in this postal service. However, the source of the full program is provided on Github if you notice y'all are having problems. The lawmaking for this article is in the primary co-operative.

CirclesAndRotatorsApp.OnInit is modified to place code in a endeavour-catch cake because additional exceptions may be thrown. The grab block just displays a message box with the text of the exception. For a full discussion of handling exceptions in wxWidgets programs, see C++ Exceptions and wxWidgets. Here is the OnInit code:

bool CirclesAndRotatorsApp::OnInit() {     try {         CirclesAndRotatorsFrame* mainFrame = new CirclesAndRotatorsFrame(nullptr, 50"Circles and Rotators");         mainFrame->Prove(true);     }     grab (std::exception& due east) {         wxMessageBox(e.what(), "CirclesAndRotators");     }     return truthful; }

The CirclesAndRotatorsFrame constructor creates a CirclesAndRotatorsCanvas object that is 800 by 800 pixels in size. While this size can be inverse, the code in this program assumes that the canvas is square (i.e. has the same number of pixels in both x and y directions). If y'all practice not create a square canvas, you will take to modify the program to recoup.

Equally with the other programs I have shown and so far, the majority of the code is in the canvas class.

The BuildCircleVertexShader method builds the vertex shader for the circle. Here is the lawmaking:

void CirclesAndRotatorsCanvas::BuildCircleVertexShader() {     const GLchar* vertexSource =         "#version 330 core\n"         "in vec2 position;"         "void primary()"         "{"         "    gl_Position = vec4(position, 0.0, 1.0);"         "}";     m_circleVertexShader = glCreateShader(GL_VERTEX_SHADER);     glShaderSource(m_circleVertexShader, 1, &vertexSource, Zip);     glCompileShader(m_circleVertexShader);     CheckShaderCompileStatus(m_circleVertexShader, "Circumvolve Vertex Shader did non compile."); }

This unproblematic vertex shader takes the two-dimensional position of the vertex and stores information technology in the gl_Position global variable. The rest of the code you have seen in the previous posts except for the telephone call to CheckShaderCompileStatus. This method checks if the shader compiled, and throws an exception if it did not:

void CirclesAndRotatorsCanvas::CheckShaderCompileStatus(GLuint shader, const std::cord& msg) const {     // bank check shader compile status, and throw exception if compile failed     GLint status;     glGetShaderiv(shader, GL_COMPILE_STATUS, &status);     if (status != GL_TRUE) {         throw std::exception(msg.c_str());     } }

Note: The status returned by glGetShaderiv contains only GL_TRUE or GL_FALSE. If GL_FALSE, then you can telephone call glGetShaderInfoLog to retrieve information on the error. Do not expect detailed error messages. This would perhaps exist a good topic for a futurity commodity.

The BuildCircleFragmentShader method determines whether the fragment (pixel) being displayed is inside or outside the circumvolve. Here is the source code for the method:

void CirclesAndRotatorsCanvas::BuildCircleFragmentShader() {     const GLchar* fragmentSource =         "#version 330 core\n"         "uniform vec2 viewDimensions;"         "compatible float outerRadius;"         "out vec4 outColor;"         "void primary()" 	"{"     // convert fragment coordinate (i.due east. pixel) to view coordinate         "   float ten = (gl_FragCoord.x - viewDimensions.ten / ii.0f) / (viewDimensions.x / two.0f);"         "   bladder y = (gl_FragCoord.y - viewDimensions.y / 2.0f) / (viewDimensions.y / 2.0f);"     // discard fragment if outside the circle         "   float len = sqrt(ten * 10 + y * y);"         "    if (len > outerRadius) {"         "        discard;"         "    }"     // else prepare its colour to dark-green         "    outColor = vec4(0.0, 1.0, 0.0, one.0);"         "}";     m_circleFragmentShader = glCreateShader(GL_FRAGMENT_SHADER);     glShaderSource(m_circleFragmentShader, 1, &fragmentSource, Naught);     glCompileShader(m_circleFragmentShader);     CheckShaderCompileStatus(m_circleFragmentShader, "Circle Fragment Shader did not compile"); }

In order to make up one's mind if the fragment is inside or exterior the circle, we need three values: the position of the fragment, given in gl_FragCoord, the radius of the circle, given by the uniform value outerRadius, and the dimensions of the canvas. gl_FragCoord gives the location of the pixel containing the fragment, simply outerRadius is the radius of the circle in device coordinates (ten and y betwixt -one and +1), so the dimensions of the canvas are required to convert between the pixel coordinates and the device coordinates.

The lines that define x and y convert the gl_FragCoord x and y coordinates into device coordinates. The length of the vector (len) from the origin (centre of the view) to the fragment is calculated and compared with the radius of the circle. If the fragment is within the circle, then the pixel colour is set to green, and if the fragment is outside the circle, the fragment is discarded. If you lot wish, rather than discard the fragment, you could set it to a different color than green or the groundwork colour then that y'all tin can see the square that contains the circle.

The BuildCircleShaderProgram method first calls BuildCircleVertexShader and BuildCircleFragmentShader, and so links them to create the shader programme. The location of the position aspect input to the vertex shader is obtained and the enabled. The locations of the two uniform variables input to the fragment shader are obtained next, and finally, the size of the canvas is set (viewDimensions in the fragment shader). Notation: the size of the sail is not modifiable, then this compatible value needs to be fix just once. Here is the source code for the BuildCircleShaderProgram method:

void CirclesAndRotatorsCanvas::BuildCircleShaderProgram() {     // build the circumvolve shaders     BuildCircleVertexShader();     BuildCircleFragmentShader();     // create and link circle shader programme     m_circleShaderProgram = glCreateProgram();     glAttachShader(m_circleShaderProgram, m_circleVertexShader);     glAttachShader(m_circleShaderProgram, m_circleFragmentShader);     glBindFragDataLocation(m_circleShaderProgram, 0, "outColor");     glLinkProgram(m_circleShaderProgram);      // set up position aspect used in circle vertex shader     GLint posAttrib = glGetAttribLocation(m_circleShaderProgram, "position");     glEnableVertexAttribArray(posAttrib);     glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, ii * sizeof(GLfloat), 0);     // ready the compatible arguments     m_circleOuterRadius = glGetUniformLocation(m_circleShaderProgram, "outerRadius");     m_viewDimensions = glGetUniformLocation(m_circleShaderProgram, "viewDimensions");     // The sheet size is fixed (and should be foursquare), so initialize the value here     glUseProgram(m_circleShaderProgram);     wxSize canvasSize = GetSize();     glUniform2f(m_viewDimensions, static_cast(canvasSize.10),         static_cast(canvasSize.y)); }

The fragment shader determines if the a fragment is within or outside of the circumvolve. Only to get a fragment to determine this, we have to define the foursquare that contains the circumvolve. This is done in the CreateSquareForCircleMethod:

void CirclesAndRotatorsCanvas::CreateSquareForCircle() {     // define vertices for the ii triangles     float points[] = {         -0.2f, -0.2f,         0.2f, -0.2f,         0.2f, 0.2f,         -0.2f, 0.2f     };     // define the indices for the triangles     GLuint elements[] = {         0, 1, 2,         2, 3, 0     };      // setup vertex assortment object     glGenVertexArrays(one, &m_circleVao);     glBindVertexArray(m_circleVao);     // upload vertex data     glGenBuffers(1, &m_circleVbo);     glBindBuffer(GL_ARRAY_BUFFER, m_circleVbo);     glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);     // upload element data     glGenBuffers(ane, &m_circleEbo);     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_circleEbo);     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW); }

Await a minute. To form a square, we need two triangles which accept a total of 6 vertices, only the points array merely contains four vertices. Yes, that is true, simply note that the first triangle is divers by the vertices v0, v1, and v2, and the 2d triangle is defined by the vertices v2, v3, and v0. Rather than requiring that these vertices be restated, OpenGL has the concept of elements. Notation that the elements array contains 6 values that correspond to the six vertex numbers for the 2 triangles. Below these definitions, there are two calls to glBindBuffer and glBufferData, one for the points array that specifies the blazon as GL_ARRAY_BUFFER, and the second for the elements array that specifies the type equally GL_ELEMENT_ARRAY_BUFFER. This tells the GPU that the first buffer contains vertex data and the 2nd buffer contains an assortment that specifies which vertices to use when drawing.

That seems like extra work, and more buffer infinite in the GPU. Assuming 4 bytes required for each float and each unsigned int in the GPU, defining 6 vertices results in 48 bytes of buffer space. Using 4 vertices and 6 elements, in that location is a total of 56 bytes, so there is more buffer space required in this case. But what happens if the vertices are specified in 3D, as would exist the case in most OpenGL programs? For only these ii triangles, specifying the triangles using merely the points array uses 72 bytes; specifying the triangles using both points and elements arrays, over again uses 72 bytes. At present add a tertiary triangle that shares ii vertices with other triangles: we have 108 bytes versus 96 bytes. As the number of shared vertices increases, the use of elements increases the savings. Since a normal OpenGL program will define hundreds or fifty-fifty thousands of attached triangles, the saving can get quite substantial.

Finally, here is the code that draws the rectangle resulting in the circle:

void CirclesAndRotatorsCanvas::OnPaint(wxPaintEvent& upshot) {     SetCurrent(*m_context);     // set up groundwork to black     glClearColor(0.0, 0.0, 0.0, i.0);     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     // use the circleShaderProgram     glUseProgram(m_circleShaderProgram);     // set outer radius for circle here. We will be modulating it in later     // example     glUniform1f(m_circleOuterRadius, 0.2f);     // draw the square that will comprise the circle.     // The circle is created within the foursquare in the circle fragment shader     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);     glFlush();     SwapBuffers(); }

We have seen about of this before. The simply differences are the three lines:

          glUseProgram(m_circleShaderProgram);     glUniform1f(m_circleOuterRadius, 0.2f);     glDrawElements(GL_TRIANGLES, half dozen, GL_UNSIGNED_INT, 0);

In previous example programs, glUseProgram was called immediately after the shader program was linked. In those cases, simply one shader program was created. As we develop the CirclesAndRotators program farther, we volition create additional shader programs. One shader plan volition be used for drawing some shapes, and additional shader programs will be used when drawing other shapes. Hence, nosotros need to telephone call glUseProgram for the advisable shader program earlier drawing any objects that utilize that shader plan.

If previous examples, glDrawArrays was called to draw the objects. If we chosen glDrawArrays here, but i triangle would be drawn. Try it and meet. To use elements, we must telephone call glDrawElements instead.

The simply remaining job is to release the GPU resources in the CirclesAndRotatorsCanvas destructor.

Here is the resulting brandish:
openglcentrecircle

The source lawmaking for the program is provided in the primary branch on GitHub.

Related Articles

This commodity is built on code and information that was provided in:

  • OpenGL Shaders

which in turn was congenital from code and information provided in:

  • Creating wxWidgets Programs with Visual Studio 2017 – Part 1
  • Visual Studio, wxWidgets and OpenGL
  • Hello Triangle: OpenGL with wxWidgets

The code provided is farther developed in:

  • Device Coordinates and Object Coordinates
  • Moving the Circle Off Centre
  • Two Rotating Circles
  • Adding a Moving Triangle
  • Circles and Rotators Blueprint and Coding Decisions

engleying1986.blogspot.com

Source: https://computingonplains.wordpress.com/drawing-circles-with-opengl/

Postar um comentário for "Draw Half Circle in Opengl"