Strange shader behaviour on Android (works fine on desktop)

I wrote a shader which layers same noise on top of each other, in this process the color.rgb values exceeds 1. This is no problem on desktop, the effect renders completely fine.



This is the point where the clouds shader breaks, if I comment this out it’s completely smooth, but obviously it’s missing detail so it’s not a fix.
finalColor += smoothstep( 0.25f, 1.0f, noiseLayer2) / 1.5f; //result in color values > 1

Android - Line commented

But this problem seems to occur in other use cases too, I’m moving an object in a sin wave, and as the _time parameter increases it gets more and more choppy in both speed and visual artifacts.
Interesingly it’s just the shader that starts to run slower, the game itself still runs at 60 fps.
Again, this renders perfectly fine on desktop.


The circle shader is quite simple so I’ll post that in full here;
sampler textureSampler : register(s0); // from SpriteBatch

sampler textureSampler : register(s0); // from SpriteBatch

float2 _scale;
float _time; // time in seconds
float _timeScale = 1; 
float _textureSize;
float4 _color;

struct VertexShaderOutput
	float4 position : SV_POSITION;
	float4 color : COLOR0;
	float2 coords : TEXCOORD0;

float4 mainPixel(VertexShaderOutput output) : COLOR0
	output.coords.x += sin(_time + output.coords.y * 0.1f) * 0.1f;
	output.coords.y += cos(_time + output.coords.x ) * 0.1f;
	float4 baseTex = tex2D(textureSampler, output.coords);

	return baseTex;


technique Sun
	pass SunPass
		PixelShader = compile ps_3_0 mainPixel();

These shaders are both applied to a rendertexture, if that matters.

I suspect it’s probably a shader settings thing? Any ideas?

I don’t think that’s a problem, but you can try to clamp it afterwards, and see if anything chages:

finalColor = saturate(finalColor);

The first video looks like the top layer is just totally pixelated. Looks like it’s using point filtering instead of linear filtering. It also looks like it’s jumping a full pixel at a time.

I guess you can just output this top layer alone and it will already show the same problem:

// replace += with =
finalColor = smoothstep( 0.25f, 1.0f, noiseLayer2) / 1.5f;

The sine wave thing sounds like a precision problem.

I tried it but it it looks exactly the same.

Yeah that’s the weird thing, I don’t change anything except that one line in the shader. Furthermore, it works perfectly on desktop.

I tried this, and it runs pixelated and slowly again :confused:

It looks like it, but since it works perfectly on desktop I suspect it’s something else.

Absolute baffeled by this issue :confused:

Sounds like noiseLayer2 is already pixelated. Try to track that pixelation down to it’s source. Is that from a render target?

OpenGL can run in lower precision on phones. MojoShader is putting this into the generated GLSL shaders

#ifdef GL_ES
precision highp float;
precision mediump int;

I think the highp should ensure that floats have the same precision as on Desktop, ints are different however. I’m not sure if there could be exceptions to this, maybe if your phone can’t do high precision. I don’t know that much about OpenGL ES, so I’m not sure. Can you run it in a simulator on a really good phone?

The noiseLayer2 is sampled from the same texture as the first noise layer, just with a different offset. It’s a texture with some perlin noise in it.

If I sample just the noise layer 2 by itself it’s smooth, it just get’s messed up when I add multiple layers of noise together (again, just on android).

Oh this sort of thing is what I would expect to cause something like this yeah!
I tried running it in the emulator already and I get the same results.

Have you tried running on a physical device? Do you get the same results on a physical device?

Yeah I pretty much always run on a physical device, that’s what the screen recordings are from.

Looks like texture compression. Is it possible that it is defaulting the texture2d’s format to Color on desktop but DXT or ETC1 on device? I think if the Texture Format is set to Compressed it will chose automatically based on platform.

Could also be incorrect mip level of you have mipmaps. Try using tex2Dlod with float4(uv,0,0) or manually calculating the derivatives if you at aliasing with tex2Dlod

While testing tex2lod I might have come across what could be wrong.

sampler2D _noiseTextureSampler = sampler_state
	Texture = <_noiseTexture>;
	AddressU = Wrap;
	AddressV = Wrap;

//take into account aspect ratio
float2 scaledCoords = output.coords * _scale * 1.125f;

//scale speed slower and diagonally
float2 _scaledTime = float2( _time * 0.075f, _time * -0.025f);
float4 noiseLayer2 = tex2D(_noiseTextureSampler, 10 + (scaledCoords / 2.0f) + _scaledTime * 0.5f);

The “10 +” is in the tex2D coordinates seems to be causing problems. I simply wanted to offset the coordinates by a large number to prevent overlap with the first noise layer. I thought it would be okay since the UV values should be wrapped. But throwing in a large number seems to cause the pixelation.

I think it might actually be a precision problem like @markus mentioned. That would explain the problem with the sin wave shader too.
For now I just changed the “10” to “0.5” and it looks smooth.

Thanks all for the help! Never would have figured this out myself.
I’ll see if I can find a fix for the precision on the sin wave and report back here later.