我们究竟如何将属性名称绑定到OpenGL中的某个位置?

时间:2017-08-19 00:02:10

标签: c# opengl opentk

我正在使用OpenTK,它是.NET的包装器。使用的OpenGL版本是由NVIDIA实现的4.5。我使用的是Windows 10 Pro。

问题

我的问题很简单。我想通过名称来解决顶点属性,而不是在着色器源中对它们的位置进行硬编码。

我有一个名为 basicTexturedVert.glsl 的顶点着色器

#version 450 core

in vec4 position;
in vec2 normal;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;

out vec2 vs_uv;

void main(void)
{
    vs_uv = normal;
    gl_Position = projectionMatrix * viewMatrix * modelMatrix * position;
}

我尝试过的事情

现在要这样做,通常我必须在程序中使用属性的名称进行GL.GetAttribLocation,它将返回它的位置。好吧,我尝试了所有内容,但它只返回in vec4 position而不是in vec2 normal的位置。我的意思是:

  • 当我对两个属性的位置进行硬编码时,GL.GetAttribLocation("position")始终返回正确的位置,但normal的相同内容会返回-1
  • 我认为它与normal的名称有关,也许它是OpenGL的保留字,所以我把它改为像abcdef这样的随机词仍然给出了相同的结果。
  • 现在我想也许它与着色器源中着色器属性的声明顺序有关,所以我在normal之前移动position,结果仍然相同。
  • 现在我正在疯狂地试图弄清楚为什么OpenGL总是为position提供正确的位置。我想也许vec2(这是两者之间唯一的区分)不是一种可接受的类型,我在线查看,该死的很好被接受。

正如你所看到的,在尝试这个之前我尝试了很多东西。我读到您可以以编程方式将属性绑定到名称并指定要选择的位置。这就是我在下面的代码中所做的。

首先,我创建我的Shader对象:

var basicTexturedVertexShader = new Shader("Basic Textured Vertex Shader",
                ShaderType.VertexShader,
                File.ReadAllText(@"Components\Shaders\Vertex\basicTexturedVert.glsl"),
                new[] { "position", "normal" }
                );
var basicTexturedFragmentShader = new Shader("Basic Textured Fragment Shader",
                ShaderType.FragmentShader,
                File.ReadAllText(@"Components\Shaders\Fragment\basicTexturedFrag.glsl")
                );

如您所见,每个着色器都被分配:   - 一个名称,以便我可以理解我正在处理哪个着色器(在调试期间)   - 着色器的类型(VertexShaderFragmentShader)   - 着色器源代码   - 并且可选地包含着色器属性名称的数组,例如在程序链接期间将分配给位置的第一个new[] { "position", "normal" }

然后我创建一个程序并将它们链接到它:

_texturedProgram = new ShaderProgram(basicTexturedVertexShader, basicTexturedFragmentShader);
_texturedProgram.Link();

现在位于_texturedProgram.Link

int location = 0; // This is a location index that starts from 0 then goes up
        foreach (var shader in _shaders) {
            DebugUtil.Info($"Attaching shader {shader.Name} of handle {shader.Handle} of type {shader.Type} to program {_handle}");
            GL.AttachShader(_handle, shader.Handle);
            // If the shader we attached has attribute names with it
            // It means we need to give them a location
            if (shader.AttributeNames != null)
            {
                foreach (var shaderAttributeName in shader.AttributeNames)
                {
                    _attributeLocation[shaderAttributeName] = location;
                    GL.BindAttribLocation(_handle, location, shaderAttributeName);

                    // We check if anything wrong happened and output it
                    ErrorCode error;
                    bool errorHappened = false;
                    while ((error = GL.GetError()) != ErrorCode.NoError) {
                        DebugUtil.Warning($"Problem during binding attrib location of {shaderAttributeName} of {shader.Name} to {location} in program {_handle}. Error: {error}");
                        errorHappened = true;
                    }
                    if (!errorHappened) {
                        DebugUtil.Info($"Shader attribute \"{shaderAttributeName}\" of {shader.Name} of program {Handle} SHOULD HAVE BEEN bound to location {location}");
                    }
                    location++;
                }
             }
          }

          // We link the program
          GL.LinkProgram(_handle);

          // Make sure the linking happened with no problem
          var info = GL.GetProgramInfoLog(_handle);
          if (!string.IsNullOrWhiteSpace(info)) {
             DebugUtil.Warning($"Info log during linking of shaders to program {_handle}: {info}");
          }
          else {
             DebugUtil.Info($"Program {_handle} linked successfully");
          }

          // We compare the locations we think have been assigned to the vertex attributes
          // to the one that are actually stored in OpenGL
          foreach (var attribute in _attributeLocation) {
                DebugUtil.Info($"[Program:{_handle}] According to OpenGL, {attribute.Key} is located in {GL.GetAttribLocation(_handle, attribute.Key)} when it is supposed to be in {attribute.Value}");
          }

          // We clean up :)
          foreach (var shader in _shaders) {
              GL.DetachShader(_handle, shader.Handle);
              GL.DeleteShader(shader.Handle);
          }

          // No need for the shaders anymore
          _shaders.Clear();

这是控制台输出:

ConsoleOutput with location=0

让我们说position的默认位置是0,这只是巧合。我们将location起始索引设置为5

ConsoleOutput with location=5

如您所见,我的代码适用于position但不适用于normal ...

1 个答案:

答案 0 :(得分:0)

看来,因为normal顶点属性导致后续阶段(=片段着色器)没有用,所以OpenGL通过去除未使用的变量来优化着色器程序。

感谢@ Ripi2指出这一点。