Я пытаюсь создать структуру материала и структуру света, которые я буду использовать в шейдере фрагментов 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); Я могу вручную установить каждую переменную через номер расположения макета. Теперь возникает вопрос: «Могу ли я установить структуры, используя имена строковых переменных?»
Нет сообщения об ошибке. Шейдер успешно компилируется, поэтому чайник заштрихован черным цветом. Единственное, что квалифицируется как ошибка, это GLint location = glGetUniformLocation(glID, name); возвращая -1, что заставляет меня поверить, что шейдер оптимизирует униформу, которую я использую.
Вы можете попытаться получить каждую форму после компиляции шейдера и распечатать соответствующие идентификаторы и имена местоположений. В этом ТАК ответе есть пример. Возможно, это даст вам некоторое представление о том, что происходит.
Я пробовал это. Происходит следующее: light.position = 3 и light.intensity = 4. Но тогда все материальные поля возвращают -1.
Я не знаю, что вы делаете неправильно в своем коде, поскольку вы не предоставили минимальный воспроизводимый пример своей проблемы, поэтому я не могу указать, что вы делаете неправильно.
Написав собственное приложение в:
Это вывод моего приложения:
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 Это не было проблемой. Я делаю свой собственный 3D-движок, поэтому к моменту ответа все эти скрипты уже были сделаны. Если честно, это все было ctrl+c и ctrl+v :)
@AlperŞahıstan Я тоже не знаю, как это сработало. Может быть, это была какая-то опция или библиотека, которую я использую. Например: я использую GLFW3 и GLEW. Если вы используете что-то другое, это может быть оно.
У меня GLFW3 + GLAD
Использование именованных структур, таких как:
struct Light{
vec3 position;
float intensity;
};
И тогда использование их в качестве униформы кажется правильным путем, согласно вики OpenGL (мог бы поклясться, что пробовал это, но да ладно...).
layout(location = 3) uniform Light light;
Так,
layout(location = 3) uniform struct{
vec3 position;
float intensity;
}light;
это либо нехорошее, либо неопределенное поведение, о котором я не знаю.
Пожалуйста, выложите само сообщение об ошибке, а не изображение ошибки.