英文:
Defining multiple structs and setting them as uniforms in Fragment Shader GLSL
问题
我试图创建一个材质结构和一个光线结构,我将在GLSL片段着色器中使用它们。当我只定义一个结构(无论是材质还是光线)时,我可以正确渲染我的场景,但当我同时定义它们时,调用glGetUniformLocation(...)
会返回-1,对于材质结构(定义的顺序无关紧要),就好像它们不存在或未被使用。我需要使用这些结构以备将来使用,请不要要求我使用POD(Plain Old Data)结构。我想学习如何将多个结构作为uniform变量上传。谢谢。
以下是顶点着色器和片段着色器的代码,以及如何设置uniform变量:
顶点着色器:
#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;
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 ------------
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 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);
vec3 h = normalize(l + vec3(0, 0, 1));
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);
}
}
设置uniform变量的方式:
// ...
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
函数中,你使用了glGetUniformLocation
来获取uniform变量的位置。如果你的uniform变量名称与GLSL代码中的命名完全匹配,那么这种方法应该正常工作。不过,如果出现问题,可以手动设置uniform变量的位置,通过在GLSL代码中使用layout(location = ...) uniform ...
来指定uniform变量的位置。这样,你可以直接通过位置来设置结构体的成员。
总之,你的GLSL代码和uniform变量设置的方式看起来没有问题,但如果你遇到问题,可以检查uniform变量名称是否正确匹配或手动设置位置。
英文:
Im trying to create a material struct and a light struct that I will use in GLSL fragment shader. I can render the scene I have correctly when I have only one struct defined (either material or light) but when I define both of them together the glGetUniformLocation(...) calls return -1 for Material structs (order of defnitions does not matter) as if they are not there or not being used. I need to use the structs for the future use please do not ask me to use PODs. I want to learn how to upload multiple structs as uniforms. Thanks.
Here is the vertex shader:
#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;
}
and fragment shader:
#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);
}
}
And the way I set uniforms:
...
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);
...
The definition of 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!!!
I was unable to find anything online about my problem
I was hoping to see the teapot rendered and blinn shaded. And I can get that if I only define one struct and then upload the other properties as PODs.
So using the fragment shader below:
//------------ 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
Of course setUniform calls are also changed accordingly.
enter image description here
But if I use both of the structs I get this:
enter image description here
Edit: Instead of accessing via glGetUniformLocation(glID, name); I can manually set each variable via layout location number. Now the question becomes "Can I set the structs using the string variable names?"
答案1
得分: 1
使用命名的结构体如下:
struct Light{
vec3 position;
float intensity;
};
然后,根据OpenGL维基的说法,将它们用作uniform变量似乎是正确的方法(我可能曾尝试过,但不记得了...)。
layout(location = 3) uniform Light light;
所以,下面这种写法:
layout(location = 3) uniform struct{
vec3 position;
float intensity;
}light;
要么不太好,要么属于我知识之外的未定义行为。
英文:
Using named structs like:
struct Light{
vec3 position;
float intensity;
};
And then using them as uniforms seems to be the correct way to go according to OpenGL wiki (I could have sworn I tried that but oh well...).
layout(location = 3) uniform Light light;
So,
layout(location = 3) uniform struct{
vec3 position;
float intensity;
}light;
this is either not good or undefined behavior which is beyond my knowledge.
答案2
得分: 0
我不知道你在代码中做错了什么,因为你没有提供一个能够复现你的问题的最小示例。所以我无法指出你做错了什么。
通过编写我的自己的应用程序来完成以下任务:
- 加载一个GLFW窗口
- 加载OpenGL上下文
- 根据你的GLSL脚本(顶点和片段)加载一个着色器
- 加载所有的uniform变量
- 打印所有的uniform变量
这是我的应用程序的输出:
SHADER: 加载uniform变量
(位置 = 0): light.intensity <- 注意:这看起来有点奇怪,因为
(位置 = 1): light.position 我没有搜索正确的位置
(位置 = 2): material.ka id来获取每个uniform变量的名称。我在猜测
(位置 = 3): material.kd 它们,就好像你从未手动设置过位置
(位置 = 4): material.ks 。打印操作发生在一个for循环中。如果你
(位置 = 5): material.shininess 从GLSL脚本中删除所有的`layout(location=*)`
(位置 = 6): normals_to_view_space ,这里打印出来的id将会正常。
SHADER: 打印uniform变量
Uniform 'light.intensity' 位置为 (4)
Uniform 'light.position' 位置为 (3)
Uniform 'material.ka' 位置为 (6)
Uniform 'material.kd' 位置为 (7)
Uniform 'material.ks' 位置为 (8)
Uniform 'material.shininess' 位置为 (9)
Uniform 'normals_to_view_space' 位置为 (2)
Uniform 'to_screen_space' 位置为 (0)
Uniform 'to_view_space' 位置为 (1)
所以,在我的测试应用程序中,uniform变量的位置被成功加载。
这是我使用的代码。请注意,我还使用了OpenGL版本4.50,使用核心配置。我的代码是从learnopengl教程提供的代码进行了适应。
main.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); // 我们的OpenGL版本必须设置为
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); // 4.50(与着色器版本相同)
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;
}
shader.hpp:
#ifndef _MY_SHADER_LOADER_
#define _MY_SHADER_LOADER_
// 你可以使用以下命令获取所有的g++标志和链接信息:
// pkg-config glfw3 glm glew --cflags --libs
//
// 使用以下命令编译:
// 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
shader.cpp:
#include "shader.hpp"
// 代码改编自: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);
}
}
// 私有
// 来源:https://stackoverflow.com
<details>
<summary>英文:</summary>
I don't know what you're doing wrong on your code, as you didn't provided a minimal reproducible example of your problem, so I can't point what you're doing it wrong.
By writing my own application to:
1. Load a GLFW window
2. Load OpenGL Context
3. Load a Shader based on your GLSL scripts (vertex and fragment)
4. Load all uniforms
5. Print all uniforms
That's the output of my application:
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)
So, in my test application, the uniforms locations were loaded **successfully**.
Here's the code I used. Note I also use the OpenGL version 4.50, with Core Profile. My code is an adaptation of the code provided from the [learnopengl tutorials](https://learnopengl.com/):
**main.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;
}
**shader.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
**shader.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;
}
}
}
**vertex.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;
}
**fragment.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);
}
}
---
Here's my project structure:
.
├── main.cpp
├── shader.hpp
├── shader.cpp
├── vertex.glsl
└── fragment.glsl
I'm using MSYS2 on windows 10, to compile and run the application.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论