近来因一些事情从图书馆借了些 计算机图形学的书籍瞅瞅,感觉不少东西还是蛮有意思的。
没事就用c++写个小类,用控制台的字符模仿像素,简单地实现下!
一张16*16的图片,仿出来就是 unsigned char *image = new unsigned char [16*16],这点接受后,我们就开始建立这个图形类的基本架构。
class MyGL
{
public:
MyGL(int width = 16, int height =16);
~MyGL();
private:
unsigned char *image_;
int width_, height_;
};
MyGL::MyGL(int width /*= 16*/, int height /*=16*/)
:width_(width), height_(height)
{
if (width_ == 0 || height_ == 0)
++width_, ++height_;
image_ = new unsigned char[width_*height_];
for (int i = 0; i < width_*height_; ++i)
{
image_[i] = ' ';
}
}
MyGL::~MyGL()
{
delete [] image_;
}
要把图形显示出来,因此添加一个paint函数。注意包含iostream头文件。
void MyGL::paint()
{
for (int i = 0; i < width_*height_; ++i)
{
if (i%width_ == 0 && i > 0)
{
std::cout << std::endl;
}
std::cout << image_[i] << " ";
}
std::cout << std::endl;
}
因为每一行之间有间隔,所以这里每“像素”后添加一个空格从而使x、y俩方向更协调。
这是我们的image都还是空的。
下面添加一个私有函数setPixel来设置特定位置的像素。这个函数在后面会被反复调用。void MyGL::setPixel(int x, int y, unsigned char c /*= 'X'*/)
{
if (x <= 0 || x > width_ || y <= 0 || y > height_)
return;
image_[(x - 1) + (y - 1)*width_] = c;
}
到目前为止,所有的准备工作都已完成。
大家可以借这个框架自己添加画线,画矩形,画圆,画椭圆 等函数,可以和我给出的函数做一些比较。
测试代码先给出。
int main()
{
MyGL g(30,30);
// 此函数根据自己情况调整。
// g.drawRect(15, 10, 5, 10);
g.paint();
std::cin.get();
return 0;
}
后面的部分会涉及到图形学的一些基本算法。
先给出比较简单的DDA算法。
DDA算法主要是根据直线公式y = kx + b来推导出来的。
算法的具体思路如下:
1. 输入直线的起点、终点;
2. 计算x方向的间距:△X和y方向的间距:△Y。
3. 确定单位步进。这里确保单位步进未跨过x,y其中某一方向的一个像素的单位。步进取小,则要求步数取大。
取MaxSteps = max(△X,△Y); 若△X>=△Y,则X方向的步进为单位步进,X方向步进一个单位,Y方向步进△Y/MaxSteps;否则相反。
在后面曲线部分更要注意到这一点。
4. 设置第一个点的像素值
5. 令循环初始值为1,循环次数为MaxSteps,定义变量x,y,执行以下计算:
a. x增加一个单位步进,y增加一个单位步进
b. 设置位置为(x,y)的像素值
函数如下:
//DDA算法
void MyGL::drawLine(int x0, int y0, int xEnd, int yEnd, unsigned char c /*= '+'*/)
{
int dx = xEnd - x0;
int dy = yEnd - y0;
int steps = (std::abs(dx) > std::abs(dy)) ? std::abs(dx) : std::abs(dy);
double xIncrement = static_cast<double>(dx) / steps;
double yIncrement = static_cast<double>(dy) / steps;
double x = static_cast<double>(x0), y = static_cast<double>(y0);
setPixel(round(x), round(y), c);
for (int i = 0; i < steps; ++i)
{
x += xIncrement;
y += yIncrement;
setPixel(round(x), round(y), c);
}
}
第一部分完。