Lazarus - OpenGL 3.3 Tutorial - Uniform Buffer Object (UBO) - Mehrer Shader und BindingPoint

Aus DGL Wiki
Wechseln zu: Navigation, Suche

Lazarus - OpenGL 3.3 Tutorial - Uniform Buffer Object (UBO) - Mehrer Shader und BindingPoint.png

Uniform Buffer Object (UBO) - Mehrer Shader und BindingPoint

Einleitung

In diesem Beispiel wird gezeigt, was der BindingPoint für einen Einfluss hat.
Es werden 3 Shader erzeugt, das es einfacher ist, habe ich 3mal die gleichen Shader-Sourcen genommen.
Bei 2 Shadern werden die UBO-Daten mit dem BindingPoint 0 verbunden, der einzelne Shader mit BindingPoint 1.


Es werden drei UNOs angelegt.
Die Uniform IDs werden füür jeden Shader einzeln ID gebraucht.
Daher habe ich es in einem Record zusammengefasst.

Man sieht auch, das 2 BindingPoints verwendet werden.

var
  UBO: record
    Rubin, Jade, Smaragdgruen: GLuint;        // Puffer-Zeiger
  end;

  ShaderData: array[0..2] of record
    Shader: TShader;
    Material_ID,
    ModelMatrix_ID,
    Matrix_ID: GLint;
  end;

  bindingPoint0: gluint = 0;
  bindingPoint1: gluint = 1;

Es werden 3 Shader geladen in die Uniform-IDs ausgelesen.

procedure TForm1.CreateScene;
var
  i: integer;
begin
  for i := 0 to 2 do begin
    with ShaderData[i] do begin
      Shader := TShader.Create([FileToStr('Vertexshader.glsl'), FileToStr('Fragmentshader.glsl')]);
      with Shader do begin
        UseProgram;
        Matrix_ID := UniformLocation('Matrix');
        ModelMatrix_ID := UniformLocation('ModelMatrix');

        Material_ID := UniformBlockIndex('Material'); // ID aus dem Shader holen.
      end;
    end;
  end;

Material-Daten in den UBO-Puffer laden und binden.

Man sieht, das beim Shader[2] ein anderer BindingPoint verwendet wird.

procedure TForm1.InitScene;
begin
  // Puffer für Rubin anlegen.
  with Material do begin
    ambient := vec3(0.17, 0.01, 0.01);
    diffuse := vec3(0.61, 0.04, 0.04);
    specular := vec3(0.73, 0.63, 0.63);
    shininess := 76.8;
  end;
  glBindBuffer(GL_UNIFORM_BUFFER, UBO.Rubin);
  glBufferData(GL_UNIFORM_BUFFER, sizeof(TMaterial), @Material, GL_DYNAMIC_DRAW);

  // Puffer für Jade anlegen.
  with Material do begin
    ambient := vec3(0.14, 0.22, 0.16);
    diffuse := vec3(0.54, 0.89, 0.63);
    specular := vec3(0.32, 0.32, 0.32);
    shininess := 12.8;
  end;
  glBindBuffer(GL_UNIFORM_BUFFER, UBO.Jade);
  glBufferData(GL_UNIFORM_BUFFER, sizeof(TMaterial), @Material, GL_DYNAMIC_DRAW);

  // Puffer für Smaragdgruen anlegen.
  with Material do begin
    ambient := vec3(0.02, 0.17, 0.02);
    diffuse := vec3(0.08, 0.81, 0.08);
    specular := vec3(0.63, 0.73, 0.63);
    shininess := 76.8;
  end;
  glBindBuffer(GL_UNIFORM_BUFFER, UBO.Smaragdgruen);
  glBufferData(GL_UNIFORM_BUFFER, sizeof(TMaterial), @Material, GL_DYNAMIC_DRAW);

  // Verbindung mit dem Shader aufbauen.
  with ShaderData[0] do begin
    glUniformBlockBinding(Shader.ID, Material_ID, bindingPoint0);
  end;

  with ShaderData[1] do begin
    glUniformBlockBinding(Shader.ID, Material_ID, bindingPoint0);
  end;

  with ShaderData[2] do begin
    glUniformBlockBinding(Shader.ID, Material_ID, bindingPoint1);
  end;

  // Die Puffer das erste mal binden.
  // Das sieht man, das der Shader[2] mit Jade gebunden wird.
  glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint0, UBO.Rubin);
  glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint1, UBO.Jade);

Die Scene wird drei mal mit unterschiedlichen Shadern gezeichnet.
Um die UBO muss man da sich nicht kümmern, das diese mit dem BindingPoint gebunden sind.

procedure TForm1.ogcDrawScene(Sender: TObject);
var
  scal, d: single;
begin
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);  // Frame und Tiefen-Buffer löschen.

  glEnable(GL_CULL_FACE);
  glCullface(GL_BACK);

  glBindVertexArray(VBCube.VAO);

  d := 6.0;
  scal := 10;

  // --- Zeichne Kugeln

  with ShaderData[0] do begin
    Shader.UseProgram;

    Matrix.Identity;
    Matrix.Translate(d, d, d);
    Matrix.Scale(scal);
    Matrix.Multiply(ModelMatrix, Matrix);

    Matrix.Uniform(ModelMatrix_ID);

    Matrix.Multiply(WorldMatrix, Matrix);
    Matrix.Multiply(FrustumMatrix, Matrix);

    Matrix.Uniform(Matrix_ID);
    glDrawArrays(GL_TRIANGLES, 0, Length(SphereVertex) * 3);
  end;

  // --- Zeichne Kugeln

  with ShaderData[1] do begin
    Shader.UseProgram;

    Matrix.Identity;
    Matrix.Translate(d + 30, d, d);
    Matrix.Scale(scal);
    Matrix.Multiply(ModelMatrix, Matrix);

    Matrix.Uniform(ModelMatrix_ID);

    Matrix.Multiply(WorldMatrix, Matrix);
    Matrix.Multiply(FrustumMatrix, Matrix);

    Matrix.Uniform(Matrix_ID);
    glDrawArrays(GL_TRIANGLES, 0, Length(SphereVertex) * 3);
  end;

  // --- Zeichne Kugeln

  with ShaderData[2] do begin
    Shader.UseProgram;

    Matrix.Identity;
    Matrix.Translate(d - 30, d, d);
    Matrix.Scale(scal);
    Matrix.Multiply(ModelMatrix, Matrix);

    Matrix.Uniform(ModelMatrix_ID);

    Matrix.Multiply(WorldMatrix, Matrix);
    Matrix.Multiply(FrustumMatrix, Matrix);

    Matrix.Uniform(Matrix_ID);
    glDrawArrays(GL_TRIANGLES, 0, Length(SphereVertex) * 3);
  end;

  ogc.SwapBuffers;
end;

Es wird nur der BindingPoint 0 geändert.
Somit sit man beim Shader[2] der mit BindingPoint 1 gebunden ist keine Änderung.

procedure TForm1.Timer2Timer(Sender: TObject);
const
  m: integer = 0;
begin
  case m of
    0: begin
      glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint0, UBO.Rubin);
    end;
    1: begin
      glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint0, UBO.Smaragdgruen);
    end;
  end;

  Inc(m);
  if m > 1 then begin
    m := 0;
  end;
end;



Der Shader ist der selbe wie im ersten Beispiel.

Vertex-Shader:

#version 330

layout (location = 0) in vec3 inPos;    // Vertex-Koordinaten
layout (location = 1) in vec3 inNormal; // Normale

// Daten für Fragment-shader
out Data {
  vec3 Pos;
  vec3 Normal;
} DataOut;

// Matrix des Modeles, ohne Frustum-Beeinflussung.
uniform mat4 ModelMatrix;

// Matrix für die Drehbewegung und Frustum.
uniform mat4 Matrix;

void main(void)
{
  gl_Position    = Matrix * vec4(inPos, 1.0);

  DataOut.Normal = mat3(ModelMatrix) * inNormal;
  DataOut.Pos    = (ModelMatrix * vec4(inPos, 1.0)).xyz;
}



Fragment-Shader

#version 330

// Licht
#define Lposition  vec3(35.0, 17.5, 35.0)
#define Lambient   vec3(1.8, 1.8, 1.8)
#define Ldiffuse   vec3(1.5, 1.5, 1.5)

// Daten vom Vertex-Shader
in Data {
  vec3 Pos;
  vec3 Normal;
} DataIn;

layout (std140) uniform Material {
  vec3  Mambient;   // Umgebungslicht
  vec3  Mdiffuse;   // Farbe
  vec3  Mspecular;  // Spiegelnd
  float Mshininess; // Glanz
};

out vec4 outColor;

vec3 Light(in vec3 p, in vec3 n) {
  vec3 nn = normalize(n);
  vec3 np = normalize(p);
  vec3 diffuse;   // Licht
  vec3 specular;  // Reflektion
  float angele = max(dot(nn, np), 0.0);
  if (angele > 0.0) {
    vec3 eye = normalize(np + vec3(0.0, 0.0, 1.0));
    specular = pow(max(dot(eye, nn), 0.0), Mshininess) * Mspecular;
    diffuse  = angele * Mdiffuse * Ldiffuse;
  } else {
    specular = vec3(0.0);
    diffuse  = vec3(0.0);
  }
  return (Mambient * Lambient) + diffuse + specular;
}

void main(void)
{
  outColor = vec4(Light(Lposition - DataIn.Pos, DataIn.Normal), 1.0);
}


Autor: Mathias

Siehe auch