Why does this basic Shader Node fail?

Hello, title says it all.

I decided to give the Shader Node system a try and wrote a tiny piece of code:

testshader.frag

void main() {
    outColor = inColor;  
}

testshader.j3sn

ShaderNodeDefinitions{
	ShaderNodeDefinition testshader {
		Type: Fragment
		Shader GLSL100: Shaders/testshader.frag
		Documentation{
			My custom shader.
            @input inColor the input color       
            @output outColor the resulting color
		}
		Input {
			vec4 inColor
		}
		Output {
			vec4 outColor
		}
	}
}

testshader.j3md

MaterialDef Simple {
    MaterialParameters {
        Color Color
    }
    Technique {
        WorldParameters {
            WorldViewProjectionMatrix
        }
        VertexShaderNodes {
            ShaderNode CommonVert {
                Definition : CommonVert : Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn
                InputMappings {
                    worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix
                    modelPosition = Global.position.xyz
                }
                OutputMappings {
                    Global.position = projPosition
                }
            }
        }
        FragmentShaderNodes {
			ShaderNode testshader {
				Definition : testshader : Shaders/testshader.j3sn
				InputMappings {
					inColor = MatParam.Color
				}
				OutputMappings {
					Global.color = outColor
				}
			}
        }
    }
}

When I try to start my test game, I get

java.lang.ArrayIndexOutOfBoundsException: 1
	at com.jme3.shader.ShaderGenerator.appendNodeDeclarationAndMain(ShaderGenerator.java:176)
	at com.jme3.shader.ShaderGenerator.generateDeclarationAndMainBody(ShaderGenerator.java:150)
	at com.jme3.shader.ShaderGenerator.buildShader(ShaderGenerator.java:121)
	at com.jme3.shader.ShaderGenerator.generateShader(ShaderGenerator.java:87)
	at com.jme3.asset.DesktopAssetManager.loadShader(DesktopAssetManager.java:405)
	at com.jme3.material.Technique.loadShader(Technique.java:218)
	at com.jme3.material.Technique.makeCurrent(Technique.java:202)
	at com.jme3.material.Material.selectTechnique(Material.java:943)
	at com.jme3.material.Material.autoSelectTechnique(Material.java:951)
	at com.jme3.material.Material.render(Material.java:1070)
	at com.jme3.renderer.RenderManager.renderGeometry(RenderManager.java:541)
	at com.jme3.renderer.queue.RenderQueue.renderGeometryList(RenderQueue.java:322)
	at com.jme3.renderer.queue.RenderQueue.renderQueue(RenderQueue.java:374)
	at com.jme3.renderer.RenderManager.renderViewPortQueues(RenderManager.java:781)
	at com.jme3.renderer.RenderManager.flushQueue(RenderManager.java:737)
	at com.jme3.renderer.RenderManager.renderViewPort(RenderManager.java:1001)
	at com.jme3.renderer.RenderManager.render(RenderManager.java:1047)
	at com.jme3.app.SimpleApplication.update(SimpleApplication.java:252)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
	at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:185)
	at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:228)
	at java.lang.Thread.run(Thread.java:744)

I simply wanted to assign the input color to the ouput color, the most simple shader node I could think of.

Did I do something wrong?

hehe nice…
actually the problem is the space between the “void main()” and the “{” in the frag shader source.

gonna fix this, but I guess, you get what’s the workaround ;).

1 Like

I’ve just pushed a fix for that, now the matching should be a lot more tolerant to spaces, and the error thrown in case it still fail is more explicit.

1 Like

Thank you very much for the quick fix.

After I’ve been playing around for some time, I have to say using the system isn’t that easy because of bugs - especially in the editor. :expressionless:
I often need to restart the SDK when using invalid shader nodes or deleting something.

I think I’ll try to use it a bit longer and post bugs here; perhaps I a few that weren’t known and are fixed easily.

Sooo…

When I create a new .j3md, switch to the editor and delete the MatParam, I get a flood of exceptions in the bottom right:
“Exception: Invalid operation (1282)”

If I look at it closer, it says:

org.lwjgl.opengl.OpenGLException: Invalid operation (1282)
    at org.lwjgl.opengl.Util.checkGLError(Util.java:59)
    at com.jme3.ystem.lwjgl.LwjglOffscreenBuffer.checkGLError(LwjglOffscreenBuffer.java:98)
    at com.jme3.ystem.lwjgl.LwjglOffscreenBuffer.runLoop(LwjglOffscreenBuffer.java:126)
    at com.jme3.ystem.lwjgl.LwjglOffscreenBuffer.run(LwjglOffscreenBuffer.java:151)
    at java.lang.Thread.run(Thread.java:744)

After about 3000 of them, the SDK gets unusable because it now takes more than 10 seconds to respond to my actions. I usually need to use the TaskManager at that point.

There is one other type of exception that comes all the time, but I couldn’t quite see it because of the flood of the other exceptions. I think it may be “could not recomplie shader”

Yeah this is an issue I don’t get. For some reasons when there is an exception in the render, the SDK keeps running it forever and the errors are stacking up.

The editor is really as a WIP right now, there are lots of issues and not much time on my side to tackle them.
However, the system in the engine should work pretty well, and I’d really like to have more people to test it thoroughly.

@nehon said: The editor is really as a WIP right now, there are lots of issues and not much time on my side to tackle them.
Totally understandable.
@nehon said: However, the system in the engine should work pretty well, and I'd really like to have more people to test it thoroughly.
Ok, I will continue to play around then. I'm a programmer after all, so I'll edit the .j3md manually when the editor doesn't play along.

I know you don’t have much time at the moment, but maybe there is some kind of todo list you can add this to:

What about defining constants in the .j3md?
For example let’s say I use a shader node like DiffuseLighting, which requires a DiffuseColor.
Maybe I don’t having to set this via material every time, but rather supply the node with my predefined white color.

Just a thought.

You can already do that in the j3md
Color DiffuseColor : 1,1,1,1

What comes after the “:” is the default value

Ah ok, I didn’t know that.
It isn’t in the node editor and I never noticed this in other .j3mds.
Also it isn’t displayed in the material editor.

Another thing:

This results in black.

However, both Color and AmbientLightColor are white. I confirmed this through removing the ColorMult node and connecting either Color or AmbientLightColor to the output color.
The generated fragement shader looks like this:


uniform vec4 m_Color;
uniform vec4 m_Color2;


void main(){
		vec4 Global_color = vec4(1.0);


	//ColorMult : Begin
	vec4 ColorMult_outColor;

    ColorMult_outColor = m_Color * m_Color2;
	Global_color = ColorMult_outColor;
	//ColorMult : End

	gl_FragColor = Global_color;
}

I can’t see anything wrong with it, though I’m not a shader guru.

AmbientLightColor only works if you have an ambient light, the test scene does not. So I guess it’s black

Ah, but I have my own test scene with Ambient Light.
There it does work! If I only connect the AmbientLightColor to the output color, it appears white in my test scene.

I noticed something else:
When I use the AlphaDiscard Node, this code gets generated:

	//AlphaDiscard : Begin
	float AlphaDiscard_alpha = Global_color.x;

    if( AlphaDiscard_alpha <= m_AlphaDiscardThreshold )discard;
	//AlphaDiscard : End

I think there might be a bug here: Isn’t Global_color.x the red component?

It might be because a float input is needed and I simply drag the blue arrow to it, without specifying which color channel should be used.

There is one other thing:

I have a small piece of fragment shader:

void main() {
    if (dist > 30.0) {discard;}
}

This code does not get included in the fragment shader.

Only when I add an inColor and outColor, the above piece gets included (I don’t need to use inColor or outColor, just connect them with the blue arrow).
I guess there is some wrong optimization going on?

@m41q said: I noticed something else: When I use the AlphaDiscard Node, this code gets generated: [code] //AlphaDiscard : Begin float AlphaDiscard_alpha = Global_color.x;
if( AlphaDiscard_alpha <= m_AlphaDiscardThreshold )discard;
//AlphaDiscard : End

[/code]

I think there might be a bug here: Isn’t Global_color.x the red component?

It might be because a float input is needed and I simply drag the blue arrow to it, without specifying which color channel should be used.


yes you have to specify the component (I think you can in the editor can’t you?)

@m41q said: There is one other thing:

I have a small piece of fragment shader:

void main() {
    if (dist > 30.0) {discard;}
}

This code does not get included in the fragment shader.

Only when I add an inColor and outColor, the above piece gets included (I don’t need to use inColor or outColor, just connect them with the blue arrow).
I guess there is some wrong optimization going on?


If there is no input nor output the node is removed, but your dist variable should have an input doesn’t it?

@nehon said: yes you have to specify the component (I think you can in the editor can't you?)
No, in the editor you are neither asked to choose nor did I find an option to specify it. Your statement made me look into the .j3md text file and there you can.
@nehon said: If there is no input nor output the node is removed, but your dist variable should have an input doesn't it?
Yes, the dist variable is defined as input. It doesn't get included in the shader code nevertheless.

I thought hard about this AmbientLightColor issue but I could not find a mistake in my scene, material or shader.
So here is my j3md, “newMatDef1.j3md”:

MaterialDef Simple {
    MaterialParameters {
    }
    Technique {
        WorldParameters {
            WorldViewProjectionMatrix
            AmbientLightColor
        }
        VertexShaderNodes {
            ShaderNode CommonVert {
                Definition : CommonVert : Common/MatDefs/ShaderNodes/Common/CommonVert.j3sn
                InputMappings {
                    worldViewProjectionMatrix = WorldParam.WorldViewProjectionMatrix
                    modelPosition = Global.position.xyz
                }
                OutputMappings {
                    Global.position = projPosition
                }
            }
        }
        FragmentShaderNodes {
            ShaderNode ColorMult {
                Definition : ColorMult : Common/MatDefs/ShaderNodes/Basic/ColorMult.j3sn
                InputMappings {
                    color2 = Global.color
                    color1 = WorldParam.AmbientLightColor
                }
                OutputMappings {
                    Global.color = outColor
                }
            }
        }
    }
}

My goal is to simply display the AmbientLightColor.

I use the shader in this material “customLightTree.j3md”:

Material My Material : MatDefs/newMatDef1.j3md {
     MaterialParameters {
     }
    AdditionalRenderState {
      FaceCull Off
    }
}

Nothing special, as you can see.

I initialize the scene in an AppState. The only thing that happens in the main game init is this AppState getting attached:
[java]
@Override
public void initialize(AppStateManager stateManager, Application app) {
snNode = new Node(“snNode”);

	AmbientLight ambLight = new AmbientLight();
	ambLight.setColor(ColorRGBA.White.mult(10f));
	((Main) app).getRootNode().addLight(ambLight);
	
	DirectionalLight dlLight = new DirectionalLight();
	dlLight.setDirection(new Vector3f(1f, -1f, -0.5f));
	snNode.addLight(dlLight);
	
	Spatial tree = app.getAssetManager().loadModel("Models/tree_maple_2.j3o");
	tree.setMaterial(app.getAssetManager().loadMaterial("Materials/tree_maple_lt_1.j3m"));
	tree.setName("tree");
	snNode.attachChild(tree);
	
	Spatial lightedTree = app.getAssetManager().loadModel("Models/tree_maple_2.j3o");
	lightedTree.setMaterial(app.getAssetManager().loadMaterial("Materials/lightedTree.j3m"));
	lightedTree.setName("lightedTree");
	snNode.attachChild(lightedTree);
	
	Spatial customLightTree = app.getAssetManager().loadModel("Models/tree_maple_2.j3o");
	customLightTree.setMaterial(app.getAssetManager().loadMaterial("Materials/customLightTree.j3m"));
	customLightTree.setName("customLightTree");
	snNode.attachChild(customLightTree);
	
	((Main) app).getRootNode().attachChild(snNode);
}

[/java]

The failing object is the customLightTree. It’s displayed completely black.
When I replace the AmbientLightColor with a Material Color, it works and the whole tree is blue or white or whatever I specify.
It doesn’t matter if I add ambLight to the rootNode or to the snNode.

@m41q said: No, in the editor you are neither asked to choose nor did I find an option to specify it. Your statement made me look into the .j3md text file and there you can.
you can select the "wire" between nodes and changes the properties directly in the propeties panel (see on the right panel, i've set the right swizzle to a)

For the other issue, I’m gonna look into it, but I guess AmbientLightColor is particular and that you have to set the LightMode to multiPass so that it’s fed to the shader (like for the Lighting material)

@nehon said: However, the system in the engine should work pretty well, and I'd really like to have more people to test it thoroughly.

I gave it a try some weeks back when I wanted to do an experimental shader, but it fell on some exception (seemed NB related). I don’t think I posted it, but I’ll see if I can dig it up and do some more testing. The feature in itself is really awesome!

I’m gonna change how the shader is evaluated using the new MaterialDebugAppState, so there shouldn’t be the stacked errors and all.