【问题标题】:Modern OpenGL - Can attributes be made conditional in shader?现代 OpenGL - 属性可以在着色器中设置条件吗?
【发布时间】:2020-06-08 15:53:14
【问题描述】:

C#/OpenTK。

我一直无法找到一种在场景中组合不同几何类型的好方法。出于效率原因,我一直在尝试将相同的着色器和单个 VAO/VBO 用于混合原始类型(三角形、线条和可能的线条)。不确定这是否可能/可取。

我遇到的主要问题是三角形被实例化,并且它们在顶点着色器中的输入需要是顶点和法线。然而这些线没有被实例化,不需要光照,而是不同的颜色,所以它们需要输入到顶点着色器中需要顶点和颜色。所以在一种情况下我想要着色器中的法线,在另一种情况下我想要颜色。

一种解决方案是在着色器中设置此条件。我在顶点着色器的顶部有这个:

layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec3 vertexColor;

如果我尝试将此设置为有条件的(即位置 2 = 正常或颜色取决于原始类型),我会收到语法错误。并不特别惊讶,因为这些行是main() 之外的定义。我认为那里不允许使用 If 语句。

另一种解决方案是单独的着色器,但我希望尽可能避免重复。

目前我已将多边形和线条分成两个单独的缓冲区,因为这似乎合乎逻辑。我可以使用相同的着色器渲染它们吗(不添加填充的虚拟值,这可以使数据结构一致但效率不高)?理想情况下,我有办法告诉它我在一种情况下传递法线,在另一种情况下传递顶点。

还有一些我不理解的行为。我认为,根据我能找到的关于该主题的所有内容,VertexAttribPointer 中的 index 参数必须对应于着色器中的 location。但这不是它的行为方式。我有顶点数据,我给出的索引为 2(在下面的代码中看到 InitVertexBuffers 的末尾),它对应于着色器中的颜色 - 但它的几何图形被正确渲染。

所以我的问题是:

1) 是否可以将着色器中的layout 定义设为有条件的?

2) 我应该将不同原始类型的数据保存在不同的缓冲区中吗?

3) VertexAttribPointer 的第一个参数 (location) 是否没有告诉 OpenGL 如何处理每组数据?如果不是,这是如何确定的?

private void InitVertexBuffers()
{

    int floatSize = sizeof(float);

    VBO1 = GL.GenBuffer();
    VAO1 = GL.GenVertexArray();

    // Bind Vertex Array Object...
    GL.BindVertexArray(VAO1);

    // Copy our vertices array in a buffer for OpenGL to use...
    GL.BindBuffer(BufferTarget.ArrayBuffer, VBO1);

    List<int> array_lengths = new List<int>() {
        vertices.Length * floatSize };

    // Calculate and store offset pointers...
    List<int> array_offsets = new List<int>() {
        0 };

    // Set vertex attributes pointers...
    GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * floatSize, (IntPtr)0, BufferUsageHint.StaticDraw);
    GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr)0, array_lengths[0], vertices);

    // Sensor offset vertices...
    GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 6 * floatSize, 0);

    // Normals...
    GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 6 * floatSize, 3 * floatSize);

    GL.EnableVertexAttribArray(0);
    GL.EnableVertexAttribArray(1);

    // ---------------------------------------------------------------------------------------------------------------------------------------------------

    VBO2 = GL.GenBuffer();
    VAO2 = GL.GenVertexArray();
    GL.BindVertexArray(VAO2);
    GL.BindBuffer(BufferTarget.ArrayBuffer, VBO2);

    array_lengths = new List<int>() {
        line_vertices.Length * floatSize,
        axis_widget_vertices.Length * floatSize,
        };

    array_offsets = new List<int>() {
        0,
        array_lengths[0] };

    GL.BufferData(BufferTarget.ArrayBuffer, (line_vertices.Length + axis_widget_vertices.Length) * floatSize, (IntPtr)0, BufferUsageHint.StaticDraw);
    GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr)array_offsets[0], array_lengths[0], line_vertices);
    GL.BufferSubData(BufferTarget.ArrayBuffer, (IntPtr)array_offsets[1], array_lengths[1], axis_widget_vertices);

    // Individual lines...
    GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 6 * floatSize, array_offsets[0]);
    GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 6 * floatSize, array_offsets[0] + 3 * floatSize);
    GL.VertexAttribPointer(2, 3, VertexAttribPointerType.Float, false, 6 * floatSize, array_offsets[1]);
    GL.VertexAttribPointer(3, 3, VertexAttribPointerType.Float, false, 6 * floatSize, array_offsets[1] + 3 * floatSize);

    for (int i = 0; i < 4; i++) // zero to three
    {
        GL.EnableVertexAttribArray(i);
    }

    GL.Enable(EnableCap.DepthTest); // enable z buffer

}

private void Draw()
{

    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

    GL.BindVertexArray(VAO1);
    SetBool(Handle, "instanced", 1);
    GL.DrawArraysInstanced(PrimitiveType.Triangles, 0, vertices.Length / 6, allNodes.Count);
    SetBool(Handle, "instanced", 0);

    GL.BindVertexArray(VAO2);
    SetBool(Handle, "use_lighting", 0);
    GL.DrawArrays(PrimitiveType.Lines, 0, line_vertices.Length / 6);
    GL.DrawArrays(PrimitiveType.Lines, line_vertices.Length / 6, axis_widget_vertices.Length / 6);
    SetBool(Handle, "use_lighting", 1);

    glControl1.SwapBuffers();

}

string vertexShaderSource =
    "# version 330 core\n" +
    "uniform mat4 model;" +
    "uniform mat4 view;" +
    "uniform mat4 projection;" +
    "uniform bool instanced;" +
    $"uniform vec3 offsets[{ allNodes.Count }];" +
    $"uniform vec3 colors[{ allNodes.Count }];" +
    "layout(location = 0) in vec3 aPos;" +
    "layout(location = 1) in vec3 aNormal;" +
    "layout(location = 2) in vec3 vertexColor;" +
    "out vec3 color_out;" +
    "out vec3 FragPos;" +
    "out vec3 Normal;" +
    "void main()" +
    "{" +
    "vec3 offset = offsets[gl_InstanceID];" +
    "gl_Position = projection * view * model * vec4(aPos + offset, 1.0);" +
    "FragPos = vec3(model * vec4(aPos + offset, 1.0));" +
    "Normal = vec3(model * vec4(aNormal, 1.0));" + // (normal has to be rotated the same as the model)
    "if (instanced) {" +
    "color_out = colors[gl_InstanceID];" +
    "} else {" +
    "color_out = vec3(1.0,1.0,1.0);" +
    "}" +
    "}";

【问题讨论】:

  • "如果可能,我希望避免重复。" 目前尚不清楚在这种情况下这意味着什么。如果一种渲染机制使用提供的颜色,而另一种使用固定的、预编译的,则这些是具有不同实现的不同着色器。对于法线来说更是如此,因为它们涉及光照,这意味着光照参数通常作为制服传递,以及进行光照计算所需的着色器代码。对于这些非常不同的渲染环境,究竟会有多少“重复”?
  • 我主要指的是重复代码。那么单独的着色器将是要走的路吗?
  • 再看一遍,我认为即使是这两种情况的着色器代码也不会有那么多重叠

标签: c# opengl glsl opentk vertex-shader


【解决方案1】:

我将在这里主要回答我自己的问题。感谢 Nicol Bolas 的评论让我认为单独的着色器是合适的。我已经实现了这个想法,它似乎是最好的方法。

我现在有单独的多边形和线数据着色器。两对着色器在开始时都被初始化,对GL.UseProgram() 的单个调用在它们之间切换。这使我能够大大简化渲染线条的着色器。

我认为(?)我在理解VertexAttribPointer() 索引号时遇到的问题是因为我对其进行的第三次和第四次调用实际上是多余的。由于数据在缓冲区中是连续的,并且VertexAttribPointer() 只是指向该数据的开头,因此前两个调用足以分别定义顶点和颜色数据的位置。然后,对GL.DrawArrays() 的调用准确地确定了要使用的数据块。

部分修改代码:

public void Render() {

    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

    GL.UseProgram(Handle); // shaders for polygons
    GL.BindVertexArray(VAO1);
    SetBool(Handle, "instanced", 1);
    GL.DrawArraysInstanced(PrimitiveType.Triangles, 0, vertices.Length / 6, allNodes.Count);
    SetBool(Handle, "instanced", 0);

    GL.BindVertexArray(VAO2);
    GL.UseProgram(Handle2); // shaders for lines

    GL.DrawArrays(PrimitiveType.Lines, 0, line_vertices.Length / 6);

    GL.DrawArrays(PrimitiveType.Lines, line_vertices.Length / 6, axis_widget_vertices.Length / 6);

    glControl1.SwapBuffers();
}

string vertexShaderSource = // for polygons
    "# version 330 core\n" +
    "uniform mat4 model;" +
    "uniform mat4 view;" +
    "uniform mat4 projection;" +
    "uniform bool instanced;" +
    $"uniform vec3 offsets[{ allNodes.Count }];" +
    $"uniform vec3 colors[{ allNodes.Count }];" +
    "layout(location = 0) in vec3 aPos;" +
    "layout(location = 1) in vec3 aNormal;" +
    "out vec3 color_out;" +
    "out vec3 FragPos;" +
    "out vec3 Normal;" +
    "void main()" +
    "{" +
    "vec3 offset = offsets[gl_InstanceID];" +
    "gl_Position = projection * view * model * vec4(aPos + offset, 1.0);" +
    "FragPos = vec3(model * vec4(aPos + offset, 1.0));" +
    "Normal = vec3(model * vec4(aNormal, 1.0));" + // (normal has to be rotated the same as the model)
    "if (instanced) {" +
    "color_out = colors[gl_InstanceID];" +
    "} else {" +
    "color_out = vec3(1.0,1.0,1.0);" +
    "}" +
    "}";

string vertexShader2 = // for lines
    "# version 330 core\n" +
    "uniform mat4 model;" +
    "uniform mat4 view;" +
    "uniform mat4 projection;" +
    "layout(location = 0) in vec3 aPos;" +
    "layout(location = 1) in vec3 vertexColor;" +
    "out vec3 color_out;" +
    "out vec3 FragPos;" +
    "void main()" +
    "{" +
    "gl_Position = projection * view * model * vec4(aPos, 1.0);" +
    "FragPos = vec3(model * vec4(aPos, 1.0));" +
    "color_out = vertexColor;" +
    "}";

string fragmentShaderSource = // for polygons
    "#version 330 core\n" +
    "in vec3 FragPos;" +
    "in vec3 Normal;" +
    "in vec3 color_out;" +
    "out vec4 FragColor;" +
    "uniform vec3 objectColor;" +
    "uniform vec3 lightColor;" +
    "uniform vec3 lightPos;" +
    "uniform bool use_lighting;" +
    "void main()" +
    "{" +
    "if (use_lighting) {" +
    "float ambientStrength = 0.5;" +
    "vec3 ambient = ambientStrength * lightColor;" +
    "vec3 norm = Normal;" +
    "vec3 lightDir = normalize(lightPos - FragPos);" +
    "float diff = max(dot(norm, lightDir), 0.0);" +
    "vec3 diffuse = diff * lightColor;" +
    "vec3 result = (ambient + diffuse) * color_out;" + // color_out = object colour from vertex shader
    "FragColor = vec4(result, 1.0);" +
    "} else {" +
    "FragColor = vec4(color_out, 1.0);" +
    "}" +
    "}";

string fragmentShader2 = // for lines
    "#version 330 core\n" +
    "in vec3 FragPos;" +
    "in vec3 color_out;" +
    "out vec4 FragColor;" +
    "void main()" +
    "{" +
    "FragColor = vec4(color_out, 1.0);" +
    "}";

【讨论】:

    猜你喜欢
    • 2021-09-22
    • 2021-09-06
    • 1970-01-01
    • 1970-01-01
    • 2018-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多