Элемент
управления
|
Действие
|
Мышь
|
Зажатая ЛКМ/ПКМ
|
Обзор сцены со
всех ракурсов
|
Вращение
колесика
|
Масштабирование
+/-
|
Нажатие ПКМ
|
Привидение к
сходному размеру
|
Клавиатура
|
Вверх
|
Вращение сцены
вертикально вверх
|
Вниз
|
Вращение сцены
вертикально вниз
|
Влево
|
Вращение сцены
в левую сторону
|
Вправо
|
Вращение сцены
в правую сторону
|
Данное приложение работает без аварийных завершений, не
вызывает исключений, не перегружает систему, не помечается антивирусными
системами как потенциально опасное программное обеспечение.
Минимальные системные требования:
- операционная система Windows XP, Vista, 7 (совместимость сWindows 8 неизвестна);
- процессор с тактовой частотой 1 Ггц или выше;
- Видеопамять 128 Мб или выше;
- Клавиатура, мышь.
4.3
Результат
В результате выполнения программы, получаем следующие
результаты, показанные на рисунках 4-8.
Рисунок 4 - Часть стакана скрыта непрозрачным кубом
Рисунок 5 - Видимость куба сквозь прозрачный стакан
Рисунок 6 - Куб находится позади стакана
Рисунок 7 - Пример масштабирования на уменьшение сцены
Рисунок 8 - Пример масштабирования на увеличение сцены
Заключение
В результате выполнения поставленной задачи, получили сцену,
состоящую из двух предметов (прозрачный стакан и непрозрачный куб) на статичном
фоне, замощенным загруженным изображением. По заданию необходимо было доказать
свойства прозрачных тел и свойства непрозрачных тел, а также проверить это
опытным путем в результате их взаимодействия. В итоге получили необходимые
результаты: непрозрачный предмет скрывает часть прозрачного предмета, а сквозь
прозрачный предмет при любом ракурсе виден непрозрачный предмет.
Для получения данных результатов был применен ряд алгоритмов:
алгоритм преобразования координат, алгоритм удаления невидимых граней,
алгоритм, определяющий лицевые и не лицевые грани с помощью вектора нормали,
проведенного к поверхности, а также алгоритм закраски трехмерного объекта с
использованием простой модели освещения, основанной на ранее вычисленных
значениях вектора нормали.
Итогом работы стало приложение "C# Alpha Blending", наглядно
показывающий принцип работы альфа-смешения, а также доказывающий возможность
создания трехмерной сцены посредством программирования на языке высокого уровня
C#.
Список
использованных источников
1
Кузнецова Е.А., Лукошков М.М., Майоров И.С. Методические указания к выполнению
лабораторных работ по дисциплине "Компьютерная геометрия и графика".
[Текст] - Издательство: " Архангельский Государственный Технический
Университет", г. Архангельск, 2006 г. - стр.36;
2 Википедия -
Свободная Энциклопедия. [Электронный Ресурс] - Режим доступа: http://ru.
wikipedia.org/ <http://ru.wikipedia.org/>, свободный;
Иванов В.П.,
Батраков А.С. Трехмерная компьютерная графика. [Текст] - Издательство:
"Радио и связь", ISBN
5-256-01204-5, г. Москва, 1995 г. - стр.224;
Роджерс Д., Адамс
Дж. Математические основы машинной графики. [Текст] - Издательство:
"Мир", перевод с английского, ISBN 5-03-002143-4, г. Москва, 2001 г. - стр.604.
Приложения
Приложение А
Листинг основных компонентов
Листинг модуля Form1.
csSystem;System. Collections. Generic;System.componentModel;System. Data;System.
Drawing;System. Linq;System. Text;System. Windows.
Forms;WindowsFormsApplication1
{
// Структура координатstruct Point3D
{double x;double y;double z;Point3D (double x,
double y, double z)
{. x = x;. y = y;. z = z;
}
}partial class Form1: Form
{b;g;image;D [] Vertex = new Point3D [48]; // Массив координат вершин (x, y, z)D []
View = new Point3D [48]; // Массив видовых координат[] Scrn = new
Point [48]; // Массив экранных координат[] Perspective =
new Point [48]; // Массив перспективных координат[] NZ = new
double [27]; // Массив нормалей для освещения
int [] Order = new int [27]; // Массив порядка отображения
видимых граней[] D = new double [27]; // Массив, содержащий средние значения
Z-гранейTeta = Math. PI / 180; // Угол тета = 45 градусов
double Phi = Math. PI / 180; // Угол фи = 45 градусовmousePress =
false;sX = 0;sY = 0;nx, ny, nz;D0 = 500;R0 = 300;Form1 ()
{();. MouseWheel += new MouseEventHandler
(Form1_MouseWheel);= new Bitmap ("Background. jpg"); // Экземпляр класса Bitmap=
Image. FromFile ("Background. jpg"); // Загружаем фоновое изображение
g = Graphics. FromImage (b); // Задаем поверхность для
рисования
}
// Построение объектов сценыПостроитьСцену ()
{. DrawImage (image, 0, 0);(int i = 0; i < 48;
i++)
{
// Преобразование из мировых в видовые координаты
View [i]. x = 20 * (int) Math. Round (Vertex [i].
x * (-Math. Sin (Teta)) + Vertex [i]. y * Math. Cos (Teta));[i]. y = 20 *
(int) Math. Round (Vertex [i]. x * (-Math. Cos (Phi) * Math. Cos (Teta)) -
Vertex [i]. y * (Math. Cos (Phi) * Math. Sin (Teta)) + Vertex [i]. z * Math.
Sin (Phi));[i]. z = - 20 * (int) Math. Round (Vertex [i]. x * (-Math. Sin (Phi) * Math.
Cos (Teta)) - Vertex [i]. y * (Math. Sin (Phi) * Math. Sin (Teta)) - Vertex
[i]. z * (Math. Cos (Phi)) + R0);
// Перспективное преобразование видовых координат
Perspective [i]. X = pictureBox1. Width / 2 +
(int) Math. Round (D0 * (View [i]. x / View [i]. z));[i]. Y = pictureBox1.
Height / 2 + (int) Math. Round (D0 * (View [i]. y / View [i]. z));(View [i]. z
== 0)
{[i]. X = 0;[i]. Y = 0;
}
{[i]. X = (int) (Math. Round (D0 * (View [i]. x /
View [i]. z)));[i]. Y = (int) (Math. Round (D0 * (View [i]. y / View [i]. z)));
}
// Получение экранных координат
Scrn [i]. X = (int) Perspective [i]. X + pictureBox1. Width
/ 2;[i]. Y = (int) Perspective [i]. Y + pictureBox1. Height / 2;
}
// Находим среднее значение по координате Z каждой грани
for (int i = 0; i < 19; i++)
{[i] = (View [i]. z + View [i + 20]. z + View [i
+ 21]. z + View [i + 1]. z) / 4;
}
// Конечная грань стакана[19] = (View
[19]. z + View [39]. z + View [20]. z + View [0]. z) / 4;
// Грань - основание стакана
for (int i = 0; i < 19; i++)
{
D [20] += View [i]. z / 20;
}
// Грани ядовито-желтого куба
D [21] = (View [40]. z + View [41]. z + View
[42]. z + View [43]. z) / 4;[22] = (View [40]. z + View [44]. z + View [47]. z
+ View [43]. z) / 4;[23] = (View [43]. z + View [47]. z + View [46]. z + View
[42]. z) / 4;[24] = (View [42]. z + View [46]. z + View [45]. z + View [41]. z)
/ 4;[25] = (View [41]. z + View [45]. z + View [44]. z + View [40]. z) / 4;[26]
= (View [44]. z + View [45]. z + View [46]. z + View [47]. z) / 4;
// Заполняем список граней<Point []
> Plane = new List<Point [] > ();(int i = 0; i < 19; i++)
{. Add (new Point [] { Scrn [i], Scrn [20 + i],
Scrn [21 + i], Scrn [1 + i] });
}. Add (new Point [] { Scrn [19], Scrn [39], Scrn
[20], Scrn [0] });. Add (new Point [] {Scrn [0], Scrn [1], Scrn [2], Scrn [3],
Scrn [4], Scrn [5], Scrn [6], Scrn [7], Scrn [8], Scrn [9], Scrn [10], Scrn
[11], Scrn [12], Scrn [13], Scrn [14], Scrn [15], Scrn [16], Scrn [17], Scrn
[18], Scrn [19] });
// Куб ядовито-желтого цвета
Plane. Add (new Point [] { Scrn [40], Scrn [41], Scrn
[42], Scrn [43] });. Add (new Point [] { Scrn [40], Scrn [44], Scrn [47], Scrn
[43] });. Add (new Point [] { Scrn [43], Scrn [47], Scrn [46], Scrn [42] });.
Add (new Point [] { Scrn [42], Scrn [46], Scrn [45], Scrn [41] });. Add (new
Point [] { Scrn [41], Scrn [45], Scrn [44], Scrn [40] });. Add (new Point [] {
Scrn [44], Scrn [45], Scrn [46], Scrn [47] });
// Список цветов для граней сцены<Color>
PlaneColor = new List<Color> ()
{. FromArgb (80, 208, 208, 208), // 1 Грань стакана. FromArgb (80, 208, 208, 208), //
2 Грань стакана
Color. FromArgb (80, 208, 208, 208), // 3 Грань стакана
Color. FromArgb (80, 208, 208, 208), // 6 Грань стакана
Color. FromArgb (80, 208, 208, 208), // 7 Грань стакана. FromArgb (80, 208, 208, 208), //
8 Грань стакана
Color. FromArgb (80, 208, 208, 208), // 9 Грань стакана
Color. FromArgb (80, 208, 208, 208), // 10 Грань стакана. FromArgb (80, 208, 208, 208), //
11 Грань стакана
Color. FromArgb (80, 208, 208, 208), // 12 Грань стакана
Color. FromArgb (80, 208, 208, 208), // 13 Грань стакана. FromArgb (80, 208, 208, 208), //
14 Грань стакана
Color. FromArgb (80, 208, 208, 208), // 15 Грань стакана
Color. FromArgb (80, 208, 208, 208), // 16 Грань стакана. FromArgb (80, 208, 208, 208), //
17 Грань стакана
Color. FromArgb (80, 208, 208, 208), // 18 Грань стакана
Color. FromArgb (80, 208, 208, 208), // 19 Грань стакана. FromArgb (80, 208, 208, 208), //
20 Грань стакана
Color. FromArgb (100, 102, 102, 102), // 21 - основание стакана
Color. FromArgb (255, 204, 255, 0), // Грань куба
Color. FromArgb (255, 204, 255, 0), // Грань куба
Color. FromArgb (255, 204, 255, 0), // Грань куба
Color. FromArgb (255, 204, 255, 0), // Грань куба
Color. FromArgb (255, 204, 255, 0), // Грань куба. FromArgb
(255, 204, 255, 0), // Грань куба
};
СортировкаГраней (27, 0); // Сортировка 27 граней по глубине,
начиная с 0-ой грани
ВычислениеНормали (); // Вычисление нормали к плоскости по
трем точкам
for (int i = 0; i < 27; i++) // Перебираем все грани
{(NZ [Order [i]] >= 0) // Если грань не видна
{A = (int) ( (double) PlaneColor [Order [i]]. A);
// ПрозрачностьR = (int) (
(double) PlaneColor [Order [i]]. R * NZ [Order [i]]); // Красный цвет * интенсивность
int G = (int) ( (double) PlaneColor [Order [i]].
G * NZ [Order [i]]); // Зеленый цвет *
интенсивность
int B = (int) ( (double) PlaneColor [Order [i]].
B * NZ [Order [i]]); // Синий цвет *
интенсивность
Color Glass = Color. FromArgb (A, R, G, B); // Задаем цвет. FillPolygon
(new SolidBrush (Glass), Plane [Order [i]]); // Закраска
}
if (NZ [Order [i]] <= 0) // Если грань видна
{A = (int) Math. Abs ( ( (double) PlaneColor
[Order [i]]. A));R = (int) Math. Abs ( ( (double) PlaneColor [Order [i]]. R *
NZ [Order [i]]));G = (int) Math. Abs ( ( (double) PlaneColor [Order [i]]. G *
NZ [Order [i]]));B = (int) Math. Abs ( ( (double) PlaneColor [Order [i]]. B *
NZ [Order [i]]));Glass = Color. FromArgb (A, R, G, B);
g. FillPolygon (new SolidBrush (Glass), Plane
[Order [i]]);
}
}
}
// Процедура нахождения нормалей к
поверхностямВычислениеНормали ()
{
// Получение нормалей к первым 19 граням стакана
for (int i = 0; i < 19; i++)
{= View [i]. y * (View [i + 20]. z - View [i +
21]. z) + View [i + 20]. y * (View [i + 21]. z - View [i]. z) + View [i + 21].
y * (View [i]. z - View [i + 20]. z);= View [i]. z * (View [i + 20]. x - View
[i + 21]. x) + View [i + 20]. z * (View [i + 21]. x - View [i]. x) + View [i +
21]. z * (View [i]. x - View [i + 20]. x);= View [i]. x * (View [i + 20]. y -
View [i + 21]. y) + View [i + 20]. x * (View [i + 21]. y - View [i]. y) + View
[i + 21]. x * (View [i]. y - View [i + 20]. y);[i] = nz / Math. Sqrt (nz * nz +
ny * ny + nx * nx);
}
// Нормаль к 19-ой грани (не поддается циклу)
nx = View [19]. y * (View [39]. z - View [20]. z)
+ View [39]. y * (View [20]. z - View [19]. z) + View [20]. y * (View [19]. z -
View [39]. z);= View [19]. z * (View [39]. x - View [20]. x) + View [39]. z *
(View [20]. x - View [19]. x) + View [20]. z * (View [19]. x - View [39]. x);=
View [19]. x * (View [39]. y - View [20]. y) + View [39]. x * (View [20]. y -
View [19]. y) + View [20]. x * (View [19]. y - View [39]. y);[19] = nz / Math.
Sqrt (nz * nz + ny * ny + nx * nx);
// Нормаль к грани (основание стакана), грань имеет 10
вершин, взято 3 (0, 10, 19)
nx = View [0]. y * (View [10]. z - View [19]. z)
+ View [10]. y * (View [19]. z - View [0]. z) + View [19]. y * (View [0]. z -
View [10]. z);= View [0]. z * (View [10]. x - View [19]. x) + View [10]. z *
(View [19]. x - View [0]. x) + View [19]. z * (View [0]. x - View [10]. x);=
View [0]. x * (View [10]. y - View [19]. y) + View [10]. x * (View [19]. y -
View [0]. y) + View [19]. x * (View [0]. y - View [10]. y);[20] = nz / Math.
Sqrt (nz * nz + ny * ny + nx * nx);
// 40, 41, 42= View [40]. y * (View [41]. z -
View [42]. z) + View [41]. y * (View [42]. z - View [40]. z) + View [42]. y *
(View [40]. z - View [41]. z);= View [40]. z * (View [41]. x - View [42]. x) +
View [41]. z * (View [42]. x - View [40]. x) + View [42]. z * (View [40]. x -
View [41]. x);= View [40]. x * (View [41]. y - View [42]. y) + View [41]. x *
(View [42]. y - View [40]. y) + View [42]. x * (View [40]. y - View [41].
y);[21] = nz / Math. Sqrt (nz * nz + ny * ny + nx * nx);
// 40, 44, 47= View [40]. y * (View [44]. z -
View [47]. z) + View [44]. y * (View [47]. z - View [40]. z) + View [47]. y *
(View [40]. z - View [44]. z);= View [40]. z * (View [44]. x - View [47]. x) +
View [44]. z * (View [47]. x - View [40]. x) + View [47]. z * (View [40]. x -
View [44]. x);= View [40]. x * (View [44]. y - View [47]. y) + View [44]. x *
(View [47]. y - View [40]. y) + View [47]. x * (View [40]. y - View [44].
y);[22] = nz / Math. Sqrt (nz * nz + ny * ny + nx * nx);
// 43, 47, 46= View [43]. y * (View [47]. z -
View [46]. z) + View [47]. y * (View [46]. z - View [43]. z) + View [46]. y *
(View [43]. z - View [47]. z);= View [43]. z * (View [47]. x - View [46]. x) +
View [47]. z * (View [46]. x - View [43]. x) + View [46]. z * (View [43]. x -
View [47]. x);= View [43]. x * (View [47]. y - View [46]. y) + View [47]. x *
(View [46]. y - View [43]. y) + View [46]. x * (View [43]. y - View [47].
y);[23] = nz / Math. Sqrt (nz * nz + ny * ny + nx * nx);
// 42, 46, 45= View [42]. y * (View [46]. z -
View [45]. z) + View [46]. y * (View [45]. z - View [42]. z) + View [45]. y *
(View [42]. z - View [46]. z);= View [42]. z * (View [46]. x - View [45]. x) +
View [46]. z * (View [45]. x - View [42]. x) + View [45]. z * (View [42]. x -
View [46]. x);= View [42]. x * (View [46]. y - View [45]. y) + View [46]. x *
(View [45]. y - View [42]. y) + View [45]. x * (View [42]. y - View [46].
y);[24] = nz / Math. Sqrt (nz * nz + ny * ny + nx * nx);
// 41, 45, 44= View [41]. y * (View [45]. z -
View [44]. z) + View [45]. y * (View [44]. z - View [41]. z) + View [44]. y *
(View [41]. z - View [45]. z);= View [41]. z * (View [45]. x - View [44]. x) +
View [45]. z * (View [44]. x - View [41]. x) + View [44]. z * (View [41]. x -
View [45]. x);= View [41]. x * (View [45]. y - View [44]. y) + View [45]. x *
(View [44]. y - View [41]. y) + View [44]. x * (View [41]. y - View [45].
y);[25] = nz / Math. Sqrt (nz * nz + ny * ny + nx * nx);
// 44, 45, 46= View [44]. y * (View [45]. z -
View [46]. z) + View [45]. y * (View [46]. z - View [44]. z) + View [46]. y *
(View [44]. z - View [45]. z);= View [44]. z * (View [45]. x - View [46]. x) +
View [45]. z * (View [46]. x - View [44]. x) + View [46]. z * (View [44]. x -
View [45]. x);= View [44]. x * (View [45]. y - View [46]. y) + View [45]. x *
(View [46]. y - View [44]. y) + View [46]. x * (View [44]. y - View [45].
y);[26] = nz / Math. Sqrt (nz * nz + ny * ny + nx * nx);
}
// Получить изображение кубаПоказатьСцену ()
{
ПостроитьСцену ();. Image = b;
}
// Метод-флаг
bool Flag (int i, int PlaneNumber, int [] Order)
{flag = true; // Флаг поднят(int j = 0; j < PlaneNumber; j++) // Перебираем грани по их номерам
if (Order [j] == i) flag = false; // Сравниваем номер грани в
массиве порядка с номером данной граниflag; // Возвращаем значение флага
}
// Метод сортировки гранейСортировкаГраней (int PlanesCount,
int PlaneNumber)
{k = 1000; // Показатель глубины
for (int i = 0; i < 27; i++) // Перебираем все грани
{(Flag (i, PlaneNumber, Order)) //
if (D [i] < k) // Если ср. знач. по Z < показателя
глубины
{ // то.= D [i]; // Показатель глубины равняется конкретному
значению[PlaneNumber] = i; // Заполнения массива по порядку расположения граней
}
}- -; // Уменьшаем количество граней на 1++; // Увеличиваем
номер грани на 1(PlanesCount > 0) // Если количество граней > 0, вызываем
рекурсию
СортировкаГраней (PlanesCount, PlaneNumber);
}
// События, обрабатываемые при загрузке формы приложения
private void Form1_Load (object sender, EventArgs
e)
// Верхняя плоскость стакана
Vertex [20] = new Point3D (-60, 100, 0); //
A-1[21] = new Point3D (-58, 100, - 19); // B-1[22] = new Point3D (-48, 100, -
36); // C-1[23] = new Point3D (-35, 100, - 50); // D-1[24] = new Point3D (-18,
100, - 58); // E-1[25] = new Point3D (0, 100, - 60); // F-1[26] = new Point3D
(18, 100, - 58); // G-1[27] = new Point3D (35, 100, - 50); // H-1[28] = new
Point3D (48, 100, - 36); // I-1[29] = new Point3D (58, 100, - 19); // J-1[30] =
new Point3D (60, 100, 0); // K-11[31] = new Point3D (58, 100, 19); // L-12[32]
= new Point3D (48, 100, 36); // M-13[33] = new Point3D (35, 100, 50); //
N-14[34] = new Point3D (18, 100, 58); // O-15[35] = new Point3D (0, 100, 60);
// P-16[36] = new Point3D (-18, 100, 58); // R-17[37] = new Point3D (-35, 100,
50); // S-18[38] = new Point3D (-48, 100, 36); // T-19[39] = new Point3D (-58,
100, 19); // U-20
// Ядовито-желтый куб (а = 50)
// Нижняя грань
Vertex [40] = new Point3D (-60, - 50, - 65); //
A[41] = new Point3D (-60, - 50, - 115); // B[42] = new Point3D (-10, - 50, -
115); // C[43] = new Point3D (-10, - 50, - 65); // D
// Верхняя грань[44] = new Point3D (-60, 0, - 65); // A-1[45] =
new Point3D (-60, 0, - 115); // B-1[46] = new Point3D (-10, 0, - 115); //
C-1[47] = new Point3D (-10, 0, - 65); // D-1
ПоказатьСцену ();
}
// Управление сценой с помощью клавиш-стрелок
private void Form1_KeyDown (object sender,
KeyEventArgs e)
{(e. KeyCode == Keys. Up)
{= Phi + Math. PI / 128;
ПоказатьСцену ();
}(e. KeyCode == Keys. Down)
{= Phi - Math. PI / 128;
ПоказатьСцену ();
}(e. KeyCode == Keys. Right)
{= Teta + Math. PI / 128;
ПоказатьСцену ();
}(e. KeyCode == Keys. Left)
{= Teta - Math. PI / 128;
ПоказатьСцену ();
}
}
// Управление сценой с помощью мышиvoid
pictureBox1_MouseMove (object sender, MouseEventArgs e)
{(mousePress)
{(e. X < sX)
{= Teta - Math. PI / 128;= e. X;
}(e. X > sX)
{= Teta + Math. PI / 128;= e. X;
}(e. Y > sY)
{= Phi - Math. PI / 128;= e. Y;
}(e. Y < sY)
{= Phi + Math. PI / 128;= e. Y;
}
ПоказатьСцену ();
}
}
// Событие, вохникающее при отпускании мыши
private void pictureBox1_MouseUp (object sender, MouseEventArgs e)
{= false;
}
// Событие, возникающее при нажатии мыши
private void pictureBox1_MouseDown (object
sender, MouseEventArgs e)
{= true;(e. Button == MouseButtons. Right)
{= 300;
ПоказатьСцену ();
}
}
// Событие при прокрутке колеса мыши
void Form1_MouseWheel (object sender,
MouseEventArgs e)
{( (e. Delta == 120) && (R0! = 150))
{- = 5;
ПоказатьСцену ();
}if ( (e. Delta == - 120) && (R0! = 800))
{+= 5;
ПоказатьСцену ();
}
}
}
}