I'm implementing skeletal animation in my android phone, and here is how I do it:
Calcualte all bone transformation matrices on the CPU side
Create a float texture to store these matrices (so this is done at the beginning of each frame). The codes look like below:
if(texID) {
glDeleteTextures(1, &texID);
texID = 0;
}
glGenTextures(1, &texID);
glBindTexture(GL_TEXTURE_2D, texID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, p);
In vertex shader, fetch matrix from this texture and apply to vertex position
This approach works fine in my Motorola Milestone XT-701 using a POWERVR GPU. But when I run it on a Qualcomm snapdragon (SE Xperia X10i and Google Nexus one), there are many triangles disappeared (looks randomly), so it seems the model is flickering.
I also tried to reduce the scene complexity by rendering only one animated model, and the flicker become less but still exists. Does anyone know what I may be doing wrong? Is this some kind of synchronization problem?
You can see the snapshots here (the first two pictures is correct, and the later two are wrong). The APK file of my grogram can be downloaded here. (it does not require any permission so don't worry)
Here is the vertex shader I used:
struct light {
lowp vec4 position; // light position for a point/spot light or
// normalized dir. for a directional light
lowp vec4 ambient_color;
lowp vec4 diffuse_color;
lowp vec4 specular_color;
lowp vec3 spot_direction;
lowp vec3 attenuation_factors;
lowp float spot_exponent;
lowp float spot_cutoff_angle;
bool compute_distance_attenuation;
};
struct material {
lowp vec4 ambient_color;
lowp vec4 diffuse_color;
lowp vec4 specular_color;
lowp vec4 emissive_color;
lowp float specular_exponent;
};
// uniforms used by the vertex shader
// uniform vec4 u_color;
uniform highp mat4 u_mvMatrix;
uniform highp mat4 u_projMatrix;
uniform bool u_enable_lighting;
uniform light u_light_state;
uniform material u_material_state;
uniform bool u_enable_texture;
uniform highp sampler2D s_jointTex; // use highp for float texture
// attributes input to the vertex shader
// attribute lowp vec4 a_color;
attribute highp vec3 a_position;
attribute lowp vec3 a_normal;
attribute mediump vec2 a_texCoord;
attribute highp float a_jointID;
// varying variables – input to the fragment shader
varying lowp vec4 v_front_color;
varying mediump vec2 v_texCoord;
vec2 mapTo2D(float idx)
{
vec2 st = vec2(idx + 0.5, 0.5);
return st / 256.0;
}
void main()
{
mat4 joint = mat4(1.0);
if(a_jointID >= 0.0)
{
float idx = a_jointID * 4.0;
joint = mat4( texture2D(s_jointTex, mapTo2D(idx)),
texture2D(s_jointTex, mapTo2D(idx+1.0)),
texture2D(s_jointTex, mapTo2D(idx+2.0)),
texture2D(s_jointTex, mapTo2D(idx+3.0)) );
gl_Position = (u_projMatrix * u_mvMatrix) * joint * vec4(a_position, 1.0); // hint compiler to extract uniform calculation
// v_front_color = vec4(1.0, 0.0, 0.0, 1.0);
}
else
{
gl_Position = (u_projMatrix * u_mvMatrix) * vec4(a_position, 1.0); // hint compiler to extract uniform calculation
// v_front_color = vec4(0.0, 1.0, 0.0, 1.0);
}
if(u_enable_lighting)
{
lowp vec4 computed_color = vec4(0.0);
vec3 normal = normalize( vec3(u_mvMatrix * joint * vec4(a_normal, 0.0) ) );
vec3 lightDir = normalize( vec3(u_mvMatrix * u_light_state.position) );
float NdotL = max(dot(normal, lightDir), 0.0);
computed_color += u_light_state.ambient_color * u_material_state.ambient_color + NdotL * u_light_state.diffuse_color * u_material_state.diffuse_color;
if(NdotL > 0.0) {
vec3 half_vec = normalize(lightDir + vec3(0.0, 0.0, 1.0)); // why?
float NdotHV = dot(normal, half_vec);
if(NdotHV > 0.0)
computed_color += u_light_state.specular_color * u_material_state.specular_color * pow(NdotHV, u_material_state.specular_exponent);
}
v_front_color = computed_color;
}
else
v_front_color = vec4(1.0, 1.0, 1.0, 1.0); // u_material_state.ambient_color; // TODO?
v_texCoord = a_texCoord;
}
Re-creating a texture each frame is not efficient. You can use the same texID and just call glTexImage* each frame.
Why don't you use 1D texture? It would remove a burden of tex-coord conversion to 2D.
Your joint matrix is 4x4, but you store it as 4 GL_RGBA vectors. This internal format does allow only [0,1] range, which is not appropriate for your task. Try using GL_RGBA_16f as internal format instead.
Related
I'm trying to save world space coordinates form fragment shader gl_FragColor output to a texture to use later on using frame buffer but it doesn't seem to work correctly
what is the correct way to do it?
//create method
fbo = new FrameBuffer(Pixmap.Format.RGB888, 256,256,true, true);
//render method
fbo.begin();
Gdx.gl.glViewport(0, 0, fbo.getWidth(), fbo.getHeight());
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
Gdx.gl20.glEnable(GL20.GL_TEXTURE_2D);
Gdx.gl20.glEnable(GL20.GL_BLEND);
fisrtShader.begin();
fisrtShader.end();
fbo.end();
texture = fbo.getColorBufferTexture();
texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
texture.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge);
textureRegion = new TextureRegion(texture);
textureRegion.flip(false, true);
the shader:
//vertex shader
#ifdef GL_ES
precision mediump float;
#endif
attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec2 a_texCoord0;
uniform mat4 u_worldTrans;
uniform mat4 u_projViewTrans;
varying vec2 v_texCoord0;
varying vec3 worldSpaceCoords;
void main() {
worldSpaceCoords = a_position + vec3(.5,.5,.5);
v_texCoord0 = a_texCoord0;
gl_Position = u_projViewTrans * u_worldTrans * vec4(a_position, 1.0);
}
//fragment shader
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoord0;
varying vec3 worldSpaceCoords;
void main() {
gl_FragColor = vec4(worldSpaceCoords.x , worldSpaceCoords.y, worldSpaceCoords.z, 1);
}
I am using libgdx to create a virtual trial room app. I am new to both Java and libgdx, so I might be wrong somewhere.
I plan to create a shader and a Mesh, and a texture is rendered on the Mesh(texture is a fabric of purple color with stripes). Then I plan to do affine transformation of very small triangles(shear, rotate, transform or translate) in the mesh to form a shirt(2D image) like below.
My idea is when the triangles transform, they take with them the part of texture with them, and the texture is modified.
Initially when I try to render the texture on the mesh, it shows a black screen instead of the purple fabric. My code:
public class MyAffineTransformation implements ApplicationListener {
Texture img;
String vertexShader;
String fragmentShader;
Mesh mesh;
ShaderProgram shader;
private float[] verts = new float[MAX_VERTS * NUM_COMPONENTS]; //Used to make triangles
private int idx = 0; //The index position
#Override
public void create() {
img = new Texture(Gdx.files.internal("fabric.jpg"));
img.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
img.setFilter(TextureFilter.Linear, TextureFilter.Linear);
mesh = new Mesh(true, 3, 3, new VertexAttribute(Usage.Position, POSITION_COMPONENTS, ShaderProgram.POSITION_ATTRIBUTE),
new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE));
mesh.setVertices(new float[] { -0.5f, -0.5f, 0.5f, -0.5f, 0, 0.5f });
mesh.setIndices(new short[] { 0, 1, 2 });
//Shader - vertex and fragment shader are posted below
vertexShader = Gdx.files.internal("affine.vert").readString();
fragmentShader = Gdx.files.internal("affine.frag").readString();
shader = new ShaderProgram(vertexShader, fragmentShader);
}
#Override
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.graphics.getGL20().glEnable(GL20.GL_TEXTURE_2D);
img.bind();
mesh.render(shader, GL20.GL_TRIANGLES, 0, 3);
}
My Vertex Shader:
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord;
uniform mat4 u_projTrans;
varying vec4 v_color;
varying vec2 v_texCoords;
void main() {
v_texCoords = a_texCoord;
gl_Position = u_projTrans * a_position;
}
Fragment Shader:
#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()
{
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
}
It shows a black screen as of now. Please help me render texture on Mesh.
Also, when that happens, is it possible to do Affine transfotmation(rotation, translation, scaling, shearing etc) of small triangles in the mesh to form a 2D shirt. Please let me know if I am wrong somewhere.
I have a bitmap on my device(which is a 6x1 cubemap), which I want to render on all the faces of the cube
InputStream is = getContext().getResources().openRawResource(R.raw.photo);
Bitmap bitmap = BitmapFactory.decodeStream(is);
int bytes = bitmap.getByteCount();
ByteBuffer pixels = ByteBuffer.allocate(bytes);
bitmap.copyPixelsToBuffer(pixels);
Here is my vertex shader:
uniform mat4 uMVPMatrix;
uniform mat4 uSTMatrix;
attribute vec4 aPosition;
attribute vec4 aTextureCoord;
attribute vec4 aColor;
varying vec2 vTextureCoord;
varying vec4 vColor;
void main() {
gl_Position = uMVPMatrix * aPosition;
vTextureCoord = (uSTMatrix * aTextureCoord).xy;
vColor = aColor;
}
Here is my fragment shader:
precision mediump float;
varying vec2 vTextureCoord;
varying vec4 vColor;
uniform samplerCube sTexture;
void main() {
gl_FragColor = textureCube(sTexture, vec3(vTextureCoord, 1.0)) * vColor;
}
And here is what I am doing in my renderer in onSurfaceCreated():
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
int[] texIds = new int[1];
GLES20.glGenTextures(1, texIds, 0);
m360PhotoTextureId = texIds[0];
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(
GLES20.GL_TEXTURE_CUBE_MAP,
mTextureId);
for (int i = 0 ; i < 6 ; i++ ){
pixels.position(0);
GLES20.glTexImage2D(
GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
0,
GLES20.GL_RGBA,
1,
1,
0,
GLES20.GL_RGBA,
GLES20.GL_UNSIGNED_BYTE,
pixels);
}
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_CUBE_MAP,
GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_CUBE_MAP,
GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_CUBE_MAP,
GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(
GLES20.GL_TEXTURE_CUBE_MAP,
GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, 0);
All I see is a black screen in my texture view, when I expect to see a photo(in the pixels) being rendered on all the faces of the cube.
Any pointers or help would be appreciated.
I tried:
Vertex shader:
uniform mat4 uMVPMatrix;
attribute vec4 aPosition;
varying vec3 vTextureCoord;
void main() {
gl_Position = uMVPMatrix * aPosition;
vTextureCoord = aPosition.xyz;
}
Fragment shader:
precision mediump float;
varying vec3 vTextureCoord;
uniform samplerCube sTexture;
void main() {
gl_FragColor = textureCube(sTexture, vTextureCoord);
}
But I get the same black screen.
A cubemap texture is a texture who's images represent the faces of a cube. The "texture coordinate" for a cubemap texture is the vector direction from the center of the cube which points to the color you want to use.
You are trying to use a regular old 2D texture coordinate, with a third component probably added to silence the compiler. You must provide directions, not 2D coordinates. You can generate them in the vertex shader from your position. But that requires knowing what space aPosition is, and you didn't tell me. So I can't show you how to do that.
Regardless, the vertex shader needs to be providing a 3D direction for the texture coordinate. It should either be generated or passed from a VS input.
Note that your program may have other problems. But this is the problem that can be deduced from your code.
I am making a model of the Earth using OpenGL ES 2.0 for Android. I draw the sphere, and each time, in the vertex shader, I want to rotate it. Each time it's drawn, I set a uniform representing the rotation angle. Here is how I calculate the new points:
Vertex Shader:
uniform mat4 u_Matrix;
uniform vec3 u_VectorToLight;
uniform vec3 u_Center;
uniform float u_RotationAngle;
attribute vec4 a_Position;
attribute vec2 a_TextureCoordinates;
attribute vec3 a_Normal;
varying vec2 v_TextureCoordinates;
varying vec3 v_VectorToLight;
varying vec3 v_Normal;
vec2 rotate(vec2 c, vec2 p, float a);
void main() {
v_TextureCoordinates = a_TextureCoordinates;
v_VectorToLight = u_VectorToLight;
v_Normal = a_Normal;
vec2 point = rotate(u_Center.xz, a_Position.xz, radians(u_RotationAngle));
gl_Position = a_Position;
gl_Position *= u_Matrix;
gl_Position.x = point.x;
gl_Position.z = point.y;
}
vec2 rotate(vec2 c, vec2 p, float a) {
p.x -= c.x;
p.y -= c.y;
float x1 = p.x * cos(a) - p.y * sin(a);
float y1 = p.x * sin(a) + p.y * cos(a);
p.x = c.x + x1;
p.y = c.y + y1;
return p;
}
Fragment Shader:
precision mediump float;
uniform sampler2D u_TextureUnit;
varying vec2 v_TextureCoordinates;
varying vec3 v_VectorToLight;
varying vec3 v_Normal;
void main() {
gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);
vec4 color = gl_FragColor.rgba;
vec3 scaledNormal = v_Normal;
scaledNormal = normalize(scaledNormal);
float diffuse = max(dot(scaledNormal, v_VectorToLight), 0.0);
gl_FragColor.rgb *= diffuse;
float ambient = 0.2;
gl_FragColor.rgb += ambient * color;
}
I rotate each individual point around the center of the sphere, using the rotate() method in the vertex shader, and this just results in a distorted earth, made smaller on the X and Z axes, but not the Y (so imagine a very thin version of the Earth). What's even more confusing is that even though I pass in a new value for the rotation angle each time, I still get the same still image. Even if it is distorted, It should still look different each time in some way, since I'm using a different angle each time. Here's how I set the uniforms:
public void setUniforms(float[] matrix, Vector vectorToLight, int texture, Point center, float angle) {
glUniformMatrix4fv(uMatrixLocation, 1, false, matrix, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(uTextureUnitLocation, 0);
glUniform3f(uRadiusLocation, center.x, center.y, center.z);
glUniform3f(uVectorToLightLocation, vectorToLight.x, vectorToLight.y, vectorToLight.z);
glUniform1f(uRotationAngleLocation, angle); // <-- I set the rotation angle
}
I believe there is a problem with how you combine the rotation around the axis with your global rotation contained in u_Matrix:
vec2 point = rotate(u_Center.xz, a_Position.xz, radians(u_RotationAngle));
gl_Position = a_Position;
gl_Position *= u_Matrix;
gl_Position.x = point.x;
gl_Position.z = point.y;
Since point only contains the rotation around the axis, and you replace x and z of gl_Position with the values from point, the resulting x and z do not have u_Matrix applied to them.
You need to first apply your axis rotation, and then apply u_Matrix to this transformed point:
vec2 point = rotate(u_Center.xz, a_Position.xz, radians(u_RotationAngle));
vec4 rotPoint = a_Position;
rotPoint.x = point.x;
rotPoint.z = point.y;
gl_Position = rotPoint;
gl_Position *= u_Matrix;
To complete this task, it would generally be much more efficient to calculate the rotation matrix once in your Java code, set it as a uniform, and then only apply a matrix multiplication in your vertex shader. With the code you have now, you will calculate a cos() and sin() value for each vertex in your shader code, unless the GLSL compiler is really smart about it. If you don't want to pass in a full matrix, I would at least pass the cosine and sine values into the shader instead of the angle.
I'm struggling a bit to apply the color for my geometry. When I specify it directly in the vertex shader ("varColor = vec4(1.0, 0.5, 0.4, 1.0);") - everything is ok. But if I use color values from the "vColor" attribue - everything gets messed up.
(Added some screenshots to show what I mean)
Can someone help me to figure out what am I doing wrong, or point me in the right direction? Thanks.
Using "varColor = vec4(1.0, 0.5, 0.4, 1.0);"
Using "varColor = vColor"
Vertex shader:
precision mediump float;
uniform mat4 modelViewProjectionMatrix;
attribute vec4 vPosition;
attribute vec2 vTexCoord;
attribute vec4 vColor;
varying lowp vec4 varColor;
varying mediump vec2 varTexCoord;
void main()
{
gl_Position = modelViewProjectionMatrix * vPosition;
varTexCoord = vTexCoord;
// varColor = vColor;
varColor = vec4(1.0, 0.5, 0.4, 1.0);
}
Fragment shader:
precision mediump float;
uniform sampler2D Texture0;
varying vec4 varColor;
varying vec2 varTexCoord;
void main()
{
gl_FragColor = texture2D(Texture0, varTexCoord) * varColor;
}
After shader is linked, I'm binding my attributes like this:
mMatrixMVP = glGetUniformLocation(mProgramId, "modelViewProjectionMatrix");
glBindAttribLocation(mProgramId, 0, "vPosition");
glBindAttribLocation(mProgramId, 1, "vTexCoord");
glBindAttribLocation(mProgramId, 2, "vColor");
mTexture = glGetUniformLocation(mProgramId, "Texture0");
glUniform1i(mTexture, 0);
Structure that holds my vertex information:
struct Vertex
{
float xyz[3];
float st[2];
unsigned char color[4]; // All assigned to value of 255
};
When rendering, after vertex buffer is bound, I'm setting vertex attributes like this:
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) offsetof(Vertex, xyz));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) offsetof(Vertex, st));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (GLvoid*) offsetof(Vertex, color));
glActiveTexture(GL_TEXTURE0);
pTexture->Bind(); // Just a "glBindTexture(GL_TEXTURE_2D, mTextureId);"
glUniform1i(mpCurrentShader->GetTexture(), 0);
After this I'm binding the index buffer and calling "glDrawElements".
Then, calling glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); and disabling all attributes with "glDisableVertexAttribArray" and finally - calling glBindBuffer(GL_ARRAY_BUFFER, 0);
You need to make your glBindAttribLocation() calls before linking the shader program.
If you link a program without specifying the location of attributes with either glBindAttribLocation(), or with layout qualifiers in the shader code, the attribute locations will be assigned automatically when the shader program is linked. If you call glBindAttribLocation() after linking the program, the new locations will only take effect if you link the shader program again.