Advertiser (Shader)

Here is a slamm shader i did today, just for fun.
I don’t know the english name of this thing (used to display different advertising) … hell, i don’t even know the french name of it, if it has one.

The result first:

Here is the code. It could/should be improved but i was tired of doing a lot of thing and let them in a non-finished state on my computer, i wanted to have at least something released even if not perfect.
There IS thing in the code that are not usefull and comes from the copy past of the unshaded shader. However i didn’t have time to remove and try and remove and try again.

Advertiser.frag

#if defined(HAS_GLOWMAP) || defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) &&  !defined(SEPARATE_TEXCOORD))
    #define NEED_TEXCOORD1
#endif

#if defined(DISCARD_ALPHA)
    uniform float m_AlphaDiscardThreshold;
#endif

uniform vec4 m_Color;
uniform sampler2D m_Texture1;
uniform sampler2D m_Texture2;

uniform int m_Fan;
uniform float m_Percent;
varying vec2 texCoord;
varying vec4 vertColor;

void main() {

    vec4 color = vec4(1.0);

    float val;
    float percent;
    float base;

    #ifdef VERTICAL
      val = texCoord.y;
    #else
      val = texCoord.x;
    #endif

    val *= m_Fan;
    
    base = floor(val);
    val -= base;
    base /= m_Fan;
    

    #ifdef DIRECTION
      percent = 1 - m_Percent;
    #else
      percent = m_Percent;
    #endif

    
    if ( val <= percent )
    {
      #ifdef STRETCH
        #ifdef VERTICAL
          color *= texture2D(m_Texture1, vec2(texCoord.x, base + (texCoord.y-base) / percent  ));
        #else
          color *= texture2D(m_Texture1, vec2(base + (texCoord.x-base) / percent, texCoord.y));
        #endif
      #else
        #ifdef VERTICAL
          color *= texture2D(m_Texture1, vec2(texCoord.x, texCoord.y - (percent-1)/m_Fan));
        #else
          color *= texture2D(m_Texture1, vec2(texCoord.x - (percent-1)/m_Fan, texCoord.y));
        #endif
      #endif
    }
    else
    {
      #ifdef STRETCH
        #ifdef VERTICAL
          color *= texture2D(m_Texture2, vec2(texCoord.x,  base + (val-percent) / ( (1-percent)*m_Fan) ));
        #else
          color *= texture2D(m_Texture2, vec2(base + (val-percent) / ( (1-percent)*m_Fan), texCoord.y));
        #endif
      #else
        #ifdef VERTICAL
          color *= texture2D(m_Texture2, vec2(texCoord.x, texCoord.y - (percent / m_Fan)));
        #else
          color *= texture2D(m_Texture2, vec2(texCoord.x - (percent / m_Fan), texCoord.y));
        #endif
      #endif
    }
    #ifdef HAS_VERTEXCOLOR
        color *= vertColor;
    #endif

    #ifdef HAS_COLOR
        color *= m_Color;
    #endif
    // Set the fragment color for example to gray, alpha 1.0
    
    gl_FragColor = color;
    
}

Advertiser.j3md

MaterialDef Simple {
    MaterialParameters {
        Boolean VertexColor (UseVertexColor)
        Boolean Direction
        Boolean Vertical
        Boolean Stretch
        Color Color
        Texture2D Texture1
        Texture2D Texture2
        Float Percent
        Int Fan
    }
    Technique {
        VertexShader GLSL100: Shaders/Advertiser/Advertiser.vert
        FragmentShader GLSL100: Shaders/Advertiser/Advertiser.frag

        WorldParameters {
            WorldViewProjectionMatrix
        }

        Defines {
            SEPARATE_TEXCOORD : SeparateTexCoord
            HAS_VERTEXCOLOR : VertexColor
            HAS_COLOR : Color
            DIRECTION : Direction
            VERTICAL : Vertical
            STRETCH : Stretch
        }
    }
}

Advertiser.vert

#import "Common/ShaderLib/Skinning.glsllib"


#if defined(HAS_COLORMAP) || (defined(HAS_LIGHTMAP) && !defined(SEPARATE_TEXCOORD))
    #define NEED_TEXCOORD1
#endif



uniform mat4 g_WorldViewProjectionMatrix;

attribute vec3 inPosition;
attribute vec2 inTexCoord;
attribute vec4 inColor;

varying vec2 texCoord;
varying vec4 vertColor;

void main() { 
    // Vertex transformation 
    texCoord = inTexCoord;
    
    #ifdef HAS_VERTEXCOLOR
        vertColor = inColor;
    #endif

    gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);

}

However this shader doesn’t work by itself (i didn’t found a way to have time inside a shader) so here is the controller i wrote to use its functionnality (basic, only two image, can be extended to more without too much difficulties)

Advertiser.java

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package avarra.misc;

import com.jme3.material.Material;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.texture.Texture;

/**
 *
 * @author Bubuche
 */
public class Advertiser extends AbstractControl
{
  private float sleepLength;
  private float animationLength;
  private float percent;
  
  private float sleep;
  
  private Material material;
  
  public Advertiser(float animationLength, float sleepLength)
  {
    this.animationLength = animationLength;
    this.sleepLength = sleepLength;
  }
  
  @Override
  public void setSpatial(Spatial spatial)
  {
    super.setSpatial(spatial);
    if ( spatial == null )
      return;
    
    material = ((Geometry) spatial).getMaterial();
  }

  @Override
  protected void controlUpdate(float tpf)
  {
    if ( percent == 1 )
    {
      sleep += tpf;
      if ( sleep >= sleepLength )
      {
        Texture t1, t2;
        t1 = material.getTextureParam("Texture1").getTextureValue();
        t2 = material.getTextureParam("Texture2").getTextureValue();
        
        material.setTexture("Texture1", t2);
        material.setTexture("Texture2", t1);
        
        percent = 0;
        material.setFloat("Percent", percent);
      }
    }
    else
    {
      sleep = 0;
      percent += tpf / animationLength;
      if ( percent > 1 ) percent = 1;
      
      material.setFloat("Percent", percent);
    }
  }

  @Override
  protected void controlRender(RenderManager rm, ViewPort vp)
  {
  }
  
}

And the application/main

AvertiserMain.java

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package avarra.misc;

import com.jme3.app.SimpleApplication;
import com.jme3.app.state.VideoRecorderAppState;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import java.io.File;

/**
 *
 * @author Bubuche
 */
public class AdvertiserMain extends SimpleApplication
{
  public static void main(String args[])
  {
    SimpleApplication sa = new AdvertiserMain();
    sa.start();
  }

  @Override
  public void simpleInitApp()
  {
    stateManager.attach(new VideoRecorderAppState(new File("advertiser.avi")));
    float w = 10;
    
    Geometry geom = new Geometry("Advertiser", new Quad(w, w/2f));
    geom.setLocalTranslation(-w/2f, -w/4f, 0);
    Material mat = new Material(assetManager, "Shaders/Advertiser/Advertiser.j3md");
    mat.setInt("Fan", 6);
    mat.setTexture("Texture1", assetManager.loadTexture("Textures/xii/image3.png"));
    mat.setTexture("Texture2", assetManager.loadTexture("Textures/xii/image4.PNG"));
    mat.setBoolean("Direction", false);
    mat.setBoolean("Vertical", true);
    mat.setBoolean("Stretch", false);
    geom.setMaterial(mat);
    geom.addControl(new Advertiser(2, 2));
    
    rootNode.attachChild(geom);
  }
}

You can have either a vertical of horizontal replacement, you can specify the number of fans (once again: don’t know the real name of this thing, the rectangle containing a piece of the image) you can choose the direction (left to right, right to left etc) and you can decide if the replacement is be stretching or by sliding.
For this last thing: stretch mode should be used when you have a lot of fans, because it will “emulate” (a bit) the fact that it’s triangles that turns.

You’ll need to edit the package of the java files, the path to images ofc.

10 Likes

Nice work, btw I think these things are called billboards (the non gamedev ones ha).

You can pass the Time uniform from the j3md.

    WorldParameters {
        Time
    }

and then use it in the frag or vert shader as:

uniform float g_Time;

Looking good! Quite tempted to use it.

My game runs under android (and others) and so wondering:

  • would it run on android
  • would it run on all android phones that support jme? I’m avoiding like hell anything that might not run on all devices to avoid support hell :D.

Well, all i do is playing with the texcoord, i.e. uv mapping, so it should work everywhere. You may have problems with texture atlas however (i need to test that).
I will also, if needed, describe the core of the shader (the “idea”, step by step) so people could re-implement it with lightning etc. As i am not a pro at all in shaders and as this one doesn’t took me a lot of time to code i bet that most people here would be able to do it while sleeping.
I don’t have an android, i can’t test it on that.