Untitled

 avatar
unknown
plain_text
a month ago
19 kB
4
Indexable
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>

#include <GL/glew.h>
//#include <GL/gl.h> // OpenGL header not necessary, included by GLEW
#include <GL/freeglut.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/matrix_inverse.hpp>

#include "GLSLProgram.h"
#include "GLTools.h"

// Standard window width
const int WINDOW_WIDTH = 640;
// Standard window height
const int WINDOW_HEIGHT = 480;
// GLUT window id/handle
int glutID = 0;

cg::GLSLProgram program;

glm::mat4x4 view;
glm::mat4x4 projection;

float zNear = 0.1f;
float zFar = 100.0f;

// Werte für die Aufgaben
int subdivision = 0;        // Unterteilungstiefe n der Kugel
float sphereRadius = 1.0f;  // Echter Radius der Kugel, wird mit r/R verändert.
float cameraDistance = 4.0f;  // Abstand der Kamera zur Kugel
bool showNormals = false;   // Schalter: Normalen anzeigen oder ausblenden.

// Diese Werte sagen OpenGL, wie viele Indizes beim Zeichnen gelesen werden sollen.
int sphereIndexCount = 0;   // Anzahl der Indizes für die Kugel. Bei GL_TRIANGLES: 3 Indizes = 1 Dreieck.
int axesIndexCount = 0;     // Anzahl der Indizes für die lokalen Achsen. Bei GL_LINES: 2 Indizes = 1 Linie.
int normalIndexCount = 0;   // Anzahl der Indizes für die Normalenlinien. Auch hier: 2 Indizes = 1 Linie.

// Rotationswinkel pro Tastendruck auf x, y oder z.
const float ROTATION_STEP = glm::radians(10.0f); // 10 Grad, in Bogenmaß umgerechnet.
/*
Struct to hold data for object rendering.
*/
class Object
{
public:
    inline Object()
        : vao(0),
        positionBuffer(0),
        colorBuffer(0),
        indexBuffer(0)
    {
    }

    inline ~Object() { // GL context must exist on destruction
        glDeleteVertexArrays(1, &vao);
        glDeleteBuffers(1, &indexBuffer);
        glDeleteBuffers(1, &colorBuffer);
        glDeleteBuffers(1, &positionBuffer);
    }

    GLuint vao;        // vertex-array-object ID

    GLuint positionBuffer; // ID of vertex-buffer: position
    GLuint colorBuffer;    // ID of vertex-buffer: color

    GLuint indexBuffer;    // ID of index-buffer

    glm::mat4x4 model; // model matrix
};

Object sphere;
Object axes;
Object normals;

// Speichert alle Punkte der Kugeloberfläche für die Normalen
std::vector<glm::vec3> sphereVerticesForNormals;

glm::vec3 pointOnSphere(float x, float y, float z)
{
    glm::vec3 p(x, y, z);

    // normalize(p) macht den Vektor genau 1 lang.
        // Danach multiplizieren wir mit sphereRadius.
        // So liegt der Punkt genau auf der Kugeloberfläche.
    return glm::normalize(p) * sphereRadius;
}



//Hier löschen wir alle Geschpeicherten Arraylist , pos, farben und indexen der punkte , 
// wenn etwas wirklisch geschpeichert ist 
void clearObject(Object& obj)
{
    if (obj.vao != 0) {
        glDeleteVertexArrays(1, &obj.vao);
    }

    if (obj.positionBuffer != 0) {
        glDeleteBuffers(1, &obj.positionBuffer);
    }

    if (obj.colorBuffer != 0) {
        glDeleteBuffers(1, &obj.colorBuffer);
    }

    if (obj.indexBuffer != 0) {
        glDeleteBuffers(1, &obj.indexBuffer);
    }

    obj.vao = 0;
    obj.positionBuffer = 0;
    obj.colorBuffer = 0;
    obj.indexBuffer = 0;
}

void uploadObject(Object& obj,
    const std::vector<glm::vec3>& vertices,
    const std::vector<glm::vec3>& colors,
    const std::vector<GLushort>& indices)
{
    clearObject(obj);

    GLuint programId = program.getHandle();
    GLuint pos;

    // Step 0: Create vertex array object.
    glGenVertexArrays(1, &obj.vao);
    glBindVertexArray(obj.vao);

    // Step 1: Create vertex buffer object for position attribute and bind it to the associated "shader attribute".
    glGenBuffers(1, &obj.positionBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, obj.positionBuffer);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), vertices.data(), GL_STATIC_DRAW);

    // Bind it to position.
    pos = glGetAttribLocation(programId, "position");
    glEnableVertexAttribArray(pos);
    glVertexAttribPointer(pos, 3, GL_FLOAT, GL_FALSE, 0, 0);

    // Step 2: Create vertex buffer object for color attribute and bind it to...
    glGenBuffers(1, &obj.colorBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, obj.colorBuffer);
    glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(glm::vec3), colors.data(), GL_STATIC_DRAW);

    // Bind it to color.
    pos = glGetAttribLocation(programId, "color");
    glEnableVertexAttribArray(pos);
    glVertexAttribPointer(pos, 3, GL_FLOAT, GL_FALSE, 0, 0);

    // Step 3: Create vertex buffer object for indices. No binding needed here.
    glGenBuffers(1, &obj.indexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, obj.indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLushort), indices.data(), GL_STATIC_DRAW);

    // Unbind vertex array object (back to default).
    glBindVertexArray(0);
}

//***************Aufgabe:1*****************//


void addTriangleCCW(std::vector<glm::vec3>& vertices,
    std::vector<glm::vec3>& colors,
    std::vector<GLushort>& indices,
    glm::vec3 p0,
    glm::vec3 p1,
    glm::vec3 p2)
{

// Ein Dreieck kann richtig herum oder falsch herum gespeichert sein.
// Die Punkte bleiben gleich, nur die Reihenfolge kann anders sein.
// Falsche Reihenfolge lasst die Vorderseite des Dreiecks zeigt dann nach innen.
// Das ist schlecht fuer Lichtberechnung.
// Deshalb pruefen wir hier die Richtung und drehen sie bei Bedarf um.


    glm::vec3 normal = glm::cross(p1 - p0, p2 - p0);
    glm::vec3 outward = p0 + p1 + p2;

    if (glm::dot(normal, outward) < 0.0f) {
        std::swap(p1, p2);
    }

    GLushort start = (GLushort)vertices.size();
    //push_back = hinten in die Liste einfügen
    vertices.push_back(p0);
    vertices.push_back(p1);
    vertices.push_back(p2);

    // Alle drei Punkte bekommen Gelb.
    colors.push_back(glm::vec3(1.0f, 1.0f, 0.0f));
    colors.push_back(glm::vec3(1.0f, 1.0f, 0.0f));
    colors.push_back(glm::vec3(1.0f, 1.0f, 0.0f));

    // Diese drei neuen Punkte bilden ein Dreieck.
    indices.push_back(start);
    indices.push_back(start + 1);
    indices.push_back(start + 2);
}




//hier wird das Oktaederfläche in kleinere Dreiecke zerlegt
void addSphereFace(std::vector<glm::vec3>& vertices,
    std::vector<glm::vec3>& colors,
    std::vector<GLushort>& indices,
    glm::vec3 a,
    glm::vec3 b,
    glm::vec3 c)
{
    // parts ist n + 1.
    // Dadurch entstehen pro Oktaederfläche genau (n + 1)^2 Dreiecke.
    int parts = subdivision + 1;

    for (int i = 0; i < parts; i++) {
        for (int j = 0; j < parts - i; j++) {
            float u0 = (float)i / parts;
            float v0 = (float)j / parts;

            float u1 = (float)(i + 1) / parts;
            float v1 = (float)j / parts;

            float u2 = (float)i / parts;
            float v2 = (float)(j + 1) / parts;

            glm::vec3 p0 = pointOnSphere((1 - u0 - v0) * a.x + u0 * b.x + v0 * c.x,
                (1 - u0 - v0) * a.y + u0 * b.y + v0 * c.y,
                (1 - u0 - v0) * a.z + u0 * b.z + v0 * c.z);

            glm::vec3 p1 = pointOnSphere((1 - u1 - v1) * a.x + u1 * b.x + v1 * c.x,
                (1 - u1 - v1) * a.y + u1 * b.y + v1 * c.y,
                (1 - u1 - v1) * a.z + u1 * b.z + v1 * c.z);

            glm::vec3 p2 = pointOnSphere((1 - u2 - v2) * a.x + u2 * b.x + v2 * c.x,
                (1 - u2 - v2) * a.y + u2 * b.y + v2 * c.y,
                (1 - u2 - v2) * a.z + u2 * b.z + v2 * c.z);

            addTriangleCCW(vertices, colors, indices, p0, p1, p2);

            if (j < parts - i - 1) {
                float u3 = (float)(i + 1) / parts;
                float v3 = (float)(j + 1) / parts;

                glm::vec3 p3 = pointOnSphere((1 - u3 - v3) * a.x + u3 * b.x + v3 * c.x,
                    (1 - u3 - v3) * a.y + u3 * b.y + v3 * c.y,
                    (1 - u3 - v3) * a.z + u3 * b.z + v3 * c.z);

                addTriangleCCW(vertices, colors, indices, p1, p3, p2);
            }
        }
    }
}

void initSphere()
{
    std::vector<glm::vec3> vertices;
    std::vector<glm::vec3> colors;
    std::vector<GLushort> indices;

    // Das Oktaeder ist die Grundform der Kugel bei n = 0.
    glm::vec3 top(0.0f, 1.0f, 0.0f);
    glm::vec3 bottom(0.0f, -1.0f, 0.0f);
    glm::vec3 front(0.0f, 0.0f, 1.0f);
    glm::vec3 right(1.0f, 0.0f, 0.0f);
    glm::vec3 back(0.0f, 0.0f, -1.0f);
    glm::vec3 left(-1.0f, 0.0f, 0.0f);

    addSphereFace(vertices, colors, indices, top, front, right);
    addSphereFace(vertices, colors, indices, top, right, back);
    addSphereFace(vertices, colors, indices, top, back, left);
    addSphereFace(vertices, colors, indices, top, left, front);

    addSphereFace(vertices, colors, indices, bottom, right, front);
    addSphereFace(vertices, colors, indices, bottom, back, right);
    addSphereFace(vertices, colors, indices, bottom, left, back);
    addSphereFace(vertices, colors, indices, bottom, front, left);

    sphereVerticesForNormals = vertices;
    sphereIndexCount = (int)indices.size();

     std::cout << "n = " << subdivision
               << ", triangles = " << sphereIndexCount / 3
               << std::endl;

    uploadObject(sphere, vertices, colors, indices);
}



//*************Aufgabe:2*************//


void initNormals()
{
    std::vector<glm::vec3> vertices;
    std::vector<glm::vec3> colors;
    std::vector<GLushort> indices;

    const float normalLength = 0.25f;
    //Start = kugelpunkt 
    for (int i = 0; i < (int)sphereVerticesForNormals.size(); i++) {
        glm::vec3 start = sphereVerticesForNormals[i];

        // Bei einer Kugel zeigt die Normale vom Mittelpunkt nach außen.
        glm::vec3 direction = glm::normalize(start);
        glm::vec3 end = start + direction * normalLength;

        GLushort startIndex = (GLushort)vertices.size();

        vertices.push_back(start);
        vertices.push_back(end);

        // Normalen sind magenta, damit man sie klar von der gelben Kugel unterscheidet.
        colors.push_back(glm::vec3(1.0f, 0.0f, 1.0f));
        colors.push_back(glm::vec3(1.0f, 0.0f, 1.0f));

        indices.push_back(startIndex);
        indices.push_back(startIndex + 1);
    }

    normalIndexCount = (int)indices.size();
    //std::cout << "Normalen: " << normalIndexCount / 2 << std::endl;
    uploadObject(normals, vertices, colors, indices);
}



//************Aufgabe:3***************//

void initAxes()
{
    std::vector<glm::vec3> vertices;
    std::vector<glm::vec3> colors;
    std::vector<GLushort> indices;

    float l = sphereRadius * 1.4f;

    // x-Achse: rot
        // Linie von Mittelpunkt (0,0,0) nach rechts (l,0,0)
    vertices.push_back(glm::vec3(0.0f, 0.0f, 0.0f));
    vertices.push_back(glm::vec3(l, 0.0f, 0.0f));
    colors.push_back(glm::vec3(1.0f, 0.0f, 0.0f));
    colors.push_back(glm::vec3(1.0f, 0.0f, 0.0f));

    // y-Achse: grün
        // Linie von Mittelpunkt (0,0,0) nach oben (0,l,0)
    vertices.push_back(glm::vec3(0.0f, 0.0f, 0.0f));
    vertices.push_back(glm::vec3(0.0f, l, 0.0f));
    colors.push_back(glm::vec3(0.0f, 1.0f, 0.0f));
    colors.push_back(glm::vec3(0.0f, 1.0f, 0.0f));

    // z-Achse: blau
        // Linie von Mittelpunkt (0,0,0) nach vorne (0,0,l)
    vertices.push_back(glm::vec3(0.0f, 0.0f, 0.0f));
    vertices.push_back(glm::vec3(0.0f, 0.0f, l));
    colors.push_back(glm::vec3(0.0f, 0.3f, 1.0f));
    colors.push_back(glm::vec3(0.0f, 0.3f, 1.0f));

    // Bei GL_LINES nimmt OpenGL immer 2 Indizes und zeichnet daraus eine Linie.
    for (GLushort i = 0; i < (GLushort)vertices.size(); i++) {
        indices.push_back(i);
    }

    // Anzahl der Achsen-Indizes speichern und in Grafikkarte speichern 
    axesIndexCount = (int)indices.size();
    uploadObject(axes, vertices, colors, indices);
}


void rebuildGeometry()
{
    initSphere();
    initAxes();
    initNormals();
}


//***********Aufgsbe:4**********//
void updateView()
{
    // Kamera bleibt auf der z-Achse und schaut weiter auf den Ursprung.
    // Nur der Abstand wird für den Zoom verändert.
    glm::vec3 eye(0.0f, 0.0f, cameraDistance);
    glm::vec3 center(0.0f, 0.0f, 0.0f);
    glm::vec3 up(0.0f, 1.0f, 0.0f);

    view = glm::lookAt(eye, center, up);
}


//Testfunktion für r/R ändert den Radius.
// - a/s ändert nur den Kameraabstand

/*void printStatus()
{
    std::cout 
        << "Radius = " << sphereRadius
        << ", Kameraabstand = " << cameraDistance
        << std::endl;
}
*/

void renderObject(Object& obj, int indexCount, GLenum mode)
{
    // Create mvp.
    glm::mat4x4 mvp = projection * view * obj.model;

    // Bind the shader program and set uniform(s).
    program.use();
    program.setUniform("mvp", mvp);

    glBindVertexArray(obj.vao);
    glDrawElements(mode, indexCount, GL_UNSIGNED_SHORT, 0);
    glBindVertexArray(0);
}

void renderSphere()
{
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    renderObject(sphere, sphereIndexCount, GL_TRIANGLES);
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

/*
 Initialization. Should return true if everything is ok and false if something went wrong.
 */
bool init()
{
    // OpenGL: Set "background" color and enable depth testing.
    glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
    glEnable(GL_DEPTH_TEST);

    // Construct view matrix.
    // Construct view matrix.
    updateView();

    // Create a shader program and set light direction.
    if (!program.compileShaderFromFile("shader/simple.vert", cg::GLSLShader::VERTEX)) {
        std::cerr << program.log();
        return false;
    }

    if (!program.compileShaderFromFile("shader/simple.frag", cg::GLSLShader::FRAGMENT)) {
        std::cerr << program.log();
        return false;
    }

    if (!program.link()) {
        std::cerr << program.log();
        return false;
    }

    sphere.model = glm::mat4(1.0f);
    axes.model = sphere.model;
    normals.model = sphere.model;

    rebuildGeometry();

    return true;
}

/*
 Rendering.
 */
void render()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    renderSphere();
    renderObject(axes, axesIndexCount, GL_LINES);

    if (showNormals) {
        renderObject(normals, normalIndexCount, GL_LINES);
    }
}

void glutDisplay()
{
    render();
    glutSwapBuffers();
}

/*
 Resize callback.
 */
void glutResize(int width, int height)
{
    // Division by zero is bad...
    height = height < 1 ? 1 : height;
    glViewport(0, 0, width, height);

    // Construct projection matrix.
    projection = glm::perspective(glm::radians(45.0f), (float)width / height, zNear, zFar);
}



//**************Aufgabe:5*************//
void applyLocalRotation(glm::vec3 axis)
{
    glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), ROTATION_STEP, axis);

    // Rechts multiplizieren bedeutet: Rotation um die lokale Achse des Objekts*
    sphere.model = sphere.model * rotation;
    

    // Achsen und Normalen rotation
    axes.model = sphere.model;
    normals.model = sphere.model;
}

/*
 Callback for char input.
 */
void glutKeyboard(unsigned char keycode, int x, int y)
{
    switch (keycode) {
    case 27: // ESC
        glutDestroyWindow(glutID);
        return;

    case '+':
        subdivision = std::min(4, subdivision + 1);
        rebuildGeometry();
        break;

    case '-':
        subdivision = std::max(0, subdivision - 1);
        rebuildGeometry();
        break;

    case 'r':
        sphereRadius = std::max(0.3f, sphereRadius - 0.1f);
        rebuildGeometry();
       // printStatus();
        break;

    case 'R':
        sphereRadius = std::min(1.8f, sphereRadius + 0.1f);
        rebuildGeometry();
        //printStatus();
        break;

    case 'a':
        cameraDistance = std::max(2.0f, cameraDistance - 0.2f);
        updateView();
        printStatus();
        break;

    case 's':
        cameraDistance = std::min(10.0f, cameraDistance + 0.2f);
        updateView();
        printStatus();
        break;

    case 'x':
        applyLocalRotation(glm::vec3(1.0f, 0.0f, 0.0f));
        break;

    case 'y':
        applyLocalRotation(glm::vec3(0.0f, 1.0f, 0.0f));
        break;

    case 'z':
        applyLocalRotation(glm::vec3(0.0f, 0.0f, 1.0f));
        break;

    case 'n':
        // Nur die Rotation wird zurückgesetzt, so wie es in der Aufgabe steht.
        sphere.model = glm::mat4(1.0f);
        axes.model = sphere.model;
        normals.model = sphere.model;
        break;

    case 'v':
        showNormals = !showNormals;
        break;
    }

    glutPostRedisplay();
}

int main(int argc, char** argv)
{
    // GLUT: Initialize freeglut library (window toolkit).
    glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    glutInitWindowPosition(40, 40);
    glutInit(&argc, argv);

    // GLUT: Create a window and opengl context (version 4.3 core profile).
    glutInitContextVersion(4, 3);
    glutInitContextFlags(GLUT_FORWARD_COMPATIBLE | GLUT_DEBUG);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);

    glutCreateWindow("Aufgabenblatt 01");
    glutID = glutGetWindow();

    // GLEW: Load opengl extensions
    //glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK) {
        return -1;
    }

#if _DEBUG
    if (glDebugMessageCallback) {
        std::cout << "Register OpenGL debug callback " << std::endl;
        glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
        glDebugMessageCallback(cg::glErrorVerboseCallback, nullptr);
        glDebugMessageControl(GL_DONT_CARE,
            GL_DONT_CARE,
            GL_DONT_CARE,
            0,
            nullptr,
            true); // get all debug messages
    }
    else {
        std::cout << "glDebugMessageCallback not available" << std::endl;
    }
#endif

    // GLUT: Set callbacks for events.
    glutReshapeFunc(glutResize);
    glutDisplayFunc(glutDisplay);
    //glutIdleFunc   (glutDisplay); // redisplay when idle

    glutKeyboardFunc(glutKeyboard);

    // init vertex-array-objects.
    bool result = init();
    if (!result) {
        return -2;
    }

    // GLUT: Loop until the user closes the window
    // rendering & event handling
    glutMainLoop();

    // Cleanup in destructors:
    // Objects will be released in ~Object
    // Shader program will be released in ~GLSLProgram

    return 0;
}
Editor is loading...
Leave a Comment