Alpha gradient in OpenGL ES shader is not smooth - android

I'm attempting to create an alpha radial gradient effect (kind of lighting) using a simple shader.
The effect is created correctly, however the gradient is not smooth.
The precision is set to highp, so I don't really know where to look.
This shader is currently running on Android, using OpenGL ES 2.0.
This is how the gradient currently looks like:
And this is my current shader:
Vertex:
precision highp float;
attribute vec4 vPosition;
attribute vec2 vStaticInterpolation;
varying vec2 interpolator;
void main() {
interpolator = vStaticInterpolation;
gl_Position = vPosition;
}
Fragment:
precision highp float;
uniform float alphaFactor;
varying vec2 interpolator;
float MAX_ALPHA = 0.75;
void main() {
float x = distance(interpolator, vec2(0.0, 0.0));
float alpha = MAX_ALPHA - MAX_ALPHA * x;
alpha = max(alpha, 0.0);
gl_FragColor = vec4(0.925, 0.921, 0.843, alpha);
gl_FragColor.a *= alphaFactor;
}
The shader receives constant attributes for interpolation (from -1.0 to 1.0) in vStaticInterpolation.
The actual color is currently hard-coded in the shader.

It looks to be related to a dithering problem.
This could depend on the OpenGL driver implementation of your mobile device (though I don't know which model you are currently using). In the past it used to be an issue.
Possible tests you could perform are:
Disable Opengl dithering:
GLES20.glDisable(GLES20.GL_DITHER);
Impose an RGB888 surface when you create the surface. It is usually done in the ConfigChooser function. I try to remember by hard, this is part of the code of my application:
new AndroidGL.ConfigChooser(8, 8, 8, 8, depth, stencil) :

Related

LibGDX shader working everywhere but my Android device

As a continuation from my previous question (GLSL : Accessing an array in a for-loop hinders performance), I have encountered an entirely new and annoying problem.
So, I have a shader that performs a black hole effect.
The shader works perfectly on my computer, the android emulator, and ShaderToy – but for some reason, even though the code is exactly the same, does not work on my Android device.
The problem occurs when I zoom in too far. For whatever reason, when my zoom reaches a certain point – the whole background zooms in and then zooms out and goes crazy. Like this :
When it should look like this :
However, it does work on my device if I change this :
#ifdef GL_ES
precision mediump float;
#endif
to this :
#ifdef GL_ES
precision highp float;
#endif
The problem with this is that it also decreases my FPS from 60 down to ~40.
I believe the problem is that my Android device's OpenGL version is "OpenGL ES 3.0" according to Gdx.gl.glGetString(GL20.GL_VERSION).
But I cannot figure out how to set my version to OpenGL 2.0 since the AndroidApplicationConfiguration class is giving me little to no options.
I've tried putting <uses-feature android:glEsVersion="0x00020000" android:required="true" /> in the manifest, but it still prints "OpenGL ES 3.0".
And I still don't even know if this is actually the cause of the problem or not, so that's why I'm asking here. Thank you for taking the time to read/answer my question :).
P.S. Here's the Shader code:
#ifdef GL_ES
precision mediump float;
#endif
const int MAX_HOLES = 4;
uniform sampler2D u_sampler2D;
varying vec2 vTexCoord0;
struct BlackHole {
vec2 position;
float radius;
float deformRadius;
};
uniform vec2 screenSize;
uniform vec2 cameraPos;
uniform float cameraZoom;
uniform BlackHole blackHole[MAX_HOLES];
void main() {
vec2 pos = vTexCoord0;
float black = 0.0;
for (int i = 0; i < MAX_HOLES; i++) {
BlackHole hole = blackHole[i];
vec2 position = (hole.position - cameraPos.xy) / cameraZoom + screenSize*0.5;
float radius = hole.radius / cameraZoom;
float deformRadius = hole.deformRadius / cameraZoom;
vec2 deltaPos = vec2(position.x - gl_FragCoord.x, position.y - gl_FragCoord.y);
float dist = length(deltaPos);
float distToEdge = max(deformRadius - dist, 0.0);
float dltR = max(sign(radius - dist), 0.0);
black = min(black+dltR, 1.0);
pos += (distToEdge * normalize(deltaPos) / screenSize);
}
gl_FragColor = (1.0 - black) * texture2D(u_sampler2D, pos) + black * vec4(0, 0, 0, 1);
}
As you have found the issue is down to a lack of precision in fp16 (mediump), which is fixed by using fp32 (highp). Most maths units will have double the throughput for fp16 vs fp32, which also explains the drop in performance.
Querying the driver GLES version will return maximum supported version, not the version of the current EGL context, so what you are seeing is expected.
Also please note that "highp" is optional in OpenGL ES 2.0 fragment shaders, so there is no guarantee that your shader will work on some GPUs in an OpenGL ES 2.0 context. The Mali-4xx series only support fp16 fragment shaders, for example (I think also some of the OpenGL ES 2.0 Vivante GPUs based on past experience).
In OpenGL ES 3.0 highp is mandatory in fragment shaders, so it would be guaranteed to work there.

Vertex texture Fetch (Texture read in vertex shader)

New to Opengl and GLSL.
I am using OpenGL es 3.0 and my GLSL version #version 300 es.
i want to get pixel(ARGB data) at every position in my vertex shader(vertex texture fetch). i have verified that my Android tablet supports vertex texture fetch.
Now i pass in the texture(image) and Texture coordinates to the vertex shader
and execute
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
Is this the right way or should i use GL_POINTS .
if i am using GL_POINTS how to pass the texture cooordinate?
could you provide any samples/example code that does a full pixel read(ARGB) in the vertex shader.
attaching my vertex shader
uniform sampler2D sTexture;
in vec4 aTextureCoord;
out vec3 colorFactor;
vec2 vTextureCoord;
vec4 tex;
void main()
{
vTextureCoord = aTextureCoord.xy;
tex = texture(sTexture,vTextureCoord);
float luminance = 0.299 * tex.r + 0.587 * tex.g + 0.114 * tex.b;
colorFactor = vec3(1.0, 1.0, 1.0);
gl_Position = vec4(-1.0 + (luminance * 0.00784313725), 0.0, 0.0, 1.0);
gl_PointSize = 1.0;
};
My texture coordinates passed are
{0.f,1.f}
{1.f,1.f} {0.f,0.f}
{1.f,0.f}
and the shader is triggered by
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
Just declare a sampler and sample it exactly as per usual. E.g.
"#version 150\n"
in vec4 position;
in vec2 texCoordinate;
uniform sampler2D texID;
uniform mat4 modelViewProjection;
void main()
{
gl_Position = modelViewProjection * (position + texture(texID, texCoordinate));
}
That will sample a 4d vector from the texture unit texID at location texCoordinate, using that to perturb position prior to applying the model-view-projection matrix. The type of geometry you're drawing makes no difference.
gl_Position = vec4(-1.0 + (luminance * 0.00784313725), 0.0, 0.0, 1.0);
That's clever. It's not going to work, but that's clever.
The thing that is confusing everyone is what you haven't told us. That you're computing the histogram by using blending. That you compute the location for each fragment based on the luminance, so you get lots of overlap. And blending just adds everything together, thus producing your histogram.
FYI: It's always best to explain what it is you're actually trying to accomplish in your question, rather than hoping that someone can deduce what you're attempting to do.
That's not going to work because you only have four vertices in this case. You have lots of fragments, but they will be generated based on interpolation from your 4 vertices. And you can't change the position of a fragment from within a fragment shader.
If you want to do what you're trying to do, you still have to render one vertex for every texel you fetch. You still need to use GL_POINTS.

How to send more per-vertex data to custom shaders in kivy

I would like to be able to pass more per-vertex-data to my own custom shaders in kivy than the usual vertex coords + texture coords. Specifically, I would like to pass a value that says which animation frame should be used in selecting the texture coords.
I found an example (http://shadowmint.blogspot.com/2013/10/kivy-textured-quad-easy-right-no.html), and succeeded in changing the format of the vertices passed to a mesh using an argument to the constructor of the Mesh, like this:
Mesh(mode = 'triangles', fmt=[('v_pos', 2, 'float'),
('v_tex0', 2, 'float'),
('v_frame_i', 1, 'float')]
I can then set the vertices to be drawn to something like this:
vertices = [x-r,y-r, uvpos[0],uvpos[1],animationFrame,
x-r,y+r, uvpos[0],uvpos[1]+uvsize[1],animationFrame,
x+r,y-r, uvpos[0]+uvsize[0],uvpos[1],animationFrame,
x+r,y+r, uvpos[0]+uvsize[0],uvpos[1]+uvsize[1],animationFrame,
x+r,y-r, uvpos[0]+uvsize[0],uvpos[1],animationFrame,
x-r,y+r, uvpos[0],uvpos[1]+uvsize[1],animationFrame,
]
..this works well when I run in Ubuntu, but when I run on my android device the drawn texture either doesn't draw, or it looks like the vertex or texture coordinate data is corrupt / not aligned or something.
Here is my shader code in case that is relevant. Again, this all behaves as I want it to when I run in ubuntu, but not when I run on android device.
---VERTEX SHADER---
#ifdef GL_ES
precision highp float;
#endif
/* vertex attributes */
attribute vec2 v_pos;
attribute vec2 v_tex0;
attribute float v_frame_i; // for animation
/* uniform variables */
uniform mat4 modelview_mat;
uniform mat4 projection_mat;
uniform vec4 color;
uniform float opacity;
uniform float sqrtNumFrames; // the width/height of the sprite-sheet
uniform float frameWidth;
/* Outputs to the fragment shader */
varying vec4 frag_color;
varying vec2 tc;
void main() {
frag_color = color * vec4(1.0, 1.0, 1.0, opacity);
gl_Position = projection_mat * modelview_mat * vec4(v_pos.xy, 0.0, 1.0);
float f = round(v_frame_i);
tc = v_tex0;
float w = (1.0/sqrtNumFrames);
tc *= w;
tc.x += w*mod(f,sqrtNumFrames); //////////// I think that the problem might
tc.y += w*round(f / sqrtNumFrames); ///////////// be related to this code, here?
}
---FRAGMENT SHADER---
#ifdef GL_ES
precision highp float;
#endif
/* Outputs from the vertex shader */
varying vec4 frag_color;
varying vec2 tc;
/* uniform texture samplers */
uniform sampler2D texture0;
uniform vec2 player_pos;
uniform vec2 window_size; // in pixels
void main (void){
gl_FragColor = frag_color * texture2D(texture0, tc);
}
I wonder if it may have to do with a version of GLSL and int / float math (in particular in identifying which image from the sprite sheet to draw, see the comments in the glsl code. One version is running on my desktop and another on the device?
Any suggestions for things to experiment with would be much appreciated!
After looking at the log from the running version on the android device (a Moto X phone), I saw that the custom shader was not linking. This appeared to be due to the use of the function round(x), which I replaced with floor(x+0.5) in both cases, and the shader now works on the phone and my desktop properly.
I think the problem is that the version of GLSL supported on the phone and on my PC are different..but I am not 100% certain about this.

Plasma Shader Performance in OpenGL ES 2.0

I am using a plasma shader in my Android (libGDX) app, which I found from here:
http://www.bidouille.org/prog/plasma
Here is my shader (slightly modified):
#define LOWP lowp
precision mediump float;
#else
define LOWP
#endif
#define PI 3.1415926535897932384626433832795
uniform float time;
uniform float alpha;
uniform vec2 scale;
void main() {
float v = 0.0;
vec2 c = gl_FragCoord.xy * scale - scale/2.0;
v += sin((c.x+time));
v += sin((c.y+time)/2.0);
v += sin((c.x+c.y+time)/2.0);
c += scale/2.0 * vec2(sin(time/3.0), cos(time/2.0));
v += sin(sqrt(c.x*c.x+c.y*c.y+1.0)+time);
v = v/2.0;
vec3 col = vec3(1, sin(PI*v), cos(PI*v));
gl_FragColor = vec4(col *.5 + .5, alpha);
}
I am rendering it via an ImmediateModeRendererGL20, as a quad.
However, it simply appears to be way too slow for my needs. I am trying to fill almost the whole screen on my Nexus 7 (first gen) with the shader, and I cannot get even close to 60 FPS.
This is really my first real trip onto the GLSL world, and I have no idea how these things usually should perform!
I am wondering how one could optimize this shader? I really don't care about the image quality, I can sacrifice it. I came to a conclusion that somekind of lookup table might be what I am after and/or dropping the resolution of the shader..? But I am not quite sure where to begin. I am still very new to GLSL and "low-level" programming has never really been my cup of tea, but I am eager to at least try!
The ultimate way to speed this up is to pre-calculate computation-heavy stuff (sin() ans cos() stuff) and bake it into texture(s), then get ready values from them. This will make it super-fast because your shader won't make any heavy computations but consume pre-calculated values.
As was stated in comments, you can optimize performance by moving certain calculations to vertex shader. For example, you can move this line
vec2 c = gl_FragCoord.xy * scale - scale/2.0;
to vertex shader without sacrificing any quality because this is a linear function so interpolation won't distort it.
Do it like this:
// vertex
...
uniform float scale;
varying mediump vec2 c;
const float TWO = 2.0;
...
void main() {
...
gl_FragCoord = ...
c = gl_FragCoord.xy * scale - scale / TWO;
...
}
// fragment
...
varying mediump vec2 c;
...
void main() {
...
// just use `c` as usual
...
}
Also, please use constants instead of literals - literals use your uniform space. While this may not affect performance it is still bad (if you use too much of them you can run out of max uniforms on certain GPUs). More on this: Declaring constants instead of literals in vertex shader. Standard practice, or needless rigor?

Standard derivatives from Fragment Shader (dFdx, dFdy), don't run correctly in Android 4.4

I'm using a fragment shader that uses dFdy dFdx functions to calculate the normal of the
face to view in a flat appearance. This shader has been running ok in gles 2.0 and 3.0. Inexplicably, shader don't work in Android 4.4 ( KitKat - gles3.0 ).
(Solved!!.. individual derivatives for each component, solve the problem).
In order check error, i prepared these shaders :
//Vertex Shader
#version 300 es
precision highp float;
precision highp int;
uniform mat4 PMatrix; //Projection Matrix (varies according to camera)
uniform mat4 MVMatrix; //Model View Matrix (no change)
in vec3 vPosition;
out vec3 vPos;
main()
{
gl_Position=PMatrix * MVMatrix * vec4(vPosition.xyz,1.0);
vPos = (MVMatrix * vec4(vPosition.xyz,1.0)).xyz;
}
// Fragment shader
#version 300 es
#extension GL_OES_standard_derivatives : enable
precision highp float;
precision highp int;
in vec3 vPos;
main()
{
// don't run correctly
// vec3 fdx = dFdx(vPos);
// vec3 fdy = dFdy(vPos);
// ***Solved!*** this run correctly in KitKat
vec3 fdx = vec3(dFdx(vPos.x),dFdx(vPos.y),dFdx(vPos.z));
vec3 fdy = vec3(dFdy(vPos.x),dFdy(vPos.y),dFdy(vPos.z));
vec3 N = normalize(cross(fdx,fdy));
fragColor = vec4(N,1.0);
}
Drawing a cube, in Android<4.4 colors remain fixed for each side, independently of camera position (correct, the color identifies each normal-face). In Android 4.4 , colors vary if you move the camera.
Analyzing each derived separately :
1.- fragColor=vec4(normalize( fdx ), 1.0); colors are constantly changing (wrong)
2.- fragColor=vec4(normalize( fdy ), 1.0); the colors remain quasi-stable (quasi-ok)
A bug in the implementation of these features in Android 4.4?
We are doing something wrong?
You said that this was originally a GLES2 shader, and you are using highp in a fragment shader unconditionally? That is a disaster waiting to happen because GLES2 implementations are not required to support highp in fragment shaders. Likewise, support for dFdx (...), dFdy (...) and fwidth (...) is optional.
You need to check for GL_OES_standard_derivatives and GL_FRAGMENT_PRECISION_HIGH in the GLES2 implementation of this fragment shader.
To that end, you might consider the accuracy hint for derivatives:
GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES (GLES2, if the extension is supported)
GL_FRAGMENT_SHADER_DERIVATIVE_HINT (GLES3)
Individual derivatives in each component solve the problem in KitKat:
// Replace vec3 fdx = dFdx(vPos) by:
vec3 fdx = vec3(dFdx(vPos.x),dFdx(vPos.y),dFdx(vPos.z));
// Replace vec3 fdy = dFdy(vPos) by:
vec3 fdy = vec3(dFdy(vPos.x),dFdy(vPos.y),dFdy(vPos.z));
I've seen similar things on some AMD desktop setups.
Where dFdy( .xyz ) worked fine on NVIDIA/intel I had to do the derivate per-component to get it correctly for some AMD cards.

Categories

Resources