Passing any data to shaders (VERY UGLY WAY!)

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Auradrummer
Posts: 260
Joined: Thu Apr 17, 2008 1:38 pm
Location: Brasopolis - Brazil

Passing any data to shaders (VERY UGLY WAY!)

Post by Auradrummer »

Hello guys,

I found a very ugly way to pass any data to shaders. Is a quite simple, but is not so ellegant.

Image
Uploaded with ImageShack.us

The numbers on the cars noose aren't part of the texture. They are applyed via shaders. The same shader applyed both numbers.

I wont post the whole shader here, because I think that is not so useful.

The data are passed this way:

main.cpp

Code: Select all

struct shaderData
{
    float number;
    //any other data to pass? Go ahead...
};
...
int main()
{
    ...
    shaderData carData;
    carData.number = 17;

    shaderData * pointer = &carData;
    car->getMaterial(3).MaterialTypeParam2 = (f32)((int)pointer);
    car->getMaterial(3).MaterialType = (E_MATERIAL_TYPE)shaderMat;
    ...
}
Now, the callback code:

Code: Select all

class MyShaderCallBack: public video::IShaderConstantSetCallBack
{
    f32 number;
    const video::SMaterial * mat;

    public:
	virtual void OnSetMaterial(const video::SMaterial& material)
	{
	    mat = &material;
	    shaderData * data;
	    data = (shaderData*)((int)mat->MaterialTypeParam2);
	    number = data->number;
	};

	virtual void OnSetConstants(video::IMaterialRendererServices* services, s32 userData)
	{
        int texture = 0;
        services->setPixelShaderConstant("tex0",(float*)(&texture),1);

        services->setPixelShaderConstant("number", reinterpret_cast<irr::f32*>(&number),1);
	};
};
Conclusion:
To make this, only I had to do is pass a pointer to my structure in the 'MaterialTypeParam2'. Inside the callback I retrieved that pointer and this does the trick.

Hope it helps!
Professional Software Developer and Amateur Game Designer ;-)
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Post by sudi »

interessting idea to attach more variables to materials. but what kind of extra data would one actually need that can't be set for a specific shader. since the normal material already supports already alot of stuff. but still a really good idea.
now the other question is that actually consistent for 32/64bit systems?
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
Luben
Posts: 568
Joined: Sun Oct 09, 2005 10:12 am
Location: #irrlicht @freenode

Post by Luben »

If the pointer points to a high enough place in memory, converting it to a float will make it loose precision, and upon casting back to an int/pointer it will point to a different memory location.
Better to do

Code: Select all

*((shaderData**)&car->getMaterial(3).MaterialTypeParam2) =  data;

(This too assumes a 32 bit pointer size)

(Not actually tested and i just woke up, etc)
Auradrummer
Posts: 260
Joined: Thu Apr 17, 2008 1:38 pm
Location: Brasopolis - Brazil

Post by Auradrummer »

@Sudi:
I needed, to make that cars you see above, to pass the cars number, between another things. To make that numbers, you can pass them as floats and that shader will treat them. Worked very well. I didn't tested it on 64 bit systems. Thanks for the "good idea", is music for a noob like me.

@Luben:
I really missed that loose of precision. In my tests, worked very fine, but I'll check your sugestion. Thank you.
Professional Software Developer and Amateur Game Designer ;-)
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Post by Mel »

The callbacks are classes too. Why not treating them as classes adding the suitable methods to change their variables inside? Whenever you need to change a variable of the shader, just use the method. No pointers, no tricks, just actual values.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
ent1ty
Competition winner
Posts: 1106
Joined: Sun Nov 08, 2009 11:09 am

Post by ent1ty »

That's the way I do it :wink:

Imho that's the way it should be done.
irrRenderer 1.0
Height2Normal v. 2.1 - convert height maps to normal maps

Step back! I have a void pointer, and I'm not afraid to use it!
Luben
Posts: 568
Joined: Sun Oct 09, 2005 10:12 am
Location: #irrlicht @freenode

Post by Luben »

@mel, entity
Isn't the callback classes per shader? Wouldn't it be kinda hard to set per-material/mesh data then?
(Unless you create multiple instances of the same shader... which would be retarded)
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Post by Mel »

That's why exists the onSetMaterial method, it is called everytime the material is set to render and it executes before the onSetConstants. And you may use the LightingManager, which implements another callback, the "onNodePreRender" which is able to perform operations prior to the rendering of any scene node, so, if you need to change variables on pre render time, those are the places, and the shader keeps the same all the time.
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
ent1ty
Competition winner
Posts: 1106
Joined: Sun Nov 08, 2009 11:09 am

Post by ent1ty »

Yeah... i need to alter the callback vars for every light rendered, and i do it like this(simplified):

Code: Select all

light - irr::video::SLight&
lightNode - irr::scene::IMeshSceneNode* with material type set to the one callback is tied to

for every light
    callback->update(light);
    lightNode->render();
irrRenderer 1.0
Height2Normal v. 2.1 - convert height maps to normal maps

Step back! I have a void pointer, and I'm not afraid to use it!
Luben
Posts: 568
Joined: Sun Oct 09, 2005 10:12 am
Location: #irrlicht @freenode

Post by Luben »

Oh, guess i've missed a change in the api then. Some years ago i suffered from there only being an OnSetConstants(either that or i was young and stupid :p), so i had to make my own very hacky solution.

Anyway, I'm not sure i follow. Are you saying that one ought to use onNodePreRender, in which one should check if the node has any materials, and if any of those materials are the shader-material in question, and if it finds such a material, it's supposed to use getters/setters of the shader-material-class-instance(which the lightmanager has a pointer to......) and then in the OnSetConstants send the data that is set with the setters to the shader .....? This wouldn't work properly anyway, since a node could have the same shader-material but different material parameters...
ent1ty
Competition winner
Posts: 1106
Joined: Sun Nov 08, 2009 11:09 am

Post by ent1ty »

No, never mind the on node render function. Mel is just trying to make you suffer :D (good job at succeeding btw :P)

Check the API instead http://irrlicht.sourceforge.net/docu/cl ... _back.html

So, on the on set material function you set some private callbach vars and then pass them on set constant
irrRenderer 1.0
Height2Normal v. 2.1 - convert height maps to normal maps

Step back! I have a void pointer, and I'm not afraid to use it!
Mel
Competition winner
Posts: 2292
Joined: Wed May 07, 2008 11:40 am
Location: Granada, Spain

Post by Mel »

Yeah, well (wasn't trying to make anyone suffer, sorry :lol:)

No, i was just pointing out that you can make use of the lighting manager to set any variable may you need when you render a scene node using a shader. I try to keep the good practice that if a routine has to respond to an event, it must respond very quickly. For example:

I have set a shader based skinning system. The bone matrices are constants within the callback interface that are used within the shader.

But those matrices must be set for each skinned mesh on the scene, and for each bone on its skeleton. Happens that the materials know very little of the scene node that use them, so this information must be set prior to the render. Hence, i have set a "container" that points to the current skinned mesh scene node that is being rendered. Using the lighting manager you can set that scene node before it is rendered.

The callback class then knows of this container, and the onSetMaterial reads from the container which is the current skinned mesh which is being rendered and calculates the suitable bone matrices, these are fed to the onSetConstants() method, and the shader performs the skinning process.

I think this way of passing data to the shaders is useful when a scene node renders all its meshbuffers with the same shader, because then, the onSetMaterial() is executed only once per scene node; the next times the setMaterial() is called, the material is the same, so, there is no need to update any data, or renderstates
"There is nothing truly useless, it always serves as a bad example". Arthur A. Schmitt
Nalin
Posts: 194
Joined: Thu Mar 30, 2006 12:34 am
Location: Lacey, WA, USA
Contact:

Post by Nalin »

Just keep in mind that your code isn't portable to 64-bit. On a 64-bit system, a pointer is 64-bits, while the size of an int is usually 32-bits. If you compile it as a 64-bit program, you will most likely have problems.
robmar
Posts: 1125
Joined: Sun Aug 14, 2011 11:30 pm

Re: Passing any data to shaders (VERY UGLY WAY!)

Post by robmar »

Good topic! I needed to do just this, thanks Auradrummer! Maybe having a few more userdata fields in material would be good.
Post Reply