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.
Related
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 am trying to implement a sprite of 8 columns and 8 rows in OpenGL ES 2.0
I made appear the first imagen but I cant figure out how to translate the Texture matrix in OpenGL ES 2.0 , the equivalent of the code in OpenGL 1.0 that I am looking is
gl.glMatrixMode(GL10.GL_TEXTURE);
gl.glLoadIdentity();
gl.glPushMatrix();
gl.glTranslatef(0.0f, 0.2f, 0f);
gl.glPopMatrix();
This are the matrix that I am using atm
/**
* Store the model matrix. This matrix is used to move models from object space (where each model can be thought
* of being located at the center of the universe) to world space.
*/
private float[] mModelMatrix = new float[16];
/**
* Store the view matrix. This can be thought of as our camera. This matrix transforms world space to eye space;
* it positions things relative to our eye.
*/
private float[] mViewMatrix = new float[16];
/** Store the projection matrix. This is used to project the scene onto a 2D viewport. */
private float[] mProjectionMatrix = new float[16];
/** Allocate storage for the final combined matrix. This will be passed into the shader program. */
private float[] mMVPMatrix = new float[16];
/**
* Stores a copy of the model matrix specifically for the light position.
*/
private float[] mLightModelMatrix = new float[16];
My Vertex shader
uniform mat4 u_MVPMatrix; // A constant representing the combined model/view/projection matrix.
uniform mat4 u_MVMatrix; // A constant representing the combined model/view matrix.
attribute vec4 a_Position; // Per-vertex position information we will pass in.
attribute vec3 a_Normal; // Per-vertex normal information we will pass in.
attribute vec2 a_TexCoordinate; // Per-vertex texture coordinate information we will pass in.
varying vec3 v_Position; // This will be passed into the fragment shader.
varying vec3 v_Normal; // This will be passed into the fragment shader.
varying vec2 v_TexCoordinate; // This will be passed into the fragment shader.
// The entry point for our vertex shader.
void main()
{
// Transform the vertex into eye space.
v_Position = vec3(u_MVMatrix * a_Position);
// Pass through the texture coordinate.
v_TexCoordinate = a_TexCoordinate;
// Transform the normal's orientation into eye space.
v_Normal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));
// gl_Position is a special variable used to store the final position.
// Multiply the vertex by the matrix to get the final point in normalized screen coordinates.
gl_Position = u_MVPMatrix * a_Position;
}
My Fragment shader:
precision mediump float; // Set the default precision to medium. We don't need as high of a
// precision in the fragment shader.
uniform vec3 u_LightPos; // The position of the light in eye space.
uniform sampler2D u_Texture; // The input texture.
varying vec3 v_Position; // Interpolated position for this fragment.
varying vec3 v_Normal; // Interpolated normal for this fragment.
varying vec2 v_TexCoordinate; // Interpolated texture coordinate per fragment.
// The entry point for our fragment shader.
void main()
{
// Will be used for attenuation.
float distance = length(u_LightPos - v_Position);
// Get a lighting direction vector from the light to the vertex.
vec3 lightVector = normalize(u_LightPos - v_Position);
// Calculate the dot product of the light vector and vertex normal. If the normal and light vector are
// pointing in the same direction then it will get max illumination.
float diffuse = max(dot(v_Normal, lightVector), 0.0);
// Add attenuation.
diffuse = diffuse * (1.0 / (1.0 + (0.25 * distance)));
// Add ambient lighting
diffuse = diffuse + 0.7;
// Multiply the color by the diffuse illumination level and texture value to get final output color.
gl_FragColor = (diffuse * texture2D(u_Texture, v_TexCoordinate));
}
You will need to perform transformations to the texture co-ordinates yourself, you could do this in one of four places:
Apply the transformation to your raw model data.
Apply the transformation in the CPU (not recommended unless you have good reason as this is what vertex shaders are for).
Apply the transformation in the vertex shader (recommended).
Apply the transformation in the fragment shader.
If you are going to apply a translation to the texture coordinates the most flexible way will be to use your maths library to create a translation matrix and pass the new matrix to your vertex shader as a uniform (the same way you pass the mMVPMatrix and mLightModelMatrix).
You can then multiply the translation matrix by the texture coordinate in the vertex shader and output the result as a varying vector.
Vertex Shader:
texture_coordinate_varying = texture_matrix_uniform * texture_coordinate_attribute;
Fragment Shader:
gl_FragColor = texture2D(texture_sampler, texture_coordinate_varying);
Please note: Your GLES 1.0 code does not actually perform a translation as you surrounded it with a push and pop.
I am trying to use Android Color Matrix in OpenGL ES 2. I have been able to use a 4x4 Matrix using the following code in the Shader (this adds also an intensity parameter):
varying vec2 textureCoordinate;
uniform lowp mat4 colorMatrix;
uniform lowp float intensity;
void main()
{
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
vec4 outputColor = textureColor * colorMatrix;
gl_FragColor = (intensity * outputColor) + ((1.0 - intensity) * textureColor);
}
But i am struggling on how i could convert Android 4x5 matrix to a vec4 matrix usable in the shader. I am not interested in Alpha channel.
The most direct approach is probably to split the ColorMatrix into a mat4 and a vec4, where the mat4 contains the multipliers for the input components, and the vec4 the constant offsets.
One detail to watch out for is the order of the matrix elements in memory. OpenGL uses column major storage, where the ColorMatrix appears to be in row major order. But it looks like you're already correcting for this discrepancy by multiplying the input vector from the right in the shader code.
The shader code will then look like this:
uniform lowp mat4 colorMatrix
uniform lowp vec4 colorOffset;
...
vec4 outputColor = textureColor * colorMatrix + colorOffset;
In the Java code, say you have a ColorMatrix named mat:
ColorMatrix mat = ...;
float[] matArr = mat.getArray();
float[] oglMat = {
matArr[0], matArr[1], matArr[2], matArr[3],
matArr[5], matArr[6], matArr[7], matArr[8],
matArr[10], matArr[11], matArr[12], matArr[13],
matArr[15], matArr[16], matArr[17], matArr[18]};
// Set value of colorMatrix uniform using oglMat.
float[] oglOffset = {matArr[4], matArr[9], matArr[14], matArr[19]};
// Set value of colorOffset uniform using oglOffset.
I am currently using a 4x4 matrix to convert from "pixel" coordinates to OpenGL ones, my scene is full screen so my screen matrix looks like this:
2f / width, 0f, 0f, 0f,
0f, -2f / height, 0f, 0f,
0f, 0f, 0f, 0f,
-1f, 1f, 0f, 1f
I am then mapping this matrix as "uScreen" in the vertex shader in this way:
uniform mat4 uScreen;
attribute vec2 aPosition;
attribute vec2 aTexPos;
varying vec2 vTexPos;
void main() {
vTexPos = aTexPos;
gl_Position = uScreen * vec4(aPosition.xy, 0.0, 1.0);
}
And draw using this fragment shader:
precision mediump float;
uniform sampler2D uTexture;
varying vec2 vTexPos;
void main() {
gl_FragColor = texture2D(uTexture, vTexPos);
}
So let's say that now i have some transformation (just scale and translate and 2D rotations) done with a 3x3 android.graphics.Matrix how can i apply these transformations to this shader? I have tried mapping it to a mat3 in the vertex shader and do "uScreen * uTransform * vec4(aPosition.xy, 0.0, 1.0)" but its not working.
Update
I have tried to map the Canvas Matrix to the shader in this way:
uniform mat4 uScreen;
uniform mat3 uTransform;
attribute vec2 aPosition;
attribute vec2 aTexPos;
varying vec2 vTexPos;
void main() {
vTexPos = aTexPos;
gl_Position = uScreen * mat4(uTransform) * vec4(aPosition.xy, 0.0, 1.0);
};
And now its properly scaling but translation is not working
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.