Access struct array in UBO

Hi,
I have the following frag. shader with pointlight implementation:

struct PointLight
{					//Size		
	vec4 position;	// 16		

	vec4 ambient;	// 16		
	vec4 diffuse;	// 16		
	vec4 specular;	// 16		

	float constant;	// 4		
	float linear;	// 4		
	float quadratic;// 4		
};					//Total  =  76

And the following C++ implementation:

#define VEC3 4
struct PointLightData
{
	float position[VEC3];

	float ambient[VEC3];
	float diffuse[VEC3];
	float specular[VEC3];

	float constant;
	float linear;
	float quadratic;

	float _padding_;

}

I then use a memcpy() to copy glm::vec3 to each float array. Also some padding to make it compatible with std140 rules.

Everything works fine until when accessing the UBO in shader via:

#define MAX_POINT_LIGHTS 4
#define MAX_SPOT_LIGHTS 4

layout(std140) uniform LightingData
{
											// Size     //Starts at  //Additional bytes to align (padding)
	DirectionalLight dLight;				// 64		0  =  (+64)  +0

	PointLight pLights[MAX_POINT_LIGHTS];   // 76		64  = (+76)  +4
											// 76		144 = (+76)  +4
											// 76		224 = (+76)  +4
											// 76		304 = (+76)  +4
	SpotLight sLights[MAX_SPOT_LIGHTS];	    // 100		384 = (+100) +12
											// 100		496 = (+100) +12
											// 100		608 = (+100) +12
											// 100		720 = (+100) +12
																	//No need to add extra bytes
	int scenePointLights;					// 4		832 = (+4)   +0
	int sceneSpotLights;					// 4		836 = (+4)   +0
											//Total		840 + 64 initial bytes = 904 bytes
};

if I use a for loop, the light data is incorrectly read. It kind of multipies the first PointLight by a huge number hence it looks over-saturated.

But if I hard code the array it works fine:

	// 1 :: Directional Light
	vec3 result = directionalLightInfluence(dLight,norm,viewDir);

	// Point lights  work
	result+= pointLightInfluence(pLights[0], norm, fragPos, viewDir);
	result+= pointLightInfluence(pLights[1], norm, fragPos, viewDir);
	result+= pointLightInfluence(pLights[2], norm, fragPos, viewDir);
	result+= pointLightInfluence(pLights[3], norm, fragPos, viewDir);
	
	// Point lights dont works
	for(int i = 0; i < scenePointLights; i++){
		result+= pointLightInfluence(pLights[i], norm, fragPos, viewDir);
	}

I have tried swapping scenePointLights with 4 but it still doesn’t work.

[TOP] Without for loop (working fine)
[BOTTOM] With for loop (not working)

I have no clue what is wrong or I’m missing something about how arrays in an UBO are accessed.

I am learning using learnopengl.com btw.

Sure is sounding like a possible driver bug.

  1. Which GPU and driver version is this?
  2. You might show the full UBO layout, so others can possibly try it.

Hey! Thanks for the reply.

Your point actually makes alot of sense. I am using a very old laptop that only supports OpenGL 3.1 on Windows and 3.3 on Linux.

I also have a zip with all the source code but I can’t post links on the forum :sleepy:

Here are both frag and vert shaders:

FRAGMENT:

#version 330 core
#define MAX_POINT_LIGHTS 4
#define MAX_SPOT_LIGHTS 4
struct Material
{
	float specular_power;
};

struct DirectionalLight
{					//Base		Aligned
	vec4 direction; // 16		0		

	vec4 ambient;	// 16		16
	vec4 diffuse;	// 16		32		
	vec4 specular;	// 16		48
};					// Total  =	48 + 16 = 64 
					// Elems. = 64
	
struct PointLight
{					//Size		
	vec4 position;	// 16		

	vec4 ambient;	// 16		
	vec4 diffuse;	// 16		
	vec4 specular;	// 16		

	float constant;	// 4		
	float linear;	// 4		
	float quadratic;// 4		
};					//Total  =  76

struct SpotLight
{				   //Base(size)		
	vec4 position; // 16		
	vec4 direction;// 16		

	vec4 ambient;  // 16		
	vec4 diffuse;  // 16		48
	vec4 specular; // 16		64

	float constant;// 4			80
	float linear;  // 4			84
	float quadratic;// 4		88

	float cutOff;  // 4			92
	float outerCutOff;// 4		96
};					//Total =   100

//Protos
vec3 directionalLightInfluence(DirectionalLight dL,vec3 normal,vec3 viewDir);
vec3 pointLightInfluence(PointLight pL,vec3 normal,vec3 fragPos,vec3 viewDir);
vec3 spotLightInfluence(SpotLight sL,vec3 normal,vec3 fragPos,vec3 viewDir);

out vec4 FragColor;

in vec3 Normals;
in vec3 fragPos;
in vec2 TexCoords;

in vec3 viewPos;

uniform sampler2D diffuseMap;
uniform sampler2D specularMap;
uniform samplerCube envMap;

uniform Material material;

layout(std140) uniform LightingData
{
											// Size     //Starts at  //Additional bytes to align (padding)
	DirectionalLight dLight;				// 64		0  =  (+64)  +0

	PointLight pLights[MAX_POINT_LIGHTS];   // 76		64  = (+76)  +4
											// 76		144 = (+76)  +4
											// 76		224 = (+76)  +4
											// 76		304 = (+76)  +4
	SpotLight sLights[MAX_SPOT_LIGHTS];	    // 100		384 = (+100) +12
											// 100		496 = (+100) +12
											// 100		608 = (+100) +12
											// 100		720 = (+100) +12
																	//No need to add extra bytes
	int scenePointLights;					// 4		832 = (+4)   +0
	int sceneSpotLights;					// 4		836 = (+4)   +0
											//Total		840 + 64 initial bytes = 904 bytes
};

void main()
{
	//Props
	vec3 norm=normalize(Normals);
	vec3 viewDir=normalize(viewPos - fragPos);

	//Vector with our lighting color

	// 1 :: Directional Light
	vec3 result = directionalLightInfluence(dLight,norm,viewDir);
	//Pointlights cause visual glitches. change to "scenePointLights" or 4 to see if your card is presumably affected
	for(int i = 0; i < 1; i++)
	{
		result += pointLightInfluence(pLights[i], norm, fragPos, viewDir);
	}
	//The following code will however work fine but won't scale well
	//result += pointLightInfluence(pLights[0], norm, fragPos, viewDir);
	//result += pointLightInfluence(pLights[1], norm, fragPos, viewDir);
	//result += pointLightInfluence(pLights[2], norm, fragPos, viewDir);
	//result += pointLightInfluence(pLights[3], norm, fragPos, viewDir);

	//3 :: Spotlight
	for(int i = 0; i < sceneSpotLights; i++)
	{
		result+=spotLightInfluence(sLights[i],norm,fragPos,viewDir);
	}
	//Results
	
	//Refelctions;
	//vec3 I = normalize(fragPos - viewPos);
	//vec3 R = reflect(I , normalize(Normals));
	//vec3 relection = texture(envMap, R).rgb;

	FragColor = vec4(result,1.0);
}

vec3 directionalLightInfluence(DirectionalLight dL,vec3 normal,vec3 viewDir)
{
	vec3 lightDir = normalize(-dL.direction.xyz);

	//Diffuse
	float diff=max(dot(normal,lightDir),0.0);

	//Specular
	vec3 reflectDir=reflect(-lightDir,normal);
	float spec=pow(max(dot(viewDir,reflectDir),0.0),material.specular_power);

	//Add all lighting
	vec3 ambient = dL.ambient.xyz  * vec3(texture(diffuseMap,TexCoords));
	vec3 diffuse = dL.diffuse.xyz * diff * vec3(texture(diffuseMap,TexCoords));
	vec3 specular = dL.specular.xyz * spec * vec3(texture(specularMap,TexCoords));

	return (ambient+diffuse+specular);

}

vec3 pointLightInfluence(PointLight pL,vec3 normal,vec3 fragPos,vec3 viewDir)
{
	vec3 lightDir=normalize(pL.position.xyz - fragPos);

	//Diffuse
	float diff=max(dot(normal,lightDir),0.0);

	//Specular
	vec3 reflectDir=reflect(-lightDir,normal);
	float spec= pow(max(dot(viewDir,reflectDir),0.0),material.specular_power);

	//Attenuation
	float distance=length(pL.position.xyz - fragPos);
	float attenuation=1.0 / (pL.constant + pL.linear * distance +
						pL.quadratic * (distance * distance));

	//Add all light
	vec3 ambient = pL.ambient.xyz * vec3(texture(diffuseMap,TexCoords));
	vec3 diffuse = pL.diffuse.xyz * diff * vec3(texture(diffuseMap,TexCoords));
	vec3 specular = pL.specular.xyz * spec * vec3(texture(specularMap,TexCoords));

	return attenuation*(ambient+diffuse+specular);
}

vec3 spotLightInfluence(SpotLight sL,vec3 normal,vec3 fragPos,vec3 viewDir)
{
	//Ambient
	vec3 ambient = sL.ambient.xyz * vec3(texture(diffuseMap, TexCoords));

	//Diffuse
	vec3 norm=normalize(normal);

	vec3 lightDir = normalize(sL.position.xyz - fragPos);
	float diff = max(dot(norm,lightDir),0.0);
	vec3 diffuse=sL.diffuse.xyz * diff * vec3(texture(diffuseMap, TexCoords));

	//Specular
	vec3 reflectDir = reflect(-lightDir, norm);
	float spec = pow(max(dot(viewDir,reflectDir),0.0),material.specular_power);
	vec3 specular=sL.specular.xyz * spec * vec3(texture(specularMap,TexCoords));
	
	//Final frag
	float theta=dot(lightDir,normalize(-sL.direction.xyz));
	float epsilon=sL.cutOff-sL.outerCutOff;
	float intensity=clamp((theta-sL.outerCutOff)/epsilon,0.0,1.0);
	
	diffuse*=intensity;
	specular*=intensity;

	//Attenuation
	float distance = length(sL.position.xyz - fragPos);
	float attenuation = 1.0 / (sL.constant + sL.linear * distance +
						sL.quadratic * (distance * distance));

	return attenuation * (ambient+diffuse+specular);
}

VERTEX:

#version 330 core
layout (location=0) in vec3 aPos;
layout (location=1) in vec3 aNormals;
layout (location=2) in vec2 aTexCoords;

layout(std140) uniform PVMatrices
{
	mat4 projection;
	mat4 view;
	vec4 cam_pos;
};

out vec3 Normals;
out vec3 fragPos;
out vec2 TexCoords;

out vec3 viewPos;

uniform mat4 model;

void main()
{
	gl_Position = projection * view * model * vec4(aPos,1.0);
	fragPos = vec3(model * vec4(aPos, 1.0));

	//Can use transpose but is expensive + its uniform scale. 
	Normals= mat3( transpose( inverse(model) ) )* aNormals;

	//Tho we need accurate lighting .... soooo we transpose
	//Normals=aNormals;

	TexCoords=aTexCoords;

	viewPos = cam_pos.xyz;
}

You might post the output of:

glxinfo | grep ':' 
lspci
lshw -short

That should give us a better idea what GL driver and GPU you’re using.

That’s spam prevention for new users. Keep using the forums and you’ll be able to post links.

If you post a link but put a few spaces in it so it’ll post, I’ll fix it up for you.

Good deal – thanks. Will look through them later. They compile just fine here, but I haven’t looked at the generated code yet.

Thanks again.

Here is the full source : https://github.com/Denzy7/dengine

As for OpenGL version for Linux : https://pastebin.com/ttqeWr4i

And Windows OpenGL Extensions Viewer output : <?xml version="1.0" encoding="UTF-8" ?><!-- http://www.realtech-vr.com/glview - Pastebin.com