基于MFC框架的OpenGL绘图:画直线(详解版)


前言

这是上一篇文章的详解版本,容我和大家详细地分享一下画线思路
依然老鹅


一、前期准备

1.成员变量

	// 背景网格的间隔
	const float GRIDGAP = 0.1f;
	
	// 记录起始点
	CPoint startPoint;
	CPoint endPoint;

	// 判断绘图状态
	bool isDrawing;

	// 保存画好的线
	int lineCount;
	CPoint linePoints[1000][2];

2.鼠标响应事件

鼠标按下时,准备绘制当前线段。将isDrawing置为true,并记录起点位置。嗯……顺便把终点位置也放到那

void CMy22uCGv1View::OnLButtonDown(UINT nFlags, CPoint point)
{
	isDrawing = true;
	startPoint = point;
	endPoint = point;

	Invalidate(false);
	COpenGLView::OnLButtonDown(nFlags, point);
}

鼠标移动时,只要在画图,就让当前线段的尾部跟着移动

void CMy22uCGv1View::OnMouseMove(UINT nFlags, CPoint point)
{
	if (isDrawing) 
	{
		endPoint = point;
		Invalidate(false);
	}
	COpenGLView::OnMouseMove(nFlags, point);
}

鼠标抬起时,结束绘制,并保存当前线段

void CMy22uCGv1View::OnLButtonUp(UINT nFlags, CPoint point)
{
	isDrawing = false;
	endPoint = point;

	linePoints[lineCount][0] = startPoint;
	linePoints[lineCount][1] = endPoint;
	lineCount++;

	Invalidate(false);
	COpenGLView::OnLButtonUp(nFlags, point);
}

二、正式画图

1. 调用Display()函数

我们所有的画图操作都会丢在这里面进行

void CMy22uCGv1View::Display()
{
	// 清空屏幕
	glClearColor(0.2f, 0.2f, 0.2f,1.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	// 画网格
	DrawGrids();

	// 画线
	DrawLines();
}

2. 画网格

总之先把背景的网格画出来

void CMy22uCGv1View::DrawGrids()
{
	// 获取视口的宽高
	CRect rc;
	GetClientRect(rc);
	float width = rc.right - rc.left;
	float height = rc.bottom - rc.top;

	// 计算画线数量
	float wNum = (int)(width / height * 10) / 10.f;

	// 调好颜色开画
	// OpenGL坐标系见上一篇文章
	glColor3f(0.5f, 0.5f, 0.5f);
	glBegin(GL_LINES);
	
	for (float i = -wNum; i < wNum + 0.0001f; i += GRIDGAP)
	// 因为是float类型所以需要加上一个小值作误差
	// GRIDGAP是之前定义的浮点型常量
	{
		// 竖线
		glVertex2f(i, -1);
		glVertex2f(i, 1);
	}
	for (float i = -1; i < 1.0001f; i += GRIDGAP)
	{
		// 横线
		glVertex2f(-wNum, i);
		glVertex2f(wNum, i);
	}
	glEnd();
}

画出来的效果就像这样:
网格背景

3. 画线

用OpenGL的glvertex方法画线。注意要把当前正在画的线(isDrawing为true)和已经画好的线都画出来

void CMy22uCGv1View::DrawLines()
{
	// 拖拽线
	glColor3f(0.95f, 0.95f, 0.95f);
	if (isDrawing)
	{
		glBegin(GL_LINES);
		// 坐标转换
		glVertex2f(ChangePos(startPoint.x, 1), ChangePos(startPoint.y, 2));
		glVertex2f(ChangePos(endPoint.x, 1), ChangePos(endPoint.y, 2));
		
		// 不转换就看不见
		// glVertex2f(startPoint.x, startPoint.y);
		// glVertex2f(endPoint.x, endPoint.y);
		glEnd();
	}
	
	// 已画线
	for (int i = 0; i < lineCount; i++)
	{
		float x0 = ChangePos(linePoints[i][0].x, 1);
		float y0 = ChangePos(linePoints[i][0].y, 2);
		float x1 = ChangePos(linePoints[i][1].x, 1);
		float y1 = ChangePos(linePoints[i][1].y, 2);

		// 把线画上
		glBegin(GL_LINES);
		glVertex2f(x0, y0);
		glVertex2f(x1, y1);
		glEnd();

		// DDA算法画圆
		DrawLine_DDA(x0, y0, x1, y1);
	}
}

这里大的来了,它就是——坐标变换
因为之前记录的startPoint和endPoint点都是屏幕坐标,随随便便就几十上百了,直接画会超出视口显示范围,导致啥也看不见,所以得先把它们转成视口坐标。这里是写了一个ChangePos()函数

float CMy22uCGv1View::ChangePos(float num, int mode) 
// 用mode判断这是x坐标还是y坐标
{
	float n = num;
	CRect rc;
	GetClientRect(rc);
	float width = rc.right - rc.left;
	float height = rc.top - rc.bottom;
	if (mode == 1) 
		// n = n / width * 4.5376 - 2.2688;
		// n = -(n / width * width / height * 2 - width / height);
		n = (width - 2 * n) / height;
	else 
		n = n / height * 2 + 1;
	return n;
}

现在就能看到我们辛辛苦苦画的线了
这线条多是一件美事啊

4. DDA算法

最后是用DDA算法沿线画圆。关于算法本身的介绍可以看上篇文章里贴的链接

void CMy22uCGv1View::DrawLine_DDA(float x0, float y0, float x1, float y1)
{
	float dx = x1 - x0;
	float dy = y1 - y0;
	float k = dy / dx;
	bool flag = false;
	if (k > 1 || k < -1) {
		flag = true;
		k = dx / dy;
		Swap(x0, y0, x1, y1);
	}
	
	// 起点调整,把圆心放在网格交点上
	float adjust = x0 >= 0 ? 1 : -1;
	float x = int(x0 / GRIDGAP + adjust * GRIDGAP * 5) * GRIDGAP;
	adjust = y0 >= 0 ? 1 : -1;
	float y = int(y0 / GRIDGAP + adjust * GRIDGAP * 5) * GRIDGAP;
	if (!flag)
		DrawCircle(x, y);
	else
		DrawCircle(y, x);

	if (x0 <= x1) // 往右 
	{
		while (x < x1) {
			x += GRIDGAP;
			if (x > x1) {
				// 终点调整
				if (x1 < (x - 0.5f * GRIDGAP)) break;
			}
			y0 += k * GRIDGAP;
			if (k >= 0) {
				if (y0 >= y + GRIDGAP * 0.5f) {
					y += GRIDGAP;
				}
			}
			else {
				if (y0 <= y - GRIDGAP * 0.5f) {
					y -= GRIDGAP;
				}
			}
			if (!flag)
				DrawCircle(x, y);
			else
				DrawCircle(y, x);
		}
	}
	else // 往左 
	{
		while (x > x1) {
			x -= GRIDGAP;
			if (x < x1) {
				// 终点调整
				if (x1 > (x + 0.5f * GRIDGAP)) break;
			}
			y0 -= k * GRIDGAP;
			if (k <= 0) {
				if (y0 >= y + GRIDGAP * 0.5f) {
					y += GRIDGAP;
				}
			}
			else {
				if (y0 <= y - GRIDGAP * 0.5f) {
					y -= GRIDGAP;
				}
			}
			if (!flag)
				DrawCircle(x, y);
			else
				DrawCircle(y, x);
		}
	}
}

画圆的函数DrawCircle()长这样:

void CMy22uCGv1View::DrawCircle(float x, float y)
{
	glBegin(GL_TRIANGLE_FAN);
	for (int i = 0; i <= 360; i += 30) {
		float p = (float)(i * 3.14 / 180);
		glVertex2f(x + (float)sin(p) * GRIDGAP * 0.5f, y + (float)cos(p) * GRIDGAP * 0.5f);
	}
	glEnd();
}

交换坐标的函数Swap()长这样:

void CMy22uCGv1View::Swap(float &x0, float &x1, float &y0, float &y1)
{
	float tmp = x0;
	x0 = x1;
	x1 = tmp;
	tmp = y0;
	y0 = y1;
	y1 = tmp;
}

这下终于可以画出本文开头一样的效果了
听我说谢谢你


总结

这就是我在MFC框架中用OpenGL画线的全过程了,希望对你有帮助~

  • 1
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值