【图形学】颜色线性插值和Wu反走样算法

颜色线性插值

绘制一条颜色渐变的直线,直线上每一个点的颜色都来自端点颜色的线性插值。线性插值公式为
P = ( 1 − t ) P s t a r t + t P e n d P 是直线上任意一个点, P s t a r t 是直线的起点, P e n d 是直线的终点 对应直线上任意一点的颜色有 c = ( 1 − t ) c s t a r t + t c e n d P=(1-t)P_{start}+tP_{end}\\ P是直线上任意一个点,P_{start}是直线的起点,P_{end}是直线的终点\\对应直线上任意一点的颜色有\\c=(1-t)c_{start}+tc_{end} P=(1t)Pstart+tPendP是直线上任意一个点,Pstart是直线的起点,Pend是直线的终点对应直线上任意一点的颜色有c=(1t)cstart+tcend
根据之前学的直线光栅化算法可知,绘制直线的时候是有主位移方向的,如果主位移方向是x,有 x = ( 1 − t ) x s t a r t + t x e n d x=(1-t)x_{start}+tx_{end} x=(1t)xstart+txend如果主位移方向是y有 y = ( 1 − t ) y s t a r t + t y e n d y=(1-t)y_{start}+ty_{end} y=(1t)ystart+tyend。带入到上面的公式有 c = x e n d − x x e n d − x s t a r t ⋅ c s t a r t + x − x s t a r t x e n d − x s t a r t ⋅ c e n d 或者 c = y e n d − y y e n d − y s t a r t ⋅ c s t a r t + y − y s t a r t y e n d − y s t a r t ⋅ c e n d c=\frac{x_{end}-x}{x_{end}-x_{start}}\cdot c_{start}+\frac{x-x_{start}}{x_{end}-x_{start}}\cdot c_{end}或者c=\frac{y_{end}-y}{y_{end}-y_{start}}\cdot c_{start}+\frac{y-y_{start}}{y_{end}-y_{start}}\cdot c_{end} c=xendxstartxendxcstart+xendxstartxxstartcend或者c=yendystartyendycstart+yendystartyystartcend
综合来看就是下面的代码
【图形学】颜色线性插值和Wu反走样算法_第1张图片

CRGB CLine::LinearInterP(double t, double tStart, double tEnd, CRGB cStart, CRGB cEnd)
{
    CRGB color;
    color = (tEnd - t) * cStart / (tEnd - tStart) + (t - tStart) * cEnd / (tEnd - tStart);
    return color;
}

在画直线的时候调用这个算法就可以画出颜色渐变的直线,这里以Bresenham算法为例。

#define COLOR(x) RGB(x.m_red*255,x.m_green*255,x.m_blue*255)

【图形学】颜色线性插值和Wu反走样算法_第2张图片

void CLine::DrawLine(CDC* pDC)
{
	int dx = abs(m_p1.x - m_p0.x);//m_p0,m_p1(CPoint2)
	int dy = abs(m_p1.y - m_p0.y);
	double k = (double)(dy) / (double)(dx);  //斜率
	BOOL wayChange = FALSE;//主方向是否发生改变,默认是x方向
	int e, mainway, subway;
	e = -dx;
	mainway = dx;
	subway = dy;
	int addx, addy;
	addx = (m_p1.x > m_p0.x) ? 1 : ((m_p1.x < m_p0.x) ? -1 : 0);
	addy = (m_p1.y > m_p0.y) ? 1 : ((m_p1.y < m_p0.y) ? -1 : 0);
	if (dy > dx) {//主方向是y
		mainway = dy;
		subway = dx;
		wayChange = TRUE;
	}
	CPoint2 p = m_p0;
	for (int i = 0; i <= mainway; i++) {
		pDC->SetPixel(p.x,p.y, COLOR(p.c));
		if (wayChange) {
			p.c = LinearInterP(p.y,m_p0.y,m_p1.y,m_p0.c,m_p1.c);
			p.y += addy;
		}	
		else {
			p.c = LinearInterP(p.x, m_p0.x, m_p1.x, m_p0.c, m_p1.c);
			p.x += addx;
		}	
		e += 2 * subway;
		if (e >= 0) {
			if (wayChange)
				p.x += addx;
			else
				p.y += addy;
			e -= 2 * mainway;
		}
	}
}

【图形学】颜色线性插值和Wu反走样算法_第3张图片

Wu反走样算法

【图形学】颜色线性插值和Wu反走样算法_第4张图片

直线扫描转换算法在处理非水平,非垂直和非45度直线会出现如图所示的锯齿或台阶转边界,这样的现象称为走样,走样只能减轻但不能避免。

Wu反走样算法是以Bresenham算法为基础对距离进行加权的一种算法。
【图形学】颜色线性插值和Wu反走样算法_第5张图片
如果采用Bresenham算法,那么显示的是点D,对于Wu反走样算法,理想直线上的点C的上下两个可能近似点都要显示,但是亮度不同。一个像素点距离理想直线距离越远,该像素点的颜色就越接近背景色,亮度就越大;一个像素点距离理想直线越近该像素点的颜色就越接近理想直线,亮度就越小。
例如点C和点D的距离为e,那么点D的亮度就是e,点E的亮度就是1-e。
算法中e的递推式就是Bresenham算法中距离误差d的递推式。
主位移方向每递增一个单位有 e i + 1 = e i + k 当 e i + 1 ≥ 1.0 时 e i + 1 要减 1 e_{i+1}=e_i+k当e_{i+1}\geq1.0时e_{i+1}要减1 ei+1=ei+kei+11.0ei+1要减1

void CALine::DrawLine(CDC* pDC)
{
	int dx = abs(m_p1.x - m_p0.x);//m_p0,m_p1(CPoint)
	int dy = abs(m_p1.y - m_p0.y);
	double k = (double)(dy) / (double)(dx);  //斜率
	BOOL wayChange = FALSE;//主方向是否发生改变,默认是x方向
	int  mainway, subway;
	double e = 0.0;
	mainway = dx;
	subway = dy;
	int addx, addy;
	addx = (m_p1.x > m_p0.x) ? 1 : ((m_p1.x < m_p0.x) ? -1 : 0);
	addy = (m_p1.y > m_p0.y) ? 1 : ((m_p1.y < m_p0.y) ? -1 : 0);
	if (dy > dx) {//主方向是y
		mainway = dy;
		subway = dx;
		wayChange = TRUE;
	}
	CPoint2 p = m_p0;
	for (int i = 0; i <= mainway; i++) {
		CRGB c0(e, e, e);
		CRGB c1(1 - e, 1 - e, 1 - e);
		if (wayChange) {
			pDC->SetPixelV(p.x + addx, p.y, COLOR(c1));
			pDC->SetPixelV(p.x, p.y, COLOR(c0));
		}
		else {
			pDC->SetPixelV(p.x, p.y + addy, COLOR(c1));
			pDC->SetPixelV(p.x, p.y, COLOR(c0));
		}
		if (wayChange) {
			p.y += addy;
		}
		else {
			p.x += addx;
		}
		e += (double)dy / dx;
		if (e >= 0.0) {
			if (wayChange)
				p.x += addx;
			else
				p.y += addy;
			e -= 1.0;
		}
	}
}

彩色的那条线是Bresenham算法画的直线,黑色的那条线是Wu反走样算法画的直线,将图片稍微放大就可以看到区别(一个比较粗糙,一个比较光滑)。
【图形学】颜色线性插值和Wu反走样算法_第6张图片

你可能感兴趣的:(图形学,算法,图形学,C++,MFC)