View more

View more

View more

### Image of the Day Submit

IOTD | Top Screenshots

### The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.

# OpenGL ES 3.0 matrix array only using first matrix

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

8 replies to this topic

### #1Rajveer  Members

Posted 05 December 2013 - 11:29 AM

I am doing GPU skinning in my vertex shader which works fine on PC, and which I'm porting to Android. My vertex shader is below, and the problem is that the creation of the matTransform matrix seems to only use the first matrix in boneMatrices:

#version 300 es

precision highp float;
precision highp int;

//Uniform count: projectionMatrix(16) + modelViewMatrix(16) + MVPMatrix(16) + textureMatrix(16) + normalMatrix(9) + lightMVPMatrices(16*5) + nShadowLights(1) + boneMatrices(16*boneMax)  = 73 + 1 + 16*shadowLightMax + 16*boneMax = (out of ~1024 components)
//GLSL ES (vectors): projectionMatrix(4) + modelViewMatrix(4) + MVPMatrix(4) + textureMatrix(4) + normalMatrix(3) + lightMVPMatrices(4*5) + nShadowLights(1) + boneMatrices(4*boneMax) = 19 + 4*shadowLightMax + 4*boneMax = 239 out of 256 vectors on Nexus 5 (shadowLightMax = 5, boneMax = 50, 17 vec4s remain, or 4 matrices and 1 vec4)
//Matrices
//uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 MVPMatrix;
uniform mat4 textureMatrix;
uniform mat3 normalMatrix;
uniform mat4 lightMVPMatrices[5];

//Bones
uniform mat4 boneMatrices[50];

//Vertex information
in vec3 position;
in vec4 colour;
in vec2 texCoord;
in vec3 normal;
in vec3 boneWeights;
in ivec4 boneIndices;

out vec4 _colour;
out vec2 _texCoord;
out vec3 _normal;
out vec3 _eyePos;
out vec4 _lightPos[5];

void main(void)
{
vec4 positionSkinned;
vec4 normalSkinned;

mat4 matTransform = boneMatrices[boneIndices[0]] * boneWeights[0];
matTransform += boneMatrices[boneIndices[1]] * boneWeights[1];
matTransform += boneMatrices[boneIndices[2]] * boneWeights[2];
float finalWeight = 1.0 - (boneWeights[0] + boneWeights[1] + boneWeights[2]);
matTransform += boneMatrices[boneIndices[3]] * finalWeight;

positionSkinned = matTransform * vec4(position, 1.0);
//positionSkinned.w = 1.0;
normalSkinned = matTransform * vec4(normal, 0.0);

gl_Position = MVPMatrix * positionSkinned;
_colour = colour;
_texCoord = (textureMatrix * vec4(texCoord, 0.0, 1.0)).xy;
_normal = normalize(normalMatrix * normalize(normalSkinned.xyz));
_eyePos = (modelViewMatrix * positionSkinned).xyz;
for(int i = 0; i < nShadowLights; i++)
_lightPos[i] = lightMVPMatrices[i] * positionSkinned;
}

I have verified that:

1) the correct matrices get pushed into boneMatrices
2) the correct bone indexes exist within boneIndices
3) the correct boneWeights exist within boneWeights
4) accessing components of boneIndices with dot notation (.x, .y, .z and .w) doesn't make a different
5) There are no OpenGL errors at all, as I check for errors after every call, and uniform size isn't an issue (if I increase boneMatrices by 5 extra matrices, I get invalid operation errors after each time I push matrices to the shader, but at this size and lower it's fine)

I have checked points 1, 2 and 3 (boneMatrices, boneIndices and boneWeights are correct) by doing the following:

1) using a specific animation which modified a few bones only (e.g. boneMatrix[6]), then hard-coding boneMatrix[6] and verifying that all vertices get properly modified by this single matrix, with the same result on PC and Android

2) drawing out boneIndices by doing the following in the vertex shader:

_colour = vec4(float(boneIndices[0]), float(boneIndices[1]), float(boneIndices[2]), float(boneIndices[3]));

and the following in the fragment shader:

gl_FragColor = _colour

with the same colours on PC and Android

3) doing the same as above but with setting _colour to:

_colour = vec4(boneWeights[0], boneWeights[1], boneWeights[2], finalWeight);

I have no idea what else to try, and it definitely seems to be that only the first matrix is used. I have also tried using vec4 instead of ivec4 for boneIndices. This is on a Nexus 5 with an OpenGL ES 3.0 context. Help!

Here are images from it running both on Windows (showing all bone matrices being used) and on Android (showing only the first being used) with bones indices used for colour.

Edited by Rajveer, 05 December 2013 - 11:55 AM.

### #2L. Spiro  Members

Posted 05 December 2013 - 02:07 PM

Do you really need 50 weights?  First-off, reduce that number to something reasonable, or at least to the actual bare minimum you need for any given part of the model if not the whole model.

Some Android devices have problems with arrays in shaders.  Are you sure your other mat4 arrays are really working too?

boneWeights should be a vec4, not a vec4, and should already be normalized.  Simplify your shader by eliminating the “finalWeight = 1.0 - …” line.

L. Spiro

### #3Rajveer  Members

Posted 05 December 2013 - 02:14 PM

I use 50 matrices for now as I'm just maximising the number of uniforms I can have, until all my assets have been finalised. Reducing this number down to around 30 (the max I'm using right now) doesn't have any effect, either way I've done the uniform space calculations at the top of the shader and am currently below the minimum that OpenGL ES guarantees.

I've not tried using the other mat4 matrix yet, just the boneMatrix one. This sounds interesting, and could potentially be the cause of my issues! Do you have any more information or sources regarding this?

Also regarding finalWeight, I only store 3 weights per vertex to reduce space used, as the fourth can just be calculated by doing "1.0 - all other weights".

Thanks!

Edited by Rajveer, 05 December 2013 - 04:21 PM.

### #4L. Spiro  Members

Posted 05 December 2013 - 07:27 PM

Also regarding finalWeight, I only store 3 weights per vertex to reduce space used, as the fourth can just be calculated by doing "1.0 - all other weights".

The CPU-side space you may or may not be saving is not worth the trade-off in performance. Any device running OpenGL ES 3.0 has plenty of CPU RAM.

Not much.
It was an issue we had in-house on some devices based on a certain type of GPU.
I wasn’t directly part of the Android team; I only heard them talking about it, and I don’t remember the details since it has been a while.

L. Spiro

Edited by L. Spiro, 05 December 2013 - 07:28 PM.

### #5Rajveer  Members

Posted 05 December 2013 - 07:39 PM

The CPU-side space you may or may not be saving is not worth the trade-off in performance. Any device running OpenGL ES 3.0 has plenty of CPU RAM.

Hmm, actually I guess you're right, I should be choosing performance over (probably negligible) memory saved, especially on mobile platforms.

Not much.
It was an issue we had in-house on some devices based on a certain type of GPU.
I wasn’t directly part of the Android team; I only heard them talking about it, and I don’t remember the details since it has been a while.

Ah that's a shame. I think the next step is for me to find somebody with an Android device with OpenGL ES 3.0 capability without an Adreno 330 (e.g. Note 3 Exynos version) to make sure it's not a problem with my device. If I do have to find a workaround, can you think of any way I can get around this (without resorting to CPU skinning, or having 30-50 individual bone matrices in my shader, unless it's possible to pass an array of uniforms into non-array but sequential locations in the shader)?

Edited by Rajveer, 05 December 2013 - 07:50 PM.

### #6hdxpete  Members

Posted 05 December 2013 - 08:07 PM

I just got done doing this in opengl es 2.0

problem i had was the GPU stores everything as floats (even if it was an int) so the rounding error between the CPU and GPU was killing me. CPU said 1.0 but GPU was generating 0.99999 which got turned to 0.

solved it like this

ivec4 index;
index.x = int(blendIndices.x + 0.5);
index.y = int(blendIndices.y + 0.5);
index.z = int(blendIndices.z + 0.5);
index.w = int(blendIndices.w + 0.5);
vec4 finalPosition;

finalPosition = blendWeights.x * ( boneMatrix[ index.x] * vec4( position, 1.0));
finalPosition += blendWeights.y * ( boneMatrix[ index.y] * vec4( position, 1.0));
finalPosition += blendWeights.z * ( boneMatrix[ index.z] * vec4( position, 1.0));
finalPosition += blendWeights.w * ( boneMatrix[ index.w] * vec4( position, 1.0));

i forget if i had to load the indices in 0123 order or 3210 order. but i was also moving from a big endian machine to a little endian machine. so it might have been unrelated to your problem.

### #7L. Spiro  Members

Posted 05 December 2013 - 08:12 PM

Adreno and Mali are the 2 that had problems related to arrays.

In Adreno, uniform arrays were not possible.

In Mali, sampler arrays were not possible.

The work-around for us was that in our shader preprocessing step we flattened out arrays such that

uniform vec4 Blah[2];

became

uniform vec4 Blah_0;
uniform vec4 Blah_1;

and we would access them as such anywhere in the shader.

That works fine for hard-coded indices such as “boneIndices[1]” -> “boneIndices_1” but I am not sure what they did for dynamic arrays.

L. Spiro

### #8Rajveer  Members

Posted 05 December 2013 - 08:33 PM

I just got done doing this in opengl es 2.0

Thanks for the suggestion, unfortunately it didn't work If this were the issue though then not everything would get floored to 0, only indicies that should be 1, and there would still be some movement (although with wrong bone indices) no? I also tried reversing the indices just in case but no luck.

Adreno and Mali are the 2 that had problems related to arrays.

Thanks for checking! That...sucks. I'll have to look to see if it's possible to use a uniform with just an offset from another uniform's location (doubt it by the sounds of the last part of your sentence). Bah!

### #9Rajveer  Members

Posted 09 December 2013 - 05:40 PM

In the end this looks to be a limitation of the Adreno driver, which doesn't support indexing a uniform array of matrices without a constant integer contrary to what is mandatory within the OpenGL ES spec. A workaround however is to just use a uniform array of vec4s, as it does seem to support indexing these with variables (as is done within their SDK).

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.