Untitled
unknown
plain_text
14 days ago
19 kB
3
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