8个回答
展开全部
C语言的话画直线用MoveTo()和LineTo()很简单啊。
帮你复制一份我学习时老师给的画线两例:
#include<graphics.h>
#include<math.h>
/*
###############################################################################
功 能:本函数的作用是用逐点比较法来画一条直线
格 式:void myline1(int x1,int y1,int x2,int y2,int color)
参数说明:x1,y1是起始点坐标,x2,y2是终止点,color是画线的颜色
调用示例:myline1(10,20,500,440,4)
###############################################################################
*/
void myline1(int x1,int y1,int x2,int y2,int color)
{
/*变量定义开始(2007/10/16增加)*/
int iTx; /*x轴终点的相对坐标xa或临时变量*/
int iTy; /*y轴终点的相对坐标ya或临时变量*/
int iDx; /*x轴方向的步长dx*/
int iDy; /*y轴方向的步长dy*/
int iFt; /*偏差Fm*/
int iSt; /*记数循环数(dx+dy)S*/
int iXt; /*x方向循环变量xm*/
int iYt; /*y方向循环变量ym*/
/*变量定义结束*/
/*变量初始化开始*/
/*如果是第三象限或第四象限则换成第一或第二象限*/
if(y2<y1)
{
iTx=x1;
x1=x2;
x2=iTx;
iTy=y1;
y1=y2;
y2=iTy;
}
iTx=x2-x1; /*取x轴的相对坐标*/
iTy=y2-y1; /*取y轴的相对坐标*/
iDx=1;
iDy=1;
iFt=0;
iSt=iTx+iTy;
if(iTx<0)iSt=-1*iTx+iTy;; /*如果在第二象限,则x轴方向步长取负值*/
iXt=0;
iYt=0;
/*变量初始化结束*/
/*数据处理开始*/
while(iSt>0)
{
putpixel(x1+iXt,y1+iYt,color);
if(iTx>=0) /*如果在第一象限*/
{
if(iFt<0) /*如果偏差小于0*/
{
iYt+=iDy; /*y方向走一步*/
iFt+=iTx;
}
else /*如果偏差大于或等于0*/
{
iXt+=iDx; /*x方向走一步*/
iFt-=iTy;
}
}
else
{
if(iFt<0) /*如果偏差小于0*/
{
iXt-=iDx; /*负x方向走一步*/
iFt+=iTy;
}
else /*如果偏差大于或等于0*/
{
iYt+=iDy; /*y方向走一步*/
iFt+=iTx;
}
}
iSt--;
}
}
/*
###############################################################################
功 能:本函数的作用是用来画一条直线
格 式:void myline2(int x1,int y1,int x2,int y2,int color)
参数说明:x1,y1是起始点坐标,x2,y2是终止点,color是画线的颜色
调用示例:myline2(10,20,500,440,4)
###############################################################################
*/
int myline2(int x1,int y1,int x2,int y2,int color)
{
int iX; /*x方向的坐标变量*/
int iY; /*y方向的坐标变量*/
int iTx; /*x方向的步长变量*/
int iTy; /*y方向的步长变量*/
float fDx; /*x方向的差分变量*/
float fDy; /*y方向的差分变量*/
float fMinf; /*算法中的f*/
float fMaxF; /*算法中的F*/
float fS; /*终点判断变量*/
fMinf=0.5; /*f=0.5*/
iX=x1;
iY=y1;
putpixel(x1,y1,color);
if(x1==x2&&y1==y2) /*如果终点和起始点相同*/
{
return(1);
}
iTx=1;
iTy=1;
fDx=(float)(x2-x1);
fDy=(float)(y2-y1);
fMaxF=fDy/fDx>0?fDy/fDx:(-fDy/fDx); /*F=|dy/dx|*/
if(fDx<0)iTx=-1;
if(fDy<0)iTy=-1;
fS=fDx>0?fDx:(-fDx);
if(fMaxF==1) /*如果F=1*/
{
iX=x1;
iY=y1;
while(fS>0)
{
iX+=iTx; /*x方向走一步*/
iY+=iTy; /*y方向走一步*/
putpixel(iX,iY,color);
fS--;
}
}
else if(fMaxF>1) /*如果F>1*/
{
fS+=fDy>0?fDy:(-fDy);
while(fS>0)
{
iY+=iTy; /*y方向走一步*/
putpixel(iX,iY,color);
fMinf+=1/fMaxF; /*f=f+1/F*/
fS--;
if(fMinf>=1) /*如果f>=1*/
{
iX+=iTx; /*x方向走一步*/
fMinf--; /*f=f-1*/
putpixel(iX,iY,color);
fS--;
}
}
}
else /*如果F<1*/
{
fS+=fDy>0?fDy:(-fDy);
while(fS>0)
{
iX+=iTx; /*x方向走一步*/
putpixel(iX,iY,color);
fMinf+=fMaxF; /*f=f+F*/
fS--;
if(fMinf>=1) /*如果f>=1*/
{
iY+=iTy; /*y方向走一步*/
fMinf--; /*f=f-1*/
putpixel(iX,iY,color);
fS--;
}
}
}
}
帮你复制一份我学习时老师给的画线两例:
#include<graphics.h>
#include<math.h>
/*
###############################################################################
功 能:本函数的作用是用逐点比较法来画一条直线
格 式:void myline1(int x1,int y1,int x2,int y2,int color)
参数说明:x1,y1是起始点坐标,x2,y2是终止点,color是画线的颜色
调用示例:myline1(10,20,500,440,4)
###############################################################################
*/
void myline1(int x1,int y1,int x2,int y2,int color)
{
/*变量定义开始(2007/10/16增加)*/
int iTx; /*x轴终点的相对坐标xa或临时变量*/
int iTy; /*y轴终点的相对坐标ya或临时变量*/
int iDx; /*x轴方向的步长dx*/
int iDy; /*y轴方向的步长dy*/
int iFt; /*偏差Fm*/
int iSt; /*记数循环数(dx+dy)S*/
int iXt; /*x方向循环变量xm*/
int iYt; /*y方向循环变量ym*/
/*变量定义结束*/
/*变量初始化开始*/
/*如果是第三象限或第四象限则换成第一或第二象限*/
if(y2<y1)
{
iTx=x1;
x1=x2;
x2=iTx;
iTy=y1;
y1=y2;
y2=iTy;
}
iTx=x2-x1; /*取x轴的相对坐标*/
iTy=y2-y1; /*取y轴的相对坐标*/
iDx=1;
iDy=1;
iFt=0;
iSt=iTx+iTy;
if(iTx<0)iSt=-1*iTx+iTy;; /*如果在第二象限,则x轴方向步长取负值*/
iXt=0;
iYt=0;
/*变量初始化结束*/
/*数据处理开始*/
while(iSt>0)
{
putpixel(x1+iXt,y1+iYt,color);
if(iTx>=0) /*如果在第一象限*/
{
if(iFt<0) /*如果偏差小于0*/
{
iYt+=iDy; /*y方向走一步*/
iFt+=iTx;
}
else /*如果偏差大于或等于0*/
{
iXt+=iDx; /*x方向走一步*/
iFt-=iTy;
}
}
else
{
if(iFt<0) /*如果偏差小于0*/
{
iXt-=iDx; /*负x方向走一步*/
iFt+=iTy;
}
else /*如果偏差大于或等于0*/
{
iYt+=iDy; /*y方向走一步*/
iFt+=iTx;
}
}
iSt--;
}
}
/*
###############################################################################
功 能:本函数的作用是用来画一条直线
格 式:void myline2(int x1,int y1,int x2,int y2,int color)
参数说明:x1,y1是起始点坐标,x2,y2是终止点,color是画线的颜色
调用示例:myline2(10,20,500,440,4)
###############################################################################
*/
int myline2(int x1,int y1,int x2,int y2,int color)
{
int iX; /*x方向的坐标变量*/
int iY; /*y方向的坐标变量*/
int iTx; /*x方向的步长变量*/
int iTy; /*y方向的步长变量*/
float fDx; /*x方向的差分变量*/
float fDy; /*y方向的差分变量*/
float fMinf; /*算法中的f*/
float fMaxF; /*算法中的F*/
float fS; /*终点判断变量*/
fMinf=0.5; /*f=0.5*/
iX=x1;
iY=y1;
putpixel(x1,y1,color);
if(x1==x2&&y1==y2) /*如果终点和起始点相同*/
{
return(1);
}
iTx=1;
iTy=1;
fDx=(float)(x2-x1);
fDy=(float)(y2-y1);
fMaxF=fDy/fDx>0?fDy/fDx:(-fDy/fDx); /*F=|dy/dx|*/
if(fDx<0)iTx=-1;
if(fDy<0)iTy=-1;
fS=fDx>0?fDx:(-fDx);
if(fMaxF==1) /*如果F=1*/
{
iX=x1;
iY=y1;
while(fS>0)
{
iX+=iTx; /*x方向走一步*/
iY+=iTy; /*y方向走一步*/
putpixel(iX,iY,color);
fS--;
}
}
else if(fMaxF>1) /*如果F>1*/
{
fS+=fDy>0?fDy:(-fDy);
while(fS>0)
{
iY+=iTy; /*y方向走一步*/
putpixel(iX,iY,color);
fMinf+=1/fMaxF; /*f=f+1/F*/
fS--;
if(fMinf>=1) /*如果f>=1*/
{
iX+=iTx; /*x方向走一步*/
fMinf--; /*f=f-1*/
putpixel(iX,iY,color);
fS--;
}
}
}
else /*如果F<1*/
{
fS+=fDy>0?fDy:(-fDy);
while(fS>0)
{
iX+=iTx; /*x方向走一步*/
putpixel(iX,iY,color);
fMinf+=fMaxF; /*f=f+F*/
fS--;
if(fMinf>=1) /*如果f>=1*/
{
iY+=iTy; /*y方向走一步*/
fMinf--; /*f=f-1*/
putpixel(iX,iY,color);
fS--;
}
}
}
}
DFRobot
2024-11-10 广告
2024-11-10 广告
图形化编程是一种直观的编程方式,它通过拖拽图形化的编程积木来构建程序,降低了编程的学习门槛。在上海智位机器人股份有限公司,我们致力于将图形化编程应用于机器人教育等领域,使学习者能够以更加轻松、有趣的方式掌握编程技能。我们的图形化编程平台界面...
点击进入详情页
本回答由DFRobot提供
展开全部
不调用画图 API,用C 或 C++ 如何实现画一条线?
Milo Yip:如何开始用 C++ 写一个光栅化渲染器?
我尝试用不同技术实现画直线的方法(完整源代码在 miloyip/line),此文简单介绍个中思路。本文的代码采用 C 语言、标准库及极简的 PNG 编码函数 svpng(),没有使用其他 API。
1. Bresenham 算法
Bresenham直线算法 [1] 是最简单的直线光栅化(rasterization)算法。

Bresenham 直线
如果像上图,直线的高度小于宽度,那么 Bresenham 直线算法会为  轴每个坐标填入一个像素,绘画每个像素时按斜率判断  是否需要调整。整个算法可以避开浮点数运算,只用整数运算实现。以下是一个简单实现:
// Modified from https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C
void bresenham(int x0, int y0, int x1, int y1) {
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = (dx > dy ? dx : -dy) / 2;
while (setpixel(x0, y0), x0 != x1 || y0 != y1) {
int e2 = err;
if (e2 > -dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
}
为了测试不同角度,我做了一个测试用例:
int main() {
memset(img, 255, sizeof(img));
float cx = w * 0.5f - 0.5f, cy = h * 0.5f - 0.5f;
for (int j = 0; j < 5; j++) {
float r1 = fminf(W, H) * (j + 0.5f) * 0.085f;
float r2 = fminf(W, H) * (j + 1.5f) * 0.085f;
float t = j * PI / 64.0f;
for (int i = 1; i <= 64; i++, t += 2.0f * PI / 64.0f) {
float ct = cosf(t), st = sinf(t);
bresenham((int)(cx + r1 * ct), (int)(cy - r1 * st), (int)(cx + r2 * ct), (int)(cy - r2 * st));
}
}
svpng(fopen("line_bresenham.png", "wb"), W, H, img, 0);
}
完整代码 line_bresenham.c
渲染结果及中间局部放大:


2. 采样方法
Bresenham 直线算法有 3 个问题:
不能控制直线宽度;
坐标为整数;
只能对像素写入一个颜色,只用单色会有严重的锯齿效果。
在图形学中,除了以逐个图元(如直线)方式渲染,我们还可以通过对每个像素进行采样(sampling),换句话说,我们可对整个图像逐像素询问:「这个像素的颜色是什么?」
用采样方式画直线时,我们可以用一个具有面积的形状去表示「直线」,例如是长方形。但在本文中,我们使用胶囊体(capsule)去表示直线。这样就能解决上面前两个问题:(1) 可用胶囊体半径设置直线的宽度;(2) 坐标可以为浮点数。不过,用最简单的采样方式,我们需要在每像素查询所有图元是否占有该像素,效率很低。
Milo Yip:如何开始用 C++ 写一个光栅化渲染器?
我尝试用不同技术实现画直线的方法(完整源代码在 miloyip/line),此文简单介绍个中思路。本文的代码采用 C 语言、标准库及极简的 PNG 编码函数 svpng(),没有使用其他 API。
1. Bresenham 算法
Bresenham直线算法 [1] 是最简单的直线光栅化(rasterization)算法。

Bresenham 直线
如果像上图,直线的高度小于宽度,那么 Bresenham 直线算法会为  轴每个坐标填入一个像素,绘画每个像素时按斜率判断  是否需要调整。整个算法可以避开浮点数运算,只用整数运算实现。以下是一个简单实现:
// Modified from https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C
void bresenham(int x0, int y0, int x1, int y1) {
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = (dx > dy ? dx : -dy) / 2;
while (setpixel(x0, y0), x0 != x1 || y0 != y1) {
int e2 = err;
if (e2 > -dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
}
为了测试不同角度,我做了一个测试用例:
int main() {
memset(img, 255, sizeof(img));
float cx = w * 0.5f - 0.5f, cy = h * 0.5f - 0.5f;
for (int j = 0; j < 5; j++) {
float r1 = fminf(W, H) * (j + 0.5f) * 0.085f;
float r2 = fminf(W, H) * (j + 1.5f) * 0.085f;
float t = j * PI / 64.0f;
for (int i = 1; i <= 64; i++, t += 2.0f * PI / 64.0f) {
float ct = cosf(t), st = sinf(t);
bresenham((int)(cx + r1 * ct), (int)(cy - r1 * st), (int)(cx + r2 * ct), (int)(cy - r2 * st));
}
}
svpng(fopen("line_bresenham.png", "wb"), W, H, img, 0);
}
完整代码 line_bresenham.c
渲染结果及中间局部放大:


2. 采样方法
Bresenham 直线算法有 3 个问题:
不能控制直线宽度;
坐标为整数;
只能对像素写入一个颜色,只用单色会有严重的锯齿效果。
在图形学中,除了以逐个图元(如直线)方式渲染,我们还可以通过对每个像素进行采样(sampling),换句话说,我们可对整个图像逐像素询问:「这个像素的颜色是什么?」
用采样方式画直线时,我们可以用一个具有面积的形状去表示「直线」,例如是长方形。但在本文中,我们使用胶囊体(capsule)去表示直线。这样就能解决上面前两个问题:(1) 可用胶囊体半径设置直线的宽度;(2) 坐标可以为浮点数。不过,用最简单的采样方式,我们需要在每像素查询所有图元是否占有该像素,效率很低。
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
展开全部
将尺放在纸上用笔在上面轻轻一滑,完美的动作创造出完美的直线。简单,不做作的线条,虽不讨大多数人的喜欢,但总有自己的方向。
出生以来即为简单的我们,却很难在多变的环境中保持透明,总是莫名其妙地被改变着。手中的笔再不能画出直线,弯曲的弧度若隐若现,跌宕起伏着我们的心,再没有直线的痛快淋漓。
忽然之间,觉察出自己的陌生,我何时变得这样。面对着无可挽回的误会或者结局,我在心里歇斯底里地喊叫,我真的不是这样,亦不想。我努力地画着曾经相似的直线,不敢看他们异样的眼神,在众多圆滑的曲线显得莽撞、突兀。我没有轻舞飞扬的勇气,在讶异的`目光中翩翩起舞。我所做的只有在所谓自己的错误上或满心愧疚或保持沉默。他们逼着我画圆,可我只了解直线。
是不是现实中真的不允许直线的存在,它完美得难以看见。是不是我真的错了,总是将童年错当成现在。是不是人都需要掩饰,太过透明只会让自己变得愚笨可笑。或许我们都该永存于缄默,那样就不会让别人看到自己的懦弱。那么多的人在阻止我拿尺的手,拼命塞给我圆规,叫我画讨人喜欢的圆。我在他们面前成了一个犯了错的罪犯,只因为我不会画圆。见多了大大小小的圆,平滑的,有瑕疵的,熟练的,稚嫩的,连自己也觉得画直线的我可恶起来。我就这样莫名地恨起自己来,听到他们达到目的后满意的笑,我不屑。我说我失去了信仰,你们失去了自尊。相比之下谁更可怜。并且,恨一件事物不代表爱它的对立面,如果恨还可以隐藏在意,那么不屑真的是悲哀的轻视。
我不是一个有主见的人,时而画直线,时而画圆。因为我没有从一而终的勇气和聪明敏锐的洞察力,这样的结果只有一个:直线不是直线,圆不是圆。我们都在纷繁的嘈杂中茫然地活着,弄痛了别人,伤害了自己。
但我知道总有一天我们会心有不甘,不甘唤醒他们的自尊。只画自己所爱的,或者,用一张白纸蒙上自己的双眼。
梦的天空,不该是座迷宫。
直线画出彩虹,心向风般自由。
爱的天空,开不开心直说。
哭过后的笑容,难过丢向天空。
出生以来即为简单的我们,却很难在多变的环境中保持透明,总是莫名其妙地被改变着。手中的笔再不能画出直线,弯曲的弧度若隐若现,跌宕起伏着我们的心,再没有直线的痛快淋漓。
忽然之间,觉察出自己的陌生,我何时变得这样。面对着无可挽回的误会或者结局,我在心里歇斯底里地喊叫,我真的不是这样,亦不想。我努力地画着曾经相似的直线,不敢看他们异样的眼神,在众多圆滑的曲线显得莽撞、突兀。我没有轻舞飞扬的勇气,在讶异的`目光中翩翩起舞。我所做的只有在所谓自己的错误上或满心愧疚或保持沉默。他们逼着我画圆,可我只了解直线。
是不是现实中真的不允许直线的存在,它完美得难以看见。是不是我真的错了,总是将童年错当成现在。是不是人都需要掩饰,太过透明只会让自己变得愚笨可笑。或许我们都该永存于缄默,那样就不会让别人看到自己的懦弱。那么多的人在阻止我拿尺的手,拼命塞给我圆规,叫我画讨人喜欢的圆。我在他们面前成了一个犯了错的罪犯,只因为我不会画圆。见多了大大小小的圆,平滑的,有瑕疵的,熟练的,稚嫩的,连自己也觉得画直线的我可恶起来。我就这样莫名地恨起自己来,听到他们达到目的后满意的笑,我不屑。我说我失去了信仰,你们失去了自尊。相比之下谁更可怜。并且,恨一件事物不代表爱它的对立面,如果恨还可以隐藏在意,那么不屑真的是悲哀的轻视。
我不是一个有主见的人,时而画直线,时而画圆。因为我没有从一而终的勇气和聪明敏锐的洞察力,这样的结果只有一个:直线不是直线,圆不是圆。我们都在纷繁的嘈杂中茫然地活着,弄痛了别人,伤害了自己。
但我知道总有一天我们会心有不甘,不甘唤醒他们的自尊。只画自己所爱的,或者,用一张白纸蒙上自己的双眼。
梦的天空,不该是座迷宫。
直线画出彩虹,心向风般自由。
爱的天空,开不开心直说。
哭过后的笑容,难过丢向天空。
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
展开全部
不能控制直线宽度;
坐标为整数;
只能对像素写入一个颜色,只用单色会有严重的锯齿效果。
在图形学中,除了以逐个图元(如直线)方式渲染,我们还可以通过对每个像素进行采样(sampling),换句话说,我们可对整个图像逐像素询问:「这个像素的颜色是什么?」
用采样方式画直线时,我们可以用一个具有面积的形状去表示「直线」,例如是长方形。但在本文中,我们使用胶囊体(capsule)去表示直线。这样就能解决上面前两个问题:(1) 可用胶囊体半径设置直线的宽度;(2) 坐标可以为浮点数。不过,用最简单的采样方式,我们需要在每像素查询所有图元是否占有该像素,效率很低。
// 判断一点 (px, py) 是否在胶囊体(两端为(ax, ay)、(bx, by),半径 r)之中
int capsule(float px, float py, float ax, float ay, float bx, float by, float r) {
float pax = px - ax, pay = py - ay, bax = bx - ax, bay = by - ay;
float h = fmaxf(fminf((pax * bax + pay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f);
float dx = pax - bax * h, dy = pay - bay * h;
return dx * dx + dy * dy < r * r;
}
// 对坐标 (x, y) 进行采样
float sample(float x, float y) {
float s = 0.0f, cx = W * 0.5f, cy = H * 0.5f;
for (int j = 0; j < 5; j++) {
float r1 = fmaxf(w, h) * (j + 0.5f) * 0.085f;
float r2 = fmaxf(w, h) * (j + 1.5f) * 0.085f;
float t = j * PI / 64.0f, r = (j + 1) * 0.5f;
for (int i = 1; i <= 64; i++, t += 2.0f * PI / 64.0f) {
float ct = cosf(t), st = sinf(t);
s = fmaxf(s, capsule(x, y, cx + r1 * ct, cy - r1 * st, cx + r2 * ct, cy - r2 * st, r) ? 1.0f : 0.0f);
}
}
return s;
}
int main() {
unsigned char *p = img;
for (int y = 0; y < H; y++)
for (int x = 0; x < W; x++, p += 3)
p[0] = p[1] = p[2] = (unsigned char)((1.0f - sample(x, y)) * 255);
svpng(fopen("line_sampling.png", "wb"), W, H, img, 0);
}
完整代码 line_sampling.c
渲染结果及中间局部放大:
注意从中间往外,直线的宽度为 1, 2, 3, 4, 5。我们甚至可以渲染宽度少于一个像素的直线。
3. 超采样
所谓锯齿,其实应称作信号混叠(aliasing)。抗混叠/抗锯齿(anti-aliasing)的最简单方法是做更多采样,即超采样(supersampling)。我们可以沿用上一个例子中的采样函数 sample(),只是在每个像素内平均多个像素采样,例如以正方栅格方式做 次采样,但计算时间也变成 25 倍。
int main() {
unsigned char *p = img;
int sw = 5, sh = 5;
for (int y = 0; y < H; y++)
for (int x = 0; x < W; x++, p += 3) {
float sum = 0;
for (int j = -sh / 2; j <= sh / 2; j++)
for (int i = -sw / 2; i <= sw / 2; i++)
sum += sample(x + (float)i / sw, y + (float)j / sh);
p[0] = p[1] = p[2] = (unsigned char)((1.0f - sum / (sw * sh)) * 255);
}
svpng(fopen("line_supersampling.png", "wb"), W, H, img, 0);
}
完整代码 line_supersampling.c
渲染结果及中间局部放大:
这个结果可算是接近最好的了,但它的缺点就是性能极低。我们尝试另一个方法。
4. 带符号距离场
在上面的采样中,我们只用布尔值来表示一个坐标是否在形状之中。我们可以用带符号距离场(signed distance field, SDF)去表示坐标与形状的带符号距离(如正数代表坐标在形状以外,距离形状的最短距离)。[2] 采用这种方式提升矢量图形的渲染品质。
坐标为整数;
只能对像素写入一个颜色,只用单色会有严重的锯齿效果。
在图形学中,除了以逐个图元(如直线)方式渲染,我们还可以通过对每个像素进行采样(sampling),换句话说,我们可对整个图像逐像素询问:「这个像素的颜色是什么?」
用采样方式画直线时,我们可以用一个具有面积的形状去表示「直线」,例如是长方形。但在本文中,我们使用胶囊体(capsule)去表示直线。这样就能解决上面前两个问题:(1) 可用胶囊体半径设置直线的宽度;(2) 坐标可以为浮点数。不过,用最简单的采样方式,我们需要在每像素查询所有图元是否占有该像素,效率很低。
// 判断一点 (px, py) 是否在胶囊体(两端为(ax, ay)、(bx, by),半径 r)之中
int capsule(float px, float py, float ax, float ay, float bx, float by, float r) {
float pax = px - ax, pay = py - ay, bax = bx - ax, bay = by - ay;
float h = fmaxf(fminf((pax * bax + pay * bay) / (bax * bax + bay * bay), 1.0f), 0.0f);
float dx = pax - bax * h, dy = pay - bay * h;
return dx * dx + dy * dy < r * r;
}
// 对坐标 (x, y) 进行采样
float sample(float x, float y) {
float s = 0.0f, cx = W * 0.5f, cy = H * 0.5f;
for (int j = 0; j < 5; j++) {
float r1 = fmaxf(w, h) * (j + 0.5f) * 0.085f;
float r2 = fmaxf(w, h) * (j + 1.5f) * 0.085f;
float t = j * PI / 64.0f, r = (j + 1) * 0.5f;
for (int i = 1; i <= 64; i++, t += 2.0f * PI / 64.0f) {
float ct = cosf(t), st = sinf(t);
s = fmaxf(s, capsule(x, y, cx + r1 * ct, cy - r1 * st, cx + r2 * ct, cy - r2 * st, r) ? 1.0f : 0.0f);
}
}
return s;
}
int main() {
unsigned char *p = img;
for (int y = 0; y < H; y++)
for (int x = 0; x < W; x++, p += 3)
p[0] = p[1] = p[2] = (unsigned char)((1.0f - sample(x, y)) * 255);
svpng(fopen("line_sampling.png", "wb"), W, H, img, 0);
}
完整代码 line_sampling.c
渲染结果及中间局部放大:
注意从中间往外,直线的宽度为 1, 2, 3, 4, 5。我们甚至可以渲染宽度少于一个像素的直线。
3. 超采样
所谓锯齿,其实应称作信号混叠(aliasing)。抗混叠/抗锯齿(anti-aliasing)的最简单方法是做更多采样,即超采样(supersampling)。我们可以沿用上一个例子中的采样函数 sample(),只是在每个像素内平均多个像素采样,例如以正方栅格方式做 次采样,但计算时间也变成 25 倍。
int main() {
unsigned char *p = img;
int sw = 5, sh = 5;
for (int y = 0; y < H; y++)
for (int x = 0; x < W; x++, p += 3) {
float sum = 0;
for (int j = -sh / 2; j <= sh / 2; j++)
for (int i = -sw / 2; i <= sw / 2; i++)
sum += sample(x + (float)i / sw, y + (float)j / sh);
p[0] = p[1] = p[2] = (unsigned char)((1.0f - sum / (sw * sh)) * 255);
}
svpng(fopen("line_supersampling.png", "wb"), W, H, img, 0);
}
完整代码 line_supersampling.c
渲染结果及中间局部放大:
这个结果可算是接近最好的了,但它的缺点就是性能极低。我们尝试另一个方法。
4. 带符号距离场
在上面的采样中,我们只用布尔值来表示一个坐标是否在形状之中。我们可以用带符号距离场(signed distance field, SDF)去表示坐标与形状的带符号距离(如正数代表坐标在形状以外,距离形状的最短距离)。[2] 采用这种方式提升矢量图形的渲染品质。
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
展开全部
在VC下运行的,bai
int dx,dy,incrE,incrNE,d,x,y;
if ((point[1].x-point[0].x)==0){ //垂直的直线
x=point[0].x;
for(y=point[0].y;y<point[1].y;y++)
pDC->SetPixel(x,y,50);
}
else if(abs((point[1].y-point[0].y)/(point[1].x-point[0].x))<=1){ //斜率du -1到 1 之间
dx=point[1].x-point[0].x;
dy=point[0].y-point[1].y;
d=dx-2*dy;
incrE=-2*dy;
incrNE=2*(dx-dy);
x=point[0].x,y=point[0].y;
pDC->SetPixel(x,y,50);
if(point[0].y>point[1].y){
while(x<point[1].x)
{
if(d>=0){
d+=incrE;
x++;
}
else
{d+=incrNE;
x++;
y--;
}
pDC->SetPixel(x,y,50);
}
}
else if(point[0].y<=point[1].y){
dy=point[1].y-point[0].y;
incrE=-2*dy;
incrNE=2*(dx-dy);
x=point[0].x,y=point[0].y;
pDC->SetPixel(x,y,50);
while(x<point[1].x)
{
if(d>=0){
d+=incrE;
x++;
}
else
{d+=incrNE;
x++;
y++;
}
pDC->SetPixel(x,y,50);
}
}
}
else { //斜率 <-1 和 >1的直线
if(point[1].x>=point[0].x){
dx=point[1].x-point[0].x;
dy=point[1].y-point[0].y;
d=2*dx-dy;
incrE=2*dx;
incrNE=2*(dx-dy);
x=point[0].x,y=point[0].y;
pDC->SetPixel(x,y,50);
while(x<point[1].x)
{
if(d<0){
d+=incrE;
y++;
}
else
{d+=incrNE;
pDC->SetPixel(x,y,50);
x++;
y++;
}
pDC->SetPixel(x,y,50);
}
}
else if((point[1].y-point[0].y)/(point[1].x-point[0].x)<-1){
dx=point[1].x-point[0].x;
dy=point[0].y-point[1].y;
d=2*dx-dy;
incrE=2*dx;
incrNE=2*(dx-dy);
x=point[0].x,y=point[0].y;
pDC->SetPixel(x,y,50);
while(y<point[1].y)
{
if(d>0){
d+=incrE;
y++;
}
else
{d+=incrNE;
x--;
y++;
}
pDC->SetPixel(x,y,50);
}
}
}
int dx,dy,incrE,incrNE,d,x,y;
if ((point[1].x-point[0].x)==0){ //垂直的直线
x=point[0].x;
for(y=point[0].y;y<point[1].y;y++)
pDC->SetPixel(x,y,50);
}
else if(abs((point[1].y-point[0].y)/(point[1].x-point[0].x))<=1){ //斜率du -1到 1 之间
dx=point[1].x-point[0].x;
dy=point[0].y-point[1].y;
d=dx-2*dy;
incrE=-2*dy;
incrNE=2*(dx-dy);
x=point[0].x,y=point[0].y;
pDC->SetPixel(x,y,50);
if(point[0].y>point[1].y){
while(x<point[1].x)
{
if(d>=0){
d+=incrE;
x++;
}
else
{d+=incrNE;
x++;
y--;
}
pDC->SetPixel(x,y,50);
}
}
else if(point[0].y<=point[1].y){
dy=point[1].y-point[0].y;
incrE=-2*dy;
incrNE=2*(dx-dy);
x=point[0].x,y=point[0].y;
pDC->SetPixel(x,y,50);
while(x<point[1].x)
{
if(d>=0){
d+=incrE;
x++;
}
else
{d+=incrNE;
x++;
y++;
}
pDC->SetPixel(x,y,50);
}
}
}
else { //斜率 <-1 和 >1的直线
if(point[1].x>=point[0].x){
dx=point[1].x-point[0].x;
dy=point[1].y-point[0].y;
d=2*dx-dy;
incrE=2*dx;
incrNE=2*(dx-dy);
x=point[0].x,y=point[0].y;
pDC->SetPixel(x,y,50);
while(x<point[1].x)
{
if(d<0){
d+=incrE;
y++;
}
else
{d+=incrNE;
pDC->SetPixel(x,y,50);
x++;
y++;
}
pDC->SetPixel(x,y,50);
}
}
else if((point[1].y-point[0].y)/(point[1].x-point[0].x)<-1){
dx=point[1].x-point[0].x;
dy=point[0].y-point[1].y;
d=2*dx-dy;
incrE=2*dx;
incrNE=2*(dx-dy);
x=point[0].x,y=point[0].y;
pDC->SetPixel(x,y,50);
while(y<point[1].y)
{
if(d>0){
d+=incrE;
y++;
}
else
{d+=incrNE;
x--;
y++;
}
pDC->SetPixel(x,y,50);
}
}
}
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询