Perché glUseProgram viene chiamato ogni frame con glUniform?

Sep 27 2020

Sto seguendo un tutorial OpenGL v3.3 che mi istruisce a modificare un attributo uniforme in uno shader di frammenti usando glUniform4f (fare riferimento al codice sotto). Per quanto ho capito, OpenGL è una macchina a stati, non svincoliamo l'attuale shaderProgram in uso, piuttosto modifichiamo un attributo in uno degli shader collegati al programma, quindi perché dobbiamo chiamare glUseProgram su ogni frame?

Capisco che questo non sia il caso delle versioni successive di OpenGL, ma mi piacerebbe comunque capire perché è il caso della v3.3

Programma OpenGL:

while (!glfwWindowShouldClose(window))
{
    processInput(window);

    glClearColor(0.2f, 1.0f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);


    glUseProgram(shaderProgram); // the function in question

    float redValue = (sin(glfwGetTime()) / 2.0f) + 0.5f;
    int colorUniformLocation = glGetUniformLocation(shaderProgram, "ourColor");
    glUniform4f(colorUniformLocation, redValue, 0.0f, 0.0f, 1.0f);

    std::cout << colorUniformLocation << std::endl;

    glBindVertexArray(VAO[0]);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glBindVertexArray(VAO[1]);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glfwSwapBuffers(window);
    glfwPollEvents();
}

Fragment Shader

#version 330 core
out vec4 FragColor;
uniform vec4 ourColor;
void main() 
{
 FragColor = ourColor;
}

Modifica: ho dimenticato di sottolineare che glUniform4f imposta un nuovo colore (in modo periodico) ogni fotogramma, l'output finale del codice sono 2 triangoli con colore animato, rimuovendo glUseProgram dal ciclo while mentre risulta in un'immagine statica, che non è è l'obiettivo prefissato del codice.

Risposte

4 jcoder Sep 27 2020 at 21:40

Nel tuo caso probabilmente non devi impostare ogni fotogramma. Tuttavia, in un programma più grande utilizzerai più shader, quindi dovrai impostare quello che desideri prima di usarlo ogni volta, e probabilmente i campioni sono scritti solo per farlo.

2 NicolBolas Sep 27 2020 at 22:31

Le variabili globali mutabili (che è effettivamente lo stato con OpenGL) sono intrinsecamente pericolose. Uno dei pericoli più importanti delle variabili globali è fare ipotesi sul loro stato attuale che si rivelano sbagliate. Questi tipi di errori rendono incredibilmente difficile capire se un pezzo di codice funzionerà correttamente o meno, poiché il suo comportamento dipende da qualcosa di esterno. Qualcosa che viene assunto sulla natura del mondo piuttosto che definito dalla funzione che lo aspetta.

Il tuo codice vuole emettere due comandi di disegno che usano uno shader particolare. Associando quello shader al punto di utilizzo, questo codice non è vincolato ad alcuna ipotesi sullo shader corrente. Non importa quale fosse lo shader precedente quando avvii il ciclo; lo stai impostando su ciò che deve essere .

Ciò rende questo codice isolato da eventuali modifiche successive che potresti apportare. Se vuoi renderizzare una terza cosa che usa uno shader diverso, il tuo codice continua a funzionare: reimposti lo shader all'inizio di ogni ciclo. Se avessi impostato lo shader solo al di fuori del loop e non lo avessi reimpostato ogni volta, il tuo codice verrebbe interrotto da eventuali modifiche successive dello shader.

Sì, in un minuscolo programma di giocattoli come questo, è probabilmente un problema facile da rintracciare e risolvere. Ma quando hai a che fare con codice che si estende su centinaia di file con decine di migliaia di righe di codice, con relazioni di dipendenza sparse tra librerie di terze parti, tutto ciò che potrebbe modificare un particolare stato OpenGL? Sì, probabilmente è meglio non dare troppo per scontato sulla natura del mondo.

Imparare presto buone abitudini è una buona cosa.

Ora per essere onesti, ri-specificare un gruppo di stati OpenGL in ogni punto del programma è anche una cattiva idea. Fare ipotesi / aspettative sulla natura del contesto OpenGL come parte di una funzione non è un male a priori. Se si dispone di una funzione di rendering per una mesh, è giusto che quella funzione presuma che l'utente abbia associato lo shader che intende utilizzare. Non è compito di questa funzione specificare tutti gli altri stati che devono essere specificati per il rendering. E in effetti, sarebbe una cattiva classe / funzione mesh se lo facesse, poiché non saresti in grado di rendere la stessa mesh con uno stato diverso.

Ma all'inizio di ogni fotogramma, o all'inizio di ogni parte principale del processo di rendering, la specifica di una linea di base dello stato OpenGL è perfettamente valida. Quando si torna all'inizio di un nuovo frame, in pratica non si dovrebbe dare per scontato nulla sullo stato di OpenGL. Non perché OpenGL non si ricorderà, ma perché potresti sbagliarti .

A.Khaled Sep 27 2020 at 23:30

Come hanno dimostrato le risposte ei commenti, nell'esempio indicato nella mia domanda, glUseProgram può essere scritto solo una volta al di fuori del ciclo while per produrre l'output desiderato, che è 2 triangoli con colori che si animano periodicamente. L'incomprensione che ho avuto è il risultato del capitolo seguente nell'e-book learnopengl.comhttps://learnopengl.com/Getting-started/Shaders dove afferma:

"l'aggiornamento di un'uniforme richiede di utilizzare prima il programma (chiamando glUseProgram), perché imposta l'uniforme sul programma shader attualmente attivo."

Pensavo che ogni volta che volevo aggiornare l'uniforme tramite glUniform * dovevo anche inviare una chiamata a glUseProgram che è una comprensione errata.