Следующие команды создают мяч, но перед этим векторное пространство (матрица моделирования) готовится так, чтобы координаты 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), которая должна отсекать отражение мяча в те моменты, когда оно пытается вылезти наружу. Отсечение не должно действовать на сам мяч, поэтому механизм отсечения надо вовремя включать и выключать.
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.