原文:从零实现3D图像引擎:(2)画2D直线不简单

1. 数学分析

1) 画直线的问题

本来我以为画直线会很容易,随便拿个直线公式,遍历X求Y画出来不就完了么,但事实并非如此。以2D直线为例,因为3D直线也只是多引入了个Z坐标而已。关键的问题:我们在数学中所学的直线是基于实数域的,而在计算机屏幕上,所画的直线是基于正整数域的,可以想象这么一个情形,在直线的某一点X=1,Y=0.01时,在屏幕上如何画呢?下图对比了实数域的直线,与基于正整数域的直线:

为什么直线在正整数域是不连续的呢,还记得斜率的的定义么:斜率m = dy / dx = (y1 - y0) / (x1 - x0)

这意味着当X坐标增加1,则Y坐标就增加m。这就是会出现上述情况的根本原因。

2) Bresenham算法

该算法由Bresenham在1965年发明,它到底做了什么事呢?其实想法很简单,就是每X移动一个像素,则考虑Y应该是如何移动。为什么我们要关注Bresenham算法呢,我们发现,这种算法实际只做了加减法,是非常适合计算机运算的,这种算法的速度是相当快的。

该算法把直线分为两种:一种是斜率<1的线,即近X轴线。另一种是斜率>1的线,即近Y轴线。

我们以近X轴直线为例,如图:

Bresenham算法的核心就是,当X加1后,如何决定Y的移动。显然可见,近X轴直线的dy<dx。所以一个直观的想法是,保存一个误差累计变量,每当X加1,误差变量便累计增加一个dy。当累计误差小于等于dx时,Y不动,当累计的误差大于dx时,Y加1,同时把累计误差减掉一个dx。这样,算法将不停的将光栅线与实际线之间的误差减到最小。

2. 函数实现

这里给出一个例子,实现了上面的算法,但只限近X轴并且是从左上往右下画的,可以很清楚的看到实现的逻辑。通用的画线在源码中已实现,可以下载获取。

int dx = x1 - x0;
int dy = y1 - y0;
int error = 0;
if (dx > dy) // 近X轴
{
for (int x = x0, y = y0; x<= x1; ++x)
{
DrawPixel(x, y, color);
error += dy; // 累计误差 if (error > dx)
{
error -= dx;
++y;
}
}
}

  针对所有情况的完整代码如下,其中在误差的计算方面进行了一些优化,起始值更居中,而不是写死的0。

int _CPPYIN_3DLib::DrawLine(int x0, int y0, int x1, int y1, DWORD color)
{
int x, y, dx, dy, dx2, dy2, xstep, ystep, error, index;
x = x0;
y = y0;
dx = x1 - x0;
dy = y1 - y0; if (dx >= 0) // 从左往右画
{
xstep = 1; // x步进正1
}
else // 从右往左画
{
xstep = -1; // x步进负1
dx = -dx; // 取绝对值
} if (dy >= 0) // 从上往下画
{
ystep = 1; // y步进正1
}
else // 从下往上画
{
ystep = -1; // y步进负1
dy = -dy; // 取绝对值
} dx2 = dx << 1; // 2 * dx
dy2 = dy << 1; // 2 * dy if (dx > dy) // 近X轴直线
{
error = dy2 - dx;
for (index = 0; index <= dx; ++index)
{
DrawPixel(x, y, color);
if (error >= 0)
{
error -= dx2;
y += ystep;
}
error += dy2;
x += xstep;
}
}
else // 近Y轴直线
{
error = dx2 - dy;
for (index = 0; index <= dy; ++index)
{
DrawPixel(x, y, color);
if (error >= 0)
{
error -= dy2;
x += xstep;
}
error += dx2;
y += ystep;
}
} return 1;
}

  

3. 源码下载

这个示例使用该函数,每帧在窗口中画500条随机颜色的直线,截图如下:

项目源代码下载:>>点击进入下载页<<

4. 补充更新

画直线还有一些算法,速度有的更快,如:

Run-Slicing

Symmetric Double Step

Quadruple Step

如果有时间我会一一实现,如果读者已经实现,请留言分享,谢谢。

【转载】从零实现3D图像引擎:(2)画2D直线不简单的更多相关文章

  1. 【转载】从零实现3D图像引擎:(1)环境配置与项目框架

    原文:从零实现3D图像引擎:(1)环境配置与项目框架 0. 要学懂3D程序设计,必然要精通3D相关的线性代数.3D几何.复分析等相关知识,我也因为如此才开始这个博客系列的写作,不自己实现,就不是自己的 ...

  2. 转载:[转]如何学好3D游戏引擎编程

      [转]如何学好3D游戏引擎编程 Albert 本帖被 gamengines 从 游戏引擎(Game Engine) 此文为转载,但是值得一看. 此篇文章献给那些为了游戏编程不怕困难的热血青年,它的 ...

  3. 棒!使用.NET Core构建3D游戏引擎

    原文地址:https://mellinoe.wordpress.com/2017/01/18/net-core-game-engine/ 作者:ERIC MELLINO 翻译:杨晓东(Savorboa ...

  4. achartengine andorid图像引擎入门

    最近在帮机械学院开发一个app 用了第三方的图像引擎——achartengine功能还算强大(虽然相比于Html那些第三方图像引擎还是差点不过也够用了) 入门: 参考http://blog.csdn. ...

  5. 排名前10的H5、Js 3D游戏引擎和框架

    由于很多人都在用JavaScript.HTML5和WebGL技术创建基于浏览器的3D游戏,所有JavaScript 3D游戏引擎是一个人们主题.基于浏览器的游戏最棒的地方是平台独立,它们能在iOS.A ...

  6. 全息眼镜HoloLens可快速捕捉真人3D图像

    http://www.d9soft.com/zixun/62287.html 北京时间3月28日午间消息,微软研发部门开发出一种新的3D视频捕捉系统“Holoportation”,可以实现将某人3D图 ...

  7. C++ 3D物理引擎库BulletPhysics基本使用

    前言:最近在接触OpenGl和DX11的时候,顺便学习了Bullet这个3D物理引擎的基本使用,记录一下. |BulletPhysics介绍 BulletPhysics是一个跨平台的开源物理引擎,也是 ...

  8. Python 画3D图像

    绘制一副3D图像 draw3D(X,Y,Z, angle) import numpy as np from matplotlib import pyplot as plt from mpl_toolk ...

  9. 纯CSS实现3D图像轮转

    CSS演武场今天继续,今天看一个纯css实现的3D图像轮转效果,请大家猛戳研究效果先,也可下载收藏先. 首先看html文件,div.billboard为效果的容器,利用10个div.poster分割图 ...

随机推荐

  1. 浅谈 JavaScript 中的继承模式

    最近在读一本设计模式的书,书中的开头部分就讲了一下 JavaScript 中的继承,阅读之后写下了这篇博客作为笔记.毕竟好记性不如烂笔头. JavaScript 是一门面向对象的语言,但是 ES6 之 ...

  2. JavaScript的DOM_动态加载脚本和样式

    一.动态加载脚本 当网站需求变大,脚本的需求也逐步变大.我们就不得不引入太多的 JS 脚本而降低了整站的性能,所以就出现了动态脚本的概念,在适时的时候加载相应的脚本. 1.动态加载js文件 比如:我们 ...

  3. mvvm模型

  4. 3109. [CQOI2013]新数独【DFS】

    Description Input 输入一共15行,包含一个新数独的实例.第奇数行包含左右方向的符号(<和>),第偶数行包含上下方向的符号(^和v).   Output 输出包含9行,每行 ...

  5. linq查询语法和方法-簡單用法

    來自:http://www.cnblogs.com/knowledgesea/p/3897665.html 1.简单的linq语法 //1 var ss = from r in db.Am_recPr ...

  6. 码农视角 - Angular 框架起步

    开发环境 1.npm 安装最新的Nodejs,便包含此工具.类似Nuget一样的东西,不过与Nuget不同的是,这玩意完全是命令行的.然后用npm来安装开发环境,也就是下边的angular cli. ...

  7. spring cloud各个模块作用

    Eureka Client:负责将这个服务的信息注册到Eureka Server中.Eureka Server:注册中心,里面有一个注册表,保存了各个服务所在的机器和端口号.ribbon:负载均衡,获 ...

  8. WebRTC博客推荐

    1. http://www.cnblogs.com/lingyunhu/ 2. http://www.jianshu.com/u/eadc7531ecb8

  9. Kmalloc和Vmalloc的区别

    kmalloc()和vmalloc()介绍kmalloc()用于申请较小的.连续的物理内存1. 以字节为单位进行分配,在<linux/slab.h>中2. void *kmalloc(si ...

  10. Token生成(转载)

    package main import ( "encoding/base64" "encoding/json" "log" "ne ...