首先是Windows10 + Visual Studio 2019 搭建OpenGL环境可以查看如下链接:
萌新向!!!Windows10 + Visual Studio 2019 搭建OpenGL环境(图文教程) - 哔哩哔哩 (bilibili.com)
//使用PC端浏览器打开,Android端似乎被删除了
一、计算机图形学第二章作业
1、题目描述
第二章 作业
1. 编程实现DDA、中点画线和Bresenham算法,对比它们的效率。
2. 编程实现中点画圆法。(重点关注算法原理和伪代码)
2、题目一
2.1关于DDA的实现
2.1.1算法原理
原理:根据直线的参数微分方程计有dy=m*dx
Step1:首先将给定端点作为了输入参数;
Step2:其次初始化,初值加上0.5保证精度;
Step3:比较起止点的水平以及垂直的差值较大的大数作为计算步数steps;
Step4:然后每步计算dx,dy分别为差数并除以steps;
Step5:最后,从起始点开始来确定相邻两点间的增量并且进行递推计算。
2.1.2算法实现
具体代码如下:
#include <Windows.h>
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <cmath>
void DDAline()
{
int x1=101,y1=101,x2=499,y2=499;
int steps;
float m, x, y, dx, dy;
x = x1 + 0.5f;
y = y1 + 0.5f;
steps = abs(x2 - x1) > abs(y2 - y1) ? abs(x2 - x1) : abs(y2 - y1);
dx = (float)(x2 - x1) / steps;
dy = (float)(y2 - y1) / steps;
//计算直线斜率和截距
m = (float)(y2 - y1) / (float)(x2 - x1);
float b = y2 - m * x2;
glClear(GL_COLOR_BUFFER_BIT);
glPointSize(1.0f);
glBegin(GL_POINTS);
for (int i = 0; i < steps; i++) {
glVertex2f((x + 400 / 2)/400.0-1.6, (400 / 2 - y)/400.0+0.5);
x=x+dx;
y=y+dy;
}
glEnd();
glFlush();
}
int main(int argc, char * argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("DrawLineByDDA");
glutDisplayFunc(&DDAline);
glutMainLoop();
return 0;
}
2.1.3结果展示
2.2中点画线
2.2.1算法原理
原理:构造判别式:d=F(M)=F(xp+1,yp+0.5) =a(xp+1)+b(yp+0.5)+c
其中a=y0-y1, b=x1-x0, c=x0 y 1-x1 y 0
当d<0,M 在L(Q点 )下方,取右上方P 2为下一个象素
当d>0,M 在L(Q点 )上方,取右下方P 1为下一个象素
当d=0,选P 1 或 P 2均可,约定取P 1为下一个象素
Step1:画线从(x0, y0 )开始,d的初值 d 0=F(x0+1, y0+0.5)=F(x0, y0)+a+0.5b =a+0.5b。 a=y0-y1, b=x1-x0
可以用2d代替d来摆脱小数,提高效率。
Step2:令 d 0=2a+b, d1=2a, d2=2a+2b,我们有如下算法 。
Step3:若当前象素处于d ³ 0情况,则取正右方象素P1 (xp+1, yp), 要判下一个象素位置,应计算 d 1=F(xp+2, yp+0.5)=a(xp+2)+b(yp+0.5)=d+a; 增量为a
• 若d<0时,则取右上方象素P 2 (xp+1, yp+1)。要判断再下一象素,则要计算 d 2= F(xp+2, yp+1.5)=a(xp+2)+b(yp+1.5)+c=d+a+b ;增量为a+b
Step4:画出对应的点
2.2.2算法实现
具体代码如下:
#include <Windows.h>
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <cmath>
float X[65536], Y[65536];
int cnt = 0;
void MidPointline(int x1, int y1, int x2, int y2)
{
int a, b, d1, d2, d, x, y;
a = y1 - y2;b = x2 - x1;d = 2 * a + b;
d1 = 2 * a;d2 = 2 * (a + b);
x = x1; y = y1;
X[0] = x;Y[0] = y;cnt++;
while (x < x2)
{
if (d < 0)
{
x++;y++;d += d2;
}
else
{
x++;d += d1;
}
X[cnt] = x;Y[cnt] = y;cnt++;
}
}
void midPoints()
{
glClear(GL_COLOR_BUFFER_BIT);
glPointSize(1.0f);
glBegin(GL_POINTS);
for (int i = 0; i < cnt; i++) {
glVertex2f((X[i] + 400 / 2) / 400.0 - 1.6, (400 / 2 - Y[i]) / 400.0 + 0.5);
}
glEnd();
glFlush();
}
int main(int argc, char* argv[])
{
MidPointline(101, 101, 499, 499);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("DrawLineBymidPoints");
glutDisplayFunc(&midPoints);
glutMainLoop();
return 0;
}
2.2.3结果展示
2.3实现Bresenham算法
2.3.1算法原理
原理如下:
Step1:设定interChange、Xsign、Ysign便于判断位置并计算误差初值e = 2 * min - max;(min和max分别为水平距离和垂直距离的最值)
Step2:设置点(Xi, Yi) 的颜色值,并求下一误差ei+1;
If ei >= 0 then ei+1 =ei+m-1;
else ei+1 = ei + m;
Step3:根据不同象限,确定X和Y变化符号的正负,进行下一次(2)(3)循环直至结束;
2.3.2算法实现
#include <Windows.h>
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <cmath>
void B()
{
int x1 = 101, y1 = 101, x2 = 499, y2 = 499;
int x, y, dx, dy, e, xSign, ySign, interChange = 0;
dx = abs(x2 - x1);
dy = abs(y2 - y1);
float m = (float)(y2 - y1) / (float)(x2 - x1);
float b = y2 - m * x2;
if (dx < dy) {
int temp;
interChange = 1;
temp = dx;
dx = dy;
dy = temp;
}
xSign = (x2 > x1) ? 1 : -1;
ySign = (y2 > y1) ? 1 : -1;
x = x1;
y = y1;
e = 2 * dy - dx;
glClear(GL_COLOR_BUFFER_BIT);
glPointSize(1.0f);
glBegin(GL_POINTS);
for (int i = 0; i <= dx; i++) {
glVertex2f((x + 400.0 / 2) / 400.0 - 1.6, (400.0 / 2 - y) / 400.0 + 0.5);
if (e > 0) {
e = e - 2 * dx;
if (interChange)
x += xSign;
else
y += ySign;
}
if (interChange)
y += ySign;
else
x += xSign;
e = e + 2 * dy;
}
glEnd();
glFlush();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("DrawLineByBresenham");
glutDisplayFunc(&B);
glutMainLoop();
return 0;
}
2.3.3结果展示
2.4三种方法效率的比较
DDA算法:
复杂度:加法+取整
优点:避免了y=kx+b 方程中的浮点乘法。
缺点:需浮点数加法及取整运算,不利于硬件实现。
中点算法:
中点算法与DDA相比,主要是加法运算和浮点数运算,但是优化后,省去了浮点运算。
主要优点:算法简单,无乘除,只有移位操作,尤其适用硬件实现
Bresenham算法:
Bresenham算法是计算机图形学使用最广泛的直线光栅化算法。Bresenham算法是每个象素只需一个整数加法,其优点还有就是可以用于其他二次曲线。
3、题目二
3.1算法原理
中点画圆法利用的也是类似于Bresenham直线算法的思想,利用判别式选择像素,只需做简单的整数运算。
在平面直角坐标系的四个象限中第二象限的1/8圆为例,若确定了一个像素点为($x_{p},y_{p}$),那么下一个点要么是右方的P1,要么是右下方的P2。
构造函数一个函数F(x,y)=$x^{2}$+$y^{2}$-$R^{2}$,当F大于0时,点在圆外,反之则在圆内。
图中的M是P1和P2的中点,所以M=($x_{p}$+1,$y_{p}-0.5$),当F(M)<0时,M在圆内,说明P1离圆弧更近,反之M在圆外,P2更近。
根据以上原理,可构造判别式:
当$d_{p}$<0时,取P1为下一像素,下一像素判别式为:
当$d_{p}$>0时,取P2为下一像素,下一像素判别式为:
我们按顺时针方式生成八分圆,所以第一个像素为(0,R),初始判别式为:
1.25-R可以简化成1-R,去除浮点数运算,因为运算过程中增量都为整数,所以减去0.25是不会影响符号的。
3.2算法实现
#include <Windows.h>
#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <cmath>
float X[65536], Y[65536];
const float R = 400.0;
int cnt = 0;
void CirclePoints(int x, int y, int offx, int offy)//利用对称性画整圆
{
X[cnt] = (x + offx) / R; Y[cnt] = (y + offy) / R;cnt++;
X[cnt] = (y + offx) / R; Y[cnt] = (x + offy) / R;cnt++;
X[cnt] = (x + offx) / R; Y[cnt] = (-y + offy) / R;cnt++;
X[cnt] = (-y + offx) / R; Y[cnt] = (x + offy) / R;cnt++;
X[cnt] = (-x + offx) / R; Y[cnt] = (y + offy) / R;cnt++;
X[cnt] = (y + offx) / R; Y[cnt] = (-x + offy) / R;cnt++;
X[cnt] = (-x + offx) / R; Y[cnt] = (-y + offy) / R;cnt++;
X[cnt] = (-y + offx) / R; Y[cnt] = (-x + offy) / R;cnt++;
}
void MidPointCircle(int x1, int y1, int r)
{
int x, y, e;
x = 0; y = r; e = 1 - r;
CirclePoints(x, y, x1, y1);
while (x <= y)
{
if (e < 0)
e += 2 * x + 3;
else
{
e += 2 * (x - y) + 5;
y--;
}
x++;
CirclePoints(x, y, x1, y1);
}
}
void B()
{
glClear(GL_COLOR_BUFFER_BIT);
glPointSize(1.0f);
glBegin(GL_POINTS);
for(int i=0;i<cnt;i++)
glVertex2f(X[i],Y[i]);
glEnd();
glFlush();
}
int main(int argc, char* argv[])
{
MidPointCircle(0, 0, 380);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("DrawMidPointCircle");
glutDisplayFunc(&B);
glutMainLoop();
return 0;
}
3.3结果展示
二、参考资料
[1]萌新向!!!Windows10 + Visual Studio 2019 搭建OpenGL环境(图文教程) - 哔哩哔哩 (bilibili.com)