Построение экранного отображения каркасной модели трехмерного объекта - C (СИ)
Формулировка задачи:
помогите пожалуйста исправить ошибки в лабе
Задание
lab5.cpp
Functions.h
Functions.cpp
компилируется все хорошо, но из требуемого функционала нормально работает только изменение числа R, т.е. пирамида увеличивается/уменьшается, а все остальное почему то не пашет никак не врублюсь почему, вроде все правильно...
з.ы. код lab5.cpp дефолтный кроме моих изменений, вставился компилятором автоматически... (т.е. все создание формы и т.п.) так для ясности...
Лабораторная работа №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: определяет точку входа для приложения. // #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; }
#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);
#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); } }
Решение задачи: «Построение экранного отображения каркасной модели трехмерного объекта»
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); } }
Объяснение кода листинга программы
- Объявлены функции:
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)
- В функции
GlobalPointToScreen
переданы параметры:World_Point
- объект типаPOINT3
, содержащий координаты точки в мировом пространствеAngle_Horizontal
- угол поворота по горизонтали в градусахAngle_Vertical
- угол поворота по вертикали в градусахR
- параметр, используемый в формуле преобразования координат
- В функции
Perspective
переданы параметры:PT
- объект типаPOINT3
, содержащий координаты точки в трехмерном пространствеd
- параметр, используемый в формуле преобразования координат
- В функции
Piramid
переданы параметры:hdc
- дескриптор контекста устройства, используемый для отрисовкиPT
- массив объектов типаPOINT
, содержащий координаты вершин пирамидыCOUNT
- количество вершин пирамиды
- В функции
Piramid
используется циклfor
, который выполняетсяCOUNT-1
раз:- Передаются координаты текущей вершины пирамиды в функцию
MoveToEx
для установки текущего положения курсора - С помощью функции
LineTo
отрисовывается линия до следующей вершины пирамиды
- Передаются координаты текущей вершины пирамиды в функцию
- В функции
Piramid
используется второй циклfor
, который также выполняетсяCOUNT-1
раз:- Передаются координаты текущей вершины пирамиды в функцию
MoveToEx
для установки текущего положения курсора - Если текущая и следующая вершины не являются последней парой вершин пирамиды, то с помощью функции
LineTo
отрисовывается линия до следующей вершины - В противном случае (когда текущая вершина является последней), с помощью функции
LineTo
отрисовывается линия до первой вершины пирамиды
- Передаются координаты текущей вершины пирамиды в функцию
- В функции
Piramid
не используются переменныеS
,M1
,M2
,M3
,res
.
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д