Міністерство освіти і науки, молоді та спорту України
Національний університет «Львівська політехніка»
Інститут комп’ютерних наук та інформаційних технологій
Кафедра автоматизованих систем управління
Розрахункова робота
на тему:
“ Замалювання фігур за методом Гуро ”
з дисципліни:
“ Комп’ютерна графіка ”
Львів-2013
Зміст
Теоретичні відомості
Порівняльний аналіз можливих методів
Вибраний алгоритм
Недоліки методу Гуро
Програма
Аналіз отриманих результатів
Вказівки користувачеві
Вступ
Замальовування методом Гуро - це метод лінійної інтерполяції освітленості в межах одного полігона. Він був винайдений в 1971 і носить ім'я свого винахідника. Це простий і ефективний метод творення відчуття зігнутості для рівного полігона. Цей метод також часто використається для скорочення глибини сцени, що прорисовується шляхом імітації зникнення вилучених об'єктів у тумані.
Отже, Метод Гуро поєднує пристойну якість картинки з відносно маленькою кількістю обчислень і компактним набором даних, який суть є просте розширення основної полігональної моделі. Однак, є важливих недоліків. Якість освітлення з використанням методу Гуро сильно залежить від розміру змальованих багатокутників. Використовується лінійна інтерполяція, так що світлові плями, такі, якими кожного боку полігону, що виглядає вельми нереалістично. Ще одна проблема - Метод Гуро не є коректним з точки зору перспективи, тобто в деяких випадках можна отримати результат, що залежить від положення спостерігача.
Теоретичні відомості
Метод тонування Гуро - метод зафарбовування в тривимірній комп'ютерній графіці (затінення), призначений для створення ілюзії гладкою криволінійної поверхні, описаної у вигляді полігональної сітки з плоскими гранями, шляхом інтерполяції кольорів прилеглих граней. Метод вперше запропонований Анрі Гуро в 1971 році.
На відміну від плоскої зафарбовування, при якій всі точки одного багатокутника малюються одним кольором, в методі Гуро інтенсивність освітлення, а, отже, і колір кожної точки - змінюється уздовж поверхні багатокутника.
Принцип методу полягає в послідовному обчисленні нормалей до кожної з граней тривимірної моделі, подальшого визначення нормалей вершин шляхом усереднення нормалей всіх прилеглих до вершини граней. Далі на підставі значень нормалей з обраної моделі відображення обчислюється освітленість кожної вершини, яка представляється інтенсивністю кольору в вершині. Розрахунок освітлення, використаний Гуро, був заснований на моделі дифузного віддзеркалення Ламберта.
Порівняльний аналіз можливих методів
Основна причина популярності алгоритмів зафарбування, заснованих на розбитті на багатокутники, - існування двох методів зафарбування: метод Гуро (Gouraud shading) і метод Фонга (Phong shading). Обидва методи дозволяють створювати згладжені зображення. Це нескладні алгоритм, в яких спочатку вираховуються параметри зафарбування в вершинах багатокутників, а потім відбувається інтерполяція по внутрішній області багатокутника.
Метод Гуро швидше методу Фонга, але з його допомогою не можна досягти деяких світлових ефектів (наприклад, відблисків). Його зазвичай використовують у програмах, де важлива швидкість: наприклад, у авіасимуляторах. Закраска методом Фонга дозволяє одержати більш якісне зображення, але вона і обходиться дорожче. У методі Гуро рахуються тільки інтенсивності в вершинах багатокутників, використовуючи функцію закраски. І потім інтерполюються для пікселів внутрішньої області. А у методі Фонга інтерполюються нормалі, і функція закраски застосовується до кожної точки. І хоча обидва цих методу стали фактично стандартними (метод Гуро використовується в багатьох графічних робочих станціях; обидва методи включені в GKS3D і PHIGS), до них існували інші способи зафарбування, розроблені, наприклад, Букнайтом (Bouknight, 1970) і Вайлі (Wylie, 1967 ).
Метод плоского зафарбовування. Цей метод додає трохи реалістичності в зображення сцени, так як яскравість зафарбовування кожної поверхні залежить деяким чином від кута падіння світла на неї.
На початку процесу зафарбовування комп'ютер обчислює нормаль для кожного багатокутника. Вектор нормалі можна уявляти собі у вигляді стрілки, що вказує напрямок лицьовій поверхні грані.
Далі комп'ютер обчислює кут між нормаллю кожного багатокутника і напрямком променя, випущеного деяким абстрактним джерелом світла. Тим багатокутників, нормаль яких паралельна напрямку світла (тобто тим площинах, які безпосередньо "дивляться" лицьовою частиною на джерело світла), приписується найбільша інтенсивність світла, а ті грані, чия нормаль становить 90 і більше градусів з напрямком світла (т. е. площині, що не "бачать" джерела) малюються чорними. Говорячи математичною мовою, яскравість поверхні пропорційна косинусу кута. Якщо ви знаєте тригонометрію, то згадайте, що косинус нуля градусів дорівнює 1.0, а косинус 90 градусів дорівнює 0.0. Таким чином, найбільша інтенсивність кольору буде тільки у поверхонь, нормаль яких утворює нульовий кут з напрямком променя, що висвітлює сцену.
Метод плаваючої псевдо нормалі. Метод плаваючої псевдо нормалі об'єднує кращі методи Гуро і Фонга. Так реалістичність виходить як у методі Фонга при швидкодії як у методі Гуро.
Спочатку комп'ютер обчислює нормалі для кожної вершини моделі. Нормаль вершини вважається як середнє між нормалями всіх багатокутників, що перетинаються в даній вершині.
Далі обчислюється місця розташування ще однієї нормалі, паралельної вектору освітленості. Якщо дана нормаль не влучає у даний багатокутник, то зафарбовуємо його методом Гуро, інакше з'єднуємо вершини багатокутника з обчисленим підставою нормалі і зафарбовує отримані багатокутники методом Гуро.
Проводячи цей процес для кожного багатокутника і видаляючи невидимі поверхні, одержимо зображення, зафарбоване методом плаваючою псевдо нормалі.
Вибраний алгоритм
Метод Гуро ґрунтується на визначенні освітленості грані в її вершинах з наступною інтерполяція отриманих величин на всю грань.
NA = ( N1 + N2 + N3 + N4 ) / 4
Рисунок 1 – Нормаль у вершині
Моделлю освітлення (функцією зафарбування) будемо називати функцію, що залежить від вектора нормалі в даній точці, положення джерела світла і вектора спостереження. В якості нормалі в вершині береться усереднена по нормалей прилеглих граней нормаль (Рис.1). Функція зафарбування застосовується в кожній вершині для розрахунку інтенсивності.
Рисунок 2
Проведемо через довільну точку S опуклого чотирикутника горизонтальну пряму (Рис.2). Освітленість Is в точці S обчислюється по інтенсивності Ia і Ih в точках перетину цієї прямої зі сторонами чотирикутника, а Ia і Ih інтерполюються по інтенсивності у відповідних вершинах:
Недоліки методу Гуро
За допомогою методу Гуро можна зображувати тільки матові поверхні, що не мають дзеркальних відблисків (оскільки відблиск буде розмазуватися по поверхні і зникне). Дійсно, у випадку, коли відблиск розташований всередині грані і не доходить до вершин, дзеркальна складова в вершинах дорівнює нулю і, отже, відблиск не з'явиться при інтерполяції. У методі Гуро є ще один недолік. Справа в тому, що виникає класичний оптичний ефект (Mach banding): на кордонах чотирикутників людське око підсилює переходи і межі сприймаються як світлі лінії (це відбувається із-за розриву похідної). Бажання усунути ці недоліки приводить нас до методу Фонга.
Програма
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Gyro
{
public partial class Form1 : Form
{
int pr;
public Form1()
{
InitializeComponent();
bmp = new Bitmap(600, 600);
g = Graphics.FromImage(bmp);
polygons = new Polygon[12];
}
double pi = Math.PI;
double rho = 500;
double teta = 0;
double phi = -Math.PI;
Point3D A = new Point3D(-150, -75, -75);
Point3D B = new Point3D(150, -75, -75);
Point3D C = new Point3D(-150, 75, -75);
Point3D D = new Point3D(150, 75, -75);
Point3D A1 = new Point3D(-150, -75, 75);
Point3D B1 = new Point3D(150, -75, 75);
Point3D C1 = new Point3D(-150, 75, 75);
Point3D D1 = new Point3D(150, 75, 75);
Polygon[] polygons;
Bitmap bmp;
Graphics g;
Pen pen = new Pen(Color.Orange, 1);
Brush brush = new SolidBrush(Color.Gray);
private void Form1_Load(object sender, EventArgs e)
{
Draw();
}
public void Draw()
{
Point3D _A = A.ToViewCoord(phi, teta, rho);
Point3D _B = B.ToViewCoord(phi, teta, rho);
Point3D _C = C.ToViewCoord(phi, teta, rho);
Point3D _D = D.ToViewCoord(phi, teta, rho);
Point3D _A1 = A1.ToViewCoord(phi, teta, rho);
Point3D _B1 = B1.ToViewCoord(phi, teta, rho);
Point3D _C1 = C1.ToViewCoord(phi, teta, rho);
Point3D _D1 = D1.ToViewCoord(phi, teta, rho);
polygons[0] = new Polygon(_A,_B, _D);
polygons[1] = new Polygon(_A, _D,_C);
polygons[2] = new Polygon(_C1, _A1, _A);
polygons[3] = new Polygon(_A, _C, _C1);
polygons[4] = new Polygon(_D1,_C1, _C );
polygons[5] = new Polygon(_C, _D, _D1);
polygons[6] = new Polygon(_D, _B, _B1);
polygons[7] = new Polygon(_D, _B1, _D1);
polygons[8] = new Polygon(_A, _B1, _B);
polygons[9] = new Polygon(_A, _A1, _B1);
polygons[10] = new Polygon(_C1, _D1, _A1);
polygons[11] = new Polygon(_D1, _B1, _A1);
double _nxA = Math.Abs((polygons[1].Normal.X + polygons[3].Normal.X + polygons[0].Normal.X + polygons[2].Normal.X + polygons[8].Normal.X + polygons[9].Normal.X) / 6);
double _nyA = Math.Abs((polygons[1].Normal.Y + polygons[3].Normal.Y + polygons[0].Normal.Y + polygons[2].Normal.Y + polygons[8].Normal.Y + polygons[9].Normal.Y) / 6);
double _nzA = Math.Abs((polygons[1].Normal.Z + polygons[3].Normal.Z + polygons[0].Normal.Z + polygons[2].Normal.Z + polygons[8].Normal.Z + polygons[9].Normal.Z) / 6);
double modulA = Math.Sqrt(_nxA * _nxA + _nyA * _nyA + _nzA * _nzA);
double _nxB = Math.Abs((polygons[8].Normal.X + polygons[0].Normal.X + polygons[7].Normal.X + polygons[6].Normal.X) / 4);
double _nyB = Math.Abs((polygons[8].Normal.Y + polygons[0].Normal.Y + polygons[7].Normal.Y + polygons[6].Normal.Y) / 4);
double _nzB = Math.Abs((polygons[8].Normal.Z + polygons[0].Normal.Z + polygons[7].Normal.Z + polygons[6].Normal.Z) / 4);
double modulB = Math.Sqrt(_nxB * _nxB + _nyB * _nyB + _nzB * _nzB);
double _nxC = Math.Abs((polygons[1].Normal.X + polygons[3].Normal.X + polygons[4].Normal.X + polygons[5].Normal.X) / 4);
double _nyC = Math.Abs((polygons[1].Normal.Y + polygons[3].Normal.Y + polygons[4].Normal.Y + polygons[5].Normal.Y) / 4);
double _nzC = Math.Abs((polygons[1].Normal.Z + polygons[3].Normal.Z + polygons[4].Normal.Z + polygons[5].Normal.Z) / 4);
double modulC = Math.Sqrt(_nxC * _nxC + _nyC * _nyC + _nzC * _nzC);
double _nxD = Math.Abs((polygons[7].Normal.X + polygons[0].Normal.X + polygons[1].Normal.X + polygons[5].Normal.X) / 4);
double _nyD = Math.Abs((polygons[7].Normal.Y + polygons[0].Normal.Y + polygons[1].Normal.Y + polygons[5].Normal.Y) / 4);
double _nzD = Math.Abs((polygons[7].Normal.Z + polygons[0].Normal.Z + polygons[1].Normal.Z + polygons[5].Normal.Z) / 4);
double modulD = Math.Sqrt(_nxD * _nxD + _nyD * _nyD + _nzD * _nzD);
double _nxA1 = Math.Abs((polygons[2].Normal.X + polygons[9].Normal.X + polygons[10].Normal.X + polygons[11].Normal.X) / 4);
double _nyA1 = Math.Abs((polygons[2].Normal.Y + polygons[9].Normal.Y + polygons[10].Normal.Y + polygons[11].Normal.Y) / 4);
double _nzA1 = Math.Abs((polygons[2].Normal.Z + polygons[9].Normal.Z + polygons[10].Normal.Z + polygons[11].Normal.Z) / 4);
double modulA1 = Math.Sqrt(_nxA1 * _nxA1 + _nyA1 * _nyA1 + _nzA1 * _nzA1);
double _nxB1 = Math.Abs((polygons[9].Normal.X + polygons[8].Normal.X + polygons[11].Normal.X + polygons[6].Normal.X) / 4);
double _nyB1 = Math.Abs((polygons[9].Normal.Y + polygons[8].Normal.Y + polygons[11].Normal.Y + polygons[6].Normal.Y) / 4);
double _nzB1 = Math.Abs((polygons[9].Normal.Z + polygons[8].Normal.Z + polygons[11].Normal.Z + polygons[6].Normal.Z) / 4);
double modulB1 = Math.Sqrt(_nxB1 * _nxB1 + _nyB1 * _nyB1 + _nzB1 * _nzB1);
double _nxC1 = Math.Abs((polygons[2].Normal.X + polygons[3].Normal.X + polygons[4].Normal.X + polygons[10].Normal.X) / 4);
double _nyC1 = Math.Abs((polygons[2].Normal.Y + polygons[3].Normal.Y + polygons[4].Normal.Y + polygons[10].Normal.Y) / 4);
double _nzC1 = Math.Abs((polygons[2].Normal.Z + polygons[3].Normal.Z + polygons[4].Normal.Z + polygons[10].Normal.Z) / 4);
double modulC1 = Math.Sqrt(_nxC1 * _nxC1 + _nyC1 * _nyC1 + _nzC1 * _nzC1);
double _nxD1 = Math.Abs((polygons[6].Normal.X + polygons[7].Normal.X + polygons[4].Normal.X + polygons[5].Normal.X + polygons[10].Normal.X + polygons[11].Normal.X ) / 6);
double _nyD1 = Math.Abs((polygons[6].Normal.Y + polygons[7].Normal.Y + polygons[4].Normal.Y + polygons[5].Normal.Y + polygons[10].Normal.Y + polygons[11].Normal.Y ) / 6);
double _nzD1 = Math.Abs((polygons[6].Normal.Z + polygons[7].Normal.Z + polygons[4].Normal.Z + polygons[5].Normal.Z + polygons[10].Normal.Z + polygons[11].Normal.Z ) / 6);
double modulD1 = Math.Sqrt(_nxD1 * _nxD1 + _nyD1 * _nyD1 + _nzD1 * _nzD1);
double iA = _nzA / modulA;
double iB = _nzB / modulB;
double iC = _nzC / modulC;
double iD = _nzD / modulD;
double iA1 = _nzA1 / modulA1;
double iB1 = _nzB1 / modulB1;
double iC1 = _nzC1 / modulC1;
double iD1 = _nzD1 / modulD1;
polygons[0].VertexNormals = new double[3] { iA, iB, iD };
polygons[1].VertexNormals = new double[3] { iA, iD, iC };
polygons[2].VertexNormals = new double[3] {iC1, iA1, iA};
polygons[3].VertexNormals = new double[3] {iA, iC, iC1};
polygons[4].VertexNormals = new double[3] {iD1, iC1, iC};
polygons[5].VertexNormals = new double[3] {iC, iD, iD1};
polygons[6].VertexNormals = new double[3] { iD, iB, iB1 };
polygons[7].VertexNormals = new double[3] { iD, iB1, iD1 };
polygons[8].VertexNormals = new double[3] { iA, iB1, iB };
polygons[9].VertexNormals = new double[3] { iA, iA1, iB1 };
polygons[10].VertexNormals = new double[3] { iC1, iD1, iA1 };
polygons[11].VertexNormals = new double[3] { iD1, iB1, iA1 };
for (int i = 0; i < 12; i++)
{
if (polygons[i].nz < 0)
polygons[i].Draw(g, Color.Red, bmp);
}
pictureBox1.Image = bmp;
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.W)
{
phi -= pi / 18;
}
if (e.KeyData == Keys.S)
{
phi += pi / 18;
}
if (e.KeyData == Keys.A)
{
teta -= pi / 18;
}
if (e.KeyData == Keys.D)
{
teta += pi / 18;
}
g.Clear(Color.White);
Draw();
}
}
}
Аналіз отриманих результатів
Дана програма замальовує грані куба методом Гуро.
Кожна грань має свій колір
Також куб обертається, що дає можливість подивитись на інші грані
Таким самим чином ми можемо намалювати інший об’єкт наприклад тетраедр.
Вказівки користувачеві
Відкрийте програму натиснувши на tetraedr.exe
Для того, щоб повертати куб патискайте клавіші w,a,s,d:
w – вгору
a – вліво
s – вниз
d – вправо
Таким чином ми зможемо подивитись шо програма замальовує дану фігуру з різних сторін методом Гуро
Щоб закрити, згорнути, чи розгорнути програму натисніть на дані кнопки .
Список використаної літератури:
Вельтмандер П.В. Основные алгоритмы компьютерной графики. –Учебное пособие в 3-х книгах. /Новосиб.ун – т. Новосибирск.
Роджерс Д. Алгоритмические основы машинной графики: Пер. с англ. – М.: Мир, 1989. – 512 с., ил. ISBN 5-03-000476-9
Иванов В.П., Батраков А.С. Трехмерная компьютерная графика. - М.: Радио и связь, 1995 - 224 с.
http://ru.wikipedia.org/wiki/Метод_тонирования_Гуро