Форум программистов, компьютерный форум, киберфорум
Наши страницы
Delphi: графика, звук, видео
Войти
Регистрация
Восстановить пароль
 
Рейтинг: Рейтинг темы: голосов - 23, средняя оценка - 4.70
snake32
1641 / 1095 / 191
Регистрация: 26.02.2009
Сообщений: 4,058
Записей в блоге: 5
#1

Простая работа с OpenGL на Delphi. Вычисление нормали. - Delphi

11.12.2010, 21:59. Просмотров 3025. Ответов 2

Решил сделать небольшой FAQ по теме.

Итак. Прежде чем начать хочу показать какой код частенько вижу от начинающих:
Delphi
1
glNormal3f((y1-y)*(z2-z)-(z1-z)*(y2-y),(z1-z)*(x2-x)-(x1-x)*(z2-z),(x1-x)*(y2-y)-(y1-y)*(x2-x) );
Всего лишь одна строчка, но ошибку сделать в таком коде очень просто. Искать долго. И выглядит такой код, мягко говоря, не очень. А вам сразу стало ясно что здесь вычисляется?
Предлагаю объединить x,y,z в одну структуру, так как по отдельности они всё равно смысла не имеют:
Delphi
1
2
3
4
type
  vec3f = packed record
    x,y,z:single;
  end;
У OpenGL есть ф-ии, которые принимают в качестве параметра адрес такой структуры. Все такие ф-ии оканчиваются на 3fv, например: glColor3fv( ), glNormal3fv( ), glVertex3fv( )
Delphi
1
2
3
4
5
var n,color,v:vec3f;
...
glNormal3fv( @n );
glColor3fv( @color );
glVertex3fv( @v );
Такие ф-ии работаю чуть быстрее, так как в стек помещается не три значения x,y,z; а всего лишь один адрес. Но главное - писать теперь можно короче. А чем меньше написано, тем проще разобраться всем. Я лично стремлюсь всегда придерживаться этого правила.

Перейдем к математике
Нормаль - это перпендикулярный единичный вектор к чему-либо. Конкретно в нашем случае я ищу нормаль к треугольнику. Нормаль можно найти через векторное произведения двух векторов, которые можно вычислить из точек треугольника:
a,b,c - координаты точек треугольника
n - нормаль
na,nb - два вектора участвующие в векторном произведении
na = a - c;
nb = b - c;
n = na x nb - собственно векторное произведение
или:
n = (a-c) x (b-c);
Теперь n - нормаль к треугольнику abc. Но это ещё не совсем нормальная нормаль ) Все нормали должны быть единичной длины. Для этого каждый элемент нормали делится на её длину:
Delphi
1
2
3
4
5
6
7
8
9
procedure Normalize( var n:vec3f );
  var len:single;
begin
len := sqrt(n.x*n.x + n.y*n.y + n.z*n.z ); // вычисляем длину нормали
n.x := n.x / len;
n.y := n.y / len;
n.z := n.z / len;
// и называется это - нормализация вектора
end;
Теперь уж точно у нас правильная нормаль.

Упрощение программирования
К сожалению, на Delphi 7 и ниже ещё нет перегрузки операторов и мы не можем записать код так как он выглядит в математическом разделе, эх а как было бы здорово:
Delphi
1
2
3
4
5
var a,b,c,na,nb:vec3f;
begin
  na := a-c; // здесь 
  nb := b-c; // и здесь компилятор D7 покроет нас матом
...
Придётся писать каждую операцию 3 раза!! для каждого эл-та структуры:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
var a,b,c,na,nb:vec3f;
begin
  //na := a-c;
  na.x := a.x - c.x;
  na.y := a.y - c.y;
  na.z := a.z - c.z;
  //nb := b-c;
  nb.x := b.x - c.x;
  nb.y := b.y - c.y;
  nb.z := b.z - c.z;
// и это только два вычисления
// если честно мне дальше лень писать
...
Разве это нормально? По-мойму нет. Максимум что можно сделать написать модуль с векторными ф-иями:
Весь код модуля VecMath
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
unit VecMath;
interface
 
type
  vec3f = packed record
    x,y,z:single;
  end;
function MakeVector( const x,y,z:single ):vec3f; // сделать вектор из трёх отдельных переменных
function LengthVector( const n:vec3f ):single; // длина вектора
function SubVector( const a, b:vec3f  ):vec3f; // вычитание векторов
function AddVector( const a, b:vec3f  ):vec3f; // сложение векторов
function CrossProduct( const a, b:vec3f  ):vec3f; // векторное произведение
procedure Normalize( var n:vec3f );// нормализация вектора
 
implementation
function MakeVector( const x,y,z:single ):vec3f;
begin
  Result.x := x;
  Result.y := y;
  Result.z := z;
end;
function LengthVector( const n:vec3f ):single;
begin
  Result := sqrt(n.x*n.x + n.y*n.y + n.z*n.z);
end;
function SubVector( const a, b:vec3f  ):vec3f; // вычитание векторов
begin
  Result.x := a.x - b.x;
  Result.y := a.y - b.y;
  Result.z := a.z - b.z;
end;
function AddVector( const a, b:vec3f  ):vec3f; // сложение векторов
begin
  Result.x := a.x + b.x;
  Result.y := a.y + b.y;
  Result.z := a.z + b.z;
end;
function CrossProduct( const a, b:vec3f  ):vec3f; // векторное произведение
begin
  Result.x := a.y*b.z - a.z*b.y;
  Result.y := a.z*b.x - a.x*b.z;
  Result.z := a.x*b.y - a.y*b.x;
end;
procedure Normalize( var n:vec3f );// нормализация вектора
  var len:single;
begin
  len := LengthVector( n );
  n.x := n.x / len;
  n.y := n.y / len;
  n.z := n.z / len;
end;
 
end.

И вот как теперь выглядит наш код:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
uses VecMath;
...
var a,b,c,na,nb,n:vec3f;
begin
// инициализируем входные данные
 // a := MakeVector();
 // b := MakeVector();
 // c := MakeVector();
// считаем нормаль
  na := SubVector( a, c );
  nb := SubVector( b, c );
  n := CrossProduct( na, nb );
  Normalize( n );
  // передаём в ОпенГЛ данные
  glNormal3fv( @n );
  glVertex3fv( @a );
  glVertex3fv( @b );
  glVertex3fv( @c );
end;
Проще не придумаешь
Дальше тем у кого нет поддержки в Delphi перегрузки операторов можете не читать. С какой именно версии она появилась я не знаю, но точно знаю что в Delphi 2010 это есть, а в Delphi 7 - отсутствует.
НАКОНЕЦТО! Свершилось чудо. Теперь Delphi можно обучить понимать операторы для собственно-написанных типов. Что ж посмотрим как это чудо поможет:
Весь код модуля VecMath для D2010
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
unit VecMath;
interface
 
type
  vec3f = packed record
    procedure Make( ax,ay,az:single ); inline;
    function Length:single; inline;
    procedure Normalize; inline;
  
    class function Dot( const a,b:vec3f ):single; static; inline;  // скалярное произведение
    class function Cross( const a,b:vec3f ):vec3f; static; inline; // векторное произведение
 
    class operator Negative( const a:vec3f ):vec3f; inline;
    class operator Add( const a,b:vec3f ):vec3f; inline;
    class operator Add( const v:vec3f; const s:single ):vec3f; inline;
    class operator Subtract( const a,b:vec3f ):vec3f; inline;
    class operator Subtract( const v:vec3f; const s:single ):vec3f; inline;
    class operator Multiply( const a,b:vec3f ):vec3f; inline;
    class operator Multiply( const v:vec3f; const s:single ):vec3f; inline;
    class operator Divide( const a,b:vec3f ):vec3f; inline;
    class operator Divide( const v:vec3f; const s:single ):vec3f; inline;
    class operator BitwiseAnd( const a,b:vec3f ):single; inline; // скалярное произведение через AND
    class operator BitwiseXor( const a,b:vec3f ):vec3f; inline;  // векторное произведение через XOR
 
    class operator Equal( const a,b:vec3f ):Boolean; inline;
    class operator NotEqual( const a,b:vec3f ):Boolean; inline;
 
    case byte of
      0:( x,y,z:single;);
      1:( m:array[0..2]of single;);
  end;
 
implementation
 
uses
  Math;
 
procedure vec3f.Make( ax,ay,az:single );
begin
  x:=ax; y:=ay; z:=az;
end;
function vec3f.Length:single;
begin
  Result := sqrt( x*x + y*y + z*z );
end;
procedure vec3f.Normalize;
  var len:single;
begin
  len := Length;
  Make( x/len, y/len, z/len );
end;
class function vec3f.Dot( const a,b:vec3f ):single;
begin
  Result := a.x*b.x + a.y*b.y + a.z*b.z;
end;
class function vec3f.Cross( const a,b:vec3f ):vec3f;
begin
  Result.Make( a.y*b.z - a.z*b.y,
               a.z*b.x - a.x*b.z,
               a.x*b.y - a.y*b.x );
end;
//--------------------------------------------
class operator vec3f.Negative( const a:vec3f ):vec3f;
begin
  Result.Make( -a.x, -a.y, -a.z );
end;
class operator vec3f.Add( const a,b:vec3f ):vec3f;
begin
  Result.Make( a.x+b.x, a.y+b.y, a.z+b.z );
end;
class operator vec3f.Add( const v:vec3f; const s:single ):vec3f;
begin
  Result.Make( v.x+s, v.y+s, v.z+s );
end;
class operator vec3f.Subtract( const a,b:vec3f ):vec3f;
begin
  Result.Make( a.x-b.x, a.y-b.y, a.z-b.z );
end;
class operator vec3f.Subtract( const v:vec3f; const s:single ):vec3f;
begin
  Result.Make( v.x-s, v.y-s, v.z-s );
end;
class operator vec3f.Multiply( const a,b:vec3f ):vec3f;
begin
  Result.Make( a.x*b.x, a.y*b.y, a.z*b.z );
end;
class operator vec3f.Multiply( const v:vec3f; const s:single ):vec3f;
begin
  Result.Make( v.x*s, v.y*s, v.z*s );
end;
class operator vec3f.Divide( const a,b:vec3f ):vec3f;
begin
  Result.Make( a.x/b.x, a.y/b.y, a.z/b.z );
end;
class operator vec3f.Divide( const v:vec3f; const s:single ):vec3f;
begin
  Result.Make( v.x/s, v.y/s, v.z/s );
end;
 
class operator vec3f.BitwiseAnd( const a,b:vec3f ):single;
begin
  Result := vec3f.Dot( a, b );
end;
class operator vec3f.BitwiseXor( const a,b:vec3f ):vec3f;
begin
  Result := vec3f.Cross( a, b );
end;
class operator vec3f.Equal( const a,b:vec3f ):Boolean;
begin
  Result := (Abs(a.x-b.x)<1e-5)and(Abs(a.y-b.y)<1e-5)and(Abs(a.z-b.z)<1e-5);
end;
class operator vec3f.NotEqual( const a,b:vec3f ):Boolean;
begin
  Result := not( a = b );
end;
 
end.

В результате использования обновленного модуля для 2010 получаем такой код:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
uses VecMath;
...
var a,b,c,n:vec3f;
begin
// инициализируем входные данные
 // a.Make();
 // b.Make();
 // c.Make();
// считаем нормаль
  n := (a-c) xor (b-c); // почти как в мат. разделе
  n.Normalize;
  // передаём в ОпенГЛ данные
  glNormal3fv( @n );
  glVertex3fv( @a );
  glVertex3fv( @b );
  glVertex3fv( @c );
end;
Идеально! Теперь даже самые сложные векторные алгоритмы можно легко запрограммировать не опасаясь допустить глупую ошибку. Пользуйтесь, и наслаждайтесь чистым кодом! Удачи!
http://www.cyberforum.ru/delphi-multimedia/thread295944.html
2
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
11.12.2010, 21:59
Я подобрал для вас темы с готовыми решениями и ответами на вопрос Простая работа с OpenGL на Delphi. Вычисление нормали. (Delphi):

Delphi. Посмотрите игру, оч простая. Жду идей для улучшения.
Вот: http://files.mail.ru/FTF1YP

OpenGl на Delphi
Нашел программу &quot;часы на opengl&quot;, но не смог разобраться, как происходит...

Delphi и OpenGl
Добрый вечер! (у кого-то ночь, утро, день). Кто-нибудь может дать ссылки на...

OpenGL и Delphi 2007
Добрый день! Не могу найти OpenGL для Delphi 2007.

Delphi+OpenGL надо разобраться
Здравствуйте дорогие форумчане, сейчас учусь совмещать делфи и OpenGL хотя до...

2
Хохол
Эксперт С++
475 / 443 / 34
Регистрация: 20.11.2009
Сообщений: 1,292
24.03.2011, 20:23 #2
// скалярное произведение через AND
// векторное произведение через XOR
Ох не нравится мне это. Неочевидно ведь нифига. Я бы тут функции с понятными именами оставил.
0
snake32
1641 / 1095 / 191
Регистрация: 26.02.2009
Сообщений: 4,058
Записей в блоге: 5
25.03.2011, 00:45  [ТС] #3
Хохол, делал на подобие из книг Борескова.
Ну если не нравится, то собственно там есть аналогичные классовые методы Dot и Cross соответственно.
Delphi
1
n := vec3f.Cross( a-c, b-c );
0
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
25.03.2011, 00:45
Привет! Вот еще темы с решениями:

Delphi и OpenGL (Камера и объекты)
Здравствуйте дорогие форумчане, может быть кто-то знает как сделать так что бы...

Delphi и 3D графика БЕЗ OpenGL
Доброго времени суток! Поставили перед нами задачу нарисовать 3D букву в Делфи...

Создание графика в Delphi с подключением OpenGL
Необходимо создать график в делфи с использованием opengl, заданного формулой:...

Как в OpenGL сделать перемещение по рисунку (Delphi 7)
Есть текстура, нужно чтобы можно было вращать, ну как бы симулировать...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
3
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru