В OpenGL есть ли способ получить список всех униформ и атрибутов, используемых шейдерной программой?
Я хотел бы получить список всех униформ и атрибутов, используемых программным объектом шейдера. glGetAttribLocation()
и glGetUniformLocation()
могут использоваться для сопоставления строки с местоположением, но мне бы очень понравился список строк без необходимости синтаксического анализа кода glsl.
Примечание. В OpenGL 2.0 glGetObjectParameteriv()
заменяется на glGetProgramiv()
. И перечисление GL_ACTIVE_UNIFORMS
и GL_ACTIVE_ATTRIBUTES
.
Ответы
Ответ 1
Переменные, разделяемые между обоими примерами:
GLint i;
GLint count;
GLint size; // size of the variable
GLenum type; // type of the variable (float, vec3 or mat4, etc)
const GLsizei bufSize = 16; // maximum name length
GLchar name[bufSize]; // variable name in GLSL
GLsizei length; // name length
Атрибуты
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &count);
printf("Active Attributes: %d\n", count);
for (i = 0; i < count; i++)
{
glGetActiveAttrib(program, (GLuint)i, bufSize, &length, &size, &type, name);
printf("Attribute #%d Type: %u Name: %s\n", i, type, name);
}
Обмундирование
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count);
printf("Active Uniforms: %d\n", count);
for (i = 0; i < count; i++)
{
glGetActiveUniform(program, (GLuint)i, bufSize, &length, &size, &type, name);
printf("Uniform #%d Type: %u Name: %s\n", i, type, name);
}
Документация/переменные OpenGL
Различные макросы, представляющие типы переменных, можно найти в
Docs. Например, GL_FLOAT
, GL_FLOAT_VEC3
, GL_FLOAT_MAT4
и т.д.
Ответ 2
Произошло изменение в том, как это делается в OpenGL. Поэтому дайте старый способ и новый способ.
Старый путь
Связанные шейдеры имеют концепцию ряда активных форм и активных атрибутов (входы вершинного шейдера). Это униформы/атрибуты, которые используются этим шейдером. Количество этих (а также немало других вещей) можно запросить с помощью glGetProgramiv:
GLint numActiveAttribs = 0;
GLint numActiveUniforms = 0;
glGetProgramiv(prog, GL_ACTIVE_ATTRIBUTES, &numActiveAttribs);
glGetProgramiv(prog, GL_ACTIVE_UNIFORMS, &numActiveUniforms);
Вы можете запросить активные единые блоки, преобразовать изменения обратной связи, атомные счетчики и т.д. таким образом.
Как только у вас будет количество активных атрибутов/униформ, вы можете начать запрашивать информацию о них. Чтобы получить информацию об атрибуте, вы используете glGetActiveAttrib
; для получения информации об униформе вы используете glGetActiveUniform
. В качестве примера, расширенного из вышеперечисленного:
GLint maxAttribNameLength = 0;
glGetProgramiv(prog, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttribNameLength);
std::vector<GLchar> nameData(maxAttribNameLength)
for(int attrib = 0; attrib < numActiveAttribs; ++attrib)
{
GLint arraySize = 0;
GLenum type = 0;
GLsizei actualLength = 0;
glGetActiveAttrib(prog, attrib, nameData.size(), &actualLength, &arraySize, &type, &nameData[0]);
std::string name((char*)&nameData[0], actualLength - 1);
}
Нечто подобное может быть сделано для униформы. Однако трюк GL_ACTIVE_UNIFORM_MAX_LENGTH
может быть ошибочным для некоторых драйверов. Поэтому я бы предложил следующее:
std::vector<GLchar> nameData(256);
for(int unif = 0; unif < numActiveUniforms; ++unif)
{
GLint arraySize = 0;
GLenum type = 0;
GLsizei actualLength = 0;
glGetActiveUniform(prog, unif, nameData.size(), &actualLength, &arraySize, &type, &nameData[0]);
std::string name((char*)&nameData[0], actualLength - 1);
}
Кроме того, для униформ существует glGetActiveUniforms
, который может запрашивать все длины имен для каждого равномерного все одновременно (а также все типы, размеры массивов, шаги и другие параметры).
Новый способ
Этот способ позволяет получить почти все сведения об активных переменных в успешно связанной программе (за исключением регулярных глобальных переменных). Расширение ARB_program_interface_query пока не широко доступно, но оно будет доступно.
Он начинается с вызова glGetProgramInterfaceiv
, чтобы запросить количество активных атрибутов/униформ. Или что еще вы можете пожелать.
GLint numActiveAttribs = 0;
GLint numActiveUniforms = 0;
glGetProgramInterfaceiv(prog, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &numActiveAttribs);
glGetProgramInterfaceiv(prog, GL_UNIFORM, GL_ACTIVE_RESOURCES, &numActiveUniforms);
Атрибуты - это только входы вершинного шейдера; GL_PROGRAM_INPUT
означает входы в первую программу в программном объекте.
Затем вы можете перебирать количество активных ресурсов, запрашивая информацию по каждому из них по очереди, glGetProgramResourceiv
и glGetProgramResourceName
:
std::vector<GLchar> nameData(256);
std::vector<GLenum> properties;
properties.push_back(GL_NAME_LENGTH);
properties.push_back(GL_TYPE);
properties.push_back(GL_ARRAY_SIZE);
std::vector<GLint> values(properties.size());
for(int attrib = 0; attrib < numActiveAttribs; ++attrib)
{
glGetProgramResourceiv(prog, GL_PROGRAM_INPUT, attrib, properties.size(),
&properties[0], values.size(), NULL, &values[0]);
nameData.resize(values[0]); //The length of the name.
glGetProgramResourceName(prog, GL_PROGRAM_INPUT, attrib, nameData.size(), NULL, &nameData[0]);
std::string name((char*)&nameData[0], nameData.size() - 1);
}
Точный же код будет работать для GL_UNIFORM
; просто замените numActiveAttribs
на numActiveUniforms
.
Ответ 3
Для тех, кто находит этот вопрос, который хочет сделать это в WebGL, здесь эквивалент WebGL:
var program = gl.createProgram();
// ...attach shaders, link...
var na = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
console.log(na, 'attributes');
for (var i = 0; i < na; ++i) {
var a = gl.getActiveAttrib(program, i);
console.log(i, a.size, a.type, a.name);
}
var nu = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
console.log(nu, 'uniforms');
for (var i = 0; i < nu; ++i) {
var u = gl.getActiveUniform(program, i);
console.log(i, u.size, u.type, u.name);
}