Урок 2. Работа с материалом и освещением
пожалуй, один из сложнейших аспектов 3D, рассматривается зачастую либо
слишком просто или наоборот, рассматриваются очень сложные аспекты.
Надеюсь, что мне удалось найти компромиссное решение.
Начиная с этого урока, все аспекты 3D будут
рассматриваться в разрезе OGL, как классической библиотеки. Далее код с
пояснениями будет портироваться под другие 3Д библиотеки.
Начинаем работать

Исходный код проекта для S60 можно скачать отсюда.
Несколько слов о OGL EX. К сожалению в этой
библиотеке не реализовано многое. Вы не встретите там блоков отрисовки,
и пожалуй самое главное дисплейных списков. Эти списки – отличный
инструмент оптимизации. Но как написано в документации, их реализация
слишком сложна.
К чему все это? Работа с эмулятором слишком
медленная. Поэтому лучше разрабатывать графическую часть на «родной
платформе». Существуют эмуляторы для РС, но они поддерживают старые
версии EX. Поэтому я рекомендую использовать имеющуюся у вас
библиотеку, учитывая ограничения.
То же самое относится и к DX.
Для иллюстрации мы создадим красивый объект –
икосаэдр. Икосаэдр имеет 20 треугольных граней и 12 вершин. Благодаря
своей правильности он может быть задан с помощью всего лишь двух чисел,
которые мы вычислим один раз и запомним.
static GLfloat angle= 3. * atan(1.)/2.5;
// Две характерные токи
static GLfloat V = cos(angle);
static GLfloat W = sin(angle);
Далее мы создадим два массива нормалей:
normp – грубо заданные нормали.
normf – точно рассчитанные нормали.
И массив вершин – vert. Который содержит 180
значащих значений типа GLfloat, которые описывают 60 точек (180/3) в 3D
и 20 треугольных плоскостей из котрых состоит икосаэдр (60/3).
Запустим проект с грубо заданными нормалями.
glEnableClientState(GL_NORMAL_ARRAY);
//glNormalPointer(GL_FLOAT,0,normf);
glNormalPointer(GL_FLOAT,0,normp);
И посмотрим на результат.

Изменим массив нормалей.
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT,0,normf);
//glNormalPointer(GL_FLOAT,0,normp);

Теперь проанализируем результаты.
Несколько важных моментов:
- Не только направление нормали, но ее модуль (длина) влияют на степень освещенности.
- Цвета вершин полигонов, влияют на цвет самого полигона (интерполируются - glShadeModel( GL_SMOOTH );).
Чтобы избавится от этого влияния (если это
необходимо), нормали масштабируют (или нормируют) т.е. делают ее длину
равной 1, оставляя неизменным направление. В нашем случае, это делает
OGL - glEnable( GL_NORMALIZE );.
Итак, что же произошло?
В первом случае, мы задали массив нормалей грубо,
как если бы наша фигура являлась сферой. И как мы видим, грани
«смазаны», фигура действительно стала напоминать сферу.
Но такой подход неверен, мы можем и должны точно задать нормали к треугольной плоскости.
Произведение двух векторов, a и b определяется как
вектор n перпендикулярный к плоскости в которой лежат исходные вектора
и рассчитывается по формулам.
void getNorm(GLfloat v1[3], GLfloat v2[3], GLfloat out[3])
{
out[0] = v1[1]*v2[2] - v1[2]*v2[1]; // normal calculate
out[1] = v1[2]*v2[0] - v1[0]*v2[2] ;
out[2] =v1[0]*v2[1] - v1[1]*v2[0];
}
Основные моменты рассмотрены. Я думаю теперь понятно, насколько важную роль играет освещение и нормали в 3D.
Пройдемся по коду.
Инициализация.
glEnable(GL_DEPTH_TEST);// Enables Depth Testing
// Включаем тест глубины.
Он необходим, для сортировки полигонов. Говоря проще, полигоны отрисовываются в порядке удаленности.
glEnable(GL_CULL_FACE);// отсечение невидимых граней.
Это повышает производительность. Мы не рисуем то что не видно.
glEnable(GL_LIGHTING);// включаем освещение
// Set up lamp.
glEnable( GL_LIGHT0 );
//glLightfv( GL_LIGHT0, GL_DIFFUSE, lightDiffuseLamp );
//glLightfv( GL_LIGHT0, GL_AMBIENT, lightAmbientLamp );
//glLightfv( GL_LIGHT0, GL_SPECULAR, lightDiffuseLamp );
//glLightfv( GL_LIGHT0, GL_POSITION, lightPositionLamp );
Включаем первый из возможных источников. Для него можно задать
различные параметры – позицию, тип освещения (диффузорное,
окружауещее), цвет.
Попробуйте различные варианты, закомментировав строчку –
glEnable(GL_COLOR_MATERIAL); // учитываем цвет материала.
Включите второй источник, и подберите разные комбинации. Можно добиться удивительно красивой картинки.
Аналогичное относится к материалу.
// Set duck material
glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, objDiffuseDuck );
glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, objAmbientDuck );
glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, objSpecularDuck );
glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, objEmissionDuck );
glMaterialx( GL_FRONT_AND_BACK, GL_SHININESS, 10 << 16 );
Он так – же может излучать, отражать свет и т.д. Все
эти параметры можно менять и динамически, как и модуль нормали. Так
создаются различные эффекты и строятся модели динамического освещения.
Java 3D.
Исходный код проекта можно скачать отсюда.
Пожалуй начнем, с того, что эта реализация отлично
оптимизирована под мобильные платформы, поэтому массивы вершин мы можем
задавать только с помощью целых чисел. Это конечно правильно, но во
многом неудобно. Поэтому «укрупним» масштаб, чтобы после приведения не
получить нули.
float V = 5.8778524f;
float W = 8.0901700f;// две характерные точки.
Далее следует обратить внимание на то, что массивы содержащие нужные нам значения создаются заранее.
// Создаем объект VertexBuffer
VertexBuffer vb = iVb = new VertexBuffer();
VertexBuffer vb1 = iVb1 = new VertexBuffer();
vb.setPositions(vertArray, 1.0f, null);
vb.setNormals(normArray);
vb.setDefaultColor(0x00FF0000);
vb1.setPositions(vertArray, 1.0f, null);
vb1.setNormals(normArray1);
vb1.setDefaultColor(0x00FF0000);
Этот подход похож на реализацию в DX. И как было
сказано ранее, реализация open GL EX не поддерживает списков, которые
позволяют сформировать массивы заранее.
Интересно будет сравнить FPS. И это мы сделаем в
уроке 4(эффект флага), когда пересчитывать координаты придется делать
динамически.
Далее все аналогично уроку 1.
В функции paint меняем значения.
//iG3D.render(iVb, iIb, iAppearance, iTransform);// рисуем икосаэдр - грубый расчет нормалей
iG3D.render(iVb1, iIb, iAppearance, iTransform); //рисуем икосаэдр - точный расчет нормалей
Заключение.
Реализация для DX и Windows Mobile будет опубликована как дополнение.
Вопросы и обсуждения на форуме mgdc.ru или verussoft.com.