I try to make an app with OpenGLES 2. There are 2 devices for testing.
Unfortunately there seems to be a difference between both.
I have the following "basic" shader code for testing:
// vertex shader
uniform mat4 uVMatrix; // View Matrix
uniform mat4 uPMatrix; // perspective Matrix
uniform vec3 uLight1Pos; // LightPos
attribute vec4 aPosition;
attribute vec4 aColor;
varying vec4 vColor;
void main() {
vColor = aColor;
mat4 MVPMatrix = uPMatrix * uVMatrix;
gl_Position = MVPMatrix * aPosition;
}
// fragment shader
uniform mat4 uVMatrix;
uniform vec3 uLight1Pos;
varying vec4 vColor;
varying vec3 vPosition;
void main() {
vec3 light2Pos = uLight1Pos;
gl_FragColor = vColor;
}
The problem is, some uniforms can't be found.
I attach and link the shader as usual but checking the IDs like:
uVMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uVMatrix");
uPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uPMatrix");
uLight1PosHandle = GLES20.glGetUniformLocation(mProgram, "uLight1Pos");
I get different values.
On my Galaxy S1 it is 2, 1, 3 (so all valid values - even though a weird order).
On my Galaxy S3 it is 0, 1, -1 (so the last one can not be found).
What am I doing wrong? Do I have to declare uniform differently on a S3 (Mali GPU)?
I realized that I have to "use" the uniforms in order to get a reference. That is the reason why I wrote vec3 light2Pos = uLight1Pos; If I dont do this, I also get no reference on the S1.
Thank you for your help!
Tobias
- EDIT -
Weird enough, I tried to change the uniform of the vertex shader from vec3 to a mat4:
uniform mat4 uLight1Pos;
mat4 lPos = uLight1Pos;
It appears that matrices work fine and I can get a handle for it when using a matrix. How comes?
Your "use" doesn't have a contribution to the output fragcolor. You just proved the S3 has a better compiler than the S1 for removing dead code.
GLSL compilers tend to eliminate unused uniforms which don't contribute to the result. This behavior is in compliance with OpenGL specification.
In your case, the compiler is allowed to eliminate the dead line vec3 light2Pos = uLight1Pos; rendering the uLight1Pos uniform useless.
Related
I'm trying ro render a 3d mesh on android, the positions of the vertices are provided like so:
var positionHandle = GLES20.glGetAttribLocation(program, "vPosition").also {
GLES20.glEnableVertexAttribArray(it)
GLES20.glVertexAttribPointer(
it, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer
)
}
My vertex shader:
uniform mat4 uMVPMatrix;
attribute vec4 vPosition;
varying vec4 positionOut;
void main() {
gl_Position = uMVPMatrix * vPosition;
positionOut = vPosition;
}
Fragment shader:
varying vec4 positionOut;
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0,1,1);
}
This code works fine on one device (Android 9 on OnePlus device) but it doesn't work on (Android 10 Samsung Galaxy device) and no errors are thrown.
If I remove the line varying vec4 positionOut; from the fragment shader the render works.
The question is why only the definition of a varying variable breaks the render? I don't use the variable now but I want to use it in the next step.
Your code is invalid as per the spec:
The fragment language has no default precision qualifier for floating point types. Hence for float, floating
point vector and matrix variable declarations, either the declaration must include a precision qualifier or
the default float precision must have been previously declared.
So you either have to declare it as for example varying mediump ec4 positionOut;, or you put into some scope of a precision statement.
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.
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.
This is the best I can do with obtaining the debug errors.
Link for a larger image version
As you can see, glGetProgramInfoLog() returns the following sentence:
"Invalid vertex shader. Link cannot proceed."
The yellow line in the Eclipse IDE is the code line where the program had just executed, via stepping-over. The green line with an arrow pointing it is where the program will execute.
I would prefer it if it tells me what line my vertex shader code is wrong at, or something that points me in a good direction. With this vague description, I can only ask you for help.
The vertex shader code looks like this:
uniform mat4 u_mvpMatrix;
uniform mat4 u_mvMatrix;
uniform vec3 u_lightPosition;
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec3 a_normal;
varying vec4 v_color;
void main() {
vec3 modelViewVertex = vec3(u_mvMatrix * a_position);
vec3 modelViewNormal = vec3(u_mvMatrix * vec4(a_normal, 0.0));
float distance = length(u_lightPosition - modelViewVertex);
vec3 lightVector = normalize(u_lightPosition - modelViewVertex);
float diffuse = max(dot(modelViewNormal, lightVector), 0.1);
diffuse = diffuse * (1.0 / (0.25 * distance * distance));
v_color = a_color * diffuse;
gl_Position = mvpMatrix * a_position;
}
I don't know where I did wrong. Can you help me find it? If you need more info, I'll add them. Thanks in advance.
EDIT 1:
There is no debug info from glGetShaderInfo().
Larger version.
Even more info:
EDIT 2:
Tried a combination of glGetShader() and glGetShaderInfo(), still no luck.
Larger version.
Found the error!
Of all this time, I was editing the wrong source file!
Kept saving/checking/saving/checking..., I didn't realize that the source code shown above isn't included in the project, therefore the program stating that my vertex shader is invalid, is because the compiler is compiling the source code with an empty vertex shader, and not the newly-modified version (which is the source code I was editing this whole time).
I'm so sorry to waste everybody's time.
I'm optimizing a game that works for both, iPhone and Android. I'm using 4 shader to draw the scene and I noticed that if I change one of them to another the fps goes from 32 to 42, even though there's only 1 sprite being drawn with that shader, and the only difference in this 2 shaders is just a product in the fragmente shader.
These are the shaders:
default-2d-tex.shader
#ifdef GL_ES
precision highp float;
precision lowp int;
#endif
#ifdef VERTEX
uniform mat4 umvp;
attribute vec4 avertex;
attribute vec2 auv;
varying vec2 vuv;
void main()
{
// Pass the texture coordinate attribute to a varying.
vuv = auv;
// Here we set the final position to this vertex.
gl_Position = umvp * avertex;
}
#endif
#ifdef FRAGMENT
uniform sampler2D map0;
uniform vec4 ucolor;
varying vec2 vuv;
void main()
{
gl_FragColor = texture2D(map0, vuv) * ucolor;
}
#endif
default-2d-tex-white.shader
#ifdef GL_ES
precision highp float;
precision lowp int;
#endif
#ifdef VERTEX
uniform mat4 umvp;
attribute vec4 avertex;
attribute vec2 auv;
varying vec2 vuv;
void main()
{
// Pass the texture coordinate attribute to a varying.
vuv = auv;
// Here we set the final position to this vertex.
gl_Position = umvp * avertex;
}
#endif
#ifdef FRAGMENT
uniform sampler2D map0;
varying vec2 vuv;
void main()
{
gl_FragColor = texture2D(map0, vuv);
}
#endif
Again,
If I modify default-2d-tex.shader and remove the product "* ucolor", the fps goes from 32 to 42, and I'm using it for just one sprite in the scene!
Is this normal? Why is this shader being so slow and how can I improve it?
EDIT:
I see this performance slowdown on both iPod and Android in an equal ratio. Both are PowerVr SGX GPUs (iPod 3rd gen and Samsung Galaxy SL -PowerVR SGX 530-). iOS version is 4.1 and Android is 2.3.3
The sprite I'm drawing is scaled to fill the screen (scaled to 4x) and I'm drawing it once per frame. It's taken from a texture map so the texture is actually larger (1024x1024) but the portion taken is 80x120. Alpha blending is enabled.
EDIT 2
I made a mistake. The sprite is scaled 11x: its 32x48.
If I don't draw that sprite at all, fps goes to 45. I'm drawing a lot of sprites in the scene, why is that one taking so much time? Could it be because it's scaled so much?
When you remove "* ucolor" from the abovementioned code many things happen:
uniform ucolor becomes unused
GLSL compiler removes this uniform from the active uniforms set
when you have you program linked the ID of the missing uniform
becomes -1
when you do glUniform4fv( -1, value ) for the removed uniform it
just exits instantly, without any update
This is pretty much less work compared to having "* ucolor" in place.
And additionally, this is one less operation per fragment in the fragment shader.
Your problem is in your fragment shader in default-2d-tex.shader. You have
uniform vec4 ucolor;
That means, that each of your color's components (RGBA) will be converted to 32-bits float value. And this drops performance heavily.
Should be:
uniform lowp vec4 ucolor;