shader producing wrong results on tablets - android

ive only been working with shaders for a few days and have written a simple edge detection shader that adds a drop shadow and an inner shadow. It works great on my galaxy s2 and iphone 4, but the galaxy tab and ipad2 produce only a very thin and rough looking shadow on both sides of the edge. Ive spent hours trying to figure out why but had no joy, please help! If i simulate an ipad2 resolution in the simulator, the effect is correct.
The shader works on the same size off screen buffer on all devices (640x480 pixels).
vertex shader:
{
attribute highp vec4 inVert; //vertex stream
uniform highp mat4 inPMVMat; //transform to projected space
attribute mediump vec2 inUV0;
attribute lowp vec4 inCol;
uniform mediump vec2 inUVOffset;
uniform mediump vec2 inUVScale;
varying mediump vec2 vTexCoord;
varying mediump float FragColor;
void main(void)
{
gl_Position = inPMVMat * inVert;//set vertex position in projected space
vTexCoord = inUV0*inUVScale;// + inUVOffset;//pass texture to fragment shader
FragColor = inCol.a;
}
}
fragment shader:
{
uniform sampler2D myTexture;
varying mediump vec2 vTexCoord;
varying mediump float FragColor;
lowp float MaxDistance, Distance;
lowp float Shift;
void main(void)
{
gl_FragColor = texture2D(myTexture, vTexCoord);
mediump vec2 PixelSize = vec2(1.0 / 640.0, 1.0 / 480.0);
mediump vec2 Direction = vec2(PixelSize.x*0.5,PixelSize.y*0.5);
if(gl_FragColor.a >= 0.5)
{
//inner shadow
MaxDistance = 15.0;
} else {
//drop shadow
MaxDistance = 12.0;
Direction = -Direction;
}
mediump vec2 Position = vTexCoord;
mediump float c;
for(Distance = MaxDistance; Distance > 0.0;Distance -= 1.0)
{
Position += Direction;
c = texture2D(myTexture,Position).a;
if(c < 0.5)
{
if(gl_FragColor.a >= 0.5)
{
//found transparent edge - do inner shadow
Shift = 1.0-((0.5/MaxDistance)*Distance);
gl_FragColor.r *= Shift;
gl_FragColor.g *= Shift;
gl_FragColor.b *= Shift;
break;
}
} else {
if(gl_FragColor.a < 0.5)
{
//found opaque edge - do dropshadow
gl_FragColor.a = ((0.8/MaxDistance)*Distance);
break;
}
}
}
gl_FragColor.a *= FragColor;
}
}
images:

I eventually found the solution to this after a lot of experimenting.
The lowp MaxDistance, Distance and Shift variables were the culprits - it works fine after on all devices after changing to mediump. I guess there is some variation in precision between devices

Related

How to remove horizontal stripe artifact from GLSL shader?

I'm working on custom photo with depth effect view.I am passing touch coordinates to GLSurfaceView renderer to "change perspective".But there are horizontal stripes between texture and it's mirrow when doing this.
My fragment shader code:
#ifdef GL_ES
precision highp float;
#endif
varying vec2 texcoordVarying;
uniform sampler2D texture;
uniform sampler2D depth;
uniform float time;
uniform vec2 touch;
uniform vec2 limit;
uniform vec4 resolution;
vec2 mirrored(vec2 v) {
vec2 m = mod(v,2.);
return mix(m,2.0 - m, step(1.0 ,m));
}
void main(void) {
vec2 uv = 1.0 * gl_FragCoord.xy / resolution.xy ;
vec2 vUv = (uv - vec2(0.5))*resolution.zw + vec2(0.5);
vUv.y = 1. - vUv.y;
vec4 tex1 = texture2D(depth,mirrored(vUv));
vec2 fake3d = vec2(vUv.x + (tex1.r - 0.5)* touch.x/limit.x, vUv.y + (tex1.r - 0.5)* touch.y/limit.y );
gl_FragColor = texture2D(texture,mirrored(fake3d));
}
I am passing coordinates like this:
queueEvent {
renderer.touchTargetX = event.rawX / renderer.width
renderer.touchTargetY = event.rawY / renderer.height
}
requestRender()
If your wrap mode is GL_REPEAT, try using GL_CLAMP_TO_EDGE.

Warp shader behaves itself differently on mobile and desktop

I try to implement warp shader (black hole). It works great on desktop, but it looks wrong on mobile devices. The problem is in its size. When I increase the size of black hole the warped edges disappear. If it has very small size it looks as I want. I am using LibGDX.
How it should look like (Desktop):
How it looks on mobile device with the same size:
When I decrease the size the warped edges appears:
Vertex shader:
attribute vec2 a_position;
void main()
{
gl_Position = vec4(a_position.xy, 0.0, 1.0);
}
Fragment shader:
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D u_texture;
uniform vec2 u_res;
uniform float u_size;
// Gravitational field at
// position pos, given a black hole
// of mass m at position center
// (ignoring G constant)
vec2 Fgrav(float m, vec2 center, vec2 pos)
{
vec2 dir = center - pos;
float distance = length(dir);
dir /= distance;
return dir * (m / (distance*distance));
}
void main(void)
{
vec2 texCoord = gl_FragCoord.xy / u_res.xy;
vec4 sceneColor;
vec2 blackHoleCenters = vec2(u_res.xy*.5);
vec2 netGrav = Fgrav( (50. - u_size) * 500., blackHoleCenters, gl_FragCoord.xy);
float netGravMag = length(netGrav);
if(netGravMag < 1.0)
{
texCoord = (gl_FragCoord.xy + netGrav*((50. - u_size) * 15.0))/u_res.xy;
}
else texCoord.xy=vec2(.5,.5);
sceneColor = texture2D(u_texture, texCoord);
gl_FragColor = sceneColor;
}
How to fix this issue? Thanks.
UPDATE:
That's how I get u_size parameter:
public float getDistance(Camera cam){
return (float) Math.sqrt(Math.pow(cam.position.x, 2) + Math.pow(cam.position.y, 2) + Math.pow(cam.position.z, 2));
}
public void render(Camera cam, Mesh quad) {
program.begin();
program.setUniformf(u_size, getDistance(cam));
...
Initializing of camera:
cam = new PerspectiveCamera(64, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
cam.position.set(0f, 0.0f, 30.0f);
cam.lookAt(0,0.0f,0);
cam.near = 0.1f;
cam.far = 450f;
cam.update();
So, the initial value of u_size is 30.

GLSL - ripling effect, bad performance on android

I needed to create some ripling effect for one sprite in my game, here's the vertexShader:
attribute vec4 a_position; // just taking in necessary attributes
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans; // Combination of view and projection matrix
varying vec2 v_texCoords;
void main() {
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position; //as I said, it is sprite so no need for modelMatrix
}
and here's the fragment:
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoords;
uniform sampler2D u_texture; //texture of sprite
uniform float time;
void main()
{
vec2 uv;
if (time > 0.0) { // time is > 0.0 when I want the ripling effect to be applied,
vec2 cPos = -1.0 + 2.0 * v_texCoords.xy; // converting tex.Coords to -1 - 1
float cLength = length(cPos); //taking length of it
uv = v_texCoords.xy +( (cPos/cLength)*cos(cLength*12.0-time*4.0)*0.03 ) // just some calculations for the ripling effect
}
else
uv = v_texCoords.xy; // if I don't want to use the ripling effect, I use normal texCoords
vec4 tex = texture2D(u_texture, uv); //sampling texture
gl_FragColor = tex;
}
It all works fine, the performance's fine on PC, but when running it on android, the performance is a lot worse... As you can see, shader's are trivial but they somehow are expensive.. Anyways, sprite I draw has width about 2000 - 4000 px and height 720. Also, when I replace v_texCoords with different vector(for example vec2(1, 1)) in cPos calc: vec2 cPos = -1.0 + 2.0 * v_texCoords.xy; the performance improves heavily..
I don't really know what's so expensive there. If anyone had some advices, I'd be happy. Thanks in advance

OpenGL ES GLSL optimization

I'm developing a game which uses glsl shader to produce terrain based on the vertex "height". This works very very fast on desktop but quite slow on android tablet. This is of course natural. I'm still a noob with GLSL and OpenGL. This is the result from the shader:
lowp setting doesn't affect that much for performance. I have now tuned this and frame rate is ~20. Can someone give ideas how to improve this shader code:
Fragment shader:
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
varying float EyeVertexZ;
//varying LOWP float Explored;
varying LOWP vec2 v_texCoords;
uniform LOWP float u_offset;
uniform sampler2D u_texture;
uniform sampler2D u_texture2;
uniform sampler2D u_texture3;
uniform vec2 resolution;
void main() {
LOWP vec4 color;
vec4 colorGrass = vec4(0.631, 0.69, 0.49 ,1.0);
float a;
float height = EyeVertexZ;
color = colorGrass;
//if height under 0.67 --> water
if(height < 0.67)
{
vec2 coords = v_texCoords.st;
coords.x -= u_offset;
LOWP vec3 nColor;
float bumpAmount = 0.2;
//bumpmapping texture
nColor = texture2D(u_texture, coords).rgb;
//water color, deep water darker
color = vec4(0.3, 0.4 + EyeVertexZ*0.33,0.6 + EyeVertexZ*0.33 ,1.0);
bool useNormals = true;
bool useShadow = true;
bool yInvert = false;
vec3 lightColor = vec3(1.0, 1.0, 1.0);
vec3 attenuation = vec3(0.5, 0.5,0.5);
vec3 ambientColor = vec3(1.0, 1.0, 1.0);
vec4 v_color = vec4(1.0, 1.0, 1.0, 1.0);
float ambientIntensity = 0.3;
vec3 light = vec3(resolution.x, 0.0, 0.5);
//some bump map programs will need the Y value flipped..
nColor.g = yInvert ? 1.0 - nColor.g : nColor.g;
//this is for debugging purposes, allowing us to lower the intensity of our bump map
LOWP vec3 nBase = vec3(0.5, 0.5, 1.0);
nColor = mix(nBase, nColor, bumpAmount);
//normals need to be converted to [-1.0, 1.0] range and normalized
LOWP vec3 normal = normalize(nColor * 2.0 - 1.0);
//here we do a simple distance calculation
LOWP vec3 deltaPos = vec3( (light.xy - gl_FragCoord.xy) / resolution.xy, light.z);
LOWP vec3 lightDir = normalize(deltaPos);
LOWP float lambert = useNormals ? clamp(dot(normal, lightDir), 0.0, 1.0) : 1.0;
//now let's get a nice little falloff
LOWP float d = sqrt(dot(deltaPos, deltaPos));
LOWP float att = useShadow ? 1.0 / ( attenuation.x + (attenuation.y*d) + (attenuation.z*d*d) ) : 1.0;
LOWP vec3 result = (ambientColor * ambientIntensity) + (lightColor.rgb * lambert) * att;
result *= color.rgb;
color = v_color * vec4(result, color.a);
}
a = height - 0.65;
a = max(a,0.0);
color = mix(color, texture2D(u_texture2, v_texCoords),a < 0.02 ? a*50.0 : 0.0);
if(height > 0.67 && height <= 0.70)
{
a = height-0.67;
color = mix( texture2D(u_texture2, v_texCoords),color, a*33.0);
}
gl_FragColor = color; //mix(color, hex, hex.a);
}
Vertex shader
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
attribute vec4 a_position;
attribute vec2 a_texCoords;
uniform mat4 u_worldView;
varying LOWP float noise;
varying float EyeVertexZ;
varying LOWP float Explored;
varying vec2 v_texCoords;
void main() {
gl_Position = u_worldView*a_position;
EyeVertexZ = a_position.z;
v_texCoords = a_texCoords;
}

libgdx: SpriteBatch, fragment shader on Samsung Android devices work incorrect

I'm developipng an 2D live wallpaper for Andoid using libgdx and its SpriteBatch class. This is a watch, so all I need, from libgdx is just drawing multiple textures, rotating them to specific angle.
I've solved this problem using SpriteBatch class and all was fine.
But now I need to add magnifying lens effect to my scene. Task is to magnify specific area with specific ratio to simulate real lens.
I've solved this by rendering to FrameBuffer, getting texture region from FrameBuffer and finally, drawing this region with custom shader, using SpriteBatch.
THE PROBLEM: on Samsung devices (I've tried on galaxy note N7000, and on Galaxy Tab 10.1) there is a small rectangle in the center of magnifying area about 16x16px, where magnification ratio slightly increases. Sorry, I can't post a screenshot now, because I don't have Samsung device.
On other devices all works fine, tried in HTC Vivid, Acer A500, Google Nexus One.
I think, the problem if in Mali GPU on Samsung devices, but don't know, how to fix it.
I've adapted fragment shader code from this question to SpriteBatch Add Fisheye effect to images at runtime using OpenGL ES
Here is it:
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
varying LOWP vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
void main() {
vec2 m = vec2(0.622 , 0.4985); // lens center point, note that in live wallpaper center of texture is (0.5,0.5)
float lensSize = 0.045; //diameter of lens
float lensCut = 0.0342; //length of cut lines
vec2 d = v_texCoords - m;
float r = sqrt(dot(d, d)); // distance of pixel from mouse
float cuttop = m.y + lensCut;
float cutbottom = m.y - lensCut;
vec2 uv;
if (r >= lensSize) {
uv = v_texCoords;
} else if ( v_texCoords.y >= cuttop) {
uv = v_texCoords;
} else if (v_texCoords.y <= cutbottom) {
uv = v_texCoords;
} else {
uv = m + normalize(d) * asin(r) / (3.14159 * 0.37);
}
gl_FragColor = texture2D(u_texture, uv);
}
Vertex shader is default from SpriteBatch sources:
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans;
varying vec4 v_color;
varying vec2 v_texCoords;
void main() {
v_color = a_color;
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
Here is my render() method:
GL20 gl = Gdx.graphics.getGL20();
gl.glEnable(GL20.GL_TEXTURE_2D);
gl.glActiveTexture(GL20.GL_TEXTURE0);
gl.glClearColor(WallpaperService.bg_red, WallpaperService.bg_green, WallpaperService.bg_blue, 1f);
gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Creating framebuffer, if it has gone
if (mFrameBuffer == null) {
mFrameBuffer = new FrameBuffer(Format.RGBA4444, BG_WIDTH, BG_HEIGHT, true);
mFrameBufferRegion = new TextureRegion(mFrameBuffer.getColorBufferTexture());
mFrameBufferRegion.flip(false, true);
}
//Camera setting according to background texture
camera = new OrthographicCamera(BG_WIDTH, BG_HEIGHT);
camera.position.set(BG_WIDTH / 2, BG_WIDTH / 2, 0);
camera.update();
batch.setProjectionMatrix(camera.combined);
//Rendering scene to framebuffer
mFrameBuffer.begin();
batch.begin();
//main Drawing goes here
batch.end();
//Drawing frame to screen with applying shaders for needed effects
if (mFrameBuffer != null) {
mFrameBuffer.end();
camera = new OrthographicCamera(width, height);
camera.position.set(width / 2, height / 2, 0);
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.setShader(null);
batch.setShader(shader);
batch.draw(mFrameBufferRegion, width / 2 - (BG_WIDTH / 2) + (MARGIN_BG_LEFT * ratio), height / 2
- (BG_HEIGHT / 2) + (MARGIN_BG_TOP * ratio), (float) BG_WIDTH / 2, (float) BG_HEIGHT / 2,
(float) BG_WIDTH, (float) BG_HEIGHT, (float) ratio, (float) ratio, 0f);
batch.setShader(null);
batch.end();
}
I've managed to fix this issue. The problem is in Mali gpu.
Mali doesn't support high precision float calculations in fragment shader.
When distance from center to point was near zero - r variable became zero.
So I've added constant coefficient to my calculations and this did the trick.
Here is working fragment shader:
precision mediump float;
varying lowp vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
const float PI = 3.14159;
const float koef = 10.0;
void main() {
vec2 uv;
vec2 m = vec2(0.622 , 0.4985); // lens center point, note that in live wallpaper center of texture is (0.5,0.5)
float lensSize = 0.045; //radius of lens
float lensCut = 0.0342; //height of cut
float cuttop = m.y + lensCut;
float cutbottom = m.y - lensCut;
float cutleft = m.x-lensSize;
float cutright = m.x+lensSize;
//don't transform pixels, that aren't in lens area
if ( v_texCoords.y >= cuttop) {
uv = v_texCoords;
} else if (v_texCoords.y <= cutbottom) {
uv = v_texCoords;
} else if (v_texCoords.x <= cutleft) {
uv = v_texCoords;
} else if (v_texCoords.x >= cutright) {
uv = v_texCoords;
} else {
vec2 p = v_texCoords*koef; //current point
vec2 d = p - m*koef; //vector differnce between current point and center point
float r = distance(m*koef,p); // distance of pixel from center
if (r/koef >= lensSize) {
uv = v_texCoords;
} else {
uv =m + normalize(d) * asin(r) / (PI*0.37*koef);
}
}
gl_FragColor = texture2D(u_texture, uv);
}

Categories

Resources