I am developing a simple program which shows a cube on the screen with lighting. I am using OpenGL ES 2.0 on an Android device. But only the bottom surface of the cube is non-transparent. Other surfaces look transparent (i think it is because of lighting. when i stop using lighting it looks like all the surfaces are non-transparent) so that you can see the surface behind it.
Here are my cube's vertices:
static float vertexs[] = { // in counterclockwise order:
// front
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
// back
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
// left
-0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
// right
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
// up
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
// bottom
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f
};
and here are the normals I am using (is the issue here?):
static float normals[] = {
// front
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
// back
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
// left
-1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
// right
1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
// up
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
// bottom
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f
};
and the shaders are below:
private final String vertexShaderCode =
// Light
"uniform vec4 u_LightAmbient;\n" +
"uniform vec4 u_LightDiffuse;\n" +
"uniform vec4 u_LightSpecular;\n" +
"uniform vec4 u_LightPos;\n" +
//Material
"uniform vec4 u_MaterialAmbient;\n" +
"uniform vec4 u_MaterialDiffuse;\n" +
"uniform vec4 u_MaterialSpecular;\n" +
"uniform float u_MaterialShininess;\n" +
// Matrices
"uniform mat4 u_ModelViewMatrix;\n" +
"uniform mat4 u_ProjectionMatrix;\n" +
"uniform mat4 u_NormalMatrix;\n" +
// vertecies
"attribute vec4 a_Position;\n" +
"attribute vec3 a_Normal;\n" +
"varying vec4 v_Color;\n" +
"void main() {\n" +
"vec4 ambient = u_LightAmbient * u_MaterialAmbient;\n" +
"vec3 P = vec3(u_ModelViewMatrix * a_Position);\n" +
"vec3 L = normalize(vec3(u_LightPos) - P);\n" +
"vec3 N = normalize(mat3(u_NormalMatrix) * a_Normal);\n" +
"vec4 diffuseP = vec4(max(dot(L, N), 0.0));\n" +
"vec4 diffuse = diffuseP * u_LightDiffuse * u_MaterialDiffuse;\n" +
"vec3 S = normalize(L + vec3(0.0, 0.0, 1.0));\n" +
"float specularP = pow(max(dot(N,S), 0.0), u_MaterialShininess);\n" +
"vec4 specular = specularP * u_LightSpecular * u_MaterialSpecular;\n" +
//hyouji sareru iro
"v_Color = ambient + specular + diffuse;\n" +
//position
"gl_Position = u_ProjectionMatrix * u_ModelViewMatrix * a_Position;\n" +
"}\n";
private final String fragmentShaderCode =
"precision mediump float;\n" +
"varying vec4 v_Color;\n" +
"void main() {\n" +
"gl_FragColor = v_Color;\n" +
"}\n";
and the drawing function is below:
public Cube() {
// initialize vertex byte buffer for shape coordinates
ByteBuffer bb = ByteBuffer.allocateDirect(vertexs.length * 4);
ByteBuffer nb = ByteBuffer.allocateDirect(normals.length * 4);
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
nb.order(ByteOrder.nativeOrder());
// create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
normalBuffer = nb.asFloatBuffer();
// add the coordinates to the FloatBuffer
vertexBuffer.put(vertexs);
float div = (float)Math.sqrt(3);
for(int i = 0; i < normals.length; i++){
normals[i] /= div;
}
normalBuffer.put(normals);
// set the buffer to read the first coordinate
vertexBuffer.position(0);
normalBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = MyRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
public void draw(float[] mvMatrix, float[] pMatrix) {
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's aPosition member
positionHandle = GLES20.glGetAttribLocation(mProgram, "a_Position");
normalHandle = GLES20.glGetAttribLocation(mProgram, "a_Normal");
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
GLES20.glEnableVertexAttribArray(normalHandle);
GLES20.glVertexAttribPointer(normalHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, normalBuffer);
modelViewMatrixHandle = GLES20.glGetUniformLocation(mProgram, "u_ModelViewMatrix");
GLES20.glUniformMatrix4fv(modelViewMatrixHandle, 1, false, mvMatrix, 0);
projectionMatrixHandle = GLES20.glGetUniformLocation(mProgram, "u_ProjectionMatrix");
GLES20.glUniformMatrix4fv(projectionMatrixHandle, 1, false, pMatrix, 0);
normalMatrixHandle = GLES20.glGetUniformLocation(mProgram, "u_NormalMatrix");
float[] tmp = new float[16];
float[] normalMatrix = new float[16];
Matrix.invertM(tmp, 0, mvMatrix, 0);
Matrix.transposeM(normalMatrix, 0, tmp, 0);
GLES20.glUniformMatrix4fv(normalMatrixHandle, 1, false, normalMatrix, 0);
lightAmbientHandle = GLES20.glGetUniformLocation(mProgram, "u_LightAmbient");
GLES20.glUniform4f(lightAmbientHandle, 0.2f, 0.2f, 0.2f, 1.0f);
lightDiffuseHandle = GLES20.glGetUniformLocation(mProgram, "u_LightDiffuse");
GLES20.glUniform4f(lightDiffuseHandle, 0.5f, 0.5f, 0.5f, 1.0f);
lightSpecularHandle = GLES20.glGetUniformLocation(mProgram, "u_LightSpecular");
GLES20.glUniform4f(lightSpecularHandle, 0.0f, 0.0f, 0.0f, 1.0f);
lightPosHandle = GLES20.glGetUniformLocation(mProgram, "u_LightPos");
materialAmbientHandle = GLES20.glGetUniformLocation(mProgram, "u_MaterialAmbient");
materialDiffuseHandle = GLES20.glGetUniformLocation(mProgram, "u_MaterialDiffuse");
materialSpecularHandle = GLES20.glGetUniformLocation(mProgram, "u_MaterialSpecular");
materialShininessHandle = GLES20.glGetUniformLocation(mProgram, "u_MaterialShininess");
// Front
setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
// Back
setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 4, 4);
// Left
setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 8, 4);
// Right
setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 12, 4);
// Top
setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 16, 4);
// Bottom
setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 20, 4);
// Disable vertex array
GLES20.glDisableVertexAttribArray(positionHandle);
}
private void setMaterial(float r, float g, float b, float a){
GLES20.glUniform4f(materialAmbientHandle, r, g, b, a);
GLES20.glUniform4f(materialDiffuseHandle, r, g, b, a);
GLES20.glUniform4f(materialSpecularHandle, r, g, b, a);
GLES20.glUniform4f(materialShininessHandle, r, g, b, a);
}
}
The result image is below
--- An additional Answer to your picture ---
Now that you posted your picture it doesn't look like the alpha value. It looks much more like the Backface Culling.So you certainly made the wrong order for the Vertices. Do you know about Culling? Please, try this:
GLES20.glDisable(GLES20.GL_CULL_FACE);
Does it look better than?
I think the problem is,that your faces are drawn "backside" and Culling is a mechanism to avoid drawing faces when they are "not seen" so if they are in the wrong order, GL doesn't render them.
It is probably not the best explainations on the net but take a look here:
http://www.altdevblogaday.com/2011/08/03/backface-culling-101/
The "order" of the vertices of your face is important. If you made them clockwise or counter clockwise determines whether OpenGL thinkgs it is rendering a back side or the front. (see glFrontFace(GLenum mode);)
I got that image from the mentioned web site.
If your Shader thinks it is rendering the rear side, this face is simply skipped to safe rendering ressources you normaly don't need. So you can see through the cube.
Your solutions are
I think the last one makes the most work but is the most promising. But if you are not sure this may be the problem, try disabling the BackfaceCulling first. If everything seems to be fine, you can still fix it.
I hope that helped.
--- my original answer ---
Well, by saying
gl_FragColor = v_Color;
in the Fragment Shader and defining earlier in the Vertex Shader:
v_Color = ambient + specular + diffuse;
That means, you give your Fragment/Pixel the Values of your light. Including the Alpha Value. So when you are making some calculations with it like
vec4 diffuseP = vec4(max(dot(L, N), 0.0));
This factor may become less than 1.0 (the Dot-Product of your vectors) And since you multiply this after with your light at all, every value may become less than 1.0 In the end your Alpha Value of the light -> the color has become a value between 1.0 and 0.0 like the light reflection itself. This may cause the transparency.
So if you'd try something like:
gl_FragColor = v_Color.xyz, 1.0;
or the alpha value of a later texture, everything will be fine.