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


    目录

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

    纹理过滤


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

     上图其实不是很直观。

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

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

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

    这个问题要怎么去解决。

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

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

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

    1. void Canvas::drawTriange(Point p1, Point p2, Point p3){
    2. ...
    3. float s = (float)(newPoint.m_y - ptMin.m_y) / (float)(ptMax.m_y - ptMin.m_y);
    4. newPoint.m_color = colorLerp(ptMin.m_color, ptMax.m_color, s);
    5. newPoint.m_uv = uvLerp(ptMin.m_uv, ptMax.m_uv, s);
    6. ...
    7. }
    1. void Canvas::drawTriangeFlat(Point pFlat1, Point pFlat2, Point pt) {
    2. //两边直线斜率
    3. float k1 = 0.0;
    4. float k2 = 0.0;
    5. if (pFlat1.m_x != pt.m_x) {
    6. k1 = (float)(pFlat1.m_y - pt.m_y) / (float)(pFlat1.m_x - pt.m_x);
    7. }
    8. if (pFlat2.m_x != pt.m_x) {
    9. k2 = (float)(pFlat2.m_y - pt.m_y) / (float)(pFlat2.m_x - pt.m_x);
    10. }
    11. //两直线b值
    12. float b1 = (float)pt.m_y - (float)pt.m_x * k1;
    13. float b2 = (float)pt.m_y - (float)pt.m_x * k2;
    14. //做垂线
    15. int yStart = MIN(pt.m_y, pFlat1.m_y);
    16. int yEnd = MAX(pt.m_y, pFlat1.m_y);
    17. if (yStart < 0)yStart = 0;
    18. if (yEnd > m_height)yEnd = m_height - 1;
    19. //颜色插值相关
    20. RGBA colorStart1, colorEnd1;
    21. RGBA colorStart2, colorEnd2;
    22. floatV2 uvStart1, uvEnd1;
    23. floatV2 uvStart2, uvEnd2;
    24. if (pt.m_y < pFlat1.m_y) {
    25. yStart = pt.m_y;
    26. yEnd = pFlat1.m_y;
    27. colorStart1 = pt.m_color;
    28. colorEnd1 = pFlat1.m_color;
    29. colorStart2 = pt.m_color;
    30. colorEnd2 = pFlat2.m_color;
    31. uvStart1 = pt.m_uv;
    32. uvEnd1 = pFlat1.m_uv;
    33. uvStart2 = pt.m_uv;
    34. uvEnd2 = pFlat2.m_uv;
    35. }
    36. else {
    37. yStart = pFlat1.m_y;
    38. yEnd = pt.m_y;
    39. colorStart1 = pFlat1.m_color;
    40. colorEnd1 = pt.m_color;
    41. colorStart2 = pFlat2.m_color;
    42. colorEnd2 = pt.m_color;
    43. uvStart1 = pFlat1.m_uv;
    44. uvEnd1 = pt.m_uv;
    45. uvStart2 = pFlat2.m_uv;
    46. uvEnd2 = pt.m_uv;
    47. }
    48. float yColorStep = 1.0 / ((float)(yEnd - yStart));
    49. int yColorStart = yStart;
    50. for (int y = yStart; y <= yEnd; ++y) {
    51. int x1 = 0;
    52. //判断是否为直角三角形
    53. if (k1 == 0) {
    54. x1 = pFlat1.m_x;
    55. }
    56. else {
    57. x1 = ((float)y - b1) / k1;
    58. }
    59. //剪裁
    60. if (x1 < 0)x1 = 0;
    61. if (x1 > m_width)x1 = m_width - 1;
    62. int x2 = 0;
    63. //判断是否为直角三角形
    64. if (k2 == 0) {
    65. x2 = pFlat2.m_x;
    66. }
    67. else {
    68. x2 = ((float)y - b2) / k2;
    69. }
    70. //剪裁
    71. if (x2 < 0)x2 = 0;
    72. if (x2 > m_width)x2 = m_width - 1;
    73. //构建这两个点
    74. float s1 = (float)(y - yColorStart) * yColorStep;
    75. RGBA _color1 = colorLerp(colorStart1, colorEnd1, s1);
    76. RGBA _color2 = colorLerp(colorStart2, colorEnd2, s1);
    77. floatV2 _uv1 = uvLerp(uvStart1, uvEnd1, s1);
    78. floatV2 _uv2 = uvLerp(uvStart2, uvEnd2, s1);
    79. Point pt1(x1, y, _color1, _uv1);
    80. Point pt2(x2, y, _color2, _uv2);
    81. //绘制直线
    82. drawLine(pt1, pt2);
    83. }
    84. }
    1. void GT::Canvas::drawLine(Point pt1, Point pt2) {
    2. int disX = abs(pt2.m_x - pt1.m_x);
    3. int disY = abs(pt2.m_y - pt1.m_y);
    4. int xNow = pt1.m_x;
    5. int yNow = pt1.m_y;
    6. int stepX = 0;
    7. int stepY = 0;
    8. //判断两个方向步进的正负
    9. stepX = pt1.m_x < pt2.m_x ? 1 : -1;
    10. stepY = pt1.m_y < pt2.m_y ? 1 : -1;
    11. //对比xy偏移量,决定步进方向选取x or y
    12. int sumStep = disX;
    13. bool useXStep = true;
    14. if (disX < disY) {
    15. sumStep = disY;
    16. useXStep = false;
    17. SWAP_INT(disX, disY);
    18. }
    19. //初始化P
    20. int p = 2 * disY - disX;
    21. RGBA _color;
    22. for (int i = 0; i <= sumStep; ++i) {
    23. float _scale = 0;
    24. if (useXStep) {
    25. if (pt2.m_x == pt1.m_x)
    26. _scale = 0;
    27. else
    28. _scale = (float)(xNow - pt1.m_x) / (float)(pt2.m_x - pt1.m_x);
    29. }
    30. else {
    31. if (pt1.m_y == pt2.m_y)
    32. _scale = 0;
    33. else
    34. _scale = (float)(yNow - pt1.m_y) / (float)(pt2.m_y - pt1.m_y);
    35. }
    36. //启用纹理
    37. if (m_enableTexture) {
    38. floatV2 _uv = uvLerp(pt1.m_uv, pt2.m_uv, _scale);
    39. if (m_texture) {
    40. _color = m_texture->getColorbyUV(_uv.x, _uv.y);
    41. }
    42. else {
    43. _color = colorLerp(pt1.m_color, pt2.m_color, _scale);
    44. }
    45. }
    46. else {
    47. _color = colorLerp(pt1.m_color, pt2.m_color, _scale);
    48. }
    49. drawPoint(xNow, yNow, _color);
    50. if (p >= 0) {
    51. if (useXStep) {
    52. yNow += stepY;
    53. }
    54. else {
    55. xNow += stepX;
    56. }
    57. p = p - 2 * disX;
    58. }
    59. //步进主坐标
    60. if (useXStep) {
    61. xNow += stepX;
    62. }
    63. else {
    64. yNow += stepY;
    65. }
    66. p = p + 2 * disY;
    67. }
    68. }

    测试一下:

    1. //===========================Render==================================================
    2. void Render() {
    3. _canvas->clear();
    4. GT::Point ptArray[] = {
    5. {0,0,GT::RGBA(255,0,0),GT::floatV2(0,0)},
    6. {500,0,GT::RGBA(255,0,0),GT::floatV2(1.0,0)},
    7. {250,300,GT::RGBA(255,0,0),GT::floatV2(0.5,1.0)}
    8. };
    9. _canvas->enableTexture(true);
    10. _canvas->bindTexture(_bkImage);
    11. _canvas->drawTriange(ptArray[0], ptArray[1], ptArray[2]);
    12. //在这里画到设备上,hMem相当于缓冲区
    13. BitBlt(hDC, 0, 0, wWidth, wHeight, hMem, 0, 0, SRCCOPY);
    14. }

    纹理过滤

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

    1. void Render() {
    2. _canvas->clear();
    3. GT::Point ptArray[] = {
    4. {0,0, GT::RGBA(255,0,0),GT::floatV2(0,0)},
    5. {500,0, GT::RGBA(255,0,0),GT::floatV2(1.0,0)},
    6. {500,300, GT::RGBA(255,0,0),GT::floatV2(1.0,1.0)}
    7. };
    8. GT::Point ptArray1[] = {
    9. {0,0, GT::RGBA(255,0,0),GT::floatV2(0,0)},
    10. {0,300, GT::RGBA(255,0,0),GT::floatV2(0.0,1.0)},
    11. {500,300, GT::RGBA(255,0,0),GT::floatV2(1.0,1.0)}
    12. };
    13. for (int i = 0; i < 3; i++) {
    14. ptArray[i].m_uv.x += 0.5;
    15. ptArray1[i].m_uv.x += 0.5;
    16. }
    17. _canvas->enableTexture(true);
    18. _canvas->bindTexture(_bkImage);
    19. _canvas->setTextureType(GT::Image::TX_REPEAT);
    20. _canvas->drawTriange(ptArray[0], ptArray[1], ptArray[2]);
    21. _canvas->drawTriange(ptArray1[0], ptArray1[1], ptArray1[2]);
    22. //在这里画到设备上,hMem相当于缓冲区
    23. BitBlt(hDC, 0, 0, wWidth, wHeight, hMem, 0, 0, SRCCOPY);
    24. }

    TX_REPEAT过滤: 

    TX_CLAMP_TO_EDGE过滤

  • 相关阅读:
    记录帖 ES的RestApi使用
    参数校验go-playground/validator
    JavaScript 66 JavaScript Async 66.2 异步的 JavaScript
    顺序队列----数据结构
    PowerBI(一) : 如何将powerBI报表嵌入内部web应用程序?
    初探富文本之富文本概述
    GC日志开启及分析
    数据采集之:巧用布隆过滤器提取数据摘要
    冬天这么冷,到底要不要坚持送孩子入托?
    【Docker】五分钟完成Docker部署Java应用,你也可以的!!!
  • 原文地址:https://blog.csdn.net/Jason6620/article/details/127947890