Help with first shader attempts

For the background i want to have an animated door, that is opening and closing then door should be animated.
Right now it is pretty simple, the door is a texture painted on a quad, so i tried to seperate the parts of the door and paint them on other quads to move them arround.

While this works, it looks ugly because of z-fighting when the parts overlap. Also it is not a small feat to have a simple triggerable animation that just moves arround some geometries, at least i found no easy way, so i thought this is a good start to dig into shaders.

The door looks something like this:
[java]
// ==========
// |########|
// |######++|
// |###+++++|
// |++++++++|
[/java]
The = and | parts are the frame that should stay where it is and allways be on top.

The # part should move upwards.

The + part should move downwards.

I started reading through https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:jme3_shadernodes and while i understand the general concept this page talks about a .j3sn file without any clue how to use it. The IDE offers no template for this filetype and i have found no refference to that filetype anywhere in assetmanager or other articles. It might be an idea to give an example here on how to actualy use those kind of files. But leaving that aside i thought if can just edit a material definition to do what i want.

For this i took the Lighting.j3md because i want to have the nice light effects included in this. I copied the file into my project together with the Lighting.frag and Lighting.vert so i can create my own version (and renaming them).

To create this animation i thought i put in a few textures, one for the base frame of the door and a few for the parts that can be moved. Also i needed a trigger to start the animation, so i added texture attributes and a boolean to the definition file:
[java]
MaterialParameters {

    // Trigger to open door
    Boolean Open : false
    // Texture that is static door base
    Texture2D BaseFrame
    // Texture to slide upwards
    Texture2D SlideUp
    // Texture to slide downwards
    Texture2D SlideDown
    // Texture to slide left
    Texture2D SlideLeft
    // Texture to slide right
    Texture2D SlideRight

    .....

[/java]

Thinking the best way for this to work would be to just put together the drawn image from those textures the best place to do this is the fragment shader, so i put the new attributes in the define of the technique and added them to the .frag file:
Definition file:
[java]
Technique {

    LightMode MultiPass

    VertexShader GLSL100:   MatDefs/LightingDoor.vert
    FragmentShader GLSL100: MatDefs/LightingDoor.frag

    WorldParameters {
        WorldViewProjectionMatrix
        NormalMatrix
        WorldViewMatrix
        ViewMatrix
        CameraPosition
        WorldMatrix
    }

    Defines {
        ...
        // added these
        OPEN : Open
        BASE_FRAME : BaseFrame
        SLIDE_UP : SlideUp
        SLIDE_DOWN : SlideDown
        SLIDE_LEFT : SlideLeft
        SLIDE_RIGHT : SlideRight

        ...
    }
}

[/java]
Fragment shader:
[java]
#ifdef BASE_FRAME
uniform sampler2D m_BaseFrame;
#endif

#ifdef SLIDE_UP
uniform sampler2D m_SlideUp;
#endif

#ifdef SLIDE_DOWN
uniform sampler2D m_SlideDown;
#endif

#ifdef SLIDE_LEFT
uniform sampler2D m_SlideLeft;
#endif

#ifdef SLIDE_RIGHT
uniform sampler2D m_SlideRight;
#endif
[/java]

So at this point i should have the textured passed to the fragment shader and con work with them.

Looking at the code of the Lighting.frag file i choosed this place to be the best spot, because when i want to compose the drawn texture out of the other textures i don’t need a diffuse map, and i don’t need the stuff done with normal maps etc in front of this block.
Original:
[java]
#ifdef DIFFUSEMAP
vec4 diffuseColor = texture2D(m_DiffuseMap, newTexCoord);
#else
vec4 diffuseColor = vec4(1.0);
#endif
[/java]

And edited it so i compose the pixel stored in diffuseColor from my textures.

The problem right now is that nothing happens. I created a material file to test it and added the textures for base frame, part that moves up and part that moves down.

In the small preview window in the IDE nothing is shown and i don’t get an error in the output window, so i dont have a clue what is wrong right now.
[java]
#ifdef DIFFUSEMAP
vec4 diffuseColor = texture2D(m_DiffuseMap, newTexCoord);
#else
vec4 diffuseColor = vec4(1.0);
#ifdef SLIDE_UP
newTexCoord = texCoord;
diffuseColor.rgb += texture2D(m_SlideUp, newTexCoord).rgb * texture2D(m_SlideUp, newTexCoord).a;
#endif
#ifdef SLIDE_DOWN
newTexCoord = texCoord;
diffuseColor.rgb += texture2D(m_SlideDown, newTexCoord).rgb * texture2D(m_SlideDown, newTexCoord).a;
#endif
#ifdef SLIDE_LEFT
newTexCoord = texCoord;
diffuseColor.rgb += texture2D(m_SlideLeft, newTexCoord).rgb * texture2D(m_SlideLeft, newTexCoord).a;
#endif
#ifdef SLIDE_RIGHT
newTexCoord = texCoord;
diffuseColor.rgb += texture2D(m_SlideRight, newTexCoord).rgb * texture2D(m_SlideRight, newTexCoord).a;
#endif
#ifdef BASE_FRAME
diffuseColor.rgb += texture2D(m_BaseFrame, texCoord).rgb * texture2D(m_BaseFrame, texCoord).a;
#endif
#endif
[/java]

Also i am at a loss how to actualy do the animation.

My current thoughts are that at some point in the game when the player opens the door i get the material from the geometry and just set the Open attribute to true.

At that point in the fragment shader i start moving the defined parts according to the passed time until the part is completely outside of the base texture.

So do i still need to define the Open attribute inside the fragment shader like i did with the textures, or can i just use something like if (Open) ?

Also i think need to check if the parts are allready outside of the base frame so i don’t need to get a pixel from it any more.
For this i thought i can use an internal variable that stores when the animation started and a boolean that it has started, then i could calculate the time that has passed since the animation was triggered and move the coordinated to get the pixel from the part textures accordingly.

Question there is can i define a variable inside the vertex shader that stores this ?

I think i have to add Time to world parameters and can access that with g_Time then. So if i do that can i define the parameters in the vertex shader and so something like this ?
[java]
// at top of file
uniform float startTime;
uniform bool animationStarted;

    // inside main
    if ((m_Open && !animationStarted) || (!m_Open && !animationStarted))
        animationStarted = true;
        startTime = g_Time;
        
    float passedTime = g_Time - AnimationStarted;

[/java]

Then i just have to move the coordinates for getting the pixels out of the part trextures accordingly.
There i have my next problem, maybe i am too much used to code explicit and strict, but don’t i have to check if the given coorsinates are actualy inside the texture i am getting the pixel from ? What happens if i try to get (20, 80) from a texture that is actualy only 26x26 big ?

And can i get the height and width somehow from a uniform sampler2D so i can determine when to stop the animation ?

I hope someone can give me some clues on how to solve this.

Or maybe you say this is total :shit: to try it this way and can point me in a better direction :wink:
Also i have googled arround but don’t find a good reference to glsl functions and objects. I find tit-bits of information here and there but nothing put together like a javadoc where you can browse all attributes a uniform sampler2D has for example. Anyone knows a good address to look something like this up ?

Edit: Sorry for strange formating of the text, but it seems somehow after a code tag the forum ignores empty lines and line breaks.

1 Like

First, let me say that it was really great to post everything you tried. Seriously. It’s very helpful for anyone who has the time to sift through it.

Unfortunately, I don’t have time right now to point out all of the issues one by one but I will try to at least give a hint as to the problem with your approach.

Defines control what is included in the shader when it is compiled. It’s like a conditional cut and paste. It’s a little strange to use it how you are but I suppose it’s not wrong.

Your main issue (I think) is that you never actually do anything frame based. Regardless of how you set these values the shader is going to render the same thing from one frame to the next. It will be different things based on how the defines are set but once you’ve set a define then nothing will change.

If you want to animate the texture then you will need to animate it. Change the texture coordinates from frame to frame, or the colors from frame to frame, etc… Right now you aren’t doing any of that. Shaders do not keep and accumulate values from one run to the next. The run once and forget everything…after all, the .frag shader is being run for every pixel rendered.

The easiest way to animate in a shader is to pass it a uniform for how far open or closed you want your door and then render it that way. For a door that slides in a fancy way like yours it will be tough, though. Animating geometry is actually easier. Fading in and out or from one to the other would be easy (just pass a float as a uniform). Sliding the whole texture would be easy. Complex animation is not so easy… you’d probably need multiple textures for each frame you want to display.

Also, from a collision detection perspective the polygon will still be there even when it’s “open”.

Thanks @pspeed but i think you have not read to the end. I wrote that the animation part is actualy one of my problems right now.

I want to slide whole texture, thats why i have a texture for each part of the door.

The SlideDown part is completely transformed downwards during the animation, it is the same size as the base and everything that is not part of this door part has an alpha value of 0, so it should not be mixed into the resulting texture.

But even the code like it is right now does not seem to work. I thout i test it first by painting the textures without animation, but even that does not work right now, and i am not sure why. I defined the textures, defined them to be passed to the shader, have a variable in the shader for it… so i have no clue where the problem is.

But you aren’t actually animating anything in the shader, is my point. I understood your post but you aren’t really understanding my answer.

There is nothing in any of the shader code posted that ever changes from frame to frame. So there will be no animation.

If you want the texture to slide then you will have to slide the texture coordinates based on time or something. The easiest thing would be to do away with all of the defines and just pass a texture offset as a uniform. But your doors won’t slide independently if they are all in the same texture. That’s actually hard (nearly impossible) to do with this approach. Though I’m just going from your ascii art. You could try posting the actual pictures or something maybe that will make it clearer.

If you want separate sliding doors then it’s actually much easier to do with conventional geometry animation and just fix the z-fighting problem.

Edit: and I understood you are asking at the end for exactly what I’m telling you is missing… but I was giving you the hint that everything you’ve done up to that point is unnecessary. You could animate the texture simply by newTexCoord.x += g_Time.

…and whatever way you end up tracking the frame time to pass to the shader could just as easily be used to move the geometry.

I have to step away for a while so I want be able to provide step by step hints anymore… so I will go back to basics.

Start from the beginning without any of your modifications.

Add a Vec2 uniform “TextureOffset” to your material parameters and to the shader. (TextureOffset in the material parameters, vec2 m_TextureOffset in the shaders)

Change this:
[java]
#ifdef DIFFUSEMAP
vec4 diffuseColor = texture2D(m_DiffuseMap, newTexCoord);
#else
vec4 diffuseColor = vec4(1.0);
#endi
[/java]

To this:
[java]
#ifdef DIFFUSEMAP
newTexCoord += m_TextureOffset;
vec4 diffuseColor = texture2D(m_DiffuseMap, newTexCoord);
#else
vec4 diffuseColor = vec4(1.0);
#endi
[/java]

Now, pass a Vector2f to the shader that you change each frame and your texture will move.

From there you should be able to figure out the rest. And/or decide to take that Vector2f and just move the geometry instead.

1 Like

Ok, understood that, but that only moves the diffusemap arround and i am stuck with one single texture.

What i had in mind was seperating the whole door into several parts.

From the ascii image, the frame that are the = and | parts are no moving at all and are in a seperated testure. Then in another texture i have the # parts, and that one moves. And yet in another texture i have the + parts which also move.

But reading through i realized that i had a mistake in my thinking, i thought i had to stay away from the diffusemap because i did not want to modify any of the textures, but essentialy they arent touched anyways, the pixel created by the shader is just composed out of the different textures.

Is the composition right for that ?
[java]diffuseColor.rgb += texture2D(m_SlideUp, newTexCoord).rgb * texture2D(m_SlideUp, newTexCoord).a;[/java]
From how i understand it this should get the rgb values of the m_SlideUp depending on their alpha value and add those to the rgb of the pixel that is later drawn onto the screen, or is this calculation wrong ?
I wonder what happens if those values go beyond 1f tho… or is that handled by the gpu then ?

But honestly, do this by animating the mesh not as a shader.

Look at the custom geometry stuff - you may even be able to do it just by moving and/or scaling a geometry depending on just what effect you are going for!

If I understand what you are trying to do right, you want some kind of iris effect as the door opens? You will have to modify four separate sets of texture coordinates (all based on the original) one set for each of the four doors. Then always combine all four using the alpha as you are doing… the thing is that where they overlap you will be mixing the colors.

So really you will have to do a conditional on a != 0 and just set the value directly instead of adding it. Then the last one wins.

…note: you could still mix them in the alpha != 1 case so that you don’t end up with as weird of aliasing at the door edges.

@zarch thats what i have done before and it caused ugly z-fighting. The problem here is that it would not look good in the world if i move a quad a little back so that no z-fighting occurs, thats why i was thinking to do it by shader. The other thing is that i might have a lot of doors in my level, some usable by the player, some triggered by a switch.

I was thinking to use MotionPaths set up for each door and trigger them at the right time. The ugly thing there was that MotionPaths can’t be attached to the Nodes like an AnimationControler, so i would have to create a custom list of those, search the ones that are responsible for the door i want to open and then trigger them. I was looking for a “nicer” way to do this.

@pspeed actualy i don’t want anything as fancy as an iris effect. Think of the classical hatch in many sci-fi movies, where the hatch has a bottom part that is lowered into the floor and a top part that is raised into the ceiling. All my doors are either like this or even only have one part that gets raised into the ceiling. So all i probably need are the textures for the door parts and the offsets for those.

What i still don’t get is what happens when the coordinate of the pixel i draw is actualy putside of the texture that has an offset ?

So lets say i draw pixel (1,1). I have raised the door part 10 pixels, so i would actualy try to get pixel (1, -11) from the door part texture… will this cause an array out of bounds error ? Or does the shader wrap the texture and start at the top then ? Or will i just get a zero vec4 ?

Why does it cause z fighting? Just don’t overlap the doors?

You set the wrap modes on the texture and it can do whatever you like :slight_smile:

<cite>@zarch said:</cite> Why does it cause z fighting? Just don't overlap the doors?

You set the wrap modes on the texture and it can do whatever you like :slight_smile:

I can’t in this setting. The walls are just a quad “thick”, so basicaly no thickness at all. But the door can be seen from both sides and there are some spots where the door should fit the wall on both sides. If i move the parts a bit behind the frame it would overlap it from one side and be behind it from the other side.

Mmh somehow i am at a loss… edited the .j3md like this:
[java]
// Texture to slide upwards
Texture2D SlideUp
Vector2 SlideUpOffset
// Texture to slide downwards
Texture2D SlideDown
Vector2 SlideDownOffset
// Texture to slide left
Texture2D SlideLeft
Vector2 SlideLeftOffset
// Texture to slide right
Texture2D SlideRight
Vector2 SlideRightOffset

  // .....
    Defines {
        // ....
        SLIDEUP : SlideUp
        SLIDEUPOFFSET : SlideUpOffset
        SLIDEDOWN : SlideDown
        SLIDEDOWNOFFSET : SlideDownOffset
        SLIDELEFT : SlideLeft
        SLIDELEFTOFFSET : SlideLeftOffset
        SLIDERIGHT : SlideRight
        SLIDERIGHTOFFSET : SlideRightOffset

        DIFFUSEMAP : DiffuseMap
        //....
    }

[/java]

Then in the .frag
[java]
#if defined(SLIDE_UP) && defined(SLIDEUPOFFSET)
uniform sampler2D m_SlideUp;
uniform vec2 m_SlideUpOffset;
#endif

#if defined(SLIDEDOWN) && defined(SLIDEDOWNOFFSET)
uniform sampler2D m_SlideDown;
uniform vec2 m_SlideDownOffset;
#endif

#if defined(SLIDELEFT) && defined(SLIDELEFTOFFSET)
uniform sampler2D m_SlideLeft;
uniform vec2 m_SlideLeftOffset;
#endif

#if defined(SLIDERIGHT) && defined(SLIDERIGHTOFFSET)
uniform sampler2D m_SlideRight;
uniform vec2 m_SlideRightOffset;
#endif

// inside main
vec2 partTexCoord;

#ifdef DIFFUSEMAP
  #if defined(SLIDEUP) &amp;&amp; defined(SLIDEUPOFFSET)
    partTexCoord = texCoord + m_SlideUpOffset;
    diffuseColor.rgb += texture2D(m_SlideUp, partTexCoord).rgb * texture2D(m_SlideUp, partTexCoord).a;
  #endif

  #if defined(SLIDEDOWN) &amp;&amp; defined(SLIDEDOWNOFFSET)
    partTexCoord = texCoord + m_SlideDownOffset;
    diffuseColor.rgb += texture2D(m_SlideDown, partTexCoord).rgb * texture2D(m_SlideDown, partTexCoord).a;
  #endif           

  #if defined(SLIDELEFT) &amp;&amp; defined(SLIDELEFTOFFSET)
    partTexCoord = texCoord + m_SlideLeftOffset;
    diffuseColor.rgb += texture2D(m_SlideLeft, partTexCoord).rgb * texture2D(m_SlideLeft, partTexCoord).a;
  #endif           

  #if defined(SLIDERIGHT) &amp;&amp; defined(SLIDERIGHTOFFSET)
    partTexCoord = texCoord + m_SlideRightOffset;
    diffuseColor.rgb += texture2D(m_SlideRight, partTexCoord).rgb * texture2D(m_SlideRight, partTexCoord).a;
  #endif  
  diffuseColor.rgb += texture2D(m_DiffuseMap, newTexCoord).rgb * texture2D(m_DiffuseMap, newTexCoord).a;
#else
  diffuseColor = vec4(1.0);
#endif

[/java]

And tried it out in a scene:
[java]
Node added = new Node(“Door”);
Mesh quad = new Quad(8, 8);
Geometry geom = new Geometry(“DoorBase”, quad);
Material mat = new Material(assetManager, “MatDefs/LightingDoor.j3md”);
mat.setTexture(“DiffuseMap”, assetManager.loadTexture(“Textures/SSIR/Doors/MedicDoorBase.png”));
mat.setTexture(“SlideUp”, assetManager.loadTexture(“Textures/SSIR/Doors/MedicDoor1.png”));
mat.setVector2(“SlideUpOffset”, Vector2f.ZERO);
mat.setTexture(“SlideDown”, assetManager.loadTexture(“Textures/SSIR/Doors/MedicDoor2.png”));
mat.setVector2(“SlideDownOffset”, Vector2f.ZERO);
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
mat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
geom.setMaterial(mat);
geom.setQueueBucket(RenderQueue.Bucket.Transparent);
added.attachChild(geom);
geom.move(-4, 0, 0);
rootNode.attachChild(added);
[/java]

But i get this error:
[java]
com.jme3.renderer.RendererException: compile error in:ShaderSource[name=MatDefs/LightingDoor.frag, defines, type=Fragment, language=GLSL100] error:0(306) : error C1008: undefined variable “m_SlideUpOffset”
0(307) : error C1008: undefined variable “m_SlideUp”
0(307) : error C1008: undefined variable “m_SlideUp”
[/java]

Did i do something wrong with the naming or something like that ?

Rule number one: never use defines. When you change them they recompile the shader. (Note: all of my advice is caveated with the rule that you can go against the main rule if you are advanced enough to explain why.)

So, stop using defines. Just stop. You don’t need them. Use uniforms.

Rule number two: read carefully the posts people have already taken the time to write. I gave an exact example in one of them of setting up a texture offset. That example wasn’t supposed to be the end product but something to get working so that you could build off of it. It definitely wasn’t meant to be ignored… so you might want to check it again.

1 Like

Don’t have 0 thickness walls…they aren’t very realistic anyway.

If you must have 0 thickness walls then as I already said animate the mesh and you can retract the doors without every overlapping with anything.

@pspeed i read what you answered me earlier, thats why i put in those offsets, i just did not use them for the diffuse map but for the textures for the parts. And i thought i need the defines because the textures and vectors might not be set, and then this would cause an error when accessing them ?

@zarch you mean just shrink the quad along the y-axis ? well that was a thought too, but this was squeezing the texture on the quad.

didn’t read this thread properly but re:

@zarch you mean just shrink the quad along the y-axis ? well that was a thought too, but this was squeezing the texture on the quad.

you can scale the texture coordinates

Move the top 2 vertices down.
Move the TEXTURE COORDINATES of the bottom 2 vertices down by the same amount.

Result is a clean sliding door.

Do the same for each other sliding door in each direction.

1 Like

When moving the top verticles down shouldnt the bottom texture cordnates be moved up, so they start further up in the texture ?

Anyway, i think i managed the shader solution now. Sliding works fine and looks good once i thrown out all the define stuff like pspeed said.

Just have to manage the offsets in update loop then, but thats ok. It probably is not the best solution, or a realy pretty one, but it’s a working one and i learned quite a bit about shaders while trying this stuff out :wink: Thanks @pspeed for you patience and @zarch for your suggestions :wink:

Edit: I now have simply added the textures and offset vectors to the material definition and the frag shader and have this in the main of the frag shader:
[java]
vec4 diffuseColor;
vec2 partTexCoord;

#ifdef DIFFUSEMAP
  // handle sliding up part
  partTexCoord = texCoord + m_SlideUpOffset;
  diffuseColor.rgb += texture2D(m_SlideUp, partTexCoord).rgb * texture2D(m_SlideUp, partTexCoord).a;
  // handle sliding down part
  partTexCoord = texCoord + m_SlideDownOffset;
  diffuseColor.rgb += texture2D(m_SlideDown, partTexCoord).rgb * texture2D(m_SlideDown, partTexCoord).a;
  // handle sliding left part
  partTexCoord = texCoord + m_SlideLeftOffset;
  diffuseColor.rgb += texture2D(m_SlideLeft, partTexCoord).rgb * texture2D(m_SlideLeft, partTexCoord).a;
  // handle sliding right part
  partTexCoord = texCoord + m_SlideRightOffset;
  diffuseColor.rgb += texture2D(m_SlideRight, partTexCoord).rgb * texture2D(m_SlideRight, partTexCoord).a;
  // put door frame on top
  diffuseColor.rgb += texture2D(m_DiffuseMap, newTexCoord).rgb * texture2D(m_DiffuseMap, newTexCoord).a;
#else
  diffuseColor = vec4(1.0);
#endif

[/java]

Trying it out with the different door assets i got, might tweak a bit here and there but i guess i can manage that from here :wink: