Определение нескольких структур и установка их в качестве юниформ в фрагментном шейдере GLSL

Я пытаюсь создать структуру материала и структуру света, которые я буду использовать в шейдере фрагментов GLSL. Я могу правильно отобразить сцену, которая у меня есть, когда у меня определена только одна структура (материальная или световая), но когда я определяю их обе вместе, glGetUniformLocation(...) вызывает return -1 для структур материала (порядок определений не имеет значения ) как будто их нет или они не используются. Мне нужно использовать структуры для будущего использования, пожалуйста, не просите меня использовать POD. Я хочу научиться загружать несколько структур как униформы. Спасибо.

Вот вершинный шейдер:

#version 450

layout (location = 0) in vec3 pos;
layout (location = 1) in vec3 norm;

layout (location = 2) out vec3 v_space_norm;
layout (location = 3) out vec3 v_space_pos;

layout(location = 0) uniform mat4 to_screen_space; // mvp
layout(location = 1) uniform mat4 to_view_space; //mv
layout(location = 2) uniform mat3 normals_to_view_space;
//layout(location = 4) float light_intensity;

void main() {
gl_Position = to_screen_space \* vec4(pos, 1.0);
v_space_norm = normals_to_view_space \* norm;
v_space_pos = (to_view_space \* vec4(pos, 1.0)).xyz;
}

и фрагментный шейдер:

#version 450
precision mediump float;
//------------ Structs ------------
// struct Light{
//      vec3 position;
//      float intensity;
// };

//------------ Variying ------------
layout (location = 2) in vec3 v_space_norm;
layout (location = 3) in vec3 v_space_pos;

//------------ Uniforms ------------
layout(location = 1) uniform mat4 to_view_space; //mv
//layout(location = 3) uniform vec3 light_position;

//layout(location = 4) uniform float light_intensity;
layout(location = 3) uniform struct{
     vec3 position;
     float intensity;
}light;

layout(location = 6) uniform struct{
     vec3 ka;
     vec3 kd;
     vec3 ks;
     float shininess;
} material;


out vec4 color;

void main() {
     vec3 v_space_norm = normalize(v_space_norm);
     vec3 l =  normalize( (to_view_space * vec4(light.position, 1)).xyz - v_space_pos);//normalize(l); //light vector
     vec3 h = normalize(l + vec3(0,0,1)); //half vector

     float cos_theta = dot(l, v_space_norm);
     if (cos_theta >= 0)
     {
          vec3 diffuse = material.kd * max(cos_theta,0);
          vec3 ambient = material.ka;
          vec3 specular= material.ks * pow(max(dot(h, v_space_norm),0), material.shininess);
          color = vec4(light.intensity * (specular + diffuse) + ambient, 1);
     }
     else
     {
          color = vec4(material.ka,1);
     }
}

И как я устанавливаю униформу:

...
program->SetUniform("light.position", light.position);
program->SetUniform("light.intensity", light.intensity);
program->SetUniform("material.ka", material.ambient);
program->SetUniform("material.kd", material.diffuse);
program->SetUniform("material.ks", material.specular);
program->SetUniform("material.shininess", material.shininess);
...

Определение SetUniform:

GLint location = glGetUniformLocation(glID, name);
if (location == -1)
{
    std::cout << "ERROR::SHADER::UNIFORM::" << name << "::NOT_FOUND"<<std::endl;
    return;
}
glUniform1f(location, value);//or what ever the type is there are definitions for all types!!!

Я не смог найти ничего в Интернете о моей проблеме

Я надеялся увидеть чайник отрендеренным, а блинн заштрихованным. И я могу получить это, если я определяю только одну структуру, а затем загружаю другие свойства как POD.

Итак, используя фрагментный шейдер ниже:

//------------ Uniforms ------------
layout(location = 1) uniform mat4 to_view_space; //mv
layout(location = 3) uniform vec3 light_position;

layout(location = 4) uniform float light_intensity;
// layout(location = 3) uniform struct{
//      vec3 position;
//      float intensity;
// }light;
...

//using light_position and light_intensity instead of light.position, light.intensity

Конечно, вызовы setUniform также изменяются соответствующим образом. введите здесь описание изображения

Но если я использую обе структуры, я получаю следующее: введите здесь описание изображения

Обновлено: вместо доступа через glGetUniformLocation(glID, name); Я могу вручную установить каждую переменную через номер расположения макета. Теперь возникает вопрос: «Могу ли я установить структуры, используя имена строковых переменных?»

Пожалуйста, выложите само сообщение об ошибке, а не изображение ошибки.

Nicol Bolas 06.02.2023 04:46

Нет сообщения об ошибке. Шейдер успешно компилируется, поэтому чайник заштрихован черным цветом. Единственное, что квалифицируется как ошибка, это GLint location = glGetUniformLocation(glID, name); возвращая -1, что заставляет меня поверить, что шейдер оптимизирует униформу, которую я использую.

Alper Şahıstan 06.02.2023 06:18

Вы можете попытаться получить каждую форму после компиляции шейдера и распечатать соответствующие идентификаторы и имена местоположений. В этом ТАК ответе есть пример. Возможно, это даст вам некоторое представление о том, что происходит.

Carl HR 06.02.2023 20:18

Я пробовал это. Происходит следующее: light.position = 3 и light.intensity = 4. Но тогда все материальные поля возвращают -1.

Alper Şahıstan 07.02.2023 00:32
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
86
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не знаю, что вы делаете неправильно в своем коде, поскольку вы не предоставили минимальный воспроизводимый пример своей проблемы, поэтому я не могу указать, что вы делаете неправильно.

Написав собственное приложение в:

  1. Загрузите окно GLFW
  2. Загрузить контекст OpenGL
  3. Загрузите шейдер на основе ваших скриптов GLSL (вершины и фрагменты)
  4. Загрузить всю униформу
  5. Распечатать всю униформу

Это вывод моего приложения:

SHADER: Loading uniforms
(location = 0 ): light.intensity       <- NOTE: This looks weird because
(location = 1 ): light.position        i'm not searching the correct location
(location = 2 ): material.ka           id for each uniform name. I'm guessing
(location = 3 ): material.kd           them as if you've never set the
(location = 4 ): material.ks           locations manually. The print
(location = 5 ): material.shininess    occurs inside a for-loop. If you
(location = 6 ): normals_to_view_space take out all `layout(location=*)`
(location = 7 ): to_screen_space       from the GLSL scripts, the ids printed
(location = 8 ): to_view_space         here will look ok.

SHADER: Printing Uniforms
Uniform 'light.intensity' location is (4)
Uniform 'light.position' location is (3)
Uniform 'material.ka' location is (6)
Uniform 'material.kd' location is (7)
Uniform 'material.ks' location is (8)
Uniform 'material.shininess' location is (9)
Uniform 'normals_to_view_space' location is (2)
Uniform 'to_screen_space' location is (0)
Uniform 'to_view_space' location is (1)

Итак, в моем тестовом приложении местоположения униформы были успешно загружены.

Вот код, который я использовал. Примечание. Я также использую OpenGL версии 4.50 с основным профилем. Мой код является адаптацией кода, предоставленного из руководств Learnopengl:

основной.cpp:

#include "shader.hpp"

int main(int argc, char **argv) {
    GLFWwindow* window = NULL;
    Shader shader;
    GLuint vao;

    glewExperimental = true;
    if (!glfwInit()) {
        std::cout << "GLFW::FAILED" << std::endl;
        return false;
    } else {
        glfwWindowHint(GLFW_SAMPLES, 4);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Our OpenGL version must be set 
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); // to 4.50 (same as the shader)
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

        window = glfwCreateWindow(800, 600, "GLSLloader.exe", NULL, NULL);

        if (window == NULL) {
            std::cout << "GLFW::FAILED::CREATE::WINDOW" << std::endl;
            return false;
        } else {
            glfwMakeContextCurrent(window);

            if (glewInit() != GLEW_OK) {
                std::cout << "GLEW::FAILED" << std::endl;
            } else {
                glGenVertexArrays(1, &vao);
                glBindVertexArray(vao);

                shader.LoadProgram();
                shader.PrintUniforms();
            }
        }
    }

    return 0;
}

шейдер.hpp:

#ifndef _MY_SHADER_LOADER_
#define _MY_SHADER_LOADER_

// You can get all g++ flags and links by using the following command:
// pkg-config glfw3 glm glew --cflags --libs
// 
// Compile with:
// g++ main.cpp shader.cpp -I./ -IC:/msys64/mingw64/bin/../include -LC:/msys64/mingw64/bin/../lib -lglfw3 -lglew32 -o GLSLloader.exe

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <istream>
#include <ostream>

#include <map>
#include <vector>
#include <list>

extern "C" {
    #include <GL/glew.h>
    #include <GLFW/glfw3.h>
}

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

using ShaderUniforms = std::map<std::string, GLuint>;
class Shader {
private:
    ShaderUniforms uniforms;
    GLuint program;

    void LoadUniforms();
    GLuint Attachment(std::string filename, GLenum stype);

public:
    Shader();
    ~Shader();

    bool LoadProgram();
    void ReleaseProgram();
    void SetUniform(std::string key, const float data);

    void PrintUniforms();
};

#endif

шейдер.cpp:

#include "shader.hpp"

// Code adaptated from: https://learnopengl.com/Getting-started/Shaders

Shader::Shader() {
    program = 0;
    uniforms = ShaderUniforms();
}

Shader::~Shader() {
    ReleaseProgram();
}

bool Shader::LoadProgram() {
    char infoLog[512];
    bool success;
    int result;

    GLuint vertex = Attachment("vertex.glsl", GL_VERTEX_SHADER);
    GLuint fragment = Attachment("fragment.glsl", GL_FRAGMENT_SHADER);

    ReleaseProgram();

    program = glCreateProgram();
    success = true;

    glAttachShader(program, vertex);
    glAttachShader(program, fragment);

    glLinkProgram(program);
    glGetProgramiv(program, GL_LINK_STATUS, &result);

    if (!result) {
        glGetProgramInfoLog(program, 512, NULL, infoLog);
        std::cout << "SHADER::FAILED::LINK-PROGRAM " << infoLog << std::endl;
        success = false;
    }

    if (vertex != 0) {
        glDeleteShader(vertex);
    }

    if (fragment != 0) {
        glDeleteShader(fragment);
    }

    if (success) {
        LoadUniforms();
        return true;
    } else {
        return false;
    }
}

void Shader::ReleaseProgram() {
    if (program != 0) {
        glDeleteProgram(program);
        program = 0;
    }

    uniforms.clear();
}

void Shader::SetUniform(std::string key, const float data) {
    ShaderUniforms::iterator it;
    
    it = uniforms.find(key);
    if (it != uniforms.end()) {
        glUniform1f(it->second, data);
    }
}

// Private
// Source: https://stackoverflow.com/a/442819/14956120
void Shader::LoadUniforms() {
    GLint n;
    GLint size;
    GLint bufSize;
    GLenum type;
    GLsizei length;
    std::string name;

    glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &n);
    glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &bufSize);

    std::vector<GLchar> uName = std::vector<GLchar>(bufSize+1, 0);

    std::cout << "SHADER: Loading uniforms" << std::endl;
    for (GLint i = 0; i < n; ++i) {
        std::fill(uName.begin(), uName.end(), 0);
        glGetActiveUniform(program, i, bufSize, &length, &size, &type, &(uName[0]));
        if (length > 0) {
            name = std::string(uName.begin(), uName.end());
            std::cout << "(location = " << i << " ): " << name << std::endl;
            uniforms.insert(std::pair<std::string, GLuint>(name, (GLuint) i));
        }
    }
}

// Private
GLuint Shader::Attachment(std::string filename, GLenum stype) {
    std::string code;
    std::ifstream file;
    std::stringstream stream;
    GLuint shader;
    char* ccode;
    char infoLog[512];
    int result;

    file.exceptions(std::ifstream::failbit | std::ifstream::badbit);

    try {
        file.open(filename);
        stream << file.rdbuf();
        file.close();
        code = stream.str();
    } catch (std::ifstream::failure& e) {
        std::cout << "SHADER::FAILED::READ::SOURCECODE (" << filename << ")" << std::endl;
        return 0;
    }

    shader = glCreateShader(stype);
    ccode = (char*) code.c_str();
    glShaderSource(shader, 1, (const GLchar**) &(ccode), NULL);
    glCompileShader(shader);

    glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
    if (!result) {
        memset(infoLog, '\0', sizeof(char) * 512);
        glGetShaderInfoLog(shader, 512, NULL, infoLog);
        std::cout << "SHADER::FAILED::COMPILE (" << filename << "): " << infoLog << std::endl;
    }

    return shader;
}

// Used for testing only
void Shader::PrintUniforms() {
    ShaderUniforms::iterator it;
    GLint location = -1;
    std::string name;

    std::cout << std::endl;
    std::cout << "SHADER: Printing Uniforms" << std::endl;
    for (it = uniforms.begin(); it != uniforms.end(); ++it) {
        name = it->first;

        location = glGetUniformLocation(program, name.c_str());
        if (location == -1) {
            std::cout << "ERROR::SHADER::UNIFORM::" << name << "::NOT_FOUND"<<std::endl;
            return;
        } else {
            std::cout << "Uniform '" << name << "' location is (" << location << ")" << std::endl;
        }
    }
}

вершина.glsl:

#version 450

layout (location = 0) in vec3 pos;
layout (location = 1) in vec3 norm;

layout (location = 2) out vec3 v_space_norm;
layout (location = 3) out vec3 v_space_pos;

layout(location = 0) uniform mat4 to_screen_space; // mvp
layout(location = 1) uniform mat4 to_view_space; //mv
layout(location = 2) uniform mat3 normals_to_view_space;
//layout(location = 4) float light_intensity;

void main() {
    // for some reason, you wrote \* instead of *. So I removed the
    // backslash character as the GLSL compiler complained about it
    gl_Position = to_screen_space * vec4(pos, 1.0);
    v_space_norm = normals_to_view_space * norm;
    v_space_pos = (to_view_space * vec4(pos, 1.0)).xyz;
}

фрагмент.glsl:

#version 450
precision mediump float;
//------------ Structs ------------
// struct Light{
//      vec3 position;
//      float intensity;
// };

//------------ Variying ------------
layout (location = 2) in vec3 v_space_norm;
layout (location = 3) in vec3 v_space_pos;

//------------ Uniforms ------------
layout(location = 1) uniform mat4 to_view_space; //mv
//layout(location = 3) uniform vec3 light_position;

//layout(location = 4) uniform float light_intensity;
layout(location = 3) uniform struct{
     vec3 position;
     float intensity;
}light;

layout(location = 6) uniform struct{
     vec3 ka;
     vec3 kd;
     vec3 ks;
     float shininess;
} material;


out vec4 color;

void main() {
     vec3 v_space_norm = normalize(v_space_norm);
     vec3 l =  normalize( (to_view_space * vec4(light.position, 1)).xyz - v_space_pos);//normalize(l); //light vector
     vec3 h = normalize(l + vec3(0,0,1)); //half vector

     float cos_theta = dot(l, v_space_norm);
     if (cos_theta >= 0)
     {
          vec3 diffuse = material.kd * max(cos_theta,0);
          vec3 ambient = material.ka;
          vec3 specular= material.ks * pow(max(dot(h, v_space_norm),0), material.shininess);
          color = vec4(light.intensity * (specular + diffuse) + ambient, 1);
     }
     else
     {
          color = vec4(material.ka,1);
     }
}

Вот структура моего проекта:

.
├── main.cpp
├── shader.hpp
├── shader.cpp
├── vertex.glsl
└── fragment.glsl

Я использую MSYS2 в Windows 10 для компиляции и запуска приложения.

Спасибо, что прошли через все эти неприятности. Я не смог предоставить всю свою кодовую базу, так как это усложнило бы вопрос. Поэтому я нашел решение проблемы, не используя анонимные структуры. Как эти layout(location = 6) uniform struct{ vec3 ka; vec3 kd; vec3 ks; float shininess; } material;Поскольку вики OpenGL в разделе «Структуры» говорит, что они не поддерживаются Я не знаю, как у вас работает, но у меня также была рабочая реализация с ними в какой-то момент (которую я не могу воспроизвести сейчас...)

Alper Şahıstan 12.02.2023 00:16

@AlperŞahıstan Это не было проблемой. Я делаю свой собственный 3D-движок, поэтому к моменту ответа все эти скрипты уже были сделаны. Если честно, это все было ctrl+c и ctrl+v :)

Carl HR 12.02.2023 00:30

@AlperŞahıstan Я тоже не знаю, как это сработало. Может быть, это была какая-то опция или библиотека, которую я использую. Например: я использую GLFW3 и GLEW. Если вы используете что-то другое, это может быть оно.

Carl HR 12.02.2023 00:31

У меня GLFW3 + GLAD

Alper Şahıstan 12.02.2023 10:27
Ответ принят как подходящий

Использование именованных структур, таких как:

struct Light{
      vec3 position;
      float intensity;
};

И тогда использование их в качестве униформы кажется правильным путем, согласно вики OpenGL (мог бы поклясться, что пробовал это, но да ладно...).

layout(location = 3) uniform Light light;

Так,

layout(location = 3) uniform struct{
     vec3 position;
     float intensity;
}light;

это либо нехорошее, либо неопределенное поведение, о котором я не знаю.

Другие вопросы по теме