10-光滑着色的三角形填充算法

参考:https://www.bilibili.com/video/BV1Kr4y1Q7kq?p=10

RGB颜色定义

struct RGB24   
{
public:
    RGB24():red(0), green(0), blue(0) {};
    RGB24(double red,double green,double blue):red(red), green(green), blue(blue){};
    friend RGB24 operator + (const RGB24& clr0, const RGB24& clr1)
    {
        return  RGB24(clr0.red + clr1.red, clr0.green + clr1.green, clr0.blue + clr1.blue);
    }; 
    friend RGB24 operator * (const RGB24& clr, double scalar)
    {
        return  RGB24(clr.red * scalar, clr.green * scalar, clr.blue * scalar);
    }
    friend RGB24 operator * ( double scalar, const RGB24& clr)
    {
        return clr * scalar;
    }
  
public:
    double red;
    double green;
    double blue;
};

颜色线性插值

  RGB24 Interp(double m,double m0,double m1,RGB24 c0,RGB24 c1) 
  {
      return (m1 - m) / (m1 - m0) * c0 + (m - m0) / (m1 - m0) * c1;
  }

扫描填充点

for (int y = P[0].y; y < P[2].y; y++)
{
    int n = y - P[0].y;

    for (int x = _pSpanLeft[n].x; x < _pSpanRight[n].x; x++)
    {
        RGB24  color;
        color = Interp(x, _pSpanLeft[n].x, _pSpanRight[n].x, _pSpanLeft[n].c, _pSpanRight[n].c);
        SetPixel(hdc, x, y, RGB(color.red * 255, color.green * 255, color.blue * 255));
    }           
}

完成代码

// 10-光滑着色的三角形填充算法
// 参考 https://www.bilibili.com/video/BV1Kr4y1Q7kq?p=10
#define UNICODE
#include <Windows.h>
#define WINDOW_TEXT L"10-光滑着色的三角形填充算法"
#define ROUND(d) int(d+0.5)  // 四舍五入
#define LEFT  true
#define RIGHT false

struct RGB24   // RGB颜色
{
public:
    RGB24():red(0), green(0), blue(0) {};
    RGB24(double red,double green,double blue):red(red), green(green), blue(blue){};
    friend RGB24 operator + (const RGB24& clr0, const RGB24& clr1)
    {
        return  RGB24(clr0.red + clr1.red, clr0.green + clr1.green, clr0.blue + clr1.blue);
    }; 
    friend RGB24 operator * (const RGB24& clr, double scalar)
    {
        return  RGB24(clr.red * scalar, clr.green * scalar, clr.blue * scalar);
    }
    friend RGB24 operator * ( double scalar, const RGB24& clr)
    {
        return clr * scalar;
    }
  
public:
    double red;
    double green;
    double blue;
};

struct Point2  // 带颜色二维点
{
    Point2() :x(0), y(0) {}
    Point2(double x, double y) :x(x), y(y) {}
    Point2(double x, double y, RGB24 c) :x(x), y(y), c(c) {}
    double  x;
    double  y;
    RGB24   c;
};

class Triangle  // 填充三角形
{

public:
    Triangle() {};
    Triangle(Point2 p0, Point2 p1, Point2 p2)
    {  
        P[0] = p0;
        P[1] = p1;
        P[2] = p2;
    }
    ~Triangle() {};
    RGB24 Interp(double m,double m0,double m1,RGB24 c0,RGB24 c1) // 颜色线性插值
    {
        return (m1 - m) / (m1 - m0) * c0 + (m - m0) / (m1 - m0) * c1;
    }
    void Fill(HDC hdc)
    {
        // 顶点按照y坐标 由小到大排序
        SortPoint();

        int nTotalScanLine = P[2].y - P[0].y + 1;   // 扫描线数量

        // 定义span起点与终点数字
        _pSpanLeft = new Point2[nTotalScanLine];
        _pSpanRight = new Point2[nTotalScanLine];

        // 判断三角形与P0 P1的位置关系,0-1-2 左手系
        int nDeltz = (P[2].x - P[0].x) * (P[1].y - P[0].y) - (P[2].y - P[0].y) * (P[1].x - P[0].x); // 叉积


        if (nDeltz > 0)   // 左三角形
        {
            _nIndex = 0;
            EdgeFlag(P[0], P[1], LEFT);
            EdgeFlag(P[1], P[2], LEFT);
            _nIndex = 0;
            EdgeFlag(P[0], P[2], RIGHT);
        }
        else // 右三角形
        {
            _nIndex = 0;
            EdgeFlag(P[0], P[2], LEFT);
            _nIndex = 0;
            EdgeFlag(P[0], P[1], RIGHT);
            EdgeFlag(P[1], P[2], RIGHT);
        }

        for (int y = P[0].y; y < P[2].y; y++)
        {
            int n = y - P[0].y;

            for (int x = _pSpanLeft[n].x; x < _pSpanRight[n].x; x++)
            {
                RGB24  color;
                color = Interp(x, _pSpanLeft[n].x, _pSpanRight[n].x, _pSpanLeft[n].c, _pSpanRight[n].c);
                SetPixel(hdc, x, y, RGB(color.red * 255, color.green * 255, color.blue * 255));
            }           
        }

        if (_pSpanLeft)
        {
            delete[] _pSpanLeft;
            _pSpanLeft = NULL;
        }

        if (_pSpanRight)
        {
            delete[] _pSpanRight;
            _pSpanRight = NULL;
        }
    }

private:   
    void EdgeFlag(Point2 pStart, Point2 pEnd, bool bFeature)  // 边标记算法
    {
        int dx = pEnd.x - pStart.x;
        int dy = pEnd.y - pStart.y;

        double m = double(dx) / dy;

        double x = pStart.x;

        for (int y = pStart.y; y < pEnd.y; y++)
        {
            RGB24 color = Interp(y, pStart.y,pEnd.y,pStart.c,pEnd.c);

            if (bFeature)
            {
                _pSpanLeft[_nIndex++] = Point2(ROUND(x), y, color);

            }
            else
            {
                _pSpanRight[_nIndex++] = Point2(ROUND(x), y, color);
            }

            x += m;
        }

    }    
    void SortPoint() // 顶点排序
    {
        Point2 pt;  // 排序后 P[0].y < P[1].y < P[2].y 

        if (P[0].y > P[1].y)
        {
            pt = P[0];
            P[0] = P[1];
            P[1] = pt;
        }

        if (P[0].y > P[2].y)
        {
            pt = P[0];
            P[0] = P[2];
            P[2] = pt;
        }

        if (P[1].y > P[2].y)
        {
            pt = P[1];
            P[1] = P[2];
            P[2] = pt;
        }
    }
private:
    Point2  P[3];        // 顶点
    Point2* _pSpanLeft;  // 跨度起点
    Point2* _pSpanRight; // 跨度终点
    int     _nIndex;     // 扫描线索引
};



LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    RECT rc;
    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
    {
        hdc = BeginPaint(hwnd, &ps);

        

        GetClientRect(hwnd, &rc);
        SetMapMode(hdc, MM_ANISOTROPIC);
        SetWindowExtEx(hdc, rc.right, rc.bottom, NULL);
        SetViewportExtEx(hdc, rc.right, -rc.bottom, NULL);
        SetViewportOrgEx(hdc, rc.right / 2, rc.bottom / 2, NULL);

        {
            Point2 p1(0,     250, RGB24(1, 0, 0));
            Point2 p2(-400, -250, RGB24(0, 1, 0));
            Point2 p3(400,  -250, RGB24(0, 0, 1));

            Triangle trangile(p1, p2, p3);
            trangile.Fill(hdc);
        }

      
        EndPaint(hwnd, &ps);
    }
    return 0;

    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{

    // Register the window class.
    const wchar_t CLASS_NAME[] = L"CAG";
    WNDCLASS wc = { };

    wc.style = CS_HREDRAW | CS_VREDRAW; // 重新绘制整个工作区
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class
        WINDOW_TEXT,                    // Window text
        WS_OVERLAPPEDWINDOW,            // Window style

        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

        NULL,       // Parent window    
        NULL,       // Menu
        hInstance,  // Instance handle
        NULL        // Additional application data
    );

    if (hwnd == NULL)
    {
        return 0;
    }
    ShowWindow(hwnd, nCmdShow);


    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值