When you compile a shader program and then link it you need to track where in the program all those uniforms and vertex attributes have been put. I do this by querying GL and looping over the number of uniforms/vertex attributes returned.
compileshader (s,l, glslProgramContext,glslVertexShader, GL_VERTEX_SHADER,VS_Compile_error,loadOK);
..
compileshader (s,l, glslProgramContext,glslGeometryShader, GL_GEOMETRY_SHADER,GS_Compile_error,loadOK);
..
glLinkProgram (glslProgramContext);
glGetProgramiv (glslProgramContext, GL_LINK_STATUS, @loadOK);
glgetProgramiv (glslProgramContext, GL_ACTIVE_UNIFORMS , @NumActiveUniforms);
glgetProgramiv (glslProgramContext, GL_ACTIVE_UNIFORM_MAX_LENGTH , @UniformBuffsize);
glgetProgramiv (glslProgramContext, GL_ACTIVE_ATTRIBUTES, @NumActiveAttributes);
glgetProgramiv (glslProgramContext, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH , @AttributeBuffsize);
glgetProgramiv (glslProgramContext, GL_ACTIVE_UNIFORM_BLOCKS, @NumActiveUniformsBlocks);
glgetProgramiv (glslProgramContext, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH , @UniformBlockBuffsize);
setlength (ActiveUniformList.Uniform, NumActiveUniforms);
setlength (ActiveUniformList.Attribute, NumActiveAttributes);
setlength (ActiveUniformList.UniformBlock, NumActiveUniformsBlocks);
//loop through list of shader attributes, and update the index reference in our attribute list to match.
for i := 0 to NumActiveAttributes -1 do
begin
glGetActiveAttrib (glslProgramContext,i, AttributeBuffsize, nil, @_size, @_type, @error[0]);
ActiveUniformList.Attribute[i].name := ansistring (error); //record the list of attributes returned by the compiler.
ActiveUniformList.Attribute[i].valueTypes := _type;
ActiveUniformList.Attribute[i].ValueTypeAsString := GLUniformTypeToString (_type);
ActiveUniformList.Attribute[i].Location := glGetAttribLocation (glslProgramContext, error);
for j := low(VertexAttributes) to high (VertexAttributes) do
begin
if VertexAttributes[j].index <> -2 then continue; //-2 = auto bind.
if NOT (string (error) = VertexAttributes[j].attribname) then continue;
VertexAttributes[j].index := glGetAttribLocation (glslProgramContext, pglchar (VertexAttributes[j].attribname) );
end;
end;
//loop through list of shader uniforms
for i := 0 to NumActiveUniforms -1 do
begin
//legacy API - default uniform block
glGetActiveUniform (glslProgramContext,i, UniformBuffsize, nil, @_size, @_type, @error[0]);
ActiveUniformList.Uniform[i].name := ansistring (error); //record the list of uniforms returned by the compiler.
ActiveUniformList.Uniform[i].valueTypes := _type;
ActiveUniformList.Uniform[i].ValueTypeAsString := GLUniformTypeToString (_type);
ActiveUniformList.Uniform[i].Location := glGetUniformLocation(glslProgramContext, error);
//uniform blocks - handle separately....
ActiveUniformList.Uniform[i].Indicies := -1;
ActiveUniformList.Uniform[i].GL_UNIFORM_BLOCK_INDEX := -1;
ActiveUniformList.Uniform[i].GL_UNIFORM_OFFSET := 0;
if ActiveUniformList.Uniform[i].Location > -1 then
begin
uniformlist := pglchar(ActiveUniformList.Uniform[i].name);
glGetUniformIndices(glslProgramContext, 1, @uniformlist, @ActiveUniformList.Uniform[i].Indicies);
if ActiveUniformList.Uniform[i].Indicies > -1 then //block uniform found
begin
glGetActiveUniformsiv (glslProgramContext, 1, @ActiveUniformList.Uniform[i].Indicies, GL_UNIFORM_TYPE, @ActiveUniformList.Uniform[i].GL_UNIFORM_TYPE);
glGetActiveUniformsiv (glslProgramContext, 1, @ActiveUniformList.Uniform[i].Indicies, GL_UNIFORM_SIZE, @ActiveUniformList.Uniform[i].GL_UNIFORM_SIZE);
glGetActiveUniformsiv (glslProgramContext, 1, @ActiveUniformList.Uniform[i].Indicies, GL_UNIFORM_NAME_LENGTH, @ActiveUniformList.Uniform[i].GL_UNIFORM_NAME_LENGTH);
glGetActiveUniformsiv (glslProgramContext, 1, @ActiveUniformList.Uniform[i].Indicies, GL_UNIFORM_BLOCK_INDEX, @ActiveUniformList.Uniform[i].GL_UNIFORM_BLOCK_INDEX);
glGetActiveUniformsiv (glslProgramContext, 1, @ActiveUniformList.Uniform[i].Indicies, GL_UNIFORM_OFFSET, @ActiveUniformList.Uniform[i].GL_UNIFORM_OFFSET);
glGetActiveUniformsiv (glslProgramContext, 1, @ActiveUniformList.Uniform[i].Indicies, GL_UNIFORM_ARRAY_STRIDE, @ActiveUniformList.Uniform[i].GL_UNIFORM_ARRAY_STRIDE);
glGetActiveUniformsiv (glslProgramContext, 1, @ActiveUniformList.Uniform[i].Indicies, GL_UNIFORM_MATRIX_STRIDE, @ActiveUniformList.Uniform[i].GL_UNIFORM_MATRIX_STRIDE);
glGetActiveUniformsiv (glslProgramContext, 1, @ActiveUniformList.Uniform[i].Indicies, GL_UNIFORM_IS_ROW_MAJOR, @ActiveUniformList.Uniform[i].GL_UNIFORM_IS_ROW_MAJOR);
addUniformBlock (index, i, UniformBlockBuffsize);
end;
end;
fillmemory ( @error[0], 4095, 0);
glGetProgramInfoLog(shaders[index].glslProgramContext, 4095, NIL, @error[0]);
The more modern OpenGL 3.3 way is to explicitly tell GL the bind locations of these resources. The choice is yours and I have no need to alter what I have since it works reasonably well.