Jump to content

  • Log In with Google      Sign In   
  • Create Account

[GLSL] send array receive struct


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
6 replies to this topic

#1 Ichi   Members   -  Reputation: 162

Like
0Likes
Like

Posted 06 July 2014 - 05:37 PM

Hello,

 

i got a quick question about passing a float array to a shader and receiving it as a struct defined inside the shader.

both my light structs (application and shader) contain 15 floats in the same order.

 

 

now if i send my array of structs of 15 floats with

 

glUniform1fv(glGetUniformLocation(programID, "lights"), 15 * lights.size(), lights[0].data());

 

and in the shader i receive it with

 

uniform float lights[MAX_LIGHTS * 15];

 

everything works fine but i would to get it this way

 

uniform Light lights[MAX_LIGHTS];

 

which doesnt seem to work :/

 

 

so how do i not receive an array of numlights * 15 floats but an array of light objects with 15 floats?



Sponsor:

#2 Wh0p   Members   -  Reputation: 340

Like
2Likes
Like

Posted 07 July 2014 - 03:10 AM

I had a similar Problem. Make sure gGetUniformLocation is not -1 (thats what I got when using an array of struct).

Generally i woud suggest to keep away from the array of structs idea and instead use a struct of array, that is a struct like

struct Lights

{

  vec3 positions[MAX_LIGHTS];

  vec3 intensity[MAX_LIGHTS];

...

};

 

in many cases this woul be more cache efficient (think about updating on the client).

 

And then there would be another approach by using a samplerBuffer for each component in your shader, that you can texelFetch your lights from. This would enable you to have a variable count of lightsources.

 

Hope this could help


Edited by Wh0p, 07 July 2014 - 03:10 AM.


#3 tanzanite7   Members   -  Reputation: 1305

Like
1Likes
Like

Posted 07 July 2014 - 06:46 AM

uniform float lights[MAX_LIGHTS * 15];
 
uniform Light lights[MAX_LIGHTS];

Did you account for the padding?

I do not see how is Light defined - but it likely has some padding (check the spec). Also, did you define a standard layout for the structure?

#4 Ichi   Members   -  Reputation: 162

Like
0Likes
Like

Posted 08 July 2014 - 02:09 PM

yes the uniformlocaion of an object allways seems to be -1. it only works if i ask it for a member like "lights.pos".

isnt there any way to get the uniform location of an object and write byte for byte into it like i tryed in my first post?

 

 

I now use a struct of arrays which feels very uncomfortable in my application. my light class looks like this
http://pastebin.com/80hK9UNJ

and the shader code:

http://pastebin.com/unUHbDND

 

now i have to make funktions for all events like copy a light, add one, remove one and so on...

i'd more like to still have the array of structs in my application which as you said is not cache efficient in lots of cases and would mean i'd have to convert all my lights to arrays if i want to give them to the shader.

 

do you may have any advice?

 

 

did you define a standard layout for the structure?

standard layout? i did define following layout: http://pastebin.com/SGgSTzCy


Edited by Ichi, 08 July 2014 - 03:53 PM.


#5 tanzanite7   Members   -  Reputation: 1305

Like
0Likes
Like

Posted 09 July 2014 - 06:47 PM

standard layout? i did define following layout: http://pastebin.com/SGgSTzCy

So, no layout whatsoever. If you do not ask for some standard layout then the driver is free to reorder all the stuff in whatever way it wants ... hence why:

yes the uniformlocaion of an object allways seems to be -1. it only works if i ask it for a member like "lights.pos".

... you have to query every member location separately etc.

Ask for fixed/standardised layout, like std140 / std430. Further information: http://www.opengl.org/wiki/Interface_Block_%28GLSL%29

#6 Wh0p   Members   -  Reputation: 340

Like
0Likes
Like

Posted 11 July 2014 - 01:11 PM

 

now i have to make funktions for all events like copy a light, add one, remove one and so on...

 

 

The most convenient method for this would to store the lights in a textureBuffer (http://www.opengl.org/wiki/Buffer_Texture) and get rid of the uniform struct array.

If I look at the light struct of yours you'll need 15 floats. So you could use a GL_RGBA32F format and put the lights into 4 aligned texels.

Then just write a getter function in you shader that looks like this

uniform samplerBuffer LightsBuffer;

[...]

Light SampleLight (int id)
{
  id *= 4;
  Light light;
  vec4 tmp = texelFetch (LightsBuffer, id);
  lights.pos = tmp;
  tmp = texelFetch (LightsBuffer, id + 1);
  lights.color = tmp.xyz;
  lights.constantAttenuation = tmp.w;
  tmp = texelFetch (LightsBuffer, id + 2);
  lights.linearAttenuation = tmp.x;
  lights.quadraticAttenuation = tmp.y;
  lights.spotCutoff = tmp.z;
  lights.spotExponent = tmp.w;
  tmp = texelFetch (LightsBuffer, id + 3);
  lights.spotDir = tmp.xyz;
}

This way you just would have to implement an update method in your light class, that copies the light data into your texture.

 

Hopefully I could give you an idea.



#7 HappyCoder   Members   -  Reputation: 2669

Like
1Likes
Like

Posted 11 July 2014 - 03:22 PM

I would recommend using Uniform Buffer Objects. You create a buffer, similar to a vertex buffer, that holds only uniform data. In your c++ code you can copy structured data into the buffer that matches structured data in the glsl code.

 

C++ Code

struct Light
{
    Vector4f lightPosition;
    Vector4f lightColor;
    Vector4f lightAttenuation;
};

Light* lights = new Light[lightCount];

// populate light info here

// create the buffer, you only need to do this once
GLuint uniformBuffer;
glGenBuffers( 1, &uniformBuffer );
glBindBuffer( GL_UNIFORM_BUFFER, uniformBuffer );

// fill the buffer, you do this anytime any of the lights change
glBufferData( GL_UNIFORM_BUFFER, sizeof(Light) * lightCount, lights,
GL_DYNAMIC_DRAW );
// binding to index 0, the glsl code needs to specify this same
// index using 'layout(binding=0)'
glBindBufferBase( GL_UNIFORM_BUFFER, 0, uniformBuffer );

GLSL code

#version 420

// Match the structure in the C++ code
// I don't use vec3 because padding
// rules sometimes differ between c++
// and OpenGL
struct Light
{
  vec4 position;
  vec4 color;
  vec4 attenuation;
};

// force this block to be assigned to index 0
// std140 specifies that the structured data should
// follow a specific set of packing rules. This will
// make the packing consistent across OpenGL implementations
layout(binding=0,std140) uniform LightBlock
{
    Light lights[lightCount];
};

void main()
{
   // use the lights
   vec4 foo = lights[0].position;
   //...
};

One advantage of this approach is you can use a struct in C++ to specify the data. This is much more intuitive. Also, you can use the same buffer and only need to bind it once between draws calls that use the same data.

 

 

 


Edited by HappyCoder, 11 July 2014 - 03:31 PM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS