Shader Injection, first try

We don’t need to over complicate ourselves with language translation that wont mean shader injection what so ever.. what we really need is an engine which loads the shader and it’s code blocks (uniforms, if defs, etc) so we will be able to write inside the code block or replace/ remove it.


Agreed... for a simple case, piece of cake...

However, what is the purpose of injection anyway if we just can replace some string?

For such case like @zzuegg metioned ( merging 2 or more shaders GLSL code), it's definitely hard...

I have to say that's not my approach (start from designable blocks and save the structure to recontruct, combine shader when needed), you can see this approach in a lot of other game designer and support by the same game engine (Unreal, Crysis, Unity, Trinigty, ....)
@atomix said:
However, what is the purpose of injection anyway if we just can replace some string?

Well lets say i have some code that i dont really need anymore and i can replace some code at that location. Keeping the block intact.

@atomix said:
For such case like @zzuegg metioned ( merging 2 or more shaders GLSL code), it's definitely hard...

Merging isnt such a big issue, i dont see it as one.

Anyway i manged today to hotswap some shader code and create material in runtime tho the results are not so spectacular, no injection is mentioned just some shader source changes. I`m currently working o a framework gonna post an example this monday ^^

@Setekh, I’m eager to learn what you’ve accomplished by then.

Just don’t end up doing it like Ogre, they have no controls what so ever. You can just write strings and attach them to the shader whenever. Some libs just generate the whole shaders that way. I’ve even seen them go into shaders and just cram strings directly into the buffers, with only a few checks to what uniforms are being used etc.



It’s considered good practice too, I think. At least it seemed so before.

Kind of late, had some issues, But better late then never right?



Here’s the injector at work, the cube uses a normal lighting shader while the sphere uses a modified instance of it.





Achieving this was not a big task(6h in total), atm I’m kind of “hacking” the material meaning there’s no core modification what so ever. I don’t really like how i made it this far, but well see where it go’s when ill release it.



Heres how i’m making the script:

[java]

Material matSphere = new Material(assetManager, “Common/MatDefs/Light/Lighting.j3md”);



InjectorScript script = _injector.createScript(matSphere, “Modifyed “+matSphere.getName()+” with Rim Shader injected.”);

script.setEditingTechnique(“Default”);

script.addDefine(“RimLighting”, “RIM_LIGHTING”);

script.setParam(“RimLighting”, VarType.Vector4, null, FixedFuncBinding.Color);

script.setShaderEditor(ShaderType.Fragment, “#define ATTENUATION”);



StringBuffer code = new StringBuffer();

code.append("#ifdef RIM_LIGHTINGn");

code.append(" vec4 rim = pow( 1.0 - dot( normal, vViewDir.xyz ), 1.5 ) * m_RimLighting * m_RimLighting.w;n");

code.append(" rim.a = 0.0;n");

code.append(“gl_FragColor += rim*diffuseColor;n”);

code.append("#endifn");



script.inject(“uniform vec4 m_RimLighting;n”, 14, 0);

script.inject(code, 277, 0);



//script.forward();

//script.back(); // switches back to the prev editor.

//script.detach();

script.attach();



matSphere = script.compile();

System.out.println("Compiled material: "+matSphere);



matSphere.setTexture(“DiffuseMap”, assetManager.loadTexture(“Interface/Logo/Monkey.jpg”));

matSphere.setColor(“RimLighting”, ColorRGBA.Orange.mult(1.3f));

[/java]

This code is placed in simpleInit() and when i invoke a InjectorScript method, an action is made that action gets to compose an sql string and when the shader files are getting loaded by the asset manager(they go through that AssetManagerHook class you see in my workspace) the sql gets to do it’s business and parse the shader source.

Ugly as fuck, but i’m hoping to make a script file later on, or something; I’m open for ideas.



Right now this injector is cool of a private project, but not something to be added in the core. But if i get clearance from the core devs i can make a new injector inside the core and code it how it should be, right now the injector script creates new matdef and techniquedef classes and thats not how it should act. I chose this since editing the lwjglrenderer is not how a contribution should look like and i can’t really detach/attach shader code with out editing the renderer.



@kwando And yeah… thats it for now :slight_smile:

2 Likes
@pspeed said:
I think everyone who has made more than one shader and had to go back and add something to every one of them has probably thought about fixing this problem. :)

I, too, have designed a "shaderlet" system where you can write code snippets (fraglets and vertlets) and have them assembled into composite shaders with auto-wiring. Tweaking my existing shaders has still been quicker than writing a shader composition system and so I haven't done anything beyond a design. Done properly, the code chunks are still editable in existing GLSL editors because they are still 100% valid GLSL code on their own.

I keep telling myself "someday" but motivation and time are at a premium right now. If my game income ever pays the bills then maybe.


Blender uses the same technique.
The best thing is that you can export any blender material to GLSL code and see all the building blocks no matter how simple your material is. The generated fragment shader will be some 56Kb plain text containing probably 100+ functions (a couple of thousand lines), the main() only calls the ones that needed for the particular material in the correct order (that's the only part generated on the fly). By using the same blocks you can get quite far without re-inventing the wheel or you can just get some ideas how others solved this problem...
I think the ogre exporter has an option to write the shader code to a file, if not there is a stand alone exporter:
http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Game_Engine/Export_GLSL

Sounds about as good as it will get, write every part in a function lbock you might ever need, and only generate the main new, and wire the functions there. Is pretty extensible as well as you could add new blocks on demand. Only question is, how wil graficcards react to so much unused code? will the compiler just remove it? also what about uniform counts (do current drivers compile them out as well if not used?)?

I think they do, they even remove unused variables inside functions so removing whole functions should be peanuts for them - at least NVidia’s compiler (I think ATI’s too) does many things with ‘#pragma optimize(on)’ e.g. removing unused uniforms.