1、用OpenGL实现微分法(DDA)直线绘制算法绘制一条直线,并用鼠标写出自己的名字。
#include <GL/glut.h>
#include <math.h>
#define MAX_LINES 100 // lines数组能保存的最大直线数量
bool isDrawing = false; // 是否正在进行绘制
int startX, startY; // 起始点坐标
int currentX, currentY; // 当前点坐标
int lines[MAX_LINES][4]; // 已绘制的直线,每条直线使用4个整数表示
int lineCount = 0; // 已绘制的直线数量
void DDALine(int x0,int y0,int x1,int y1) //书本DDA算法绘制直线
{
glLineWidth(5.0f);
glBegin(GL_LINE_STRIP);
int dx, dy, eps1, k;
float x, y, xIncre, yIncre;
dx = x1 - x0;
dy = y1 - y0;
x = x0;
y = y0;
if (abs(dx) > abs(dy))
eps1 = abs(dx);
else
eps1 = abs(dy);
xIncre = (float)dx / (float)eps1;
yIncre = (float)dy / (float)eps1;
for (k = 0; k <= eps1; k++)
{
glVertex2f(int(x + 0.5), int(y + 0.5));
x += xIncre;
y += yIncre;
}
glEnd();
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0f, 0.0f, 0.0f);
// 绘制已保存的直线
for (int i = 0; i < lineCount; i++)
{
DDALine(lines[i][0], lines[i][1], lines[i][2], lines[i][3]);
}
if (isDrawing)
DDALine(startX, startY, currentX,currentY);
glFlush();
}
void mouse(int button, int state, int x, int y)
{
int height = glutGet(GLUT_WINDOW_HEIGHT); // 获取窗口高度
if (button == GLUT_LEFT_BUTTON)
{
if (state == GLUT_DOWN)
{
startX = x;
startY = height - y - 1;
currentX = startX;
currentY = startY;
isDrawing = true;
}
else if (state == GLUT_UP)
{
isDrawing = false;
lines[lineCount][0] = startX; //每次绘制完,将直线的两点保存到lines数组中
lines[lineCount][1] = startY;
lines[lineCount][2] = currentX;
lines[lineCount][3] = currentY;
lineCount++;
}
}
glutPostRedisplay();
}
void motion(int x, int y)
{
int height = glutGet(GLUT_WINDOW_HEIGHT); // 获取窗口高度
if (isDrawing)
{
currentX = x;
currentY = height - y - 1;
}
glutPostRedisplay();
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, 0, height);
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(1280, 720);
glutCreateWindow("DDALine算法绘制线段");
glClearColor(1.0f, 1.0f, 1.0f, 0.0);
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
第一道题目用DDA算法绘制直线,DDA算法实现实现的绘制并不难,只要把书本中的代码中的putpixel部分换成opengl库中绘制顶点的glVertex2f函数就行了。
由于是第一次绘制直线,难的在直线如何绘制?考虑鼠标的捕捉,如果鼠标左键按下就要记住当前的直线起点位置,需要注意的是,x轴的坐标与屏幕上的x轴坐标一致,但y轴的坐标需要通过openGL窗口的高度减去Windows窗口的鼠标光标的坐标,才能得出正确的y轴坐标。依次赋值给startX和startY。然后再把获取的坐标赋值给当前x、y的坐标currentX和currentY,然后在每次鼠标左键没有被按下的时候,需要将直线的起点和终点分别保存到一个lines数组中,这个数组是100行四列的数组,记载了最多能在窗口上绘制100条直线。数组作为记忆,以便绘制的时候,每绘制一条直线,它不会消失。在DDAline函数中,第一行我将绘制线条的线宽度设置为5.0f,用glLineWidth函数实现。
在鼠标移动motion函数记载中也是要获取当前x、y的坐标,赋值给currentX和currentY,也需要注意y轴的坐标需要通过窗口高度减去y轴坐标。
还要注意最后执行一个reshape函数调整下窗口进行矩阵投影,即窗口再整形回调函数reshape。
2.用OpenGL实现Bresenham直线绘制算法绘制一条直线,并用鼠标写出自己的名字。
#include <GL/glut.h>
#include <math.h>
#define MAX_LINES 100 // lines数组能保存的最大直线数量
bool isDrawing = false; // 是否正在进行绘制
int startX, startY; // 起始点坐标
int currentX, currentY; // 当前点坐标
int lines[MAX_LINES][4]; // 已绘制的直线,每条直线使用4个整数表示
int lineCount = 0; // 已绘制的直线数量
void BresenhamLine0to1(int x0, int y0, int x1, int y1)
{
glLineWidth(5.0f);
glBegin(GL_LINE_STRIP);
int dx = x1 - x0;
int dy = y1 - y0;
int e = -dx;
int x = x0;
int y = y0;
if (dx >= 0)
{
for (; x <= x1; x++)
{
glVertex2f(x, y);
e = e + 2 * dy;
if (e > 0)
{
y++;
e = e - 2 * dx;
}
}
}
else
{
for (; x >= x1; x--)
{
glVertex2f(x, y);
e = e - 2 * dy;
if (e > 0)
{
y--;
e = e + 2 * dx;
}
}
}
glEnd();
}
void BresenhamLine1ToInfinity(int x0, int y0, int x1, int y1)
{
glLineWidth(5.0f);
glBegin(GL_LINE_STRIP);
int dx = x1 - x0;
int dy = y1 - y0;
int e = -dy;
int x = x0;
int y = y0;
if (dx >= 0)
{
for (; y <= y1; y++)
{
glVertex2f(x, y);
e = e + 2 * dx;
if (e > 0)
{
x++;
e = e - 2 * dy;
}
}
}
else
{
for (; y >= y1; y--)
{
glVertex2f(x, y);
e = e - 2 * dx;
if (e > 0)
{
x--;
e = e + 2 * dy;
}
}
}
glEnd();
}
void BresenhamLineMinus1to0(int x0, int y0, int x1, int y1)
{
glLineWidth(5.0f);
glBegin(GL_LINE_STRIP);
int dx = x1 - x0;
int dy = y1 - y0;
int e = -dx;
int x = x0;
int y = y0;
if (dx >= 0)
{
for (; x <= x1; x++)
{
glVertex2f(x, y);
e = e - 2 * dy;
if (e >= 0)
{
y--;
e = e - 2 * dx;
}
}
}
else
{
for (; x >= x1; x--)
{
glVertex2f(x, y);
e = e + 2 * dy;
if (e >= 0)
{
y++;
e = e + 2 * dx;
}
}
}
glEnd();
}
void BresenhamLineMinusInfinityToMinus1(int x0, int y0, int x1, int y1)
{
glLineWidth(5.0f);
glBegin(GL_LINE_STRIP);
int dx = x1 - x0;
int dy = y1 - y0;
int e = -dy;
int x = x0;
int y = y0;
if (dx >= 0)
{
for (; y >= y1; y--)
{
glVertex2f(x, y);
e = e + 2 * dx;
if (e > 0)
{
x++;
e = e + 2 * dy;
}
}
}
else
{
for (; y <= y1; y++)
{
glVertex2f(x, y);
e = e - 2 * dx;
if (e >= 0)
{
x--;
e = e - 2 * dy;
}
}
}
glEnd();
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0f, 0.0f, 0.0f);
// 绘制已保存的直线
int dx, dy;
float k;
for (int i = 0; i < lineCount; i++)
{
int x0 = lines[i][0];
int y0 = lines[i][1];
int x1 = lines[i][2];
int y1 = lines[i][3];
dx = x1 - x0;
dy = y1 - y0;
k = (float)dy / dx;
if (k >= 0)
{
if (k <= 1)
BresenhamLine0to1(x0, y0, x1, y1);
else
BresenhamLine1ToInfinity(x0, y0, x1, y1);
}
else
{
if (k >= -1)
BresenhamLineMinus1to0(x0, y0, x1, y1);
else
BresenhamLineMinusInfinityToMinus1(x0, y0, x1, y1);
}
}
if (isDrawing)
{
int x0 = startX;
int y0 = startY;
int x1 = currentX;
int y1 = currentY;
dx = x1 - x0;
dy = y1 - y0;
k = (float)dy / dx;
if (k >= 0)
{
if (k <= 1)
BresenhamLine0to1(x0, y0, x1, y1);
else
BresenhamLine1ToInfinity(x0, y0, x1, y1);
}
else
{
if (k >= -1)
BresenhamLineMinus1to0(x0, y0, x1, y1);
else
BresenhamLineMinusInfinityToMinus1(x0, y0, x1, y1);
}
}
glFlush();
}
void mouse(int button, int state, int x, int y)
{
int height = glutGet(GLUT_WINDOW_HEIGHT); // 获取窗口高度
if (button == GLUT_LEFT_BUTTON)
{
if (state == GLUT_DOWN)
{
startX = x;
startY = height - y - 1;
currentX = startX;
currentY = startY;
isDrawing = true;
}
else if (state == GLUT_UP)
{
isDrawing = false;
lines[lineCount][0] = startX; //每次绘制完,将直线的两点保存到lines数组中
lines[lineCount][1] = startY;
lines[lineCount][2] = currentX;
lines[lineCount][3] = currentY;
lineCount++;
}
}
glutPostRedisplay();
}
void motion(int x, int y)
{
int height = glutGet(GLUT_WINDOW_HEIGHT); // 获取窗口高度
if (isDrawing)
{
currentX = x;
currentY = height - y - 1;
}
glutPostRedisplay();
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, 0, height);
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(1280, 720);
glutCreateWindow("Bresenham算法绘制线段");
glClearColor(1.0f, 1.0f, 1.0f, 0.0);
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutMotionFunc(motion);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
第二个题目是使用Bresenham算法实现鼠标的绘制。他比第一个题目难的在这个算法的实现需要用四个函数,每个函数要分两个区域。由于Bensenham算法是根据直线斜率进行判断的,需要分四个函数。斜率范围依次是0<=k<=1,-1<=k<=0,-∞<=k<-1,1<k<=+∞。而每个函数也要通过X轴变化的增量进行判断。根据书本只给出的0<=k<=1斜率变化的代码,书本只给出的是在这个斜率范围朝右向上角延伸的,并没有给出朝左下角延伸的这个区间。所以通过x轴变化的增量,用dx>0或者dx<0判断,而dx<0的部分,要对for循环中的一些正负号和变量进行变换,才能实现在这个斜率范围的直线绘制,后面每一个函数都按这样的套路来,如果正负号变量弄反了,直线是会绘制错误的,所以这部分特别耗时间。凡是以x轴的步长为循环的变量,e的初始值都为-dx,还是以y轴的部长为循环的变量e的初始值都为-dy。
在绘制完四个函数之后,再直线绘制显示的部分display函数需要将绘制的点的斜率计算出来,把每一个点的斜率计算出来,注意计算出斜率的部分k=dy/dx,需要用浮点数进行计算。根据它的斜率在负无穷~-1~0~1~正无穷,这四个区间判断该使用哪个函数。其余的步骤跟第一个题目的一样。