Animated shader

Started by Magicorange, April 19, 2014, 06:09:57 PM

Previous topic - Next topic

Magicorange

Hello,

I'm trying to animate a texture on my objects using a time variable, the problem is that it doesn't move.
This is where I update the time variable (in the onDrawFrame method ) :


...
if (System.currentTimeMillis() - time >= 1000) {
Logger.log(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
currentTime = System.nanoTime() - startTime;//startTime is initialized in the class' constructor with : startTime = System.nanoTime();
animateTextures();
}
fps++;
...



Here we set the uniforms in the method that creates the object :


...
String uniformTime = mContext.getResources().getString(R.string.global_uniform_time);

ArrayList<ShaderUniform> uniformsTop = new ArrayList<ShaderUniform>();
uniformsTop.add(new ShaderUniform("colorMap", 1));
uniformsTop.add(new ShaderUniform("noiseMap", 1));
uniformsTop.add(new ShaderUniform(uniformTime, currentTime));
uniformsTop.add(new ShaderUniform("alpha", 1.0));
uniformsTop.add(new ShaderUniform("baseSpeed", 0.005));
uniformsTop.add(new ShaderUniform("noiseScale", 0.1337));
uniformsTop.add(new ShaderUniform("invRadius", 0.0005f));


ShaderUtils.setShader(mContext, towerTop, shadersDir+"towers/"+"vertexShader.glsl", shadersDir+"towers/"+"fragmentShader.glsl", uniformsTop);

...



ShaderUniform is a class I've made that has a type parameter, and ShaderUtils.setShader is a method that will iterate through the arraylist and choose the right setStaticUniform method according to this type parameter.

And in the animateTextures method, this is how the shader should update the currentTime uniform:

  t.getTop().getShader().setUniform(uniformTime, currentTime); //uniformTime is the name of the variable




If needed, the fragment shader :


precision mediump float;

varying vec3 lightVec[2];
varying vec3 eyeVec;
varying vec2 texCoord;

uniform sampler2D colorMap;
uniform sampler2D noiseMap;
uniform float time;
uniform float baseSpeed;
uniform float noiseScale;
uniform float invRadius;
uniform float alpha;

uniform vec3 diffuseColors[8];
uniform vec3 specularColors[8];
uniform vec4 ambientColor;


float coef = 10.0;

void main ()
{
//Speed & direction of the texture
vec2 uvTimeShift = texCoord + vec2( -0.4, 0.5 ) * time * baseSpeed;
vec4 noise = texture2D( noiseMap, uvTimeShift );
vec2 uvNoisyTimeShift = texCoord + noiseScale * vec2( noise.r, noise.g ); //noise quantity
vec4 baseColor = texture2D( colorMap, uvNoisyTimeShift );
baseColor.a = alpha;

//...
gl_FragColor = baseColor;
}


Note that I got this working on another project that used Three.JS and WebGL, I have simply taken this code again and slightly modified it, but the time variable thingy has not changed (in the javascript version, the time uniform was updated in the renderLoop using a delta provided by a ThreeJS method).
It's meant to animate water textures using a noisemap,  I use it for a kind of lava effect.

Is it possible to update a uniform in a shader (then the way I do it is probably wrong), or does the problem comes from my time variable ?
I can't figure out yet why this is not working (and I have already searched if someone had the same problem, but it didn't help me that much), any help would be appreciated.

Thanks in advance for your answer.

EgonOlsen

You can of course update a uniform. It might not work in your case, because your uniform is a float and your time value is a long...

Magicorange

#2
Actually it's a float, there may be some precision loss as nanoTime() returns a long though. I've tried a cast to float but it doesn't change the result.

private float currentTime, startTime;


currentTime = (float)( System.nanoTime() - startTime);

My setShader method, called when the object is created, looks like this :


public static void setShader(Context mContext, Object3D obj, String vertPath, String fragPath, ArrayList<ShaderUniform> uniforms)
{

InputStream fragShader;
InputStream vertShader;
try
{
fragShader = mContext.getAssets().open(fragPath);
vertShader = mContext.getAssets().open(vertPath);

String fragmentShader = Loader.loadTextFile(fragShader);
String vertexShader = Loader.loadTextFile(vertShader);

GLSLShader shader = new GLSLShader(vertexShader, fragmentShader);
obj.setShader(shader);

for(ShaderUniform s : uniforms)
{
if (s.getValue() instanceof Integer)
{
Log.d("TAG", "UNIFORM INTEGER : "+s.getName());
shader.setStaticUniform(s.getName(), (Integer)s.getValue());
}

else if (s.getValue() instanceof Float)
{
Log.d("TAG", "UNIFORM FLOAT: "+s.getName());
shader.setStaticUniform(s.getName(), (Float)s.getValue());
}

else if (s.getValue() instanceof Float[])
{
shader.setStaticUniform(s.getName(), (float[])s.getValue());
}

...



I have tried to replace the uniform's primitives values set during the creation with wrappers like (uniformName, new Float(float value)) but it still does not solve the problem.

EgonOlsen

I'm confused by the mix of jPCT's setUniform calls and your own wrappers. Are you actually setting the uniform directly or via some wrapper? If it's via the wrapper, are you sure that it actually sets the value correctly?
As said, there's no problem with setting a uniform at runtime...i'm constantly doing this...

Magicorange

#4
Ok, as I said when I create my objects, I basically do this  :


Object3D towerTop = Primitives.getSphere(1);
//choosing the texture stuff here
//..
towerTop.setTexture(topTex); //texture chosen previously
towerTop.calcTextureWrapSpherical();
//towerTop.setSpecularLighting(true);
towerTop.build();

String uniformTime = mContext.getResources().getString(R.string.global_uniform_time);

ArrayList<ShaderUniform> uniformsTop = new ArrayList<ShaderUniform>();
uniformsTop.add(new ShaderUniform("colorMap", 1));
uniformsTop.add(new ShaderUniform("noiseMap", 1));
uniformsTop.add(new ShaderUniform(uniformTime, currentTime));
uniformsTop.add(new ShaderUniform("alpha", 1.0));
uniformsTop.add(new ShaderUniform("baseSpeed", 0.005));
uniformsTop.add(new ShaderUniform("noiseScale", 0.1337));
uniformsTop.add(new ShaderUniform("invRadius", 0.0005f));


ShaderUtils.setShader(mContext, towerTop, shadersDir+"towers/"+"vertexShader.glsl", shadersDir+"towers/"+"fragmentShader.glsl", uniformsTop);

//then I add the object to the world etc



At this point, I have created wrappers and added them to the arraylist, the wrapper class looks like that :


public class ShaderUniform<T> {
private String name;
private T value;

public ShaderUniform(String name, T value) {
super();
this.name = name;
this.value = value;
}

//getters & setters...



Then I call my setShader method :


ShaderUtils.setShader(mContext, towerTop, shadersDir+"towers/"+"vertexShader.glsl", shadersDir+"towers/"+"fragmentShader.glsl", uniformsTop);



And this method does what I have posted before, it just takes the value attribute of the ShaderUniform and checks its class, if it's an integer it will call the setStaticUniform method that takes an integer parameter, if it's a float, the float method etc.

I'm not 100% sure that the line s.getValue() instanceof Float works perfectly, as I'm giving a float primitive parameter as the value when I create a ShaderUniform. I suppose that it's working as I'm getting the Log messages (UNIFORM INTEGER,FLOAT etc...).

And when I'm trying to update the uniform, I do it directly using the setUniform method of JPCT, not mine (I'm lazy ^^, this would force me to make another method that takes a single ShaderUniform parameter, check its class again and update it with setUniform, while I can directly do it in the render loop :p )

EgonOlsen

How you create the objects actually doesn't matter. It's more about how you are setting the uniforms and as said, i'm a little confused about that...the best way to track this down might be a test case. Can you upload the project or a simple version that shows the issue?

Magicorange

I'm not sure, this is a student project, I'm not meant to give it to anyone till it's finished :/
The quoted parts of the code are the only ones where I used the uniforms on this object, only at its creation, and only a simple call to setUniform in the onDrawFrame method.

EgonOlsen

Then try to create a simple test case instead. Updating a uniform isn't a problem. Is has to have something to do with either your shader or the way in which you are updating the uniform, but can't tell it based on the code snippets alone.

Magicorange

Ok, I'll do this tomorrow and see if the problem occurs again.