Каркас Windows-приложения. Особенности нового каркаса. Перемещение в пространстве (с текстурированными стенами), страница 20

Следующие команды создают мяч, но перед этим векторное пространство (матрица моделирования) готовится так, чтобы координаты Y всех примитивов (а мяч по-прежнему собирается из них), поменяли свой знак (команда glScalef (1,-1, 1);). Так как поверхность пола имеет координату Y=0, то эта установка равносильна созданию отражения в зеркале, роль которого играет пол.

В настоящий момент интересно добиться промежуточного результата — увидеть отражение мяча. Если вы правильно вставите три последних, рассмотренных нами фрагмента внутрь функции Draw и (с помощью клавиши 'A') подвините невидимый мяч вниз, то сможете увидеть отражение мяча. Ниже приведен код этой промежуточной версии функции Draw.

void Draw ()

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

glPushMatrix();

glTranslatef (0, -0.6f, z);

glColorMask (0, 0, 0, 0);   // Set Color Mask

glEnable (GL_STENCIL_TEST); // Enable Stencil Buffer For "marking" The Floor

glStencilFunc (GL_ALWAYS, 1, 1);// Always Passes, 1 Bit Plane, 1 As Mask

//==== Keep If Test Fails, Keep If Test Passes But Buffer Test Fails, Replace If Test Passes 

glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE);// Set stencil buffer to 1 where we draw any polygon

glDisable (GL_DEPTH_TEST);

DrawFloor();  // Draw the floor to the stencil buffer. We only want to mark it in the stencil buffer

glEnable(GL_DEPTH_TEST);

glColorMask (1, 1, 1, 1);

glStencilFunc (GL_EQUAL, 1, 1); // We draw only where the stencil is 1 (i.e. where the floor was drawn)

glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP);   // Don't Change The Stencil Buffer

glPushMatrix();

glScalef (1,-1, 1);         // Mirror Y Axis

glTranslatef (0, height, 0); // Position The Ball

glRotatef (angleX, 1, 0, 0);

glRotatef (angleY, 0, 1, 0);

DrawBall();

glPopMatrix();

glDisable (GL_STENCIL_TEST); // We don't need the stencil buffer any more

angleX += xv; angleY += yv;

glPopMatrix();

}

Запустите и проверьте. Еще более реалистичное отражение мяча вы получите, если добавите код, изображающий пол. Заметьте, что изображение пола происходит после создания отражения мяча, поэтому его надо смешать с тем, что уже присутствует в буфере кадра. Следующий фрагмент выключает освещение, включает смешивание цветов и задает полу белый, но на 20% прозрачный цвет. Вставьте этот фрагмент перед строкой, изменяющей углы вращения мяча и запустите приложение. Перемещайте мяч вниз и наблюдайте за его отражением в полу. Попробуйте варьировать степень непрозрачности (opacity) при установке цвета.

glDisable (GL_LIGHTING);  // Since We Use Blending, We Disable Lighting

glEnable (GL_BLEND);      //Otherwise The Reflected Object Wont Show

glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

glColor4f (1, 1, 1, 0.8f);  // 80% Alpha

DrawFloor();

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

glEnable (GL_LIGHTING);

glDisable (GL_BLEND);

glTranslatef (0, height, 0); // Position the ball

glRotatef (angleX, 1, 0, 0);

glRotatef (angleY, 0, 1, 0);

DrawBall();

Вы, конечно, заметили, что после того как мяч пересек границу пола возникает непредвиденный эффект выхода отражения за пределы его жизненного пространства. Такого в жизни вы не увидите, но в сюрреалистичном мире OpenGL бывает и не такое. Для устранения этого эффекта можно использовать прием, называемый отсечением (clipping). Он вам знаком, так как отсечение давно работает во всех наших примерах. Это отсечение шестью поверхностями frustum'а. Сам frustum задается при вызове функции gluPerspective в обработчике OnSize.

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