【计算机图形学】【实验报告】DDA画线算法、Bresenham中点画线算法、多边形填充算法(附代码)
实 验 报 告
一、实验目的
- 了解光栅化图形学的主要理论和知识。
- 了解OpenGL图形库的构成,会设计OpenGL的程序。
- 掌握基本图形的生成原理,学会设计基本图形的生成算法。包括画线算法、画圆算法和多边形填充算法的原理和实现方法。
- 设计中点画线算法,对给定的任意起点和终点的线段,采用中点画线算法设计画线程序,并能保证程序的正确性。
- 掌握多边形填充算法的基本原理和方法,掌握有序边表法和扫描线种子填充算法的原理。根据给定的类设计实现有序边表法填充多边形的程序。
二、实验内容与实验步骤
中点画线算法:
- 完成中点画线算法的设计,调用OpenGL的画点的函数,采用中点画线算法完成二维线段的绘制算法。
- 补全了lineBres函数以及pointFun函数。
- lineBres函数:一共有四个参数,表示起始点和终止点的坐标信息。分四种情况实现中点画线算法,分别是:0<=m<=1,m>=1,-1<=m<=0以及m<=-1。在函数中调用glVertex2d(x, y)函数,对每个坐标点(x,y)进行绘制。
- pointFun函数:利用glColor3f(GLfloat, GLfloat, GLfloat)函数设置画线颜色,之后调用lineBres函数绘制每条线。
- 实现后调用OpenGL的画线函数画线对比自己的实验结果。
DDA画线算法:
- 完成DDA画线算法的设计,调用OpenGL的画点的函数,采用DDA画线算法完成二维线段的绘制算法。
- 调用文件里所给的lineDDA函数,并创建与pointFun函数具有相同功能的DDALine函数。
- lineDDA函数:一共有四个参数,表示起始点和终止点的坐标信息。
- DDALine函数:利用glColor3f(GLfloat, GLfloat, GLfloat)函数设置画线颜色,之后调用lineDDA函数绘制每条线。
多边形填充算法:
- 根据给定的类设计实现有序边表法填充多边形的程序。
- 创建drawFrame()函数,用以勾画填充图形的外轮廓。
- 导入参考文件里的myclass.h以及myclass.cpp文件,调用其中的类以及函数,在main.cpp文件内创建新函数scanFill()函数,实现多边形填充。
- 在main()函数内调用drawFrame()函数以及scanFill()函数,实现描边以及填充。
三、实验环境
- 操作系统:macOS Big Sur Version 11.6
- 调试软件:Xcode Version 13.0
- 上机地点:xxx
- 机器台号:xxx
四、实验过程与分析
中点画线算法:
lineBres函数:
流程图
调试中的问题
- 环境配置错误
修改:Build Settings --> Linking --> Other Linker Flags 添加 “-framework OpenGL”
- 定义函数出错
修改:将函数改名为ro(猜测round和其他功能重名)
坐标信息
原点位置:视口中央
X正方向:视口内右方向
Y正方向:视口内上方向
视口大小:600*600
DDA画线算法:
lineDDA函数:
流程图
调试中的问题
- 最开始文件中给的循环代码为:while (x <= x2),运行结果:
缺少斜率为负的情况。
修改:将循环判定部分修改为for (int i=1; i<=epsl; i++),即可绘制出正确图案。
坐标信息
坐标原点位置:视口中央
X正方向:视口内右方向
Y正方向:视口内上方向
视口大小:600*600
多边形填充算法:
scanFill函数:
流程图
调试中的问题
- 问题:输入的数据类型和定义不符
修改:将point均改为引用类型。
- 问题:指针应采用->样式调用
修改:把head.GetNextedge改为head->GetNextedge
- 问题:运行函数后没有图形。
修改:在myDisplay()函数末尾添加glFlush()函数调用。
坐标信息
坐标原点位置:视口中央
X正方向:视口内右方向
Y正方向:视口内上方向
视口大小:600*600
五、实验结果总结
中点画线算法:
DDA画线算法:
多边形填充算法:
代码:
// 画线算法
#define GL_SILENCE_DEPRECATION
#include<iostream>
#include<GLUT/glut.h> //windows改成<GL/glut.h>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
using namespace std;
void init(void)
{
glClearColor(1.0, 1.0, 1.0, 0.0); // Set display-window color to white.
glMatrixMode(GL_PROJECTION); // Set projection parameters.
glPointSize(2.0f);
gluOrtho2D(-200, 200, -200, 200);
GLfloat sizes[2];
GLfloat step;
glGetFloatv(1.0f,sizes);
glGetFloatv(1.0f, & step);
}
int ro(const float a)
{
int b = int(a+0.5);
return b;
}
void lineDDA(int x1, int y1, int x2, int y2);
void lineBres(int x1, int y1, int x2, int y2);
void myReshape(GLsizei w, GLsizei h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-w/2, w/2, -h/2, h/2, -10, 10);
}
void myDisplay() {
glClearColor(0.0, 0.0, 1.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0);
//glRectf(100, 100, 20, 20);
lineDDA(10, 10, 200, 150);
lineBres(10, 30, 200, 170);
glFlush();
}
void lineDDA(int x1, int y1, int x2, int y2) {
float dx, dy;
int epsl;
float xIncre, yIncre;
dx = x2 - x1;
dy = y2 - y1;
float x = x1;
float y = y1;
if (abs(dx) > abs(dy))
epsl = abs(dx);
else
epsl = abs(dy);
xIncre = (float)dx / epsl;
yIncre = (float)dy / epsl;
glBegin(GL_POINTS);
for (int i=1; i<=epsl; i++)
{
glVertex2i(int(x + 0.5), int(y + 0.5));
x += xIncre;
y += yIncre;
}
}
void lineBres(int x1, int y1, int x2, int y2)
{
int x = x1;
int y = y1;
if (x1 > x2)
{
x = x1; x1 = x2; x2 = x; x = x1;
y = y1; y1 = y2; y2 = y; y = y1;
}
glBegin(GL_POINTS);
int a = y1 - y2;
int b = x2 - x1;
float k = 0;
int d;
if (x1 != x2)
{
k = -1.0 * a / b;
}
else
{
if (y2 >= 0)
{
while (y < y2)
{
glVertex2i(x, y);
y++;
}
}
else
{
while (y > y2)
{
glVertex2i(x, y);
y--;
}
}
}
if (k >= 0.0 && k < 1.0)
{
d = 2 * a + b;
while (x <= x2)
{
glVertex2i(x, y);
if (d >= 0)
{
d += 2 * a;
x++;
}
else {
d += 2 * a + 2 * b;
x++;
y++;
}
}
}
else if (k >= 1.0)
{
d = 2 * b + a;
while (y <= y2)
{
glVertex2i(x, y);
if (d >= 0)
{
d += 2 * b + 2 * a;
x++;
y++;
}
else {
d += 2 * b;
y++;
}
}
}
else if (k > -1.0 && k <= 0.0)
{
d = 2 * a - b;
while (x <= x2)
{
glVertex2i(x, y);
if (d >= 0)
{
d += 2 * a - 2 * b;
x++;
y--;
}
else {
d += 2 * a;
x++;
}
}
}
else
{
d = a - 2 * b;
while (y >= y2)
{
glVertex2i(x, y);
if (d >= 0)
{
d += -2 * b;
y--;
}
else {
d += 2 * a - 2 * b;
x++;
y--;
}
}
}
glEnd();
}
void pointFun(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_POINTS);
glColor3f(1.0, 0.0, 0.0);//红
lineBres(1, 1, 100, 50);//(1,1) (100,50)
glColor3f(1.0, 0.5, 0.0);//橙
lineBres(1, 1, 50, 100);//(1,1) (50,100)
glColor3f(1.0, 1.0, 0.0);//黄
lineBres(-1, 1, -100, 50);//(-1,1) (-100,50)
glColor3f(0.0, 1.0, 0.0);//绿
lineBres(-1, 1, -50, 100);//(-1,1) (-50,100)
glColor3f(0.0, 1.0, 1.0);//青
lineBres(-1, -1, -100, -50);//(-1,-1) (-100,-50)
glColor3f(0.0, 0.0, 1.0);//蓝
lineBres(-1, -1, -50, -100);//(-1,-1) (-50,-100)
glColor3f(1.0, 0.0, 1.0);//紫
lineBres(1, -1, 100, -50);//(1,-1) (100,-50)
glColor3f(0.0, 0.0, 0.0);//黑
lineBres(1, -1, 50, -100);//(1,-1) (50,-100)
glColor3f(0.0, 0.0, 0.0);//黑
lineBres(0, 0, 100, 0);//(0,0) (100,0)
glColor3f(0.0, 0.0, 0.0);//黑
lineBres(0, 0, 0, 100);//(0,0) (0,100)
glEnd();
glFlush();
}
void DDALine(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_POINTS);
glColor3f(1.0, 0.0, 0.0);//红
lineDDA(1, 1, 100, 50);//(1,1) (100,50)
glColor3f(1.0, 0.5, 0.0);//橙
lineDDA(1, 1, 50, 100);//(1,1) (50,100)
glColor3f(1.0, 1.0, 0.0);//黄
lineDDA(-1, 1, -100, 50);//(-1,1) (-100,50)
glColor3f(0.0, 1.0, 0.0);//绿
lineDDA(-1, 1, -50, 100);//(-1,1) (-50,100)
glColor3f(0.0, 1.0, 1.0);//青
lineDDA(-1, -1, -100, -50);//(-1,-1) (-100,-50)
glColor3f(0.0, 0.0, 1.0);//蓝
lineDDA(-1, -1, -50, -100);//(-1,-1) (-50,-100)
glColor3f(1.0, 0.0, 1.0);//紫
lineDDA(1, -1, 100, -50);//(1,-1) (100,-50)
glColor3f(0.0, 0.0, 0.0);//黑
lineDDA(1, -1, 50, -100);//(1,-1) (50,-100)
glColor3f(0.0, 0.0, 0.0);//黑
lineDDA(0, 0, 100, 0);//(0,0) (100,0)
glColor3f(0.0, 0.0, 0.0);//黑
lineBres(0, 0, 0, 100);//(0,0) (0,100)
glEnd();
glFlush();
}
void sysLine(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_LINES);
glColor3f(1.0, 0.0, 0.0);//红
glVertex2f(1, 1);
glVertex2f(100, 50);
glColor3f(1.0, 0.5, 0.0);//橙
glVertex2f(1, 1);
glVertex2f(50, 100);
glColor3f(1.0, 1.0, 0.0);//黄
glVertex2f(-1, 1);
glVertex2f(-100, 50);
glColor3f(0.0, 1.0, 0.0);//绿
glVertex2f(-1, 1);
glVertex2f(-50, 100);
glColor3f(0.0, 1.0, 1.0);//青
glVertex2f(-1, -1);
glVertex2f(-100, -50);
glColor3f(0.0, 0.0, 1.0);//蓝
glVertex2f(-1, -1);
glVertex2f(-50, -100);
glColor3f(1.0, 0.0, 1.0);//紫
glVertex2f(1, -1);
glVertex2f(100, -50);
glColor3f(0.0, 0.0, 0.0);//黑
glVertex2f(1, -1);
glVertex2f(50, -100);
glVertex2f(0, 0);
glVertex2f(100, 0);
glVertex2f(0, 0);
glVertex2f(0, 100);
glEnd();
glFlush();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv); // Initialize GLUT.
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // Set display mode.
glutInitWindowPosition(50, 100); // Set top-left display-window position.
glutInitWindowSize(600, 600); // Set display-window width and height.
glutCreateWindow("DDA画线算法"); // Create display window.
init(); // Execute initialization procedure.
glutDisplayFunc(DDALine); // Send graphics to display window.
glutMainLoop();// Send graphics to display window. // Display everything and wait.
return 0;
}
// 多边形填充算法
#define GL_SILENCE_DEPRECATION
#include <iostream>
using namespace std;
#include <GLUT/glut.h> // windows改成<GL/glut.h>
#include "myclass.h"
void drawFrame();
void scanFill();
void myReshape(GLsizei w, GLsizei h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-w / 2, w / 2, -h / 2, h / 2, -10, 10);
}
void myDisplay() {
glClearColor(1.0, 1.0, 1.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 0.0, 0.0);
//glRectf(100, 100, 20, 20);
drawFrame();
scanFill();
glFlush();
}
int main(int argc, char* argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(600, 600);
glutCreateWindow("多边形填充");
glutDisplayFunc(myDisplay);
glutReshapeFunc(myReshape);
glutMainLoop();
return 0;
}
void drawFrame(){
Point2D point_1(0,0);
Point2D point_2(-100,100);
Point2D point_3(100,100);
Point2D point_4(0,200);
glBegin(GL_LINE_LOOP);
glVertex2i(point_1.GetX(),point_1.GetY());
glVertex2i(point_2.GetX(),point_2.GetY());
glVertex2i(point_3.GetX(),point_3.GetY());
glVertex2i(point_4.GetX(),point_4.GetY());
glEnd();
}
void scanFill(){
NewEdgeTable NET;
ActiveEdgeTable AET;
ActiveEdge point1(0, -1, 100);
NET.InsertNewEdge(&point1, 0);
ActiveEdge point2(0, 1, 100);
point1.SetNext(&point2);
ActiveEdge point3(-100, 1, 200);
NET.InsertNewEdge(&point3, 100);
ActiveEdge point4(100, -1, 200);
point3.SetNext(&point4);
int i=0;
//遍历新边表
for (ActiveEdge* point : NET.rowTable){
ActiveEdge * ptr = AET.head->GetNextedge();
//遍历活性边表 删去非必要的边
while (ptr != NULL) {
if (ptr->GetMax()==i){
ActiveEdge* ptr2 = ptr;
ptr = ptr->GetNextedge();
AET.DeleteActiveEdge(ptr2);
}else{
float x = ptr->GetCurrentX()+ptr->GetDX();
ptr->SetCurrentX(x);
ptr = ptr->GetNextedge();
}
}
//放入活性边表
while (point != NULL) {
ActiveEdge* point2 = point;
point = point->GetNextedge();
AET.InsertActiveEdge(point2);
}
AET.sort();
ptr = AET.head->GetNextedge();
glBegin(GL_LINES);
glColor3f(0.0, 0.0, 0.0);
//遍历新的活性边表 填充
while (ptr!=NULL){
int x = ptr->GetCurrentX();
glVertex2i(x, i);
ptr = ptr->GetNextedge();
x = ptr->GetCurrentX();
glVertex2i(x, i);
ptr = ptr->GetNextedge();
}
glEnd();
i++;
}
}
六、附录
- 设想:可以考虑用每一步都定格一小段时间看真实的扫描过程,但因能力不足未实现。
- 资料:
- 思考题:
- Q:对比DDA画线算法,Bresenham中点画线算法有哪些优点?A:D DA算法中的y和k都必须用浮点数表示,并且每一步运算都要对y进行舍入取整,这不利于硬件实现。中点画线法只包含整数变量,并且不含乘除法,速度更快效率更高。
- Q:举例说明种子填充算法适合什么应用场景? A:如果要填充的区域是以图像元数据方式给出的,通常使用种子填充算法进行区域填充。种子填充算法需要给出图像数据的区域,以及区域内的一个点,这种算法比较适合人机交互方式进行的图像填充操作,不适合计算机自动处理和判断填色。
// 大家随便看看,当初做实验真的是被整麻了……心态一整个没有hhh。
// 要是有错误欢迎指出,用到了麻烦点个赞点个关注~
版权声明:本文为Kqp12_27原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。