31-背面剔除算法

参考:https://www.bilibili.com/video/BV1QP4y1e7w5

20面体建模

背面剔除

算法设计

完整代码

// 31-背面剔除算法
// 参考 https://www.bilibili.com/video/BV1QP4y1e7w5

#define UNICODE
#include <Windows.h>
#include <Windowsx.h>
#include <math.h>
#define WINDOW_TEXT L"31-背面剔除算法"
#define ROUND(d) int(floor(d)+0.5)  // 四舍五入
#define PI        3.1415926

struct Point2  // 二维点
{
    double x;
    double y;
    double w;  
    Point2() :x(0), y(0), w(1) {}
    Point2(double x, double y) :x(x), y(y), w(1) {}

    friend Point2 operator + (Point2 pt0, Point2 pt1)
    {
        return  Point2(pt0.x + pt1.x, pt0.y + pt1.y);
    }

    friend Point2 operator * (Point2 pt, double n)
    {
        return  Point2(pt.x * n, pt.y * n);
    }

    friend Point2 operator * (double n, Point2 pt)
    {
        return  Point2(pt.x * n, pt.y * n);
    }
};

struct Point3 :Point2  // 三维点
{
    double z;
    Point3() :z(0) {}
    Point3(double x, double y, double z) :Point2(x, y), z(z) {}
    friend Point3 operator + (Point3 pt0, Point3 pt1)
    {
        return  Point3(pt0.x + pt1.x, pt0.y + pt1.y, pt0.z + pt1.z);
    }

    friend Point3 operator * (double scalar, const Point3& pt)
    {
        return  Point3(pt.x * scalar, pt.y * scalar, pt.z * scalar);
    }

    friend Point3 operator * (const Point3& pt, double scalar)
    {
        return  Point3(pt.x * scalar, pt.y * scalar, pt.z * scalar);
    }
    friend Point3 operator / (const Point3& pt, double scalar)
    {
        return  Point3(pt.x / scalar, pt.y / scalar, pt.z / scalar);
    }


    double DotProduct(const Point3& p0, const Point3& p1)   // 向量的点积
    {
        return(p0.x * p1.x + p0.y * p1.y + p0.z * p1.z);
    }

    Point3 CrossProduct(const Point3& v0, const Point3& v1) // 向量的叉积
    {
        return Point3(v0.y * v1.z - v0.z * v1.y,
                      v0.z * v1.x - v0.x * v1.z,
                      v0.x * v1.y - v0.y * v1.x);
    }
};

class  Vector3  // 向量类
{ 
public:
    double x;
    double y;
    double z;
public:
    Vector3() :x(0), y(0), z(0) {};
    virtual ~Vector3() {};
    Vector3(double x, double y, double z) :x(x), y(y), z(z){};  // 绝对向量        
    Vector3(const Point3& P) { x = P.x; y = P.y; z = P.z; };
    Vector3(const Point3& P0, const Point3& P1)   // 相对向量
    {
        x = P1.x - P0.x;
        y = P1.y - P0.y;
        z = P1.z - P0.z;
    }
    double  Magnitude() // 计算向量模      
    {
        return sqrt(x * x + y * y + z * z);
    }

    Vector3 Normalize()// 规范化向量
    {
        Vector3 vector;
        double magintude = sqrt(x * x + y * y + z * z);
        if (fabs(magintude)<1e-4)
        {
            magintude = 1.0;
        }
        vector.x = x / magintude;
        vector.y = y / magintude;
        vector.z = z / magintude;

        return vector;
    }
    friend Vector3 operator -(const Vector3& v)    // 向量取反
    {
        return Vector3(-v.x,-v.y,-v.z);
    }
    friend Vector3 operator +(const Vector3& v0, const Vector3& v1)
    {
        return Vector3(v0.x + v1.x , v0.y + v1.y , v0.z + v1.z);
    }
    friend Vector3 operator -(const Vector3& v0, const Vector3& v1)
    {
        return Vector3(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z);
    }
    friend Vector3 operator *(double scalar, const Vector3& v)
    {
        return Vector3(scalar * v.x, scalar * v.y, scalar * v.z);
    }
    friend Vector3 operator *(const Vector3& v,double scalar)
    {
        return Vector3(scalar * v.x, scalar * v.y, scalar * v.z);
    }
    friend Vector3 operator /(const Vector3& v,double scalar)
    {
        return Vector3(v.x/ scalar,  v.y / scalar, v.z / scalar);
    }
    friend Vector3 operator +=(const Vector3& v0, const Vector3& v1)
    {
        return Vector3(v0.x + v1.x, v0.y + v1.y, v0.z + v1.z);
    }
    friend Vector3 operator -=(const Vector3& v0, const Vector3& v1)
    {
        return Vector3(v0.x - v1.x, v0.y - v1.y, v0.z - v1.z);
    }
    friend double  DotProduct(const Vector3& v0, const Vector3& v1)
    {
        return(v0.x * v1.x + v0.y * v1.y + v0.z * v1.z);
    }
    friend Vector3 CrossProduct(const Vector3& v0, const Vector3& v1) 
    {
        return Vector3(v0.y * v1.z - v0.z * v1.y,
                       v0.z * v1.x - v0.x * v1.z,
                       v0.x* v1.y - v0.y * v1.x);
    }

};

class  Transform3
{
public:
    Point3* _P;          // 顶点数组
    int     _ptNumber;   // 顶点数量
    double  _M[4][4];    // 变换矩阵
public:
    Transform3() :_P(NULL), _ptNumber(0)
    {
        memset(_M, 0, sizeof(_M));
    };
    ~Transform3() {};

    void MultiplyMatrix()  //矩阵相乘
    {
        Point3* PTemp = new Point3[_ptNumber];
        for (int i = 0; i < _ptNumber; i++)
        {
            PTemp[i] = _P[i];
        }
        for (int i = 0; i < _ptNumber; i++)
        {
            _P[i].x = _M[0][0] * PTemp[i].x + _M[0][1] * PTemp[i].y + _M[0][2] * PTemp[i].z + _M[3][0]; // * PTemp[i].w
            _P[i].y = _M[1][0] * PTemp[i].x + _M[1][1] * PTemp[i].y + _M[1][2] * PTemp[i].z + _M[3][1]; // * PTemp[i].w
            _P[i].z = _M[2][0] * PTemp[i].x + _M[2][1] * PTemp[i].y + _M[2][2] * PTemp[i].z + _M[3][2]; // * PTemp[i].w
            _P[i].w = _M[3][0] * PTemp[i].x + _M[3][1] * PTemp[i].y + _M[3][2] * PTemp[i].z + _M[3][3]; // * PTemp[i].w
        }

        delete[]PTemp;
    }

    void SetMatrix(Point3* P, int ptNumber) //顶点数组初始化
    {
        _P = P;
        _ptNumber = ptNumber;
    }
    void Identity()
    {
        _M[0][0] = 1.0, _M[0][1] = 0.0, _M[0][2] = 0.0, _M[0][3] = 0.0;
        _M[1][0] = 0.0, _M[1][1] = 1.0, _M[1][2] = 0.0, _M[1][3] = 0.0;
        _M[2][0] = 0.0, _M[2][1] = 0.0, _M[2][2] = 1.0, _M[2][3] = 0.0;
        _M[3][0] = 0.0, _M[3][1] = 0.0, _M[3][2] = 0.0, _M[3][3] = 1.0;
    }

    void Translate(double tx, double ty, double tz)//平移变换
    {
        Identity();
        _M[3][0] = tx;
        _M[3][1] = ty;
        _M[3][2] = tz;
        MultiplyMatrix();
    }

    void Scale(double sx, double sy, double sz)//缩放变换
    {
        Identity();
        _M[0][0] = sx;
        _M[1][1] = sy;
        _M[2][2] = sz;
        MultiplyMatrix();
    }

    void RotateX(double beta)//绕X轴旋转变换
    {
        Identity();
        beta = beta * PI / 180;
        _M[1][1] = cos(beta), _M[1][2] = -sin(beta);
        _M[2][1] = sin(beta), _M[2][2] = cos(beta);
        MultiplyMatrix();
    }

    void  RotateY(double beta)//绕Y轴旋转变换
    {
        Identity();
        beta = beta * PI / 180;
        _M[0][0] = cos(beta), _M[0][2] = sin(beta);
        _M[2][0] = -sin(beta), _M[2][2] = cos(beta);
        MultiplyMatrix();
    }

};

struct T2
{
    double u;
    double v;
    T2() :u(0), v(0) {}
    T2(double u, double v) :u(u), v(v) {}

    friend T2 operator+(const T2& t0, const T2& t1)
    {
        return T2(t0.u + t1.u, t0.v + t1.v);
    }

    friend T2 operator/(const T2& t, double n)
    {
        return T2(t.u / n, t.v / n);
    }

};

class  Projection  // 透视投影
{
public:
    Point3 Eye;   // 视点
    double R;     // 视点球坐标
    double Phi;   // 
    double Theta; // 
    double D;     // 
    double K[8];  // 透视常数
public:
    Projection()
    {
        R = 1000;
        D = 800;
        Phi = 90;
        Theta = 0;      // 世界坐标z轴正上方 看向原点
        InitialParameter();
    }
    ~Projection() {}

    void InitialParameter() // 初始化参数
    {
        K[0] = sin(PI * Theta / 180);
        K[1] = sin(PI * Phi / 180);
        K[2] = cos(PI * Theta / 180);
        K[3] = cos(PI * Phi / 180);
        K[4] = K[1] * K[2];
        K[5] = K[0] * K[1];
        K[6] = K[2] * K[3];
        K[7] = K[0] * K[3];
        Eye = Point3(R * K[5], R * K[5], R * K[4]);  // 设置视点
    }

    void SetEye(double r, double phi, double theta)   // 设置视点
    {
        R = r;
        Phi = phi;
        Theta = theta;
        InitialParameter();
    }

    Point3 GetEye()
    {
        return Eye;
    }

    Point2 OrthographicProjection(Point3 worldPoint) // 正交投影
    {
        return Point2(worldPoint.x, worldPoint.y);
    }

    Point2 CavalierProjection(Point3 worldPoint)     // 斜等侧投影
    {
        Point2 screenPoint; // 屏幕坐标
        double cota = 1;
        double beta = PI / 4;
        screenPoint.x = worldPoint.x - worldPoint.z * cota * cos(beta);
        screenPoint.y = worldPoint.y - worldPoint.z * cota * cos(beta);
        return screenPoint;
    }

    Point2 CabinetProjection(Point3 worldPoint)      // 斜二侧投影
    {
        Point2 screenPoint; // 屏幕坐标
        double cota = 0.5;
        double beta = PI / 4;
        screenPoint.x = worldPoint.x - worldPoint.z * cota * cos(beta);
        screenPoint.y = worldPoint.y - worldPoint.z * cota * cos(beta);
        return screenPoint;
    }

    Point2 PerspectiveProjection2(Point3 worldPoint) // 二维透视投影
    {
        Point3 viewPoint;  // 观察坐标系
        viewPoint.x = K[2] * worldPoint.x - K[0] * worldPoint.z;
        viewPoint.y = -K[7] * worldPoint.x + K[1] * worldPoint.y - K[6] * worldPoint.z;
        viewPoint.z = -K[5] * worldPoint.x - K[3] * worldPoint.y - K[4] * worldPoint.z + R;

        Point2 screenPoint; // 屏幕坐标
        screenPoint.x = D * viewPoint.x / viewPoint.z;
        screenPoint.y = D * viewPoint.y / viewPoint.z;

        return screenPoint;
    }

};

class Face
{
public:
    int ptNumber;      // 面的顶点数量
    int ptIndex[3] ;   // 面的顶点索引
public:
    Face():ptNumber(0){};
    ~Face() {};
};

class CIcosahedro
{
    
public:
    Point3     V[12];        // 顶点数组
    Face       F[20];        // 小面数组
    Projection _projection;  // 投影
public:
    CIcosahedro()
    {
        ReadVertex();
        ReadFace();
    };
    ~CIcosahedro() {};
    void ReadVertex()
    {
        const double phi = 0.618;// 黄金分割数
        V[ 0].x = 0;     V[ 0].y = 1;     V[ 0].z = phi;
        V[ 1].x = 0;     V[ 1].y = 1;     V[ 1].z = -phi;
        V[ 2].x = 1;     V[ 2].y = phi;   V[ 2].z = 0;
        V[ 3].x = 1;     V[ 3].y = -phi;  V[ 3].z = 0;
        V[ 4].x = 0;     V[ 4].y = -1;    V[ 4].z = -phi;
        V[ 5].x = 0;     V[ 5].y = -1;    V[ 5].z = phi;
        V[ 6].x =  phi;  V[ 6].y = 0;     V[ 6].z = 1;
        V[ 7].x = -phi;  V[ 7].y = 0;     V[ 7].z = 1;
        V[ 8].x =  phi;  V[ 8].y = 0;     V[ 8].z = -1;
        V[ 9].x = -phi;  V[ 9].y = 0;     V[ 9].z = -1;
        V[10].x = -1;    V[10].y =  phi;  V[10].z = 0;
        V[11].x = -1;    V[11].y = -phi;  V[11].z = 0;

    }
    void ReadFace()
    {
        F[0].ptIndex[0] = 0;      F[0].ptIndex[1] = 6;    F[0].ptIndex[2] = 2;
        F[1].ptIndex[0] = 2;      F[1].ptIndex[1] = 6;    F[1].ptIndex[2] = 3;
        F[2].ptIndex[0] = 3;      F[2].ptIndex[1] = 6;    F[2].ptIndex[2] = 5;
        F[3].ptIndex[0] = 5;      F[3].ptIndex[1] = 6;    F[3].ptIndex[2] = 7;
        F[4].ptIndex[0] = 0;      F[4].ptIndex[1] = 7;    F[4].ptIndex[2] = 6;
        F[5].ptIndex[0] = 2;      F[5].ptIndex[1] = 3;    F[5].ptIndex[2] = 8;
        F[6].ptIndex[0] = 1;      F[6].ptIndex[1] = 2;    F[6].ptIndex[2] = 8;
        F[7].ptIndex[0] = 0;      F[7].ptIndex[1] = 2;    F[7].ptIndex[2] = 1;
        F[8].ptIndex[0] = 0;      F[8].ptIndex[1] = 1;    F[8].ptIndex[2] = 10;
        F[9].ptIndex[0] = 1;      F[9].ptIndex[1] = 9;    F[9].ptIndex[2] = 10;

        F[10].ptIndex[0] = 1;     F[10].ptIndex[1] = 8;   F[10].ptIndex[2] = 9;
        F[11].ptIndex[0] = 3;     F[11].ptIndex[1] = 4;   F[11].ptIndex[2] = 8;
        F[12].ptIndex[0] = 3;     F[12].ptIndex[1] = 5;   F[12].ptIndex[2] = 4;
        F[13].ptIndex[0] = 4;     F[13].ptIndex[1] = 5;   F[13].ptIndex[2] = 11;
        F[14].ptIndex[0] = 7;     F[14].ptIndex[1] = 10;  F[14].ptIndex[2] = 11;
        F[15].ptIndex[0] = 0;     F[15].ptIndex[1] = 10;  F[15].ptIndex[2] = 7;
        F[16].ptIndex[0] = 4;     F[16].ptIndex[1] = 11;  F[16].ptIndex[2] = 9;
        F[17].ptIndex[0] = 4;     F[17].ptIndex[1] = 9;   F[17].ptIndex[2] = 8;
        F[18].ptIndex[0] = 5;     F[18].ptIndex[1] = 7;   F[18].ptIndex[2] = 11;
        F[19].ptIndex[0] = 9;     F[19].ptIndex[1] = 11;  F[19].ptIndex[2] = 10;

    }

    void Draw(HDC hdc)
    {
        Point2 point[3];                         // 二维投影点
        Point3 Eye = _projection.GetEye();       // 视点

        for (int nFace = 0; nFace < 20; nFace++) // 面循环
        {
            Vector3 viewVector(V[F[nFace].ptIndex[0]], Eye); // 面的视向量
            viewVector = viewVector.Normalize();             // 视向量规范化

            Vector3 vector01(V[F[nFace].ptIndex[0]], V[F[nFace].ptIndex[1]]);
            Vector3 vector02(V[F[nFace].ptIndex[0]], V[F[nFace].ptIndex[2]]);
            Vector3 faceNormal = CrossProduct(vector01, vector02); // 面法向量
            faceNormal = faceNormal.Normalize();                   // 法向量规范

            if (DotProduct(viewVector, faceNormal) >= 0) // !!!背面提出算法
            {
                for (int nPoint = 0; nPoint < 3; nPoint++)
                {
                    point[nPoint] = _projection.PerspectiveProjection2(V[F[nFace].ptIndex[nPoint]]);
                }
                MoveToEx(hdc, point[0].x, point[0].y,NULL );
                LineTo(hdc, point[0].x, point[0].y);
                LineTo(hdc, point[1].x, point[1].y);
                LineTo(hdc, point[2].x, point[2].y);
                LineTo(hdc, point[0].x, point[0].y);
            }
        }

    }
};



static bool     m_Play = false;
static wchar_t* m_str = L"左键旋转";
CIcosahedro     m_icosahedro;    // 二十面体;
Transform3      m_transform;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    HDC hdc;
    RECT rect;
    static double alpha;
    static double beta;

    switch (uMsg)
    {
    case WM_TIMER:
        alpha += 0.5;
        beta += 0.5;
        InvalidateRect(hwnd, NULL, FALSE);
        return 0;

    case WM_LBUTTONDOWN:
        m_Play = !m_Play;
        if (m_Play)
        {
            SetTimer(hwnd, 0, 0, NULL);
        }
        else
        {
            KillTimer(hwnd, 0);
        }
        return 0;

    case WM_PAINT:
    {
        hdc = BeginPaint(hwnd, &ps);
        GetClientRect(hwnd, &rect);
        SetMapMode(hdc, MM_ANISOTROPIC);
        SetWindowExtEx(hdc, rect.right, rect.bottom, NULL);
        SetViewportExtEx(hdc, rect.right, -rect.bottom, NULL);
        SetViewportOrgEx(hdc, rect.right / 2, rect.bottom / 2, NULL);
        // 内存DC
        HDC     memDC = CreateCompatibleDC(hdc);                                   // 创建兼容DC 画板
        HBITMAP newBitmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);  // 创建画布
        HGDIOBJ oldBitmap = SelectObject(memDC, newBitmap);                        // 将画布选入画板
        FillRect(memDC, &rect, (HBRUSH)(COLOR_WINDOW + 1));
        DrawText(memDC, m_str, wcslen(m_str), &rect, NULL);
        SetMapMode(memDC, MM_ANISOTROPIC);
        SetWindowExtEx(memDC, rect.right, rect.bottom, NULL);
        SetViewportExtEx(memDC, rect.right, -rect.bottom, NULL);
        SetViewportOrgEx(memDC, rect.right / 2, rect.bottom / 2, NULL);

        // 绘制图形
        {
            double scale = 300;

            m_icosahedro.ReadFace();
            m_icosahedro.ReadVertex();         

            m_transform.SetMatrix(m_icosahedro.V, 12);  // 设置顶点
            m_transform.Scale(scale, scale, scale);     // 放大正二十面体

            if (m_Play)
            {
                m_transform.RotateX(alpha);
                m_transform.RotateY(beta);
            }

            m_icosahedro.Draw(memDC);
        }      

        // 内存dc复制到设备
        BitBlt(hdc, ROUND(-rect.right / 2), ROUND(-rect.bottom / 2), rect.right, rect.bottom, memDC, ROUND(-rect.right / 2), ROUND(-rect.bottom / 2), SRCCOPY);
        SelectObject(memDC, oldBitmap);
        DeleteObject(newBitmap);
        DeleteDC(memDC);
        EndPaint(hwnd, &ps);
    }
    return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值