目录
上图其实不是很直观。
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过滤