Построение экранного отображения каркасной модели трехмерного объекта - 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.