How to: Moving clouds (Shader, Ctrl Code & Photoshop how to)

Thought I would share this with people. The shader doesn’t actually handle the cloud movement unfortunately. There was an issue with switching direction and having the clouds jump from the previous placement to another. So, you pass in the offset (shown below). It does, however, allow you to define a color which is ramped both in color and intensity, based on transparency… which looks pretty nice. Will be adding a control for simulating rain/snow etc soon as well. It’s done… just needs to be cleaned up.



Here is an example vid:

http://youtu.be/YEWkBUlTkXw



Anyways, use it if you want...

First off... here is the j3md, vert & frag...

Clouds.j3md
[java]
MaterialDef Clouds {

MaterialParameters {
Texture2D ColorMap
Color Color ( Color )
Vector2 Offset
Float Alpha
}

Technique {
VertexShader GLSL100: Clouds.vert
FragmentShader GLSL100: Clouds.frag

WorldParameters {
WorldViewProjectionMatrix
}

Defines {
HAS_COLORMAP : ColorMap
HAS_COLOR : Color
HAS_ALPHA : Alpha
HAS_OFFSET : Offset
}
}
}
[/java]

Clouds.vert
[java]
uniform mat4 g_WorldViewProjectionMatrix;
attribute vec3 inPosition;

#ifdef HAS_COLORMAP
attribute vec2 inTexCoord;
varying vec2 texCoord1;
uniform vec2 m_Offset;
#endif

void main(){
#ifdef HAS_COLORMAP
texCoord1 = vec2((inTexCoord[0]+m_Offset[0]),(inTexCoord[1]+m_Offset[1]));
#endif

gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
}
[/java]

Clouds.frag
[java]
#ifdef HAS_COLORMAP
uniform sampler2D m_ColorMap;
varying vec2 texCoord1;
#endif

#ifdef HAS_COLOR
uniform vec4 m_Color;
#endif

#ifdef HAS_ALPHA
uniform float m_Alpha;
#endif

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

#ifdef HAS_COLORMAP
color *= texture2D(m_ColorMap, texCoord1);
#endif

#ifdef HAS_COLOR
vec4 n_Color = vec4((m_Color[0]*color.a),(m_Color[1]*color.a),(m_Color[2]*color.a),color.a);
color = mix(color, n_Color, 0.25f);
#endif

#ifdef HAS_ALPHA
color.a *= m_Alpha;
#endif

gl_FragColor = color;
}
[/java]

Here is how you might use it in a control:

[java]
// Define the mat and texture at somepoint
tex_SkyClouds1 = assetManager.loadTexture("Textures/Clouds1.png");
tex_SkyClouds1.setMinFilter(MinFilter.BilinearNoMipMaps);
tex_SkyClouds1.setMagFilter(MagFilter.Bilinear);
tex_SkyClouds1.setWrap(WrapMode.Repeat);

mat_SkyClouds1 = new Material(assetManager, "MatDefs/Clouds.j3md");
mat_SkyClouds1.setTexture("ColorMap", tex_SkyClouds1);
mat_SkyClouds1.setVector2("Offset", new Vector2f(0,0));
mat_SkyClouds1.setFloat("Alpha", 1f);
mat_SkyClouds1.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
mat_SkyClouds1.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
mat_SkyClouds1.getAdditionalRenderState().setDepthWrite(false);

// Also need to define a few variables for modifying speed and direction
float cloud1IncX = 0.0f, cloud2IncY = 0.0f;
float cloudSpeed = 0.3f,
float cloudRotation = FastMath.HALF_PI+0.02f;
Vector2f cloudOffset = Vector2f.ZERO;

// In the update loop...
cloud1IncX += ((cloudSpeed*tpf)*FastMath.sin(cloudRotation));
cloud1IncY += ((cloudSpeed*tpf)*FastMath.cos(cloudRotation));
// This next step is actually not necessary... but whatever.
if (cloud1IncX > 1) cloud1IncX -= 1;
if (cloud1IncX < 1) cloud1IncX += 1;
if (cloud1IncY > 1) cloud1IncY -= 1;
if (cloud1IncY < 1) cloud1IncY += 1;
// Update the material - this is necessary
mat_SkyClouds1.setVector2("Offset", new Vector2f(cloud1IncX,cloud1IncY));
[/java]

And here is how to create decent looking clouds in Photoshop:

1. Create new layer
2. Filters > Render > Clouds (better with Filter Forge as it creates seamless tiles)
3. In Layer Palet click the Add Layer Mask button
4. Select the Mask icon on the layer (Layer Palet.. you'll see it)
5. Do a select all on the image and Ctrl+C
6. Select the Channels tab
7. Click the View icon next to the added mask channel (looks like an eye) to turn it on
8. Click the RGB view icon to turn it off
9. Click the Mask channel to make sure you have it selected and Ctrl+V the image into the mask
10. That's all that is needed to create the grayscale transparency.

One last thing about using this method of rendering moving clouds. Using 2 seperate layers moving at two different speeds in slightly different directions give the clouds a measure of depth and makes them appear to change in shape over time.

Anyways... good luck with. Feel free to change/update/expand/clean up/improve/etc anything. But please do share your improvements for the rest of us!
8 Likes

@t0neg0d : Big thank, it looks really good!



I’ve use some of this Shader technique, which share here

http://www.iquilezles.org/apps/shadertoy/

for my Filter , some just like badTV effect, some can use in translation between scene, etc … Hope some one find it useful!

Oh… couple more things.



You can adjust and update the Alpha to fade in and out the clouds.



And…



This also works great for ground mist or any other texture you would like to move/animate. I’m sure with very little effort you could update the lighting material to do the same thing.

Hi @t0neg0d !



Would you like to add your shader to this shader library: http://code.google.com/p/jme-glsl-shaders/



If yes, so I need a very simple basic scene example. Just a polygon with a texture and applied shader.

@mifth said:
Hi @t0neg0d !

Would you like to add your shader to this shader library: http://code.google.com/p/jme-glsl-shaders/

If yes, so I need a very simple basic scene example. Just a polygon with a texture and applied shader.


Np at all... I should have time to throw that together tonight and post it here.

Ooops... just noticed in the code above the initial vec4 color is declared as vec4(1.0) when in fact it should be vec4 (0.0).. this doesn't matter unless you don't define a color to ramp... my bad!

EDIT: Um... ignore this. It is supposed to be set to 1.0 hahaha

Ok, will be waiting for you. )

This could be very useful, thanks!

@mifth Sorry I have to link these files this way… for some reason I am having minor issues with JME. Here are direct links to the files… and which folders they go in.



The main app file:

Main.java



The j3md (goes under MatDefs directory):

MovingTexture.j3md



The vert & frag files (goes under the Shaders directory)

MovingTexture.vert

MovingTexture.frag



And lastly, these go under the Textures directory:



Lava1.jpg

Clouds1.png



I’ll be adding a video to the initial post to show what the sample app does in a couple minutes.

1 Like

that looks really good man, good stuff :smiley:

nice!

@t0neg0d , I added you shader to the lib, thanks for the contribution! It’s in the Mercurial Repository.

As i understand you use CPU animation. Would you like to make GPU animation? Like I did in FakeParticleBlow shader…



http://code.google.com/p/jme-glsl-shaders/source/browse/assets/Shaders/FakeParticleBlow/FakeParticleBlow.frag

http://code.google.com/p/jme-glsl-shaders/source/browse/assets/Shaders/FakeParticleBlow/FakeParticleBlow.vert

@mifth This can be done easily enough, however, you won’t be able to update the direction if done that way. Though, if you would like, I can put out the version I had done that was GPU based as well. Just let me know!

I think that would be cool if there will be GPU version too.



Edited: if you want I can add you as a comitter to the shader library project.

very nice! :stuck_out_tongue:

@mifth Sorry for having to do this this way, here is the GPU version of this shader. Posting the .j3md, .vert, .frag & main.java file here, ftp access to my server is fubar atm. I tried to minimize the texture jump when switching speed or rotation by using mod() on g_Time… but it is still there. The closer the coords are to 0,0 the less the jump is of course. If I could figure out how to properly use in/out variables, I could solve this issue. I get confused about how to declare them between the vert and frag and whether they are doing what they are supposed to.



Is it:

.vert = in vec2 offsets;

.frag = out vec2 offsets;

?

or?



This would open up a world of possibilities if I could get this working in any form :frowning:



Anyways, here are the files… I’ll keep trying to figure this out.



.j3md:

[java]

MaterialDef MovingTexture {



MaterialParameters {

Texture2D ColorMap

Color Color ( Color )

Float Alpha

Float Rotation : 0.0

Float Speed

}



Technique {

VertexShader GLSL100: Shaders/MovingTexture.vert

FragmentShader GLSL100: Shaders/MovingTexture.frag



WorldParameters {

WorldViewProjectionMatrix

Time

}



Defines {

HAS_COLORMAP : ColorMap

HAS_COLOR : Color

HAS_ALPHA : Alpha

IS_MOVING : Speed

}

}

}

[/java]



.vert:

[java]

uniform mat4 g_WorldViewProjectionMatrix;

attribute vec3 inPosition;

uniform float g_Time;



const float pi = 3.14159;



#ifdef IS_MOVING

uniform float m_Speed;

uniform float m_Rotation;

#endif



#ifdef HAS_COLORMAP

attribute vec2 inTexCoord;

varying vec2 texCoord1;

#endif



void main(){

#ifdef HAS_COLORMAP

texCoord1 = inTexCoord;

#ifdef IS_MOVING

int i;

float time = (mod(g_Time,i)m_Speed);

texCoord1.x += (time
sin((m_Rotation*(pi/180))));

texCoord1.y += (timecos((m_Rotation(pi/180))));

#endif

#endif

gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);

}

[/java]



.frag

[java]

#ifdef HAS_COLORMAP

uniform sampler2D m_ColorMap;

varying vec2 texCoord1;

#endif



#ifdef HAS_COLOR

uniform vec4 m_Color;

#endif



#ifdef HAS_ALPHA

uniform float m_Alpha;

#endif



void main(){

vec4 color = vec4(1.0);



#ifdef HAS_COLORMAP

color *= texture2D(m_ColorMap, texCoord1);

#endif



#ifdef HAS_COLOR

vec4 n_Color = vec4((m_Color[0]*color.a),(m_Color[1]*color.a),(m_Color[2]*color.a),color.a);

color = mix(color, n_Color, 0.25f);

#endif



#ifdef HAS_ALPHA

color.a *= m_Alpha;

#endif



gl_FragColor = color;

}

[/java]



.java

[java]

package mygame;



import com.jme3.app.SimpleApplication;

import com.jme3.material.Material;

import com.jme3.material.RenderState.BlendMode;

import com.jme3.math.Quaternion;

import com.jme3.math.Vector3f;

import com.jme3.renderer.RenderManager;

import com.jme3.renderer.queue.RenderQueue.Bucket;

import com.jme3.scene.Geometry;

import com.jme3.scene.shape.Box;

import com.jme3.texture.Texture;

import com.jme3.texture.Texture.MagFilter;

import com.jme3.texture.Texture.MinFilter;

import com.jme3.texture.Texture.WrapMode;



/**

  • test
  • @author normenhansen

    /

    public class Main extends SimpleApplication {

    Quaternion q = new Quaternion();

    Texture tex_Lava, tex_Clouds;

    Material mat_Lava, mat_Clouds;



    float lavaSpeed = 0.3f;

    float lavaDirection = 15;

    float lavaAlpha = 1;



    float cloudsSpeed = 0.3f;

    float cloudsDirection = 45;

    float cloudsAlpha = .5f;

    boolean cloudsSwitch = false;

    float inc = .1f;



    public static void main(String[] args) {

    Main app = new Main();

    app.start();

    }



    @Override

    public void simpleInitApp() {

    // Lava

    Box b = new Box(Vector3f.ZERO, 1, 1, 1);

    Geometry geom = new Geometry("Box", b);

    geom.setLocalRotation(q.set(0.12f,0.12f,0.12f,0f));



    tex_Lava = assetManager.loadTexture("Textures/Lava1.jpg");

    tex_Lava.setMinFilter(MinFilter.NearestNoMipMaps);

    tex_Lava.setMagFilter(MagFilter.Nearest);

    tex_Lava.setWrap(WrapMode.Repeat);



    mat_Lava = new Material(assetManager, "MatDefs/MovingTexture.j3md");

    mat_Lava.setTexture("ColorMap", tex_Lava);

    mat_Lava.setFloat("Speed", lavaSpeed);

    mat_Lava.setFloat("Rotation", lavaDirection);

    mat_Lava.setFloat("Alpha", lavaAlpha);

    mat_Lava.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);



    geom.setMaterial(mat_Lava);

    rootNode.attachChild(geom);



    // Clouds

    Box b2 = new Box(Vector3f.ZERO, 1.2f, 1.2f, 1.2f);

    Geometry geom2 = new Geometry("Box2", b2);

    geom2.setLocalRotation(q.set(0.12f,0.12f,0.12f,0f));

    geom2.setQueueBucket(Bucket.Transparent);



    tex_Clouds = assetManager.loadTexture("Textures/Clouds1.png");

    tex_Clouds.setMinFilter(MinFilter.NearestNoMipMaps);

    tex_Clouds.setMagFilter(MagFilter.Nearest);

    tex_Clouds.setWrap(WrapMode.Repeat);



    mat_Clouds = new Material(assetManager, "MatDefs/MovingTexture.j3md");

    mat_Clouds.setTexture("ColorMap", tex_Clouds);

    mat_Clouds.setFloat("Speed", cloudsSpeed);

    mat_Clouds.setFloat("Rotation", cloudsDirection);

    mat_Clouds.setFloat("Alpha", cloudsAlpha);

    mat_Clouds.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);



    geom2.setMaterial(mat_Clouds);

    rootNode.attachChild(geom2);

    }



    @Override

    public void simpleUpdate(float tpf) {

    //Update Clouds

    if (cloudsSwitch) {

    cloudsAlpha += tpf;

    if (cloudsAlpha > 1) {

    cloudsSwitch = false;

    cloudsSpeed = ((float)Math.random()
    .7f)+0.3f;

    cloudsDirection = ((float)Math.random()*360);

    mat_Clouds.setFloat("Speed", cloudsSpeed);

    mat_Clouds.setFloat("Rotation", cloudsDirection);

    } else

    mat_Clouds.setFloat("Alpha", cloudsAlpha);

    } else {

    cloudsAlpha -= tpf;

    if (cloudsAlpha < 0)

    cloudsSwitch = true;

    else

    mat_Clouds.setFloat("Alpha", cloudsAlpha);

    }

    }



    @Override

    public void simpleRender(RenderManager rm) {

    //TODO: add render code

    }

    }

    [/java]
1 Like

Hey @t0neg0d,



I think I found the problem. I’ve been working on getting your moving texture shader up and running into the same problem you have, the jumping texture. I figured out the problem is that, every time the speed changes, there’s a small window where the IS_MOVING is not defined. At that exact moment, the texture jumps back to its starting position.



I’m trying to figure out a way around this. But the problem is, I can’t find a way to transfer the TexCoord1.x and the TexCoord1.y to the next iteration of the shader.

Same problem I was having with this… I couldn’t find a way to do it. I ended up using the version where I update the uniforms. :frowning: Do let us know if you find a way!

I think it has to do with the shader getting recompiled. I had strange effects like this, too.

@survivor said:
I think it has to do with the shader getting recompiled. I had strange effects like this, too.


No, in ractoc's case (as I think originally in this thread's case) it was a flawed approach using direction + time to change texture coordinates. So if you change direction then the texture coordinates change by huge amounts since you are basically moving the tip of a really long vector.