提示:这里可以添加本文要记录的大概内容:
本人最近在学习计算机视觉的东西,然后在B站发现一个挺不错的课程计算机图形学全套算法讲解,于是想着每看一节课,便来总结一次,写一下博客,方便自己与后人的参考。
双缓冲是指一个显示缓冲区(显示设备上下文)和一个那日村缓冲区(内存设备上下文)。双缓冲机制是一种基本的动画技术,常用于解决单缓冲擦除图像时带来的屏幕闪烁问题。
本博客将以小球碰撞案例来展现该算法。
先上效果图:
该项目使用的编辑器是VS 2017。
打开VS2017,选择 新建项目 -> MFC 应用, 然后将项目名命名为:CV_ClassOne,点击确定,再选择 单个文档 -> MFC Standard
# CSphere.h 头文件
#pragma once
#include
class CSphere
{
public:
CSphere();
~CSphere();
public:
void SetParameter(int R, CPoint point); // 初始化小球
void Draw(CDC* pDC); // 绘制小球
void MoveCenterPoint(CPoint distance); // 移动小球
int GetRadius(); // 得到小球半径大小
CPoint GetCenterPoint(); // 得到小球圆心位置
private:
int radius; // 小球半径
CPoint centerPoint; // 小球圆心
};
# CSphere.cpp 源文件
#include "pch.h"
#include "CSphere.h"
CSphere::CSphere()
{
radius = 10;
centerPoint = CPoint(200, 100);
}
CSphere::~CSphere()
{
}
void CSphere::SetParameter(int R, CPoint point)
{
radius = R;
centerPoint = point;
}
void CSphere::Draw(CDC * pDC)
{
// 设置圆的范围
CPoint leftTop = CPoint(centerPoint.x - radius, centerPoint.y - radius); // 左上角
CPoint rightBottom = CPoint(centerPoint.x + radius, centerPoint.y + radius); // 右下角
// 设置画笔,将圆的边缘设为红色
CPen pen(PS_SOLID, 2, RGB(255, 0, 0));
pDC->SelectObject(&pen);
// 设置画刷,将圆的内部填充为红色
CBrush brush(RGB(255, 0, 0));
pDC->SelectObject(&brush);
// 绘制圆形
pDC->Ellipse(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y);
}
void CSphere::MoveCenterPoint(CPoint distance)
{
centerPoint.x += distance.x;
centerPoint.y += distance.y;
}
int CSphere::GetRadius()
{
return radius;
}
CPoint CSphere::GetCenterPoint()
{
return centerPoint;
}
相关代码如下:
// CV_ClassOneView.h: CCVClassOneView 类的接口
//
#pragma once
#include "CSphere.h"
class CCVClassOneView : public CView
{
protected: // 仅从序列化创建
CCVClassOneView() noexcept;
DECLARE_DYNCREATE(CCVClassOneView)
// 特性
public:
CCVClassOneDoc* GetDocument() const;
// 重写
public:
virtual void OnDraw(CDC* pDC); // 重写以绘制该视图
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
// 实现
public:
virtual ~CCVClassOneView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
// 生成的消息映射函数
protected:
DECLARE_MESSAGE_MAP()
// 我添加的东西如下,其它都是创建项目时就自动创建的
protected:
CSphere sphere; // 创建小球对象
CPoint distance; // 定义球的移动距离
bool displaying; // 用于判定小球的运动状态
public:
afx_msg void OnTimer(UINT_PTR nIDEvent); // 定时器,用于更新球的位置
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); // 双击鼠标左键,播放或停止小球运动
// 操作
public:
void DoubleBuffer(CDC* pDC);
void CollisionDetection();
};
#ifndef _DEBUG // CV_ClassOneView.cpp 中的调试版本
inline CCVClassOneDoc* CCVClassOneView::GetDocument() const
{ return reinterpret_cast<CCVClassOneDoc*>(m_pDocument); }
#endif
// CV_ClassOneView.cpp: CCVClassOneView 类的实现
// 函数体前注明 Modify 的即为我修改过的函数,注明 New 的即为我新添加的函数
#include "pch.h"
#include "framework.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义,并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "CV_ClassOne.h"
#endif
#include "CV_ClassOneDoc.h"
#include "CV_ClassOneView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CCVClassOneView
IMPLEMENT_DYNCREATE(CCVClassOneView, CView)
BEGIN_MESSAGE_MAP(CCVClassOneView, CView)
// 标准打印命令
ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
ON_WM_TIMER()
ON_WM_LBUTTONDBLCLK()
END_MESSAGE_MAP()
// CCVClassOneView 构造/析构
/*
Modify
*/
CCVClassOneView::CCVClassOneView() noexcept
{
int R = GetSystemMetrics(SM_CXFULLSCREEN) / 20; // 将小球半径设置为 最大化窗体的显示区域宽度的 1/20
sphere.SetParameter(R, CPoint(200, 100)); // 将小球的起始点设置为(200,100)
distance = CPoint(1, 1); // 将小球x、y方向的移动距离都定为1
displaying = false; // 初始化未播放状态
}
CCVClassOneView::~CCVClassOneView()
{
}
BOOL CCVClassOneView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
return CView::PreCreateWindow(cs);
}
// CCVClassOneView 绘图
/*
New
*/
void CCVClassOneView::DoubleBuffer(CDC* pDC)
{
// 获取当前窗口客户区(即图片中的黑色区域)的大小
CRect rect;
GetClientRect(&rect);
// 创建一个与显示DC相兼容的内存DC
CDC memDC;
memDC.CreateCompatibleDC(pDC);
CBitmap newBitMap, *oldBitMap;
newBitMap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height()); // 创建兼容内存位图,用以存储更新后的图像
oldBitMap = memDC.SelectObject(&newBitMap); // 将新位图放进内存DC中,并返回旧位图
sphere.Draw(&memDC); // 绘制新位图,并存到内存DC中
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); // 将内存DC的位图复制到显示DC用以显示在屏幕上
memDC.SelectObject(oldBitMap); // 将旧位图归还给内存DC
newBitMap.DeleteObject(); // 释放新位图资源
memDC.DeleteDC(); // 释放内存DC
}
/*
New
*/
void CCVClassOneView::CollisionDetection()
{
// 获取当前窗口客户区(即图片中的黑色区域)的大小
CRect rect;
GetClientRect(&rect);
// 获取当前小球位置信息
CPoint centerPoint = sphere.GetCenterPoint();
int radius = sphere.GetRadius();
// 根据小球边缘坐标与窗口大小位置,更新移动距离
// 注意:在窗口中,左顶点为原点,从左到右为X轴正方向,从上到下是Y轴正方向
if (centerPoint.x - radius < 0)
distance.x = 1;
if (centerPoint.x + radius > rect.Width())
distance.x = -1;
if (centerPoint.y - radius < 0)
distance.y = 1;
if (centerPoint.y + radius > rect.Height())
distance.y = -1;
}
/*
Modify
*/
void CCVClassOneView::OnDraw(CDC* pDC)
{
CCVClassOneDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// 使用双缓冲绘图算法
DoubleBuffer(pDC);
}
// CCVClassOneView 打印
BOOL CCVClassOneView::OnPreparePrinting(CPrintInfo* pInfo)
{
// 默认准备
return DoPreparePrinting(pInfo);
}
void CCVClassOneView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: 添加额外的打印前进行的初始化过程
}
void CCVClassOneView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: 添加打印后进行的清理过程
}
// CCVClassOneView 诊断
#ifdef _DEBUG
void CCVClassOneView::AssertValid() const
{
CView::AssertValid();
}
void CCVClassOneView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CCVClassOneDoc* CCVClassOneView::GetDocument() const // 非调试版本是内联的
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CCVClassOneDoc)));
return (CCVClassOneDoc*)m_pDocument;
}
#endif //_DEBUG
// CCVClassOneView 消息处理程序
/*
New
*/
void CCVClassOneView::OnTimer(UINT_PTR nIDEvent)
{
CollisionDetection(); // 检测小球当前位置是否发生碰撞,然后更新相应移动距离
sphere.MoveCenterPoint(distance); // 更新小球位置
Invalidate(FALSE); // 调用OnDraw()函数
CView::OnTimer(nIDEvent);
}
/*
New
*/
void CCVClassOneView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
displaying = !displaying; // 鼠标左键双击,即改变播放状态
if (displaying)
SetTimer(1, 10, NULL); // 如果 displaying = true, 则设置定时器开始更新小球位置,进而显示出小球的运动
else
KillTimer(1); // 如果 displaying = false,则关闭该定时器,小球不再运动
CView::OnLButtonDblClk(nFlags, point);
}
以上就是所有相关代码。但是我仍有一个疑问:
如果其他人有什么问题,或者需要我整个项目的代码,也可以评论或者私信我。我会经常看的!