C++模拟OpenGL库——图片处理及纹理系统(四):UV纹理坐标

目录

引入UV纹理坐标及三角形绘制设置

纹理过滤


引入UV纹理坐标及三角形绘制设置

 上图其实不是很直观。

UV坐标要解决的问题就是:

假设我有一张500×500的纹理图片;

我要把它映射到一张200×200的图片中;

这个问题要怎么去解决。

这里提出的UV坐标,就是一种”比例“,用(u,v)直接乘以原图的像素坐标(x,y),那么我们就可以直接获得原像素的颜色值,从而映射到目标图片中。

我们在Canvas画布类中添加纹理相关的属性与方法:

同理的,绘制中颜色既然要插值,uv坐标同样需要插值:

void Canvas::drawTriange(Point p1, Point p2, Point p3){
    ...
    float s = (float)(newPoint.m_y - ptMin.m_y) / (float)(ptMax.m_y - ptMin.m_y);
	newPoint.m_color = colorLerp(ptMin.m_color, ptMax.m_color, s);
	newPoint.m_uv = uvLerp(ptMin.m_uv, ptMax.m_uv, s);
    ...
}
	void Canvas::drawTriangeFlat(Point pFlat1, Point pFlat2, Point pt) {
		//两边直线斜率
		float k1 = 0.0;
		float k2 = 0.0;

		if (pFlat1.m_x != pt.m_x) {
			k1 = (float)(pFlat1.m_y - pt.m_y) / (float)(pFlat1.m_x - pt.m_x);
		}
		if (pFlat2.m_x != pt.m_x) {
			k2 = (float)(pFlat2.m_y - pt.m_y) / (float)(pFlat2.m_x - pt.m_x);
		}

		//两直线b值
		float b1 = (float)pt.m_y - (float)pt.m_x * k1;
		float b2 = (float)pt.m_y - (float)pt.m_x * k2;

		//做垂线
		int yStart = MIN(pt.m_y, pFlat1.m_y);
		int yEnd = MAX(pt.m_y, pFlat1.m_y);
		if (yStart < 0)yStart = 0;
		if (yEnd > m_height)yEnd = m_height - 1;


		//颜色插值相关
		RGBA colorStart1, colorEnd1;
		RGBA colorStart2, colorEnd2;

		floatV2 uvStart1, uvEnd1;
		floatV2 uvStart2, uvEnd2;

		if (pt.m_y < pFlat1.m_y) {
			yStart = pt.m_y;
			yEnd = pFlat1.m_y;

			colorStart1 = pt.m_color;
			colorEnd1 = pFlat1.m_color;
			colorStart2 = pt.m_color;
			colorEnd2 = pFlat2.m_color;

			uvStart1 = pt.m_uv;
			uvEnd1 = pFlat1.m_uv;
			uvStart2 = pt.m_uv;
			uvEnd2 = pFlat2.m_uv;
		}
		else {
			yStart = pFlat1.m_y;
			yEnd = pt.m_y;

			colorStart1 = pFlat1.m_color;
			colorEnd1 = pt.m_color;
			colorStart2 = pFlat2.m_color;
			colorEnd2 = pt.m_color;

			uvStart1 = pFlat1.m_uv;
			uvEnd1 = pt.m_uv;
			uvStart2 = pFlat2.m_uv;
			uvEnd2 = pt.m_uv;
		}
		float yColorStep = 1.0 / ((float)(yEnd - yStart));
		int yColorStart = yStart;


		for (int y = yStart; y <= yEnd; ++y) {
			int x1 = 0;
			//判断是否为直角三角形
			if (k1 == 0) {
				x1 = pFlat1.m_x;
			}
			else {
				x1 = ((float)y - b1) / k1;
			}
			//剪裁
			if (x1 < 0)x1 = 0;
			if (x1 > m_width)x1 = m_width - 1;

			int x2 = 0;
			//判断是否为直角三角形
			if (k2 == 0) {
				x2 = pFlat2.m_x;
			}
			else {
				x2 = ((float)y - b2) / k2;
			}
			//剪裁
			if (x2 < 0)x2 = 0;
			if (x2 > m_width)x2 = m_width - 1;

			//构建这两个点
			float s1 = (float)(y - yColorStart) * yColorStep;
			RGBA _color1 = colorLerp(colorStart1, colorEnd1, s1);
			RGBA _color2 = colorLerp(colorStart2, colorEnd2, s1);

			floatV2 _uv1 = uvLerp(uvStart1, uvEnd1, s1);
			floatV2 _uv2 = uvLerp(uvStart2, uvEnd2, s1);

			Point pt1(x1, y, _color1, _uv1);
			Point pt2(x2, y, _color2, _uv2);

			//绘制直线
			drawLine(pt1, pt2);
		}
	}
	void GT::Canvas::drawLine(Point pt1, Point pt2) {
		int disX = abs(pt2.m_x - pt1.m_x);
		int disY = abs(pt2.m_y - pt1.m_y);

		int xNow = pt1.m_x;
		int yNow = pt1.m_y;

		int stepX = 0;
		int stepY = 0;

		//判断两个方向步进的正负
		stepX = pt1.m_x < pt2.m_x ? 1 : -1;
		stepY = pt1.m_y < pt2.m_y ? 1 : -1;

		//对比xy偏移量,决定步进方向选取x or y
		int sumStep = disX;
		bool useXStep = true;
		if (disX < disY) {
			sumStep = disY;
			useXStep = false;
			SWAP_INT(disX, disY);
		}

		//初始化P
		int p = 2 * disY - disX;
		RGBA _color;
		for (int i = 0; i <= sumStep; ++i) {
			
			float _scale = 0;
			if (useXStep) {
				if (pt2.m_x == pt1.m_x)
					_scale = 0;
				else
					_scale = (float)(xNow - pt1.m_x) / (float)(pt2.m_x - pt1.m_x);
			}
			else {
				if (pt1.m_y == pt2.m_y)
					_scale = 0;
				else
					_scale = (float)(yNow - pt1.m_y) / (float)(pt2.m_y - pt1.m_y);
			}

			//启用纹理
			if (m_enableTexture) {
				floatV2 _uv = uvLerp(pt1.m_uv, pt2.m_uv, _scale);
				if (m_texture) {
					_color = m_texture->getColorbyUV(_uv.x, _uv.y);
				}
				else {
					_color = colorLerp(pt1.m_color, pt2.m_color, _scale);
				}
			}
			else {
				_color = colorLerp(pt1.m_color, pt2.m_color, _scale);
			}
			drawPoint(xNow, yNow, _color);

			if (p >= 0) {
				if (useXStep) {
					yNow += stepY;
				}
				else {
					xNow += stepX;
				}
				p = p - 2 * disX;
			}
			//步进主坐标
			if (useXStep) {
				xNow += stepX;
			}
			else {
				yNow += stepY;
			}
			p = p + 2 * disY;
		}
	}

测试一下:

//===========================Render==================================================
void Render() {
    _canvas->clear();

    GT::Point ptArray[] = {
        {0,0,GT::RGBA(255,0,0),GT::floatV2(0,0)},
        {500,0,GT::RGBA(255,0,0),GT::floatV2(1.0,0)},
        {250,300,GT::RGBA(255,0,0),GT::floatV2(0.5,1.0)}
    };

    _canvas->enableTexture(true);
    _canvas->bindTexture(_bkImage);
    _canvas->drawTriange(ptArray[0], ptArray[1], ptArray[2]);

    //在这里画到设备上,hMem相当于缓冲区
    BitBlt(hDC, 0, 0, wWidth, wHeight, hMem, 0, 0, SRCCOPY);
}

纹理过滤

直接上代码看效果。其实就是OpenGL里的纹理过滤:纹理 - LearnOpenGL CN (learnopengl-cn.github.io)

void Render() {
    _canvas->clear();

    GT::Point ptArray[] = {
        {0,0,       GT::RGBA(255,0,0),GT::floatV2(0,0)},
        {500,0,     GT::RGBA(255,0,0),GT::floatV2(1.0,0)},
        {500,300,   GT::RGBA(255,0,0),GT::floatV2(1.0,1.0)}
    };

    GT::Point ptArray1[] = {
        {0,0,       GT::RGBA(255,0,0),GT::floatV2(0,0)},
        {0,300,     GT::RGBA(255,0,0),GT::floatV2(0.0,1.0)},
        {500,300,   GT::RGBA(255,0,0),GT::floatV2(1.0,1.0)}
    };

    for (int i = 0; i < 3; i++) {
        ptArray[i].m_uv.x += 0.5;
        ptArray1[i].m_uv.x += 0.5;
    }

    _canvas->enableTexture(true);
    _canvas->bindTexture(_bkImage);
    _canvas->setTextureType(GT::Image::TX_REPEAT);
    _canvas->drawTriange(ptArray[0], ptArray[1], ptArray[2]);
    _canvas->drawTriange(ptArray1[0], ptArray1[1], ptArray1[2]);

    //在这里画到设备上,hMem相当于缓冲区
    BitBlt(hDC, 0, 0, wWidth, wHeight, hMem, 0, 0, SRCCOPY);
}

TX_REPEAT过滤: 

TX_CLAMP_TO_EDGE过滤

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值