Построение экранного отображения каркасной модели трехмерного объекта - C (СИ)

Узнай цену своей работы

Формулировка задачи:

помогите пожалуйста исправить ошибки в лабе Задание
Лабораторная работа №5 ПОСТРОЕНИЕ ЭКРАННОГО ОТОБРАЖЕНИЯ КАРКАСНОЙ МОДЕЛИ ТРЕХМЕРНОГО ОБЪЕКТА Цель работы: научиться использовать аппарат видовых, перспективных и экранных преобразований для генерации плоских проекций трехмерных объектов, представленных в виде каркасных геометрических моделей. Теоретические сведения При проецировании объемного объекта на экранную плоскость для каждой точки объекта производится выполнение последовательности преобразований, переводящих точку из трехмерного пространства в двухмерное. Первоначально предполагается, что точка задана в мировых координатах (Xw,Yw,Zw) и требуется найти ее отображение на экране (Xs,Ys). Процесс визуализации будет проходить в три этапа: а) видовое б) перспективное в) экранное преобразование. Для видового преобразования необходима точка наблюдения (View Point), определяющая положение глаза наблюдателя или камеры. В математическом выражении, представляющем собой произведение вектора однородных координат точки объекта на матрицу преобразования, используются: величина R (расстояние от точки наблюдения до начала мировой системы координат), полярные углы  и  (в горизонтальной и вертикальной плоскости соответственно). | Xe Ye Ze 1 | = | Xw Yw Zw 1 | * V -sin  -cos  cos  -sin  cos  0 V = cos  -cos  sin  -sin  sin  0 0 sin  -cos  0 0 0 R 1 Данная матрица получена в результате смены мировой системы координат на пользовательскую с началом координат в точке наблюдения. Перспективные преобразования учитывают некоторые эффекты, связанные с особенностями зрительной системы человека и позволяют сделать изображение более реалистичным. При применении ортогональной проекции, в которой проекторами являются параллельные линии, просто отбрасывается координата Ze, а (Xe,Ye) представляет точку в двухмерном пространстве. В центральной проекции, центр проецирования в общем случае является точкой схода всех лучей-проекторов. Разместив центр в точке наблюдения и задав расстояние d от нее до экранной плоскости, получим следующие выражения: X = d * (Xe / Ze) Y = d * (Ye / Ze) Оценочное значение величины d подбирается исходя из соотношения: d = R * ( <размер изображения> / <размер объекта>) Экранное преобразование осуществляет коррекцию координат (X,Y) в (Xs,Ys) с учетом таких параметров, как текущее разрешение графического дисплея, сдвига по осям, направления оси ординат. Построение каркасного отображения трехмерного объекта, или так называемой «проволочной» (wireframe) модели, заключается в выполнении описанных преобразований для каждой точки - вершины объекта и последующей прорисовки ребер в виде отрезков, соединяющих найденные проекции вершин. В случае если требуется получить изображение твердотельного объекта, применяется один из алгоритмов удаления невидимых линий и поверхностей. Порядок выполнения работы 1. Получить задание на лабораторную работу (трехмерный объект для исследования). 2. Составить геометрическую модель трехмерного объекта, включающую описание координат всех его вершин и ребер. 3. Написать и отладить программу, выполняющую видовые, перспективные и экранные преобразования. В результате работы программы на экране должно строится каркасное отображение трехмерного объекта в параллельной и перспективной проекциях. Подобрать соответствующие параметры: размеры изображения, объекта, d, и т.д. Программа должна позволять интерактивно с клавиатуры изменять значения d, R и полярных углов точки наблюдения. 4. Исследовать полученные проекции трехмерного объекта под разными углами наблюдения, с различными расстояниями до наблюдателя и экранной плоскости, оценить искажения. Содержание отчета 1. Модель исследуемого трехмерного объекта. 2. Листинг программы. 3. Распечатки экранных проекций объекта с различным положением точки наблюдения. 4. Выводы о проделанной работе. Контрольные вопросы 1. Для чего используются видовые, перспективные и экранные преобразования? 2. Какие виды проекций бывают? 3. Что представляет собой каркасная модель трехмерного графического объекта? В чем ее достоинства и недостатки? Литература 1. Аммерал Л. Машинная графика на языке Си. В 4-х кн. Кн.1. Принципы программирования в машинной графике. Пер. с англ. - М: Сол Систем, 1992. 2. Карпов Е.В. Геометрическое моделирование и машинная графика в САПР: Методические указания к курсовому проектированию. - Пенза: ПГТУ, 1994. - 41 c.
lab5.cpp
// lab5.cpp: определяет точку входа для приложения.
//
 
#include "stdafx.h"
#include "lab5.h"
#include "Functions.h"
 
const int ScreenWidth = GetSystemMetrics(SM_CXSCREEN);   //Разрешение экрана по вертикали
const int ScreenHeight = GetSystemMetrics(SM_CYSCREEN);  //Разрешение экрана по горизонтали
 
const int Space = 100;  //Отступ от краев экрана
 
const int FormWidth = ScreenWidth - 2 * Space;    //Ширина формы
const int FormHeight = ScreenHeight - 2 * Space;  //Высота формы
 
#define MAX_LOADSTRING 100
 
// Глобальные переменные:
HINSTANCE hInst;                                // текущий экземпляр
TCHAR szTitle[MAX_LOADSTRING];                  // Текст строки заголовка
TCHAR szWindowClass[MAX_LOADSTRING];            // имя класса главного окна
 
// Отправить объявления функций, включенных в этот модуль кода:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);
 
int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
 
    // TODO: разместите код здесь.
    MSG msg;
    HACCEL hAccelTable;
 
    // Инициализация глобальных строк
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_LAB5, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);
 
    // Выполнить инициализацию приложения:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
 
    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_LAB5));
 
    // Цикл основного сообщения:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
 
    return (int) msg.wParam;
}

//
//  ФУНКЦИЯ: MyRegisterClass()
//
//  НАЗНАЧЕНИЕ: регистрирует класс окна.
//
//  КОММЕНТАРИИ:
//
//    Эта функция и ее использование необходимы только в случае, если нужно, чтобы данный код
//    был совместим с системами Win32, не имеющими функции RegisterClassEx'
//    которая была добавлена в Windows 95. Вызов этой функции важен для того,
//    чтобы приложение получило "качественные" мелкие значки и установило связь
//    с ними.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;
 
    wcex.cbSize = sizeof(WNDCLASSEX);
 
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LAB5));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_LAB5);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
 
    return RegisterClassEx(&wcex);
}
 
//
//   ФУНКЦИЯ: InitInstance(HINSTANCE, int)
//
//   НАЗНАЧЕНИЕ: сохраняет обработку экземпляра и создает главное окно.
//
//   КОММЕНТАРИИ:
//
//        В данной функции дескриптор экземпляра сохраняется в глобальной переменной, а также
//        создается и выводится на экран главное окно программы.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;
 
   hInst = hInstance; // Сохранить дескриптор экземпляра в глобальной переменной
 
   /*hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);*/
 
   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, 
       Space, Space, FormWidth, FormHeight, NULL, NULL, hInstance, NULL);
 
   if (!hWnd)
   {
      return FALSE;
   }
 
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
 
   return TRUE;
}
 
const int Count = 7;
 
POINT3 WPT[Count]; //Мировые координаты углов пирамиды
POINT SPT[Count];  //Экранные координаты углов пирамиды
 
double R = 100;  //Расстояние от точки наблюдения до начала мировой системы координат
double A = 90;   //Полярный угол в горизонтальной плоскости
double B = 90;   //Полярный угол в вертикальной плоскости
double d;

//
//  ФУНКЦИЯ: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  НАЗНАЧЕНИЕ:  обрабатывает сообщения в главном окне.
//
//  WM_COMMAND  - обработка меню приложения
//  WM_PAINT    -Закрасить главное окно
//  WM_DESTROY   - ввести сообщение о выходе и вернуться.
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
 
    //Вычисление координат пирамиды относительно размеров формы
 
    const int z = 500;
    const int w = FormWidth;
    const int h = FormHeight;
 
    WPT[0] = PointEx(Round(w/2), Round(h/10), z);
    WPT[1] = PointEx(Round(w/3), Round(h/2), z);
    WPT[2] = PointEx(Round(w/3) + (Round(w/2) - Round(w/3))*2, Round(h/2), z);
    WPT[3] = PointEx(w - Round(w/8), h - Round(h/3), z);
    WPT[4] = PointEx(w - Round(w/3), h - Round(h/6), z);
    WPT[5] = PointEx(Round(w/3), h - Round(h/6), z);
    WPT[6] = PointEx(Round(w/8), h - Round(h/3), z);
 
    int size_img = FormWidth * FormHeight; //Размер изображения
    int size_object = (WPT[3].x - WPT[6].x) * (WPT[4].y - WPT[0].y); //Размер объекта
    d = R * ((double) size_img / (double) size_object);
 
    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Разобрать выбор в меню:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_KEYDOWN:
        {
            switch(wParam)
            {
                case 'A':
                    {
                        A = A + 0.1;
                        InvalidateRect(hWnd, NULL, TRUE);
                        UpdateWindow(hWnd);
                    } break;
                case 'D':
                    {
                        A = A - 0.1;
                        InvalidateRect(hWnd, NULL, TRUE);
                        UpdateWindow(hWnd);
                    }
                case 'W':
                    {
                        B = B + 0.1;
                        InvalidateRect(hWnd, NULL, TRUE);
                        UpdateWindow(hWnd);
                    } break;
                case 'S':
                    {
                        B = B + 0.1;
                        InvalidateRect(hWnd, NULL, TRUE);
                        UpdateWindow(hWnd);
                    } break;
                case 0x31:
                    {
                        R = R + R/2;
                        InvalidateRect(hWnd, NULL, TRUE);
                        UpdateWindow(hWnd);
                    } break;
                case 0x32:
                    {
                        R = R - R/2;
                        InvalidateRect(hWnd, NULL, TRUE);
                        UpdateWindow(hWnd);
                    } break;
                case 0x33:
                    {
                        d = d + d/2;
                        InvalidateRect(hWnd, NULL, TRUE);
                        UpdateWindow(hWnd);
                    } break;
                case 0x34:
                    {
                        d = d - d/2;
                        InvalidateRect(hWnd, NULL, TRUE);
                        UpdateWindow(hWnd);
                    } break;
            }
        } break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // TODO: добавьте любой код отрисовки...
        for(int i = 0; i < Count; i++)
        {
            POINT3 EPT = GlobalPointToScreen(WPT[i],A,B,R);
            SPT[i] = Perspective(EPT,d);
        }
        Piramid(hdc, SPT, Count);
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
 
// Обработчик сообщений для окна "О программе".
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;
 
    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}
Functions.h
#include "stdafx.h"
 
#include <windows.h>
#include <math.h>
#include <stdio.h>
 
#define Pi 3.14
 
struct POINT3
{
    int x;
    int y;
    int z;
};
 
POINT Point(int x, int y);
POINT3 PointEx(int x, int y, int z);
int Round(double x);
double Abs(double X);
double DegreeToRad(double Angle);
POINT3 GlobalPointToScreen(POINT3 World_Point, double Angle_Horizontal, double Angle_Vertical, int R);
POINT Perspective(POINT3 PT, double d);
void Piramid(HDC hdc, POINT *PT, int COUNT);
Functions.cpp
#include "stdafx.h"
#include "Functions.h"
 
POINT Point(int x, int y)
{
    POINT res;
    res.x = x;
    res.y = y;
    return res;
}
 
POINT3 PointEx(int x, int y, int z)
{
    POINT3 res;
    res.x = x;
    res.y = y;
    res.z = z;
    return res;
}
 
int Round(double x)
{
    if(x < 0) x -= 0.5; else x += 0.5;
    return (int) x;
}
 
double Abs(double X)
{
    if(X < 0) X = (double) - X;
    return X;
}
 
double DegreeToRad(double Angle)
{
    return (( (double) Angle * Pi) / 180);
}
 
POINT3 GlobalPointToScreen(POINT3 World_Point, double Angle_Horizontal, double Angle_Vertical, int R)
{
    int M1[4];
    double M2[4][4];
 
    double A = DegreeToRad(Angle_Horizontal);
    double B = DegreeToRad(Angle_Vertical);
 
    M1[0] = World_Point.x;
    M1[1] = World_Point.y;
    M1[2] = World_Point.z;
    M1[3] = 1;
 
    M2[0][0] = - sin(A);
    M2[1][0] = cos(A);
    M2[2][0] = 0;
    M2[3][0] = 0;
 
    M2[0][1] = - cos(B) * cos(A);
    M2[1][1] = - cos(B) * sin(A);
    M2[2][1] = sin(B);
    M2[3][1] = 0;
 
    M2[0][2] = - sin(B) * cos(A);
    M2[1][2] = - sin(B) * sin(A);
    M2[2][2] = - cos(B);
    M2[3][2] = R;
 
    M2[0][3] = 0;
    M2[1][3] = 0;
    M2[2][3] = 0;
    M2[3][3] = 1;
 
    int M3[4];
    int S = 0;
 
    for(int i = 0; i < 4; i++)
    {
        for(int j = 0; j < 4; j++)
        {
            double X = *M2[i,j];
            S = S + Round(M1[i] * X);
        }
        M3[i] = S; S = 0;
    }
 
    POINT3 res = PointEx(Abs(M3[0]),Abs(M3[1]),Abs(M3[2]));
    return res;
}
 
POINT Perspective(POINT3 PT, double d)
{
    int x = Round(d * ((double) PT.x / (double) PT.z));
    int y = Round(d * ((double) PT.y / (double) PT.z));
    POINT res = Point(x,y);
    return res;
}
 
void Piramid(HDC hdc, POINT *PT, int COUNT)
{
    for(int i = 1; i < COUNT; i++)
    {
        MoveToEx(hdc,PT[0].x,PT[0].y,NULL);
        LineTo(hdc,PT[i].x,PT[i].y);
    }
 
    for(int i = 1; i < COUNT; i++)
    {
        MoveToEx(hdc,PT[i].x,PT[i].y,NULL);
        if(i != COUNT-1) 
            LineTo(hdc,PT[i+1].x,PT[i+1].y);
        else
            LineTo(hdc,PT[1].x,PT[1].y);
    }
}
компилируется все хорошо, но из требуемого функционала нормально работает только изменение числа R, т.е. пирамида увеличивается/уменьшается, а все остальное почему то не пашет никак не врублюсь почему, вроде все правильно... з.ы. код lab5.cpp дефолтный кроме моих изменений, вставился компилятором автоматически... (т.е. все создание формы и т.п.) так для ясности...

Решение задачи: «Построение экранного отображения каркасной модели трехмерного объекта»

textual
Листинг программы
#include "stdafx.h"
#include "Functions.h"
 
POINT Point(int x, int y)
{
    POINT res;
    res.x = x;
    res.y = y;
    return res;
}
 
POINT3 PointEx(int x, int y, int z)
{
    POINT3 res;
    res.x = x;
    res.y = y;
    res.z = z;
    return res;
}
 
int Round(double x)
{
    if(x < 0) x -= 0.5; else x += 0.5;
    return (int) x;
}
 
double Abs(double X)
{
    if(X < 0) X = (double) - X;
    return X;
}
 
double DegreeToRad(double Angle)
{
    return (( (double) Angle * Pi) / 180);
}
 
POINT3 GlobalPointToScreen(POINT3 World_Point, double Angle_Horizontal, double Angle_Vertical, int R)
{
    int M1[4];
    double M2[4][4];
 
    double A = DegreeToRad(Angle_Horizontal);
    double B = DegreeToRad(Angle_Vertical);
 
    M1[0] = World_Point.x;
    M1[1] = World_Point.y;
    M1[2] = World_Point.z;
    M1[3] = 1;
 
    M2[0][0] = - sin(A);
    M2[1][0] = cos(A);
    M2[2][0] = 0;
    M2[3][0] = 0;
 
    M2[0][1] = - cos(B) * cos(A);
    M2[1][1] = - cos(B) * sin(A);
    M2[2][1] = sin(B);
    M2[3][1] = 0;
 
    M2[0][2] = - sin(B) * cos(A);
    M2[1][2] = - sin(B) * sin(A);
    M2[2][2] = - cos(B);
    M2[3][2] = R;
 
    M2[0][3] = 0;
    M2[1][3] = 0;
    M2[2][3] = 0;
    M2[3][3] = 1;
 
    int M3[4];
    int S = 0;
 
    for(int i = 0; i < 4; i++)
    {
        for(int j = 0; j < 4; j++)
            S = S + Round(M1[j] * (double) M2[i][j]);
        M3[i] = S; S = 0;
    }
 
    POINT3 res = PointEx(Abs(M3[0]),Abs(M3[1]),Abs(M3[2]));
    return res;
}
 
POINT Perspective(POINT3 PT, double d)
{
    int x = Round(d * ((double) PT.x / (double) PT.z));
    int y = Round(d * ((double) PT.y / (double) PT.z));
    POINT res = Point(x,y);
    return res;
}
 
void Piramid(HDC hdc, POINT *PT, int COUNT)
{
    for(int i = 1; i < COUNT; i++)
    {
        MoveToEx(hdc,PT[0].x,PT[0].y,NULL);
        LineTo(hdc,PT[i].x,PT[i].y);
    }
 
    for(int i = 1; i < COUNT; i++)
    {
        MoveToEx(hdc,PT[i].x,PT[i].y,NULL);
        if(i != COUNT-1) 
            LineTo(hdc,PT[i+1].x,PT[i+1].y);
        else
            LineTo(hdc,PT[1].x,PT[1].y);
    }
}

Объяснение кода листинга программы

  1. Объявлены функции:
    • Point(int x, int y)
    • PointEx(int x, int y, int z)
    • Round(double x)
    • Abs(double X)
    • DegreeToRad(double Angle)
    • GlobalPointToScreen(POINT3 World_Point, double Angle_Horizontal, double Angle_Vertical, int R)
    • Perspective(POINT3 PT, double d)
    • Piramid(HDC hdc, POINT *PT, int COUNT)
  2. В функции GlobalPointToScreen переданы параметры:
    • World_Point - объект типа POINT3, содержащий координаты точки в мировом пространстве
    • Angle_Horizontal - угол поворота по горизонтали в градусах
    • Angle_Vertical - угол поворота по вертикали в градусах
    • R - параметр, используемый в формуле преобразования координат
  3. В функции Perspective переданы параметры:
    • PT - объект типа POINT3, содержащий координаты точки в трехмерном пространстве
    • d - параметр, используемый в формуле преобразования координат
  4. В функции Piramid переданы параметры:
    • hdc - дескриптор контекста устройства, используемый для отрисовки
    • PT - массив объектов типа POINT, содержащий координаты вершин пирамиды
    • COUNT - количество вершин пирамиды
  5. В функции Piramid используется цикл for, который выполняется COUNT-1 раз:
    • Передаются координаты текущей вершины пирамиды в функцию MoveToEx для установки текущего положения курсора
    • С помощью функции LineTo отрисовывается линия до следующей вершины пирамиды
  6. В функции Piramid используется второй цикл for, который также выполняется COUNT-1 раз:
    • Передаются координаты текущей вершины пирамиды в функцию MoveToEx для установки текущего положения курсора
    • Если текущая и следующая вершины не являются последней парой вершин пирамиды, то с помощью функции LineTo отрисовывается линия до следующей вершины
    • В противном случае (когда текущая вершина является последней), с помощью функции LineTo отрисовывается линия до первой вершины пирамиды
  7. В функции Piramid не используются переменные S, M1, M2, M3, res.

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

6   голосов , оценка 4 из 5
Похожие ответы