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



страница7/27
Дата17.11.2018
Размер7.85 Mb.
ТипУрок
1   2   3   4   5   6   7   8   9   10   ...   27
MAX_PARTICLES будет равно любому значению, которое мы зададим. В нашем случае 1000. В третьей строке будет переключаться 'режим радуги' (включен или выключен). Мы установим по умолчанию включенным этот режим. sp и rp - переменные, которые мы будем использовать, чтобы предотвратить автогенерацию повторений нажатия клавиш пробел или ввод (enter), когда они нажаты.

#include   // Заголовочный файл для Windows

#include     // Заголовочный файл для стандартной библиотеки ввода/вывода(НОВОЕ)

#include     // Заголовочный файл для библиотеки OpenGL32

#include    // Заголовочный файл для библиотеки GLu32

#include // Заголовочный файл для библиотеки GLaux

#define MAX_PARTICLES 1000 // Число частиц для создания ( НОВОЕ )

HDC        hDC=NULL;  // Приватный контекст устройства GDI

HGLRC      hRC=NULL;  // Постоянный контекст рендеринга

HWND       hWnd=NULL; // Сохраняет дескриптор окна

HINSTANCE  hInstance; // Сохраняет экземпляр приложения

 

bool  keys[256];      // Массив для работы с клавиатурой



bool  active=TRUE;    // Флаг активации окна, по умолчанию = TRUE

bool  fullscreen=TRUE;// Флаг полноэкранного режима


bool rainbow=true; // Режим радуги? ( НОВОЕ )


bool sp; // Пробел нажат? ( НОВОЕ )
bool rp; // Ввод нажат? ( НОВОЕ)

В следующих 4 строках - разнообразные переменные. Переменная slowdown (торможение) контролирует, как быстро перемещаются частицы. Чем больше ее значение, тем медленнее они двигаются. Чем меньше ее значение, тем быстрее они двигаются. Если значение задано маленькое, частицы будут двигаться слишком быстро! Скорость, с которой частиц перемещаются, будет задавать их траекторию движения по экрану. Более медленные частицы не будут улетать далеко. Запомните это.

Переменные xspeed и yspeed позволяют нам контролировать направлением хвоста потока частиц. xspeed будет добавляться к текущей скорости частицы по оси X. Если у xspeed - положительное значение, то наша частица будет смещаться направо. Если у xspeed - отрицательное значение, то наша частица будет смещаться налево. Чем выше значение, тем больше это смещение в соответствующем направлении. yspeed работает также, но по оси Y. Причина, по которой я говорю 'БОЛЬШЕ' в заданном направлении означает, что есть и другие коэффициенты, воздействующие на направление траектории частицы. xspeed и yspeed позволяют перемещать частицу в том направлении, в котором мы хотим.

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

float slowdown=2.0f; // Торможение частиц 
float xspeed; // Основная скорость по X (с клавиатуры изменяется направление хвоста)
float yspeed; // Основная скорость по Y (с клавиатуры изменяется направление хвоста)
float zoom=-40.0f; // Масштаб пучка частиц

Теперь мы задаем еще одну переменную названную loop. Мы будем использовать ее для задания частиц и вывода частиц на экран. col будет использоваться для сохранения цвета, с каким созданы частицы. delay будет использоваться, чтобы циклически повторять цвета в режиме радуги.

Наконец, мы резервируем память для одной текстуры (текстура частицы). Я решил использовать текстуру вместо точек по нескольким причинам. Наиболее важная причина, что точки замедляют быстродействие и выглядят очень плохо. Во-вторых, текстуры - более крутой способ :). Вы можете использовать квадратную частицу, крошечное изображение вашего лица, изображение звезды, и т.д.  Больше возможностей!

GLuint loop;       // Переменная цикла


GLuint col;        // Текущий выбранный цвет
GLuint delay;      // Задержка для эффекта радуги
GLuint texture[1]; // Память для нашей текстуры

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

Мы начинаем с булевой переменной active. Если эта переменная ИСТИННА, то наша частица жива и летит. Если она равно ЛОЖЬ, то наша частица мертва, или мы выключили ее! В этой программе я не использую active, но ее удобно иметь на всякий случай (прим. переводчика: никогда неизвестно что будет потом, главное следовать определенным принципам).

Переменные life и fade управляют тем, как долго частица будет отображаться, и насколько яркой она будет, пока жива. Переменная life постепенно уменьшается на значение fade. В этой программе некоторые частицы будут гореть дольше, чем другие.

typedef struct // Структура частицы
{
  bool active; // Активность (Да/нет)
  float life;  // Жизнь
  float fade;  // Скорость угасания

Переменные rg и b задают красную, зеленую и синюю яркости нашей частицы. Чем ближе r к 1.0f, тем более красной будет частица. Если все 3 переменных равны 1.0f, то это создаст белую частицу.

  float r; // Красное значение
  float g; // Зеленное значение
  float b; // Синие значение

Переменные xy и z задают, где частица будет отображена на экране. x задает положение нашей частицы по оси X.y задает положение нашей частицы по оси Y, и, наконец, z задает положение нашей частицы по оси Z.

  float x; // X позиция
  float y; // Y позиция
  float z; // Z позиция

Следующие три переменные важны. Эти три переменные управляют тем, как быстро частица перемещается по заданной оси, и в каком направлении двигается. Если xi имеет отрицательное значение, то наша частица будет двигаться влево. Если положительное, то вправо. Если yi имеет отрицательное значение, то наша частица будет двигаться вниз. Если положительное, то вверх. Наконец, если zi имеет отрицательное значение, то частица будет двигаться вглубь экрана, и, если положительное, то вперед к зрителю. 

  float xi; // X направление
  float yi; // Y направление
  float zi; // Z направление

Наконец, последние 3 переменные! О каждой из этих переменных можно думать как о гравитации. Если xg имеет положительное значение, то нашу частицу будет притягивать вправо. Если отрицательное, то нашу частицу будет притягивать влево. Поэтому, если наша частица перемещает влево (отрицательно) и мы применяем положительную гравитацию, то скорость в итоге замедлится настолько, что наша частица начнет перемещать в противоположном направлении. yg притягивает вверх или вниз, и zg притягивает вперед или назад от зрителя.

  float xg; // X гравитация
  float yg; // Y гравитация
  float zg; // Z гравитация

particles - название нашей структуры.

}
particles; // Структура Частиц

Затем мы создаем массив называемый particle. Этот массив имеет размер MAX_PARTICLES. В переводе на русский язык: мы создаем память для хранения 1000 (MAX_PARTICLES) частиц. Это зарезервированная память будет хранить информацию о каждой индивидуальной частице.

particles particle[MAX_PARTICLES]; // Массив частиц (Место для информации о частицах)

Мы сокращаем код программы, при помощи запоминания наших 12 разных цветов в массиве цвета. Для каждого цвета от 0 до 11 мы запоминаем красную, зеленую, и, наконец, синею яркость. В таблице цветов ниже запомнено 12 различных цветов, которые постепенно изменяются от красного до фиолетового цвета.

static GLfloat colors[12][3]= // Цветовая радуга

{
  {1.0f,0.5f,0.5f},{1.0f,0.75f,0.5f},{1.0f,1.0f,0.5f},{0.75f,1.0f,0.5f},
  {0.5f,1.0f,0.5f},{0.5f,1.0f,0.75f},{0.5f,1.0f,1.0f},{0.5f,0.75f,1.0f},
  {0.5f,0.5f,1.0f},{0.75f,0.5f,1.0f},{1.0f,0.5f,1.0f},{1.0f,0.5f,0.75f}
};

LRESULT  CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Объявление WndProc

Код загрузки картинки не изменился. 

AUX_RGBImageRec *LoadBMP(char *Filename)     // Загрузка картинки

{

 FILE *File=NULL;          // Индекс файла



 

 if (!Filename)            // Проверка имени файла

 {

  return NULL;             // Если нет вернем NULL



 }

 

 File=fopen(Filename,"r"); // Проверим существует ли файл



 

 if (File)                 // Файл существует?

 {

  fclose(File);            // Закрыть файл



  return auxDIBImageLoad(Filename); // Загрузка картинки и вернем на нее указатель

 }

 return NULL;              // Если загрузка не удалась вернем NULL



}

 

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



int LoadGLTextures()                      // Загрузка картинки и конвертирование в текстуру

{

 int Status=FALSE;                        // Индикатор состояния



 

 AUX_RGBImageRec *TextureImage[1];        // Создать место для текстуры

 

 memset(TextureImage,0,sizeof(void *)*1); // Установить указатель в NULL



 

Наша текстура загружается кодом, который будет загружать нашу картинку частицы и конвертировать ее в текстуру с линейным фильтром.

if (TextureImage[0]=LoadBMP("Data/Particle.bmp")) // Загрузка текстуры частицы

{

  Status=TRUE; // Задать статус в TRUE


  glGenTextures(1, &texture[0]); // Создать одну текстуру

  glBindTexture(GL_TEXTURE_2D, texture[0]);


  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY,

    0, GL_RGB,   GL_UNSIGNED_BYTE, TextureImage[0]->data);


}

if (TextureImage[0])            // Если текстура существует

{

 if (TextureImage[0]->data)     // Если изображение текстуры существует



 {

   free(TextureImage[0]->data); // Освобождение памяти изображения текстуры

 }

 free(TextureImage[0]);         // Освобождение памяти под структуру



}

 

  return Status;        // Возвращаем статус



}

Единственное изменение, которое я сделал в коде изменения размера, было увеличение области просмотра. Вместо 100.0f, мы можем теперь рассматривать частицы на 200.0f единиц в глубине экрана.

// Изменение размеров и инициализация окна GL

GLvoid ReSizeGLScene(GLsizei width, GLsizei height) 


{
  if (height==0) // Предотвращение деления на ноль, если окно слишком мало
  {
    height=1; // Сделать высоту равной единице
  }

  //Сброс текущей области вывода и перспективных преобразований 


  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION); // Выбор матрицы проекций


  glLoadIdentity(); // Сброс матрицы проекции

  // Вычисление соотношения геометрических размеров для окна


  gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,200.0f); // ( МОДИФИЦИРОВАНО )

  glMatrixMode(GL_MODELVIEW); // Выбор матрицы просмотра модели


  glLoadIdentity(); // Сброс матрицы просмотра модели
}

Если Вы используете код урока 1, замените его на код ниже. Я добавил этот код для загрузки  нашей текстуры и включения смешивания для наших частиц. 

int InitGL(GLvoid)       // Все начальные настройки OpenGL здесь
{
  if (!LoadGLTextures()) // Переход на процедуру загрузки текстуры
  {
    return FALSE;        // Если текстура не загружена возвращаем FALSE
  }

Мы разрешаем плавное затенение, очищаем фон черным цветом, запрещаем тест глубины, разрешаем смешивание и наложение текстуры. После разрешения наложения текстуры мы выбираем нашу текстуру частицы.

  glShadeModel(GL_SMOOTH);    // Разрешить плавное затенение

  glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Черный фон

  glClearDepth(1.0f);         // Установка буфера глубины

  glDisable(GL_DEPTH_TEST);   // Запрещение теста глубины

  glEnable(GL_BLEND);         // Разрешаем смешивание
  glBlendFunc(GL_SRC_ALPHA,GL_ONE); // Тип смешивания

  // Улучшенные вычисления перспективы 


  glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
  glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);  // Улучшенные точечное смешение 
  glEnable(GL_TEXTURE_2D);                 // Разрешение наложения текстуры 
  glBindTexture(GL_TEXTURE_2D,texture[0]); // Выбор нашей текстуры 

В коде ниже инициализируется каждая из частиц. Вначале мы активизируем каждую частицу. Если частица - не активна, то она не будет появляться на экране, независимо оттого, сколько жизни у нее осталось.

После того, как мы сделали частицу активной, мы даем ей жизнь. Я сомневаюсь, что тот способ, с помощью которого я задаю жизнь, и угасание частицы, это самый лучший способ, но повторюсь еще раз, что это отлично работает! Полная жизнь - 1.0f. Это также дает частице полную яркость.

for (loop=0;loop
{
  particle[loop].active=true; // Сделать все частицы активными
  particle[loop].life=1.0f;   // Сделать все частицы с полной жизнью 

Мы задаем, как быстро частица угасает, при помощи присвоения fade случайного значения. Переменная life будет уменьшаться на значение fade, каждый раз, после того как частица будет отображена. Случайное значение будет от 0 до 99. Его мы делим его на 1000, поэтому мы получим очень маленькое значение с плавающей запятой. В завершении мы добавим 0.003 к конечному результату так, чтобы скорость угасания никогда не равнялось 0.

  //Случайная скорость угасания
  particle[loop].fade=float(rand()%100)/1000.0f+0.003f;

 

Теперь, когда наша частица активна, и мы дали ей жизнь, пришло время задать ей цвет. Вначале, мы хотим, чтобы все частицы были разным цветом. Поэтому я, делаю каждую частицу одним из 12 цветов, которые мы поместили в нашу таблицу цветов вначале этой программы. Математика проста. Мы берем нашу переменную loop и умножаем ее на число цветов в нашей таблице цвета, и делим на максимальное число частиц (MAX_PARTICLES). Это препятствует тому, что заключительное значение цвета будет больше, чем наш максимальное число цветов (12).



Вот пример: 900 * (12/900) =12. 1000 * (12/1000) =12, и т.д.

  particle[loop].r=colors[loop*(12/MAX_PARTICLES)][0]; // Выбор красного цвета радуги


  particle[loop].g=colors[loop*(12/MAX_PARTICLES)][1]; // Выбор зеленного цвета радуги
  particle[loop].b=colors[loop*(12/MAX_PARTICLES)][2]; // Выбор синего цвета радуги

Теперь мы зададим направление, в котором каждая частица двигается, наряду со скоростью. Мы умножаем результат на 10.0f, чтобы создать впечатление взрыва, когда программа запускается.

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

  particle[loop].xi=float((rand()%50)-26.0f)*10.0f; // Случайная скорость по оси X 


  particle[loop].yi=float((rand()%50)-25.0f)*10.0f; // Случайная скорость по оси Y
  particle[loop].zi=float((rand()%50)-25.0f)*10.0f; // Случайная скорость по оси Z

Наконец, мы задаем величину гравитации, которая воздействует на каждую частицу. В отличие от реальной гравитации, под действием которой все предметы падают вниз, наша гравитация сможет смещать частицы вниз, влево, вправо, вперед или назад (прим. переводчика: скорее всего это электромагнитное поле, а не гравитация). Вначале мы зададим гравитацию в полсилы, которая притягивает вниз. Чтобы сделать это, мы устанавливаем xg в 0.0f. Т.е. нет перемещения влево или вправо по плоскости X. Мы устанавливаем yg в -0.8f. Это создает притяжение вниз в полсилы. Если значение положительное, то притяжение вверх. Мы не хотим, чтобы частицы притягивались к нам или от нас, поэтому мы установим zg в 0.0f.

  particle[loop].xg=0.0f;  // Зададим горизонтальное притяжение в ноль 
  particle[loop].yg=-0.8f; // Зададим вертикальное притяжение вниз 
  particle[loop].zg=0.0f;  // зададим притяжение по оси Z в ноль 
}
return TRUE; // Инициализация завершена OK
}

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

Мы сбрасываем матрицу просмотра модели только однажды. Мы позиционируем частицы, используя командуglVertex3f() вместо использования перемещения их, при таком способе вывода частиц мы не изменяем матрицу просмотра модели при выводе наших частиц. 

int DrawGLScene(GLvoid) // Здесь мы все рисуем 


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

  glLoadIdentity(); // Сброс матрицы просмотра модели

Мы начинаем наш вывод с цикла. Этот цикл обновит каждую из наших частиц. 

  for (loop=0;loop
  {

Вначале мы проверим, активна ли наша частица. Если она не активна, то ее не надо модифицировать. В этой программе они активны всегда. Но в вашей программе, Вы сможете захотеть сделать некоторые частицы неактивными.

    if (particle[loop].active) // Если частицы не активны 
    {

Следующие три переменные xy и z - временные переменные, которые мы будем использовать, чтобы запомнить позицию частицы по x, y и z. Отмечу, что мы добавляем zoom к позиции по z, так как наша сцена смещена в экран на значение zoomparticle[loop].x - это наша позиция по x для любой частицы, которую мы выводим в цикле.particle[loop].y - это наша позиция по y для нашей частицы, и particle[loop].z - это наша позиция по z.

      float x=particle[loop].x; // Захватим позицию X нашей частицы 
      float y=particle[loop].y; // Захватим позицию Н нашей частицы
      float z=particle[loop].z+zoom; // Позиция частицы по Z + Zoom

Теперь, когда мы имеем позицию частицы, мы можем закрасить частицу. particle[loop].r - это красная яркость частицыparticle[loop].g – это зеленая яркость, и particle[loop].b – это синяя яркость. Напомню, что я использую жизнь частицы (life) для альфа значения. По мере того, как частица умирает, она становится все более и более прозрачной, пока она, в конечном счете, не исчезнет. Именно поэтому, жизнь частиц никогда не должна быть больше чем 1.0f. Если Вы хотите, чтобы частицы горели более долго, пробуйте уменьшить скорость угасания так, чтобы частица не так быстро исчезла.

      // Вывод частицы, используя наши RGB значения, угасание частицы согласно её жизни 
      glColor4f(particle[loop].r,particle[loop].g,particle[loop].b,particle[loop].life);

Мы задали позицию частицы и цвет. Все, что мы должны теперь сделать - вывести нашу частицу. Вместо использования текстурированного четырехугольника, я решил использовать текстурированную полоску из треугольников, чтобы немного ускорить программу. Некоторые 3D платы могут выводить треугольники намного быстрее чем, они могут выводить четырехугольники. Некоторые 3D платы конвертируют четырехугольник в два треугольника за Вас, но некоторые платы этого не делают. Поэтому мы сделаем эту работу сами. Мы начинаемся с того, что сообщаем OpenGL, что мы хотим вывести полоску из треугольников.

      glBegin(GL_TRIANGLE_STRIP); // Построение четырехугольника из треугольной полоски 

Цитата непосредственно из красной книги (OpenGL Red Book): полоска из треугольников рисуется как ряд треугольников (трехсторонних полигонов) используя вершины V0, V1, V2, затем V2, V1, V3 (обратите внимание на порядок), затем V2, V3, V4, и так далее. Порядок должен гарантировать, что все треугольники будут выведены с той же самой ориентацией так, чтобы полоска могла правильно формировать часть поверхности. Соблюдение ориентации важно для некоторых операций, типа отсечения. Должны быть, по крайней мере, 3 точки, чтобы было что-то выведено.



http://www.opengl.org.ru/lesson/nehe19-1.jpg 
Рисунок 1. Полоска из двух треугольников

 

Поэтому первый треугольник выведен, используя вершины 0, 1 и 2. Если Вы посмотрите на рисунок 1, Вы увидите, что точки вершин 0, 1 и 2 действительно составляют первый треугольник (верхняя правая, верхняя левая, нижняя правая). Второй треугольник выведен, используя вершины 2, 1 и 3. Снова, если Вы посмотрите на рисунок 1, вершины 2, 1 и 3 создают второй треугольник (нижняя правая, верхняя левая, нижняя правая). Заметьте, что оба треугольника выведены с тем же самым порядком обхода (против часовой стрелки). Я видел несколько сайтов, на которых заявлялось, что каждый второй треугольник должен быть в противоположном направлении. Это не так. OpenGL будет менять вершины, чтобы гарантировать, что все треугольники выведены тем же самым способом!



Есть две хорошие причины, для того чтобы использовать полоски из треугольников. Во-первых, после определения первых трех вершин начального треугольника, Вы должны только определять одну единственную точку для каждого другого дополнительного треугольника. Эта точка будет объединена с 2 предыдущим вершинами для создания треугольника. Во-вторых, сокращая количество данных, необходимых для создания треугольников ваша программа будет работать быстрее, и количество кода или данных, требуемых для вывода объекта резко сократиться.

Примечание: число треугольников, которые Вы видите на экране, будет равно числу вершин, которые Вы зададите минус 2. В коде ниже мы имеем 4 вершины, и мы видим два треугольника.

      glTexCoord2d(1,1); glVertex3f(x+0.5f,y+0.5f,z); // Верхняя правая
      glTexCoord2d(0,1); glVertex3f(x-0.5f,y+0.5f,z); // Верхняя левая
      glTexCoord2d(1,0); glVertex3f(x+0.5f,y-0.5f,z); // Нижняя правая 
      glTexCoord2d(0,0); glVertex3f(x-0.5f,y-0.5f,z); // Нижняя левая

Наконец мы сообщаем OpenGL, что мы завершили вывод нашей полоски из треугольников.

      glEnd(); // Завершение построения полоски треугольников 

Теперь мы можем переместить частицу. Математически это может выглядеть несколько странно, но довольно просто. Сначала мы берем текущую позицию x частицы. Затем мы добавляем значение смещения частицы по x, деленной на slowdown/1000. Поэтому, если наша частица была в центре экрана на оси X (0), наша переменная смещения (xi) для оси X равна +10 (смещение вправо от нас) и slowdown было равно 1, мы сместимся направо на 10/(1*1000), или на 0.01f. Если мы увеличим slowdown на 2, мы сместимся только на 0.005f. Буду надеяться, что это поможет Вам понять, как работает замедление (slowdown).

Это также объясняет, почему умножение начальных значений на 10.0f заставляет пиксели перемещаться намного быстрее, создавая эффект взрыва.

Мы используем ту же самую формулу для осей y и z, для того чтобы переместить частицу по экрану.

      // Передвижение по оси X на скорость по X

      particle[loop].x+=particle[loop].xi/(slowdown*1000); 


      // Передвижение по оси Y на скорость по Y

      particle[loop].y+=particle[loop].yi/(slowdown*1000);


      // Передвижение по оси Z на скорость по Z

      particle[loop].z+=particle[loop].zi/(slowdown*1000); 

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


Каталог: forum
forum -> Лечение гепатозов
forum -> Система ведения овцеводства в крестьянско-фермерских и личных хозяйствах населения
forum -> Yaesuft-857 (ft-897) – переходник для подключения cat интерфейса к микрофонному разъему трансивера
forum -> Васильев Владимир Юрьевич
forum -> tl-wr1043ND Беспроводной гигабитный маршрутизатор серии n copyright & trademarks
forum -> Цели и задачи Контакта rtf docx
forum -> Назовите не менее трех результатов революции 1905- 1907гг. Приведите не менее трех положений, отражающих значение революции для отечественной истории начала 20 века


Поделитесь с Вашими друзьями:
1   2   3   4   5   6   7   8   9   10   ...   27


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

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