Урок Инициализация в Windows я начинаю это пособие непосредственного с кода, разбитого на секции, каждая из которых будет подробно комментироваться. Первое, что вы должны сделать это создать проект в Visual C++



страница14/27
Дата17.11.2018
Размер7.85 Mb.
ТипУрок
1   ...   10   11   12   13   14   15   16   17   ...   27

L — вектор освещения

  • N — вектор нормали

  • Dl — цвет падающего света

  • Dm — цвет рассеяния материала

  • При наложении микрорельефа попиксельно меняется N

  • При наложении микрорельефа методом тиснения используется аппроксимация (L*N)

    Аппроксимация коэффициента рассеяния L*N

     

    В текстурной карте содержится поле высот



     

    • [0,1] — диапазон значений, принимаемых функцией рельефности

    • Первая производная определяет величину уклона m (материала) в данной точке (Заметьте, что m — чисто одномерная величина. Считайте, что m — это оценка grad(s,t) (градиента) в данной точке — прим. Дженса)

    • m увеличивает/уменьшает базовый коэффициент рассеяния Fd

    • (Fd+m) приближенно определяет (L*N) для каждого пикселя

     

    Приближенное вычисление производной

     

    Используется приближенное вычисление производной



    • Берется высота H0 в точке (s,t)

    • Определяется высота H1 в точке, слегка сдвинутой в направлении источника света, (s+ds,t+dt)

    • Исходная высота H0 вычитается из возмущенной высоты H1

    • Разница является оценкой мгновенного угла наклона m=H1-H0

    Вычисление рельефа

     

    http://www.opengl.org.ru/lesson/image001.jpg

     

    1) Исходный рельеф (H0).



     

    http://www.opengl.org.ru/lesson/image002.jpg

     

    2) На исходный рельеф (H0) накладывается другой, (H1), слегка сдвинутый в направлении источника света.



     

    http://www.opengl.org.ru/lesson/image003.jpg

     

    3) Из второго вычитается первый (H0-H1). Появляются освещенные (Bbright) и затемненные (Ddark) участки.



     

     

    Вычисление освещенности



     

    Вычисляется цвет фрагмента Cf



    • Cf (L*N) x Dl x Dm

    • (L*N) ~ (Fd + (H1-H0))

    • Dm x Dl закодировано в текстуре поверхности Ct.

      • Если хватит соображения, можно управлять Dl по отдельности (мы управляем им, пользуясь освещением OpenGL —прим. Дженса)

    • Cf = (Fd + (H0-H1)) x Ct

    И все? Так просто!

     

    Нет, мы еще не закончили. Мы должны:



    • Нарисовать текстуру (в любом графическом редакторе — прим. Дженса)

    • Вычислить сдвиги координат текстуры (ds,dt)

    • Вычислить коэффициент рассеяния Fd (управляется с помощью освещения в OpenGL — прим. Дженса)

    • Обе величины используют вектора нормали N и освещения L (в нашем случае явным образом вычисляются только(ds,dt) — прим. Дженса)

    • Теперь займемся математикой

    Создание текстуры

     

    Берегите текстуры!



    • В настоящее время аппаратура мультитекстурирования поддерживает максимум две текстуры! (Это утверждение устарело, но его надо иметь в виду, если хотите сохранить обратную совместимость — прим. Дженса)

    • Рельеф использует канал АЛЬФА (у нас это не так; но если на вашей машине карточка с чипом TNT, можете попробовать повторить предложенное здесь самостоятельно — прим. Дженса)

      • Максимальная высота = 1.0

      • Уровень нулевой высоты = 0.5

      • Максимальная глубина = 0.0

    • Цвета поверхности — каналы RGB

    • Внутренний формат должен быть GL_RGBA8 !!

    Вычисление смещения текстур

     

    Отображение вектора освещения в пространство нормали



    • Нужно получить систему координат нормали

    • Создадим систему координат из нормали к поверхности и вектора "вверх" (мы передаем направления texCoord генератору смещения в явном виде — прим. Дженса)

      • Нормаль — ось z

      • Перпендикулярно ей идет ось x

      • Направление "вверх", или ось y, получена как произведение x- и z-векторов

    • Построим матрицу 3x3 Mn из осей

    • Отобразим вектор освещения в пространстве нормали.(Mn называют также ортонормальным базисом. Можете рассматривать Mn*v как представление v в базисе, формирующем касательное пространство, а не обычное. Заметьте, что ортонормальный базис инвариантен к масштабированию, то есть при умножении векторов нормализация не теряется! — прим. Дженса)

    Вычисление смещения текстур (продолжение)

     

    Используем вектор освещения в пространстве нормали для смещения



    • L’ = Mn x L

    • Используем L’xL’y для (ds,dt)

    • Используем L’z как коэффициент диффузного отражения (Совсем нет! Если вы не владелец TNT-карточки, используйте освещение OpenGL, потому что вам обязательно придется выполнять дополнительный проход — прим. Дженса)

      • Если вектор освещения близок к нормали, L’x и L’y малы.

      • Если вектор освещения близок к касательной, L’x и L’y значительны.

    • Что, если L’z меньше нуля?

      • Свет на стороне, обратной к нормали

      • Приравняем его вклад к нулю

    Реализация на TNT

    • Вычисления векторов и координат текстур  на хосте

    • Передаем коэффициент рассеяния как alpha

      • Можно использовать цвет вершины для передачи цвета диффузного рассеяния источника света

    • H0 и цвет поверхности берем из текстурного блока 0

    • H1 берем из текстурного блока 1 (та же самая текстура, но с другими координатами)

    • Используем расширение ARB_multitexture

    • Это расширение для комбайнов (точнее, речь идет о расширении NVIDIA_multitexture_combiners, поддерживаемом всеми карточками семейства TNT — прим. Дженса)

    Реализация на TNT (продолжение)

     

    Первичная установка комбайна 0:



    • (1-T0a) + T1a - 0.5 (T0a означает "текстурный блок 0, альфа-канал" — прим. Дженса)

      • (T1a-T0a) отображается в диапазон (-1,1), но аппаратура сжимает его до (0,1)

      • Смещение на 0.5 балансирует потерю от сжатия (подумайте о применении масштабирования с коэффициентом 0.5, ведь можно использовать разные карты рельефа — прим. Дженса)

    • Цвет диффузного рассеяния источника света можно регулировать с помощью T0c

    Установка RGB комбайна 1:

    • (T0c * C0a + T0c * Fda - 0.5 )*2

      • Смещение на 0.5 балансирует потерю от сжатия

      • Умножение на 2 осветляет изображение

    Конец теории ( Наложение микрорельефа методом тиснения )

     

    Мы у себя делаем все не совсем так, как это предложено для TNT, поскольку хотим, чтобы наша программа работала на любом железе, однако здесь есть пара-тройка ценных идей. Во-первых, то, что на большинстве карточек наложение рельефа — многопроходная операция (хотя это не относится к семейству TNT, где рельефность можно реализовать за один двухтекстурный проход). Сейчас вы, наверное, оценили, какая отличная вещь — возможность мультитекстурирования. Теперь мы напишем 3-проходный немультитекстурный алгоритм, который можно (и мы это сделаем) реализовать за два мультитекстурных прохода.



     

    Кроме того, вы, вероятно, поняли, что нам придется проводить умножения матриц на матрицы и матриц на вектора. Но об этом можно не беспокоиться: в OpenGL операция умножения матриц реализована (если точность правильная) и умножения матрицы на вектор реализована в функции VMatMult(M,v), где матрица M умножается на вектор v и результат сохраняется в v, то есть v:=M*v. Все передаваемые матрицы и вектора должны быть гомогенны (то бишь в одной системе координат — прим. перев.) и представлять собой матрицы 4x4 и четырехмерные вектора. Такие требования гарантируют быстрое и правильное умножение векторов и матриц по правилам OpenGL.

     

    // Вычисляет v=vM, M — матрица 4x4 в порядке столбец-строка, v — четырехмерный вектор-строка (т.е. транспонированный)



    void VMatMult(GLfloat *M, GLfloat *v) {

      GLfloat res[3];

      res[0]=M[ 0]*v[0]+M[ 1]*v[1]+M[ 2]*v[2]+M[ 3]*v[3];

      res[1]=M[ 4]*v[0]+M[ 5]*v[1]+M[ 6]*v[2]+M[ 7]*v[3];

      res[2]=M[ 8]*v[0]+M[ 9]*v[1]+M[10]*v[2]+M[11]*v[3];

      v[0]=res[0];

      v[1]=res[1];

      v[2]=res[2];

      v[3]=M[15];  // Гомогенные координаты

    }

     



    Начало теории ( Алгоритмы наложения микрорельефа методом тиснения )

     

    Сейчас мы обсудим два разных алгоритма. Первый я нашел несколько дней назад здесь:


    http://www.nvidia.com/marketing/Developer/DevRel.nsf/TechnicalDemosFrame?OpenPage  

     

    Программа называется GL_BUMP и была написана Диего Тартара (Diego Tartara) в 1999 году. Диего создал очень симпатичный пример наложения микрорельефа, хотя и не лишенный некоторых недостатков.



     

    Однако давайте взглянем на алгоритм:



    1. Все вектора должны быть заданы ЛИБО в координатах объекта, ЛИБО в мировых координатах.

    2. Вычисляется вектор v направления из текущей вершины к источнику света

    3. v нормализуется

    4. v проецируется на касательную плоскость (Касательная плоскость — такая, которая касается поверхности в данной точке. Для нас эта точка — текущая вершина.).

    5. (s,t) сдвигается на величины соответственно x и y координат спроецированного вектора v.

    Выглядит неплохо! В основном здесь повторен алгоритм, предложенный Майклом Голдом — мы рассмотрели его в предыдущем теоретическом блоке. Однако у нового варианта есть существенный недочет: Тартара берет проекцию только в плоскости xy! Для наших целей этого недостаточно, поскольку теряется необходимая z-компонента вектора v.

     

    Диего выполняет освещение так же, как и мы: через встроенный в OpenGL механизм расчета. Поскольку мы не можем позволить себе комбинаторный метод, предложенный Голдом (наша программа должна работать на любом оборудовании, а не только на чипах TNT!), хранить коэффициент диффузного рассеяния в альфа-канале нельзя. Вспомним, что нас в любом случае будет 3 прохода немультитекстурного / 2 прохода мультитекстурного наложения. Почему бы не применить механизм освещения из OpenGL в последнем проходе, чтобы разобраться с окружающим освещением и цветами? Правда, это возможно (и красиво выглядит) только потому, что у нас нет геометрически сложных сцен — имейте это в виду. Если, не дай Бог, возникнет нужда просчитать рельеф нескольких тысяч треугольников, придется вам изобретать что-то новое.



     

    Далее, Диего использует мультитекстурирование, которое как мы увидим впоследствии, далеко не так просто, как может показаться для данного случая.

     

    Вернемся к нашей реализации. Она практически совпадает с рассмотренным алгоритмом, за исключением шага проецирования, где мы используем другой подход:



    • Мы применяем СИСТЕМУ КООРДИНАТ ОБЪЕКТА, то есть не используем в вычислениях матрицу вида модели (modelview). Из-за этого возникает неприятный побочный эффект: если куб приходится вращать, его система координат остается неизменной, в то время как мировая система (она же система координат наблюдателя) поворачивается. Однако положение источника света не должно изменяться, то есть мировые координаты источника должны оставаться постоянными. Чтобы скомпенсировать поворот, применим широко распространенный трюк: вместо пересчета каждой вершины куба в пространство мировых координат для последующего расчета рельефа, повернем источник в том же пространстве на величину, обратную повороту куба (используем инвертированную матрицу вида модели куба). Это делается очень быстро, поскольку раз мы знаем, как матрица вида модели была создана, то можем оперативно ее инвертировать. Позже мы вернемся к этому вопросу.

    • Вычислим текущую вершину "c" нашей поверхности (просто взяв ее из массива data).

    • Затем вычислим нормаль n длиной 1 (в военное время длина нормали может достигать четырех! :) — прим. перев.) Обычно вектор нормали известен для каждой грани куба. Это важно, так как, получая нормализованные вектора, мы уменьшаем время расчета. Определим вектор освещения v от c к источнику света l.

    • Осталось рассчитать матрицу Mn для получения ортонормальной проекции. Получится f.

    • Вычислим сдвиг текстурных координат, умножив каждый из параметров s и t на v и MAX_EMBOSSds = s*v*MAX_EMBOSSdt=t*v*MAX_EMBOSS. Обратите внимание: s, t и v — вектора, а MAX_EMBOSS — нет.

    • Во втором проходе добавим сдвиг к текстурным координатам.

    Что в модели хорошего:

    • Она быстрая (вычисляется только один квадратный корень и пара умножений на вершину).

    • Она здорово выглядит.

    • Работает с любыми поверхностями, не только с плоскостями.

    • Работает на всех акселераторах.

    • glBegin/glEnd-совместима: не требует "запрещенных" GL-команд.

    Какие недостатки:

    • Модель не вполне физически корректна.

    • Остаются мелкие артефакты.

    http://www.opengl.org.ru/lesson/image004.jpg

     

    На этом рисунке показано, где расположены вектора. t и s можно получить путем вычитания смежных векторов, но нужно следить за тем, чтобы они были верно направлены и нормализованы. Синей точкой помечена вершина, к которой проведена привязка texCoord2f(0.0f,0.0f).



     

    Конец теории ( Алгоритмы наложения микрорельефа методом тиснения )

    Давайте сначала рассмотрим формирование сдвига текстурных координат. Функция называется SetUpBumps(), потому что именно этим она и занимается:

     

    // Выполнение сдвига текстуры



    // n : нормаль к поверхности. Должна иметь длину 1

    // c : текущая вершина на поверхности (координаты местоположения)

    // l : положение источника света

    // s : направление s-координаты текстуры в пространстве объекта

    //     (должна быть нормализована!)

    // t : направление t-координаты текстуры в пространстве объекта

    //     (должна быть нормализована!)

    void SetUpBumps(GLfloat *n, GLfloat *c, GLfloat *l, GLfloat *s, GLfloat *t) {

      GLfloat v[3];                // Вектор от текущей точки к свету

      GLfloat lenQ;                // Используется для нормализации

      // Вычислим и нормализуем v

      v[0]=l[0]-c[0];

      v[1]=l[1]-c[1];

      v[2]=l[2]-c[2];

      lenQ=(GLfloat) sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);

      v[0]/=lenQ;

      v[1]/=lenQ;

      v[2]/=lenQ;

      // Получим величины проекции v вдоль каждой оси системы текстурных координат

      c[0]=(s[0]*v[0]+s[1]*v[1]+s[2]*v[2])*MAX_EMBOSS;

      c[1]=(t[0]*v[0]+t[1]*v[1]+t[2]*v[2])*MAX_EMBOSS;

    }

     



    Не так уж все и сложно, а? Но знание теории необходимо для понимания и управления эффектом (я даже сам разобрался в ЭТОМ, пока писал урок).

     

    Мне нравится, чтобы во время работы презентационных программ по экрану летал логотип. У нас их целых два. Вызов doLogo() сбрасывает матрицу GL_MODELVIEW, поэтому он будет выполнен на последней стадии визуализации.



     

    Функция отображает два логотипа: OpenGL и логотип мультитекстурного режима, если он включен. Логотипы содержат альфа-канал и, соответственно, полупрозрачны. Для реализации этого эффекта использованыGL_SRC_ALPHA и GL_ONE_MINUS_SRC_ALPHA, как рекомендовано OpenGL-документацией. Логотипы планарны, поэтому проводить z-сортировку нет необходимости. Числа, взятые для их высот, подобраны эмпирически (a.k.a. методом научного тыка) так, чтобы все помещалось в края экрана. Нужно включить смешивание и выключить освещение, чтобы избежать неприятных эффектов, а чтобы гарантировать размещение логотипов поверх сцены, достаточно сбросить матрицу GL_MODELVIEW и установить функцию глубины в GL_ALWAYS.

     

    void doLogo(void) {



      // ВЫЗЫВАТЬ В ПОСЛЕДНЮЮ ОЧЕРЕДЬ!!! отображает два логотипа

      glDepthFunc(GL_ALWAYS);

      glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

      glEnable(GL_BLEND);

      glDisable(GL_LIGHTING);

      glLoadIdentity();

      glBindTexture(GL_TEXTURE_2D,glLogo);

      glBegin(GL_QUADS);

        glTexCoord2f(0.0f,0.0f);  glVertex3f(0.23f, -0.4f,-1.0f);

        glTexCoord2f(1.0f,0.0f);  glVertex3f(0.53f, -0.4f,-1.0f);

        glTexCoord2f(1.0f,1.0f);  glVertex3f(0.53f, -0.25f,-1.0f);

        glTexCoord2f(0.0f,1.0f);  glVertex3f(0.23f, -0.25f,-1.0f);

      glEnd();

      if (useMultitexture) {

        glBindTexture(GL_TEXTURE_2D,multiLogo);

        glBegin(GL_QUADS);

          glTexCoord2f(0.0f,0.0f);  glVertex3f(-0.53f, -0.25f,-1.0f);

          glTexCoord2f(1.0f,0.0f);  glVertex3f(-0.33f, -0.25f,-1.0f);

          glTexCoord2f(1.0f,1.0f);  glVertex3f(-0.33f, -0.15f,-1.0f);

          glTexCoord2f(0.0f,1.0f);  glVertex3f(-0.53f, -0.15f,-1.0f);

        glEnd();

      }


    }

     

    Здесь начинается функция, реализующая наложение микрорельефа без использования мультитекстурирования. Это трехпроходная реализация. На первом шаге GL_MODELVIEW инвертируется путем применения к тождественной ей матрице всех шагов, применяемых позже к GL_MODELVIEW, но в обратном порядке и с инвертированными величинами. Такая матрица преобразования, будучи применена к объекту, "отменяет" воздействие GL_MODELVIEW. Мы получим ее от OpenGL вызовом glGetFloatv(). Напоминаю, что матрица должна быть массивом из 16 величин и что она транспонирована!



     

    Кстати: если вы не уверены, в каком порядке была создана матрица вида модели, подумайте о возможности использования мировой системы координат, потому что классическая инверсия произвольной матрицы — вычислительно очень дорогостоящая операция. Впрочем, при обработке значительного числа вершин инверсия матрицы вида модели может быть более приемлемым выходом и, возможно, будет выполняться быстрее, чем расчет мировых координат для каждой вершины.

     

    bool doMesh1TexelUnits(void) {



      GLfloat c[4]={0.0f,0.0f,0.0f,1.0f};       // Текущая вершина

      GLfloat n[4]={0.0f,0.0f,0.0f,1.0f};       // Нормаль к текущей поверхности

      GLfloat s[4]={0.0f,0.0f,0.0f,1.0f};       // s-вектор, нормализованный

      GLfloat t[4]={0.0f,0.0f,0.0f,1.0f};       // t-вектор, нормализованный

      GLfloat l[4];                             // Содержит координаты источника освещения,

                                                // который будет переведен в мировые координаты

      GLfloat Minv[16];                         // Инвертированная матрица вида модели

      int i;


     

      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Очистка экрана и буфера глубины

     

      // Инвертируем матрицу вида модели. Заменяет один Push/Pop и один glLoadIdentity();



      // Выполняется проведением всех преобразований в обратную сторону в обратном порядке

      glLoadIdentity();

      glRotatef(-yrot,0.0f,1.0f,0.0f);

      glRotatef(-xrot,1.0f,0.0f,0.0f);

      glTranslatef(0.0f,0.0f,-z);

      glGetFloatv(GL_MODELVIEW_MATRIX,Minv);

      glLoadIdentity();

      glTranslatef(0.0f,0.0f,z);

      glRotatef(xrot,1.0f,0.0f,0.0f);

      glRotatef(yrot,0.0f,1.0f,0.0f);

     

      // Преобразование положения источника в систему координат объекта:



      l[0]=LightPosition[0];

      l[1]=LightPosition[1];

      l[2]=LightPosition[2];

      l[3]=1.0f;                // Гомогенные координаты

      VMatMult(Minv,l);

     

    На первом шаге надо:



    • Использовать текстуру рельефа

    • Отключить смешивание

    • Отключить освещение

    • Использовать несмещенные текстурные координаты

    • Построить геометрию

    Будет визуализирован куб, содержащий только текстуру рельефа.

     

      glBindTexture(GL_TEXTURE_2D, bump[filter]);



      glDisable(GL_BLEND);

      glDisable(GL_LIGHTING);

      doCube();

     

    На втором шаге надо:



    • Использовать инвертированную текстуру рельефа

    • Включить смешивание GL_ONEGL_ONE

    • Освещение остается отключенным

    • Использовать смещенные координаты текстуры (это значит, что перед просчетом каждой грани куба придется вызывать SetUpBumps()).

    • Построить геометрию

    Здесь будет визуализирован куб с корректно наложенной картой высот, но без цветов.

     

    Можно было бы уменьшить время вычисления, повернув вектор освещения в обратную сторону. Однако этот способ не работает корректно, так что мы сделаем все просто: повернем каждую нормаль и центр так же, как делаем это с геометрией.



     

      glBindTexture(GL_TEXTURE_2D,invbump[filter]);

      glBlendFunc(GL_ONE,GL_ONE);

      glDepthFunc(GL_LEQUAL);

      glEnable(GL_BLEND);

     

      glBegin(GL_QUADS);



        // Передняя грань

        n[0]=0.0f;

        n[1]=0.0f;

        n[2]=1.0f;

        s[0]=1.0f;

        s[1]=0.0f;

        s[2]=0.0f;

        t[0]=0.0f;

        t[1]=1.0f;

        t[2]=0.0f;

        for (i=0; i<4; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }


        // Задняя грань

        n[0]=0.0f;

        n[1]=0.0f;

        n[2]=-1.0f;

        s[0]=-1.0f;

        s[1]=0.0f;

        s[2]=0.0f;

        t[0]=0.0f;

        t[1]=1.0f;

        t[2]=0.0f;

        for (i=4; i<8; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }


        // Верхняя грань

        n[0]=0.0f;

        n[1]=1.0f;

        n[2]=0.0f;

        s[0]=1.0f;

        s[1]=0.0f;

        s[2]=0.0f;

        t[0]=0.0f;

        t[1]=0.0f;

        t[2]=-1.0f;

        for (i=8; i<12; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }


        // Нижняя грань

        n[0]=0.0f;

        n[1]=-1.0f;

        n[2]=0.0f;

        s[0]=-1.0f;

        s[1]=0.0f;

        s[2]=0.0f;

        t[0]=0.0f;

        t[1]=0.0f;

        t[2]=-1.0f;

        for (i=12; i<16; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }


        // Правая грань

        n[0]=1.0f;

        n[1]=0.0f;

        n[2]=0.0f;

        s[0]=0.0f;

        s[1]=0.0f;

        s[2]=-1.0f;

        t[0]=0.0f;

        t[1]=1.0f;

        t[2]=0.0f;

        for (i=16; i<20; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }


        // Левая грань

        n[0]=-1.0f;

        n[1]=0.0f;

        n[2]=0.0f;

        s[0]=0.0f;

        s[1]=0.0f;

        s[2]=1.0f;

        t[0]=0.0f;

        t[1]=1.0f;

        t[2]=0.0f;

        for (i=20; i<24; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }


      glEnd();

     

    На третьем шаге надо:



    • Использовать основную (цветную) текстуру

    • Включить смешивание GL_DST_COLORGL_SRC_COLOR

    • Уравнения смешивания фактически получает множитель 2: (Cdst*Csrc)+(Csrc*Cdst)=2(Csrc*Cdst)!

    • Включить освещение для расчета фонового и диффузного освещения

    • Сбросить матрицу GL_TEXTURE с целью вернуться к "нормальным" текстурным координатам

    • Построить геометрию

    Это заключительная стадия расчета, с учетом освещения. Чтобы корректно переключаться между мультитекстурным и однотекстурным режимами, надо сначала выставить среду текстурирования в "нормальный" режим GL_MODULATE. Если захочется отказаться от наложения цветной текстуры, достаточно ограничиться первыми двумя проходами и пропустить третий.

     

      if (!emboss) {



        glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

        glBindTexture(GL_TEXTURE_2D,texture[filter]);

        glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);

        glEnable(GL_LIGHTING);

        doCube();

      }


     

    На финальном шаге надо:



    • Обновить геометрию (особенно вращение)

    • Отобразить логотипы

      xrot+=xspeed;

      yrot+=yspeed;

      if (xrot>360.0f) xrot-=360.0f;

      if (xrot<0.0f) xrot+=360.0f;

      if (yrot>360.0f) yrot-=360.0f;

      if (yrot<0.0f) yrot+=360.0f;

     

      /* ПОСЛЕДНИЙ ПРОХОД: Даешь логотипы! */



      doLogo();

      return true;                // Продолжаем

    }

     

    Следующая новая функция выполнит всю задачу за 2 прохода с использованием мультитекстурирования. Будут задействованы два текстурных блока, большее их количество резко усложнит уравнения смешивания. Лучше уж заниматься оптимизацией под TNT. Обратите внимание, практически единственное отличие от doMesh1TexelUnits()в том, что для каждой вершины отсылается не один, а два набора текстурных координат.



     

    bool doMesh2TexelUnits(void) {

      GLfloat c[4]={0.0f,0.0f,0.0f,1.0f};     // Здесь храним текущую вершину

      GLfloat n[4]={0.0f,0.0f,0.0f,1.0f};     // Вектор нормали к текущей поверхности

      GLfloat s[4]={0.0f,0.0f,0.0f,1.0f};     // s-вектор, нормализованный

      GLfloat t[4]={0.0f,0.0f,0.0f,1.0f};     // t-вектор, нормализованный

      GLfloat l[4];                           // Хранит координаты источника света,

                                              // для перевода в пространство координат объекта

      GLfloat Minv[16];                       // Инвертированная матрица вида модели

      int i;


     

      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Очистим экран и буфер глубины

     

      // Инвертируем матрицу вида модели. Заменяет один Push/Pop и один glLoadIdentity();



      // Выполняется проведением всех преобразований в обратную сторону в обратном порядке

      glLoadIdentity();

      glRotatef(-yrot,0.0f,1.0f,0.0f);

      glRotatef(-xrot,1.0f,0.0f,0.0f);

      glTranslatef(0.0f,0.0f,-z);

      glGetFloatv(GL_MODELVIEW_MATRIX,Minv);

      glLoadIdentity();

      glTranslatef(0.0f,0.0f,z);

     

      glRotatef(xrot,1.0f,0.0f,0.0f);



      glRotatef(yrot,0.0f,1.0f,0.0f);

     

      // Преобразуем координаты источника света в систему координат объекта



      l[0]=LightPosition[0];

      l[1]=LightPosition[1];

      l[2]=LightPosition[2];

      l[3]=1.0f;                // Гомогенные координаты

      VMatMult(Minv,l);

     

    На первом шаге надо:



    • Отменить смешивание

    • Отменить освещение

    Установить текстурный комбайн 0 на

    • Использование текстуры рельефа

    • Использование несмещенных координат текстуры

    • Выполнение операции GL_REPLACE, то есть простое отображение текстуры

    Установить текстурный комбайн 1 на

    • Использование сдвинутых текстурных координат

    • Выполнение операции GL_ADD, эквивалента однотекстурного ONE-ONE-смешивания.

    Будет рассчитан куб с наложенной картой эрозии поверхности.

     

      // ТЕКСТУРНЫЙ БЛОК #0



      glActiveTextureARB(GL_TEXTURE0_ARB);

      glEnable(GL_TEXTURE_2D);

      glBindTexture(GL_TEXTURE_2D, bump[filter]);

      glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);

      glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE);

     

      // ТЕКСТУРНЫЙ БЛОК #1



      glActiveTextureARB(GL_TEXTURE1_ARB);

      glEnable(GL_TEXTURE_2D);

      glBindTexture(GL_TEXTURE_2D, invbump[filter]);

      glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);

      glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);

     

      // Общие флаги



      glDisable(GL_BLEND);

      glDisable(GL_LIGHTING);

     

    Теперь визуализируем грани одну за одной, как это было сделано в doMesh1TexelUnits(). Единственное отличие — вместо glTexCoord2f() используется glMultiTexCoor2fARB(). Обратите внимание, надо прямо указывать, какой текстурный блок вы имеете в виду. Параметр должен иметь вид GL_TEXTUREi_ARB, где i лежит в диапазоне [0..31]. ( Это что же за карточка с 32 текстурными блоками? И зачем она?)



     

      glBegin(GL_QUADS);

        // Передняя грань

        n[0]=0.0f;

        n[1]=0.0f;

        n[2]=1.0f;

        s[0]=1.0f;

        s[1]=0.0f;

        s[2]=0.0f;

        t[0]=0.0f;

        t[1]=1.0f;

        t[2]=0.0f;

        for (i=0; i<4; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]);

          glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }

        // Задняя грань



        n[0]=0.0f;

        n[1]=0.0f;

        n[2]=-1.0f;

        s[0]=-1.0f;

        s[1]=0.0f;

        s[2]=0.0f;

        t[0]=0.0f;

        t[1]=1.0f;

        t[2]=0.0f;

        for (i=4; i<8; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]);

          glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }


        // Верхняя грань

        n[0]=0.0f;

        n[1]=1.0f;

        n[2]=0.0f;

        s[0]=1.0f;

        s[1]=0.0f;

        s[2]=0.0f;

        t[0]=0.0f;

        t[1]=0.0f;

        t[2]=-1.0f;

        for (i=8; i<12; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]);

          glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }

        // Нижняя грань



        n[0]=0.0f;

        n[1]=-1.0f;

        n[2]=0.0f;

        s[0]=-1.0f;

        s[1]=0.0f;

        s[2]=0.0f;

        t[0]=0.0f;

        t[1]=0.0f;

        t[2]=-1.0f;

        for (i=12; i<16; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]);

          glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }


        // Правая грань

        n[0]=1.0f;

        n[1]=0.0f;

        n[2]=0.0f;

        s[0]=0.0f;

        s[1]=0.0f;

        s[2]=-1.0f;

        t[0]=0.0f;

        t[1]=1.0f;

        t[2]=0.0f;

        for (i=16; i<20; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]);

          glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }

        // Левая грань



        n[0]=-1.0f;

        n[1]=0.0f;

        n[2]=0.0f;

        s[0]=0.0f;

        s[1]=0.0f;

        s[2]=1.0f;

        t[0]=0.0f;

        t[1]=1.0f;

        t[2]=0.0f;

        for (i=20; i<24; i++) {

          c[0]=data[5*i+2];

          c[1]=data[5*i+3];

          c[2]=data[5*i+4];

          SetUpBumps(n,c,l,s,t);

          glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]);

          glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);

          glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);

        }


      glEnd();

     

    На втором шаге надо:



    • Использовать основную текстуру

    • Включить освещение

    • Отменить смещение текстурных координат => сброc матрицы GL_TEXTURE

    • Текстурную среду вернуть в состояние GL_MODULATE, чтобы заработало освещение OpenGL (иначе не получится!)

    Здесь уже будет полностью готов куб.

     

      glActiveTextureARB(GL_TEXTURE1_ARB);



      glDisable(GL_TEXTURE_2D);

      glActiveTextureARB(GL_TEXTURE0_ARB);

      if (!emboss) {

        glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

        glBindTexture(GL_TEXTURE_2D,texture[filter]);

        glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);

        glEnable(GL_BLEND);

        glEnable(GL_LIGHTING);

        doCube();

      }


     

    На последнем шаге надо



    • Обновить геометрию (особенно вращение)

    • Отобразить логотипы

      xrot+=xspeed;

      yrot+=yspeed;

      if (xrot>360.0f) xrot-=360.0f;

      if (xrot<0.0f) xrot+=360.0f;

      if (yrot>360.0f) yrot-=360.0f;

      if (yrot<0.0f) yrot+=360.0f;

     

      /* ПОСЛЕДНИЙ ПРОХОД: да будут логотипы! */



      doLogo();

      return true;                // Продолжим

    }

     

    И, для сравнения, функция, рисующая куб без рельефа — почувствуйте разницу!



     

    bool doMeshNoBumps(void) {

      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);      // Очистить экран и буфер глубины

      glLoadIdentity();              // Сбросить вид

      glTranslatef(0.0f,0.0f,z);

     

      glRotatef(xrot,1.0f,0.0f,0.0f);



      glRotatef(yrot,0.0f,1.0f,0.0f);

     

      if (useMultitexture) {



        glActiveTextureARB(GL_TEXTURE1_ARB);

        glDisable(GL_TEXTURE_2D);

        glActiveTextureARB(GL_TEXTURE0_ARB);

      }


     

      glDisable(GL_BLEND);

      glBindTexture(GL_TEXTURE_2D,texture[filter]);

      glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);

      glEnable(GL_LIGHTING);

      doCube();

     

      xrot+=xspeed;



      yrot+=yspeed;

      if (xrot>360.0f) xrot-=360.0f;

      if (xrot<0.0f) xrot+=360.0f;

      if (yrot>360.0f) yrot-=360.0f;

      if (yrot<0.0f) yrot+=360.0f;

     

      /* ПОСЛЕДНИЙ ПРОХОД: логотипы */



      doLogo();

      return true;                // Продолжим

    }

     

    Все, что должна делать функция drawGLScene() — определить, какой из вариантов doMesh вызвать:

     

    bool DrawGLScene(GLvoid)              // Здесь все рисуется



    {

      if (bumps) {

        if (useMultitexture && maxTexelUnits>1)

          return doMesh2TexelUnits();

        else return doMesh1TexelUnits();

      }


      else return doMeshNoBumps();

    }

     



    Убиваем GLWindow. Функция не изменялась, а потому не приведена:

     

    GLvoid KillGLWindow(GLvoid)           // Уничтожим окно корректно



    >…<

     

    Функция создает GLWindow; не изменена, поэтому пропущена:

     

    BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)



    >…<

     

    Основной цикл обработки сообщений; не изменен, поэтому пропущен:



     

    LRESULT CALLBACK WndProc(  HWND hWnd, // Указатель окна

            UINT uMsg,                    // Сообщение для этого окна

            WPARAM wParam,                // Дополнительная информация о сообщении

            LPARAM lParam)                // Дополнительная информация о сообщении

    >…<


     

    Основная функция окна. Здесь добавлена обработка различных дополнительных кнопок:


  • 1   ...   10   11   12   13   14   15   16   17   ...   27


    База данных защищена авторским правом ©vossta.ru 2019
    обратиться к администрации

        Главная страница