Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

09.07.2014, 18:10

glDrawElements Segfault

Ich bin gerade dabei mich in OpenGL einzuarbeiten. Erste Versuche, wie das Zeichnen von Dreiecken, haben auch schon geklappt. Das war allerdings alles noch ziemlich statisch mit allem Code in einer Datei. Deshalb habe ich meinen Code überarbeitet um ihn "objektorientierter" zu machen.
Meine Idee war es eine Klasse Shape zu schreiben, die zu einem Element auf dem Bildschirm (in meinem Beispiel ein 2D Pfeil) VBO, VAO und EBO verwaltet. Diese sollten zuerst erzeugt und der Klasse dann im Konstruktor übergeben werden. Im Destruktor der Klasse sollte dann aufgeräumt werden. Außerdem sollte die Klasse eine Methode draw besitzen, mit der sie sich selbst zeichnet.
Somit sollte ich in der Lage sein mir mehrere Shapes zu initialisieren, in einem vector zu speichern und diesen vector dann im main Loop zu durchlaufen und die Shapes zu zeichnen. Mit nur einem Shape funktioniert das auch. Sobald ich aber mehrere Shapes in meinen vector einfüge bekomme ich beim zeichnen des zweiten Shapes einen Segfault beim aufruf von glDrawElements.

Hier mal mein bisheriger Code. Die Window Klasse besteht zum größten Teil aus Initialisierungen. Wirklich relevant sind nur die Methoden "sendDataToOpenGL" und "mainLoop"
Window.h

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <vector>

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>

#include "ShaderUtils.h"
#include "Shape.h"
#include "ShapeGenerator.h"

class Window{
    GLFWwindow* glfwWindow;

    GLuint shaderProgram;

    std::vector<Shape*> shapes;

    GLFWwindow* createGLFWwindow(int width, int height, std::string title);
    void adjustViewport();
    void sendDataToOpenGL();
    void useShaders();
    void mainLoop();
    void quit();

public:
    Window(int width, int height, std::string title);
};


Window.cpp

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include "Window.h"

Window::Window(int width, int height, std::string title){

    if(glfwInit() != GL_TRUE){
        std::cout << "Failed to initialize GLFW!" << std::endl;
        glfwTerminate();
        //TODO quit execution
    }

    glfwWindow = createGLFWwindow(width, height, title);
    if(glfwWindow == NULL) {
        //TODO quit execution
    };

    glClearColor(0.0f, 0.0f, 0.1f, 1.0f);

    useShaders();
    sendDataToOpenGL();
    mainLoop();
    quit();
}

void Window::useShaders(){

    // create Shader
    GLuint vertexShader = ShaderUtils::loadShader("../res/shaders/VertexShader.vs", GL_VERTEX_SHADER);
    GLuint fragmentShader = ShaderUtils::loadShader("../res/shaders/FragmentShader.fs", GL_FRAGMENT_SHADER);

    shaderProgram = ShaderUtils::linkShaderProgram(vertexShader, fragmentShader, 0);

    glUseProgram(shaderProgram);

}

void Window::sendDataToOpenGL(){

    // Location of variable "position" (VertexShader) in VAO
    GLint positionLoc = glGetAttribLocation(shaderProgram, "position");
    GLint colorLoc = glGetAttribLocation(shaderProgram, "color");

    Shape* arrow = ShapeGenerator::createArrow(positionLoc, colorLoc);
    shapes.push_back(arrow);

    Shape* tri = ShapeGenerator::createTriangle(positionLoc, colorLoc);
    //shapes.push_back(tri);
}

void Window::mainLoop(){

    // main Loop
    while(!glfwWindowShouldClose(glfwWindow)){
        adjustViewport();
        glClear(GL_COLOR_BUFFER_BIT);

        for(auto itr = shapes.begin(); itr != shapes.end(); ++itr){
            (*itr)->draw();
        }

        glfwSwapBuffers(glfwWindow);
        glfwPollEvents();
    }

}

void Window::quit(){
    glfwDestroyWindow(glfwWindow);
    glfwTerminate();

    for(auto itr = shapes.begin(); itr != shapes.end(); ++itr){
        delete *itr;
    }
}

void Window::adjustViewport(){
    GLint height, width;
    glfwGetWindowSize(glfwWindow, &width, &height);
    glViewport(0, 0, width, height);
}

GLFWwindow* Window::createGLFWwindow(int width, int height, std::string title){
    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);

    GLFWwindow* window = glfwCreateWindow(width, height, title.c_str(), NULL, NULL);
    if(window == NULL){
        std::cout << "Window could not be created!" << std::endl;
        glfwTerminate();
        return NULL;
    }

    // Context creation...
    glfwMakeContextCurrent(window);

    // ...before glew initialisation ;)
    glewExperimental=GL_TRUE;
    GLenum err = glewInit();
    if(err != GLEW_OK){
        std::cout << "Failed to Init GLEW!" << std::endl;
        std::cout << glewGetErrorString(err) << std::endl;
        glfwTerminate();
        return NULL;
    }

    std::cout << glGetString(GL_VERSION) << std::endl;

    return window;
}


Shape.h

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#pragma once
#include <GL/glew.h>
#include <glm/glm.hpp>

struct Vertex{
    glm::vec3 position;
    glm::vec3 color;
};

class Shape{

    GLuint VBOID, EBOID, VAOID;
    GLuint numElements;
    GLint positionLoc, colorLoc;

    void generateVAO();
public:
    Shape();
    Shape(GLuint pVBOID, GLuint pEBOID, GLuint pNumElements, GLint pPositionLoc, GLint pColorLoc);
    ~Shape();

    void draw();
};


Shape.cpp

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "Shape.h"
#include "Window.h"
#include <iostream>

Shape::Shape(GLuint pVBOID, GLuint pEBOID, GLuint pNumElements, GLint pPositionLoc, GLint pColorLoc): VBOID(pVBOID), EBOID(pEBOID), numElements(pNumElements), positionLoc(pPositionLoc), colorLoc(pColorLoc){
    generateVAO();
}

void Shape::generateVAO(){
    glGenVertexArrays(1, &VAOID);
    glBindVertexArray(VBOID);

    // Location of variable "position" (VertexShader) in VAO
    glEnableVertexAttribArray(positionLoc);
    glEnableVertexAttribArray(colorLoc);
    glBindBuffer(GL_ARRAY_BUFFER, VBOID);

    // Variable Location, Elements per Vertex, Datatype, normalized, stride, startposition
    glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat), 0);
    glVertexAttribPointer(colorLoc, 3, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat), (void*)(3 * sizeof(float)));
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindVertexArray(0);
}

Shape::~Shape(){
    glDeleteBuffers(1, &VBOID);
    glDeleteBuffers(1, &EBOID);
    glDeleteBuffers(1, &VAOID);
}

void Shape::draw(){

    std::cout << "1" << std::endl;

    glBindVertexArray(VBOID);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOID);

    std::cout << "2" << std::endl;

    glDrawElements(GL_TRIANGLES, 3 * numElements, GL_UNSIGNED_SHORT, NULL);

    std::cout << "3" << std::endl;

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}


ShapeGenerator.h

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
#pragma once
#include <GL/glew.h>
#include "Shape.h"

class ShapeGenerator{

public:
    static Shape* createTriangle(GLint positionLoc, GLint colorLoc);
    static Shape* createArrow(GLint positionLoc, GLint colorLoc);

};


ShapeGenerator.cpp

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include "ShapeGenerator.h"

Shape* ShapeGenerator::createTriangle(GLint positionLoc, GLint colorLoc){
    GLuint VBOID, EBOID;

    glGenBuffers(1, &VBOID);
    glGenBuffers(1, &EBOID);

    Vertex vertecies[] = {
        glm::vec3(+0.00f, +0.75f, +0.00f),
        glm::vec3(+1.0f, +0.0f, +0.0f),
        glm::vec3(-0.75f, -0.75f, +0.00f),
        glm::vec3(+0.0f, +1.0f, +0.0f),
        glm::vec3(+0.75f, -0.75f, +0.00f),
        glm::vec3(+0.0f, +0.0f, +1.0f),
    };

    GLushort indexdata[] = {
        0, 1, 2
    };

    glBindBuffer(GL_ARRAY_BUFFER, VBOID);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertecies), vertecies, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOID);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexdata), indexdata, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    return new Shape(VBOID, EBOID, 1, positionLoc, colorLoc);
}

Shape* ShapeGenerator::createArrow(GLint positionLoc, GLint colorLoc){
    GLuint VBOID, EBOID;

    glGenBuffers(1, &VBOID);
    Vertex vertecies[] = {
        glm::vec3(+0.00f, +0.75f, +0.0f),   // 0
        glm::vec3(+0.0f, +0.0f, +1.0f),
        glm::vec3(-0.50f, +0.25f, +0.0f),   // 1
        glm::vec3(+0.5f, +0.0f, +0.5f),
        glm::vec3(-0.25f, +0.25f, +0.0f),   // 2
        glm::vec3(+0.5f, +0.0f, +0.5f),
        glm::vec3(-0.25f, -0.50f, +0.0f),   // 3
        glm::vec3(+1.0f, +0.0f, +0.0f),
        glm::vec3(+0.25f, -0.50f, +0.0f),   // 4
        glm::vec3(+1.0f, +0.0f, +0.0f),
        glm::vec3(+0.25f, +0.25f, +0.0f),   // 5
        glm::vec3(+0.5f, +0.0f, +0.5f),
        glm::vec3(+0.50f, +0.25f, +0.0f),   // 6
        glm::vec3(+0.5f, +0.0f, +0.5f),
    };

    glBindBuffer(GL_ARRAY_BUFFER, VBOID);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertecies), vertecies, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    GLushort indexdata[] = {
        0, 1, 2,
        0, 2, 5,
        0, 5, 6,
        2, 3, 5,
        3, 4, 5
    };

    glGenBuffers(1, &EBOID);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOID);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexdata), indexdata, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    Shape* ret = new Shape(VBOID, EBOID, 5, positionLoc, colorLoc);
    return ret;
}


Auf dem Weg bis hierher hatte ich schon öfters Probleme. Bisher habe ich aber immer alles in den Griff bekommen. Diesmal bin ich wirklich mit meinem Latein am Ende. Ist mein ganzer Ansatz schon Mist? Und wenn nicht, woher kommt der Fehler?

Gruß Gnoccy

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

2

09.07.2014, 18:15

C-/C++-Quelltext

1
glDrawElements(GL_TRIANGLES, 3 * numElements, GL_UNSIGNED_SHORT, NULL);

Wieso 3 * numElements?
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

3

09.07.2014, 18:16

3 Vertecies pro Dreieck. numElements ist die ANzahl der Dreiecke.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

4

09.07.2014, 18:18

Ja und du zeichnest Dreiecke und nicht Vertices. Daher ist die 3 da falsch.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

5

09.07.2014, 18:36

Wenn ich da jetzt aber einfach das 3 * rausnehme, bekomme ich zwar keinen Segfault mehr, aber es wird auch nur noch das erste der 5 Dreiecke gezeichnet, aus denen der Pfeil besteht.
Sicher das der Parameter die Anzahl der Elemente und nicht der Vetrecies angibt? Wären es Vertecies würde sich das genau mit meinem Ergebnis decken. 5 Vertecies -> 1 + 2/3 Dreiecke. 2/3 Dreiecke können nicht gezeichnet werden, also ein Dreieck.
Und mal angenommen der Parameter würde doch die Anzahl der Dreiecke und nicht der Vertecies angeben, wieso bekomme ich den Segfault dann erst wenn ich versuche mehr als ein Element zu zeichnen? Dann müsste ich doch schon beim erstem Element aus dem Speicher laufen.

Edit:
Obwohl du laut Doc recht hast:
http://www.opengl.org/sdk/docs/man3/

Aber ich verstehs trotzdem nicht...
Vor allem, wieso bekomme ich dann jetzt nicht das gewünschte Ergebnis?

Edit2:
Ich bekomme immer noch einen Segfault. Ich hatte nur vergessen das Dreieck ein zu kommentieren.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Gnoccy« (09.07.2014, 18:45)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

6

09.07.2014, 18:48

Bist Du sicher, dass du für glBufferData einen Array übergeben darfst, der danach invalid wird, weil er beim Verlassen der Methode zerstört wird?
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

7

09.07.2014, 18:53

Die Sache mit dem "Count" hat mich auch schon häufig ziemlich verwirrt.
Die Doku verwendet den Begriff "Elements" was wirklich sehr uneindeutig ist. Ein Element ist aber eben kein Primitiv.
So gibt "Count" NICHT nicht die Anzahl der Primitiven an, sondern die Anzahl Indices oder Vertices(in der Regel und wenn man mehrfach indizierte auch mehrfach zählt). Besonders verwirrend ist auch, dass es unter DirectX, soweit ich mich erinnere, auch genau anders war.

8

09.07.2014, 18:53

Eigentlich schon. Ich lasse mich da natürlich gerne eines besseren belehren, aber so wie ich glBufferData verstehe wird der Inhalt des übergebenen Arrays kopiert und von OpenGL irgendwo in den Tiefen meiner Grafikkarte gespeichert. Außerdem gilt doch in C++ pass-by-value, also müsste sowieso eine Kopie angelegt werden.

Die Sache mit dem "Count" hat mich auch schon häufig ziemlich verwirrt.
Die Doku verwendet den Begriff "Elements" was wirklich sehr uneindeutig ist. Ein Element ist aber eben kein Primitiv.
So gibt "Count" NICHT nicht die Anzahl der Primitiven an, sondern die Anzahl Indices oder Vertices(in der Regel und wenn man mehrfach indizierte auch mehrfach zählt). Besonders verwirrend ist auch, dass es unter DirectX, soweit ich mich erinnere, auch genau anders war.


Also war mein erster Ansatz doch richtig? Bleibt die Frage, woher der Segfault kommt.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

9

09.07.2014, 19:04

Hast Du mal auf irgendwelche glErrors geprüft und ob überhaupt alle Buffer korrekt angelegt werden konnten?
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

10

09.07.2014, 19:11

Ja, * 3 könnte so passen, wenn "numElements" die Anzahl Primitiven ist, was dann aber nicht dem Verständnis der OpenGL Spec entspricht. Ich würde das mal entsprechend in "numPrimitives" oder "numIndices" umbennen, damit es schneller zu verstehen ist und keien Verwechselungen mehr auftreten.

Also ich habe mir den Code nochmal angesehen.
EBO ist übrigens eine komische Bezeichnung. Normalerweise nennt man es IBO(Index Buffer Obj).
Außerdem speichert ein VAO das aktuelle Index Buffer-Binding mit. Es wäre also zu empfehlen den Index Buffer nur bei der VAO Erstellung einmal daran zu binden. Ich würde dir dringend empfehlen die "glGenBuffers"Aufrufe in den Konstruktor zu schieben. RAII mäßig und außerdem dann symmetrisch zur Destruktion. Die Erzeugung der Shapes in den Methoden auf den Heap finde ich auch schlecht. Das wäre auf dem Stack super aufgehoben und außerdem ist es so unsicher und ein Speicherleck(damit auch in OpenGL) leicht möglich. Gib das Objekt einfach direkt zurück.

C-/C++-Quelltext

1
glBindVertexArray(VBOID);

Sieht falsch aus: Du bindest die VBO ID als VAO?

Werbeanzeige