OpenGL - rotace

V této krátké kapitole, podobně jako v té předešlé, naváži na kapitolu o perspektivě, tentokrát na téma rotace os. Bude to ještě jednodušší kapitola, protože se nenaučíte nic z OpenGL funkcí, ale jen jak využít linmath.h.

Události myši

Proberu postupně všechny tři způsoby rotace podle os. Všechny příklady mají společnou obsluhu události myší, viz events.h:

static bool rotationOn = false;
static double lastX, lastY;

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
        if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
                glfwSetWindowShouldClose(window, GLFW_TRUE);
                return;
        }
}

static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
        if (button != GLFW_MOUSE_BUTTON_LEFT) {
                return;
        }
        if (action == GLFW_PRESS) {
                rotationOn = true;
                glfwGetCursorPos(window, &lastX, &lastY);
                return;
        }
        else {
                rotationOn = false;
        }
}

static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos)
{
        double x, y;
        if(!rotationOn) return;
        x = ypos - lastY;
        y = xpos - lastX;
        lastX = xpos;
        lastY = ypos;

        double xAngle = degToRad(x/5.0);
        double yAngle = degToRad(y/5.0);
        updateAngles(xAngle, yAngle);
}
 

Odstranil jsem většinu obsluhy pro události klávesnice (nechal jsem jen zavírání okna stiskem ESC).

Ve funkci mouse_button_callback() si zaznamenávám souřadnice, kde uživatele stiskne levé tlačítko (kde začiná myší tahat). Také si zaznamenám, zda je tlačítko stisknuté, nebo ne (rotationOn).

Funkce cursor_position_callback() se opakovaně volá při přesunu myši. Pokud je rotationOn TRUE, tak spočítám vzdálenost od místa kde uživatel začal s tažením a podle toho nastavím úhel vodorovného (yAngle) a svislého (xAngle) pootočení.

Nakonec se zavolá funkce updateAngles(), která se postará o nastavení matic transformací. Tato funkce bude v každém příkladu dělat něco jiného.

Funkci cursor_position_callback() je potřeba ještě zaregistrovat (dělám to v funkci main()):

glfwSetCursorPosCallback(window, cursor_position_callback);

Rotace kolem os x a y

Tento způsob rotace je velmi jednoduchý. Hlavičkový soubor update-angles.h definuje funkci updateAngles() takto:

extern  GLfloat xAngle, yAngle;

void updateAngles(GLfloat _xAngle, GLfloat _yAngle) {
        xAngle -= _xAngle;
        yAngle -= _yAngle;
}

Znaménka měním proto, aby se jehlan otáčel v tom správném směru. Externí proměnné xAngle a yAngle jsou pak definované v souboru main.c jako globální proměnné:

        //...
        GLfloat xAngle = 0.0, yAngle= 0.0;

        int main(void) {
        //...

Ve funkci draw() se tyto proměnné použijí pro otočení matice kolem osy X a Y.

        //...
        mat4x4 uMVMatrix, uPMatrix;

        mat4x4_perspective(uPMatrix, degToRad(45.0), ((GLfloat) width)/height, 1.0, 100.0);

        mat4x4_translate(uMVMatrix, 0.0, 0.0, -10.0);
        mat4x4_rotate_Y(uMVMatrix, uMVMatrix, yAngle);
        mat4x4_rotate_X(uMVMatrix, uMVMatrix, -xAngle);

        glUniformMatrix4fv(variables.uMVMatrixLoc, 1, GL_FALSE, (const GLfloat *) uMVMatrix);
        glUniformMatrix4fv(variables.uPMatrixLoc, 1, GL_FALSE, (const GLfloat *) uPMatrix);
        //...

A to je k rotaci vše.

Rotační matice

Druhý příklad si definuje matici rotace rotationMatrix, do které si ukládá postupně každý pohyb myší.

Stěžejní částí příkladu je zase soubor update-angles.h:

extern mat4x4 rotationMatrix;

void updateAngles(GLfloat _xAngle, GLfloat _yAngle) {
        mat4x4 newRotationMatrix;
        mat4x4_identity(newRotationMatrix);
        mat4x4_rotate_X(newRotationMatrix, newRotationMatrix, _xAngle);
        mat4x4_rotate_Y(newRotationMatrix, newRotationMatrix, -_yAngle);
        mat4x4_mul(rotationMatrix, newRotationMatrix, rotationMatrix);
}

Funkce updateAngles() nejdříve vytvoří transformační matici, do které uloží rotace kolem os x a y.

Touto maticí pak vynásobí rotationMatrix, která si tak „pamatuje“ předchozí rotace a kumuluje celkovou rotaci.

V souboru main.c je pak definována globální proměnná rotationMatrix, která se nedjříve inicializuje jako jednotková matice (v main()):

//...
mat4x4 rotationMatrix;

//...
int main(void)
{
//...
mat4x4_identity(rotationMatrix);

ticking(&program, window);
//...
}
A pak se použije ve fuknci draw():
//...
        mat4x4 uMVMatrix, uPMatrix;

        mat4x4_perspective(uPMatrix, degToRad(45.0), ((GLfloat) width)/height, 1.0, 100.0);
        mat4x4_translate(uMVMatrix, 0.0, 0.0, -10.0);
        mat4x4_mul(uMVMatrix, uMVMatrix, rotationMatrix);

        glUniformMatrix4fv(variables.uMVMatrixLoc, 1, GL_FALSE, (const GLfloat *) uMVMatrix);
        glUniformMatrix4fv(variables.uPMatrixLoc, 1, GL_FALSE, (const GLfloat *) uPMatrix);
//...

Nyní se objekty otáčejí v závislosti na svém předchozím pootočení, takže vypadá otáčení přirozeněji a je možné otočit i zelenou osu x.

Quaternions

Třetí animace využívá quaternionů, díky kterým dokáže stále rotovat podle „původních“ os x i y, ikdyž jsou (spolu s tělesem) už nějak natočené.

Soubor update-angles.h vypadá takto:

extern mat4x4 rotationMatrix;
extern quat rotationQuat;

void updateAngles(GLfloat _xAngle, GLfloat _yAngle) {
        quat xQuat, yQuat, tmpQuat;

        vec3 axis = { 1,0,0 };
        quat_rotate(xQuat, _xAngle, axis);
        tmpQuat[0] = rotationQuat[0];
        tmpQuat[1] = rotationQuat[1];
        tmpQuat[2] = rotationQuat[2];
        tmpQuat[3] = rotationQuat[3];
        quat_mul(rotationQuat, tmpQuat, xQuat);

        axis[0] = 0;
        axis[1] = 1;
        quat_rotate(yQuat, _yAngle, axis);
        tmpQuat[0] = rotationQuat[0];
        tmpQuat[1] = rotationQuat[1];
        tmpQuat[2] = rotationQuat[2];
        tmpQuat[3] = rotationQuat[3];
        quat_mul(rotationQuat, tmpQuat, yQuat);

        mat4x4_from_quat(rotationMatrix, rotationQuat);
}

Tentokrát se pro „zapamatování“ předchozího stavu používá quaternion rotationQuat. Matice rotationMatrix se vytváří z tohoto quaternionu pomocí funkce mat4x4_from_quat().

Funkce quat_rotate() vytvoří quaternion, který bude rotovat o úhel daný druhým argumentem, podle os, které jsou určené třetím argumentem.

Funkce quat_mul() násobí quaterniony z druhého a třetího argumentu a výsledek uloží do prvního. Aby to správně fungovalo, tak výsledný quaternion nesmí být odkaz na stejné pole jako je třetí nebo čtvrtý quaternion. Proto ve funkci updateAngles() používám pomocnou proměnnou tmpQuat.

Matice rotationMatrix se použije v main.c úplně stejně, jako v předchozím příkladu. Jediná změna v main.c tak je inicializace quaternionu rotationQuat ve funkci main():

        //...
        mat4x4_identity(rotationMatrix);
        quat_identity(rotationQuat);
        //...

Matice rotationMatrix musí být také inicializována, aby obsahovala rozumnou hodnotu, než uživatel tažením myši poprvé zavolá updateAngles().

A tím je tato kapitola u konce. V příští kapitole se vám rozsvítí.

Komentář Hlášení chyby
Created: 24.12.2016
Last updated: 24.11.2017
Tato stánka používá ke svému běhu cookies, díky kterým je možné monitorovat, co tu provádíte (ne že bych to bez cookies nezvládl). Také vás tu bude špehovat google analytics. Jestli si myslíte, že je to problém, vypněte si cookies ve vašem prohlížeči, nebo odejděte a už se nevracejte :-). Prohlížením tohoto webu souhlasíte s používáním cookies. Dozvědět se více..