OpenGL - 2D Renderer

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
Brainfreeze
Beiträge: 21
Registriert: 20.06.2012, 20:14

OpenGL - 2D Renderer

Beitrag von Brainfreeze »

Hi,

ich möchte ein kleines 2D Spiel programmieren.
Mit Hilfe der rastertek.com Tutorials versuche ich das in OpenGL zu machen.

Im Moment soll erstmal nur ein einfarbiges Rechteck gerendert werden.
Aus irgend einem Grund wird mir aber nur ein schwarzes Fenster angezeigt.
Ich sitz schon seit Stunden dran, und finde einfach keinen Fehler.

Hier man die wichtigesten Renderer-Methoden.

Code: Alles auswählen

//erstellen einen leeren Vertexbuffer, wird beim Erstellen des Renderers ausgeführt
void Grafics::Renderer::CreateVertexBuffer()
{
    this->MakeCurrent();

    // Generate an ID for the vertex buffer.
    glGenBuffers(1, &this->VertexBufferId);
    glBindBuffer(GL_ARRAY_BUFFER, this->VertexBufferId);
    glBufferData(GL_ARRAY_BUFFER, Grafics::MaxVertices * sizeof(Grafics::VERTEX), NULL, GL_DYNAMIC_DRAW);

    glGenVertexArrays(1, &this->VertexArrayId);

    // Bind the vertex array object to store all the buffers and vertex attributes we create here.
    glBindVertexArray(this->VertexArrayId);

    glEnableVertexAttribArray(0);  // Vertex position.
    glEnableVertexAttribArray(1);  // Vertex color.
    glEnableVertexAttribArray(2);  // Vertex Texture.

    // Specify the location and format of the position portion of the vertex buffer.
    glBindBuffer(GL_ARRAY_BUFFER, this->VertexBufferId);
    glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Grafics::VERTEX), 0);

    // Specify the location and format of the color portion of the vertex buffer.
    glBindBuffer(GL_ARRAY_BUFFER, this->VertexBufferId);
    glVertexAttribPointer(1, 4, GL_FLOAT, false, sizeof(Grafics::VERTEX), (unsigned char*)NULL + (3 * sizeof(float)));

    // Specify the location and format of the texture portion of the vertex buffer.
    glBindBuffer(GL_ARRAY_BUFFER, this->VertexBufferId);
    glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(Grafics::VERTEX), (unsigned char*)NULL + (7 * sizeof(float)));
}

Code: Alles auswählen

//IndexBuffer anlegen
void Grafics::Renderer::CreateRectangleIndexBuffer()
{
    this->MakeCurrent();

    int indexcount = Grafics::MaxVertices / 4 * 6;

    GLushort *pIndices = new GLushort[indexcount];
    unsigned long n = 0;

    for (int i=0;i<indexcount;i+=6)
    {
        pIndices[i] = n;
        pIndices[i+1] = n+1;
        pIndices[i+2] = n+2;
        pIndices[i+3] = n+2;
        pIndices[i+4] = n+3;
        pIndices[i+5] = n;
        n += 4;
    }

    // Generate an ID for the index buffer.
    glGenBuffers(1, &this->RectangleIndexBufferId);

    // Bind the index buffer and load the index data into it.
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->RectangleIndexBufferId);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexcount * sizeof(GLushort), pIndices, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

Code: Alles auswählen

//Wird vor dem Rendern ausgeführt
bool Grafics::Renderer::BeforeRender()
{
    this->MakeCurrent();

    //ClearColor geändert?
    if (this->ChangeClearColor == true)
    {
        glClearColor(this->ClearColor.GetR(), this->ClearColor.GetG(), this->ClearColor.GetB(), this->ClearColor.GetAlpha());
        this->ChangeClearColor = false;
    }

    glViewport(0, 0, this->Width, this->Height); // Set the viewport size to fill the window  
    glClear(GL_COLOR_BUFFER_BIT); // Clear required buffers  

    return true;
}

Code: Alles auswählen

bool Grafics::Renderer::Render()
{
    if (this->VerticesInBuffer > 0)
    {
        //ggf. Indexbuffer erstellen
        if (this->RectangleIndexBufferId == 0)
        {
            this->CreateRectangleIndexBuffer();
        }

        if (this->CurrentShaderChanged == true && this->CurrentShader->Init == true)
        {
            glUseProgram(this->CurrentShader->GetShaderProgram());
            this->CurrentShaderChanged = false;
        }

        // Bind the vertex buffer and load the vertex (position and color) data into the vertex buffer.
        glBindBuffer(GL_ARRAY_BUFFER, this->VertexBufferId);
        glBufferData(GL_ARRAY_BUFFER, this->VerticesInBuffer * sizeof(Grafics::VERTEX), this->VertexData, GL_DYNAMIC_DRAW);
        glEnableClientState(GL_VERTEX_ARRAY);

        // Enable the two vertex array attributes.
        glEnableVertexAttribArray(0);  // Vertex position.
        glEnableVertexAttribArray(1);  // Vertex color.
        glDisableVertexAttribArray(2); // Vertex Texture

        //Rendern 
        glBindBuffer(GL_ARRAY_BUFFER, this->VertexBufferId);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->RectangleIndexBufferId);
        glBindVertexArray(this->VertexArrayId);

        // Render the vertex buffer using the index buffer.
        int indexcount = this->VerticesInBuffer / 4 * 6;
        glDrawElements(GL_TRIANGLES, indexcount, GL_UNSIGNED_SHORT, 0);
        
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(2);
    }

    this->VerticesInBuffer = 0;

    return true;
}

Code: Alles auswählen

bool Grafics::Renderer::AfterRender()
{
    this->Render();
    SwapBuffers(this->DeviceContext);
    return true;
}

Code: Alles auswählen

//Rechteck in den Buffer schreiben
void Grafics::Renderer::DrawRectangle(Grafics::Primitives::Rectangle* rect)
{
	//ggf. aktuellen Vertexbuffer rendern
	if (this->CurrentRendering == RENDERING_PRIMITVE)
	{
		if (this->VerticesInBuffer > 0 &&
			(this->VerticesInBuffer+4 > Grafics::MaxVertices
			|| this->CurrentPrimitiveTopology != PRIMITIVE_TRIANGLE))
		{
			this->Render();
		}
	}
	else
	{
		this->Render();
	}

	this->CurrentPrimitiveTopology = PRIMITIVE_TRIANGLE;
	this->CurrentRendering = RENDERING_PRIMITVE;

	unsigned int n = this->VerticesInBuffer;

	this->VertexData[n++] = *rect->GetVertex(0);
	this->VertexData[n++] = *rect->GetVertex(1);
	this->VertexData[n++] = *rect->GetVertex(2);
	this->VertexData[n++] = *rect->GetVertex(3);

	this->VerticesInBuffer += 4;
}

Code: Alles auswählen

//Erstellen des ShaderProgram
bool Grafics::Shaders::ColorShader::Initialize(Grafics::Renderer* renderer)
{
    this->Renderer = renderer;

    const char* vertexShaderBuffer;
    const char* fragmentShaderBuffer;
    int status;

    std::string vsfile = ShaderPath + this->VShaderFile;
    std::string psfile = ShaderPath + this->FShaderFile;

    vertexShaderBuffer = LoadShaderSourceFile(vsfile.c_str());
    if(!vertexShaderBuffer)
    {
        return false;
    }

    // Load the fragment shader source file into a text buffer.
    fragmentShaderBuffer = LoadShaderSourceFile(psfile.c_str());
    if(!fragmentShaderBuffer)
    {
        return false;
    }

    //MakeCurrent
    this->Renderer->MakeCurrent();

    // Create a vertex and fragment shader object.
    this->VertexShader = glCreateShader(GL_VERTEX_SHADER);
    this->FragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

    // Copy the shader source code strings into the vertex and fragment shader objects.
    glShaderSource(this->VertexShader, 1, &vertexShaderBuffer, NULL);
    glShaderSource(this->FragmentShader, 1, &fragmentShaderBuffer, NULL);

    // Release the vertex and fragment shader buffers.
    delete [] vertexShaderBuffer;
    vertexShaderBuffer = 0;
    
    delete [] fragmentShaderBuffer;
    fragmentShaderBuffer = 0;

    // Compile the shaders.
    glCompileShader(this->VertexShader);
    glGetShaderiv(this->VertexShader, GL_COMPILE_STATUS, &status);
    if(status != 1)
    {
        this->ThrowShaderError(this->VertexShader);
        return false;
    }

    // Check to see if the fragment shader compiled successfully.
    glCompileShader(this->FragmentShader);
    glGetShaderiv(this->FragmentShader, GL_COMPILE_STATUS, &status);
    if(status != 1)
    {
        // If it did not compile then write the syntax error message out to a text file for review.
        this->ThrowShaderError(this->FragmentShader);
        return false;
    }

    // Create a shader program object.
    this->ShaderProgram = glCreateProgram();

    // Attach the vertex and fragment shader to the program object.
    glAttachShader(this->ShaderProgram, this->VertexShader);
    glAttachShader(this->ShaderProgram, this->FragmentShader);

    // Bind the shader input variables.
    glBindAttribLocation(this->ShaderProgram, 0, "inputPosition");
    glBindAttribLocation(this->ShaderProgram, 1, "inputColor");

    // Link the shader program.
    glLinkProgram(this->ShaderProgram);

    // Check the status of the link.
    glGetProgramiv(this->ShaderProgram, GL_LINK_STATUS, &status);
    if(status != 1)
    {
        // If it did not link then write the syntax error message out to a text file for review.
        this->ThrowLinkerError(this->ShaderProgram);
        return false;
    }

    this->Init = true;
    return true;
}

Code: Alles auswählen

//Vertex-Shader
#version 400

//Input Variables
in vec3 inputPosition;
in vec4 inputColor;

//Output Variables
out vec4 color;

void main()
{
gl_Position = vec4(inputPosition, 1.0f);
    color = inputColor;
}

Code: Alles auswählen

//Fragment-Shader
#version 400

//Input Variables
in vec4 inputColor;

//Output Variables
out vec4 outputColor;

void main()
{
    outputColor = inputColor;
}
Findet hier irgendjemand nen Fehler, oder hat ne Idee was ich sonst noch falschmachen könnte?

Gruß
Benutzeravatar
xq
Establishment
Beiträge: 1582
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: OpenGL - 2D Renderer

Beitrag von xq »

So auf die Schnelle sehen die Shader etwas gurke aus. in/out benutzt im Normalfall Namen, heißt so viel, wenn du im Vertex-Shader ein "out vec4 color;" hast, brauchst du im Pixel-Shader ein "in vec4 color;" und kein "in vec4 inputColor;"
Du bekommst momentan als inputColor im PixelShader vec4(0), also nur Nullen.
Richtig wäre der PS so:

Code: Alles auswählen

//Fragment-Shader
#version 400

//Input Variables
in vec4 color;

//Output Variables
out vec4 outputColor;

void main()
{
outputColor = color;
}
Was mir auch noch auffällt:

Code: Alles auswählen

glEnableVertexAttribArray(0); // Vertex position.
glEnableVertexAttribArray(1); // Vertex color.
glDisableVertexAttribArray(2); // Vertex Texture
// render 
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
Du hast doch aber deine VertexArrays schon in CreateVertexBuffer erstellt und aktiviert. Das Aktivieren/Deaktivieren benötigt viel Zeit und ich bin mir nicht sicher, ob dabei nicht deine vorherigen Einstellungen kaputt gehen. Wenn du verschiedene Vertex Arrays verwenden willst, nimm dazu glGenVertexArrays und glBindVertexArray. Du erstellst dir ein Vertex Array, befüllst es mit den richtigen Puffern. Wenn du jetzt das Vertex Array rendern willst, bindest du es und rufst glDraw* auf.
Ich verwende pro Mesh ein Vertex Array in meiner Engine, da ich damit nur einen einzigen Status-Change habe und damit eine bei weitem niedrigere Renderzeit.

Was ich dir zudem empfehlen würde:
Verwende layout-specifier für deine VS-Input-Variablen. Damit kannst du genau festlegen, in welchem Vertex Array deine Variablen stehen sollen.

Grüße
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Brainfreeze
Beiträge: 21
Registriert: 20.06.2012, 20:14

Re: OpenGL - 2D Renderer

Beitrag von Brainfreeze »

Hi,

danke für die Hinweise.

Du meinst also, für jedes ShaderProgramm soll ich ein eigenes VertexArray anlegen, und das dann einfach vor dem Rendern Binden?


Das mit den Shadern war wirklich das Problem!!
Super vielen dank, jetz kann ich endlich weitermachen ;)




Gruß
Benutzeravatar
xq
Establishment
Beiträge: 1582
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: OpenGL - 2D Renderer

Beitrag von xq »

Nein, für jedes Mesh/Quad. Im besten Fall hast du nacher pro Zeichnen eines Objektes drei Calls:
UseProgram (Shader aktivieren), BindVertexArray (VertexBuffer+IndexBuffer binden) und glDrawElements (Das eingestellte zeichnen).
Bei UseProgram gehört dann noch dazu, die Uniforms (Shadervariablen) richtig einzustellen,
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Antworten