好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

OpenGL光栅化作业:【bresenham算法】GL_POINTS为基础画圆

As usual先读题。

2.3 Draw a Circle

Implement your circle rasterization algorithm in OpenGL. You can only use integer arithmetic in your code.

Input: A point as the center of a circle and a radius, that make 3 integers Output: A circle in a window You can only use the  GL_POINTS  as primitives. Others primitives or build-in draw is not allowed to use.

嘛,所以其实也是跟前面画线段的算法一样,也是用GL_POINTS画出足够多且密集的离散点在视觉意义上产生一个圆形。有很多类似的地方,控制输入范围为[-500,500]区间的整数再规格化到[-1,1]的浮点数等等,这些分析,就不废话了直接看前面那篇讲画线段的前半截就好。

由于在画圆方面没有什么思路,所以参考了一下网上的画圆算法。注明参考出处:http://www.cnblogs.com/gamesky/archive/2012/09/03/2668932.html

并且

由于数学确实差(……)所以也没有做算法推导,直接用了以上这篇博文推导出的结论。也就是:

【先假设圆心的坐标为原点】。

先算出从(0, r)到(r/√2, r/√2)之间的1/8圆弧的点的坐标值,其他部分直接用轴对称和中心对称变换,最后画出完整的圆形。

也就是计算出那特定的1/8圆弧上的点的坐标(x,y),再绘制出相应的(x, -y), (-x, y), (-x, -y), (y, x), (-y, x), (y, -x), (-y, -x)。

 

而那1/8圆弧上因为x上的变化比y大,所以在x方向上取样,在y方向上量化。递推思想就是:

d0=1-r

if d<0  d=d+2*x+3

if d>=0  d=d+2*(x-y)+5, y=y-1

x=x+1

 

并且直接在glBegin()和glEnd()之间用一个while(y>x)的循环,绘制出(x,y)和它相应的对称点就好,也不需要用画线段时导入数组的办法了(毕竟哪里一不小心就下标越界还占内存orz)。当然这里在调用glVertex的时候传入的参数(1)首先要是浮点数,所以仍然要做一个规格化处理normalize()。(2)要考虑到圆心坐标其实未必是原点的情况,对得到的浮点数坐标还要做一个浮点圆心坐标(fcx, fcy)的平移。

也就是

   while (y> x) {
                  float  fx= normalize(x);
                  float  fy= normalize(y);
                  //  (x,y) (-x,-y) (-x,y) (x,-y) 
                glVertex2f(fcx+fx,fcy+ fy);
                glVertex2f(fcx -fx,fcy- fy);
                glVertex2f(fcx -fx,fcy+ fy);
                glVertex2f(fcx +fx,fcy- fy);
                
                  //  (y,x) (-y,-x) (-y,x) (y,-x) 
                glVertex2f(fcx+fy,fcy+ fx);
                glVertex2f(fcx -fy,fcy- fx);
                glVertex2f(fcx -fy,fcy+ fx);
                glVertex2f(fcx +fy,fcy- fx);
                
                  if (d< 0  ) {
                    d =d+ 2 *x+ 3  ;
                }   else   {
                    d =d+ 2 *(x-y)+ 5  ;
                     -- y;
                }
                 ++ x;
            } 

其次就是画完y>x部分的1/8圆弧和与其对称的圆弧之外,我发现这样写其实没有考虑到x=y,x=-y的点的情况。虽然这样圆看起来也没什么影响,但由于强迫症,所以还是加上一段:

             //  need the 4 points on the diagonal 
             float  diagonal= float (r)/sqrt( 2  );
            glVertex2f(fcx +diagonal,fcy+ diagonal);
            glVertex2f(fcx -diagonal,fcy+ diagonal);
            glVertex2f(fcx +diagonal,fcy- diagonal);
            glVertex2f(fcx -diagonal,fcy+diagonal);

不过我想了一下,在前面的while循环判断条件改成y>=x可能就好了,虽然可能会出现重复绘制的情况(……),不过写都写了就这样吧呵呵呵呵呵呵呵

 

然后如果看了前面那篇参考博文的内容,其他反正都挺好懂的就不讲废话了直接上完全代码。

  1  #include <iostream>
  2  #include <math.h>
  3  #include <GLFW/glfw3.h>
  4   using   namespace   std;
   5   //  g++ -o demo circle.cpp -lglfw3 -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo 
  6   float  normalize( int   input) {
   7       return   float (input)/ 500  ;
   8   }
   9  
 10   //  -400 100 200 
 11   int   main() {
  12       int   r, cx,cy;
  13      cout <<  "  Please give the coordinate of the center of the circle? like 'x y'  "  <<  endl
  14      <<  "  Notice: x and y should be integers between 0 and 500  "  <<  endl;
  15      cin >> cx >>  cy;
  16      cout <<  "  Please give the radius r of the circle  "  <<  endl
  17      <<  "  Notice: r should be an integer between 0 and 500  "  <<  endl;
  18      cin >>  r;
  19       float  fcx= normalize(cx);
  20       float  fcy= normalize(cy);
  21      
 22       int  x= 0 ,y= r;
  23       int  d= 1 - r;
  24      
 25      
 26       bool  init =  true  ;
  27       if (! glfwInit()) {
  28          cout <<  "  Initialization failed  "  <<  endl;
  29          init =  false  ;
  30           return  - 1  ;
  31       }
  32      
 33       //  create a window with its OpenGL context 
 34      GLFWwindow* window1 = glfwCreateWindow( 640 ,  640 ,  "  Circle  "  , NULL, NULL);
  35      
 36       if  (! window1) {
  37          cout <<  "  Window or OpenGL context creation failed  "  <<  endl;
  38           glfwTerminate();
  39           return  - 1  ;
  40       }
  41       if (init&& window1) {
  42           //   Make the window's context current */ 
 43           glfwMakeContextCurrent(window1);
  44          
 45           while  (! glfwWindowShouldClose(window1)) {
  46               //   Keep running 
 47               /*   Draw a triangle   */ 
 48               glBegin(GL_POINTS);
  49              
 50              glColor3f( 1 ,  0.52 ,  0.0 );     //   Orange
  51               //  glVertex2f 
 52              
 53               while (y> x) {
  54                   float  fx= normalize(x);
  55                   float  fy= normalize(y);
  56                   //  (x,y) (-x,-y) (-x,y) (x,-y) 
 57                  glVertex2f(fcx+fx,fcy+ fy);
  58                  glVertex2f(fcx-fx,fcy- fy);
  59                  glVertex2f(fcx-fx,fcy+ fy);
  60                  glVertex2f(fcx+fx,fcy- fy);
  61                  
 62                   //  (y,x) (-y,-x) (-y,x) (y,-x) 
 63                  glVertex2f(fcx+fy,fcy+ fx);
  64                  glVertex2f(fcx-fy,fcy- fx);
  65                  glVertex2f(fcx-fy,fcy+ fx);
  66                  glVertex2f(fcx+fy,fcy- fx);
  67                  
 68                   if (d< 0  ) {
  69                      d=d+ 2 *x+ 3  ;
  70                  }  else   {
  71                      d=d+ 2 *(x-y)+ 5  ;
  72                      -- y;
  73                   }
  74                  ++ x;
  75               }
  76               //  need the 4 points on the diagonal 
 77               float  diagonal= float (r)/sqrt( 2  );
  78              glVertex2f(fcx+diagonal,fcy+ diagonal);
  79              glVertex2f(fcx-diagonal,fcy+ diagonal);
  80              glVertex2f(fcx+diagonal,fcy- diagonal);
  81              glVertex2f(fcx-diagonal,fcy+ diagonal);
  82              
 83              x= 0  ;
  84              y= r;
  85           
 86              
 87              glEnd();

 

简单说一下在代码最初的调试更改出现过一些什么弱智问题。

当输入圆心不是原点的时候,整个圆就支离破碎裂成了好几块,裂出的圆弧位置还不一样。调试了半天发现原来是对称那部分出了问题。把该加上fy的部分和fx的部分写反了(……)。

  62                   //  (y,x) (-y,-x) (-y,x) (y,-x) 
  63                  glVertex2f(fcx+fy,fcy+ fx);
   64                  glVertex2f(fcx-fy,fcy- fx);
   65                  glVertex2f(fcx-fy,fcy+ fx);
   66                  glVertex2f(fcx+fy,fcy-fx);

还有就是圆看起来不够圆,像个椭圆。其实就是应该把窗口尺寸调整到长宽一致的正方形就好了(……),因为长宽尺寸不一致的话x轴和y轴的单位长度就不一样嘛。

 

另外就是绘制好了之后用鼠标调整窗口尺寸或者放大后,图形就消失了(——!)。对照没这个问题的线段绘制代码看了半天,原来是为了保持窗口一直显示图形,glBegin()和glEnd()之间的代码会反复循环地绘制直到窗口关闭。所以在glEnd()前要重新给x和y赋初值,不然后面就绘制不出来了。这个我以为已经改好了,刚才试着跑了一下发现交了作业的代码忘记改这个了(——!),所以临时加上去了给x和y赋初值的代码。不知道为什么看起来圆的线条粗了一些,唉不想讨论了就让往事随风(……)。

 

结果展示

 

 

查看更多关于OpenGL光栅化作业:【bresenham算法】GL_POINTS为基础画圆的详细内容...

  阅读:56次