Search code examples
androidopengl-esopengl-es-2.0transparentlighting

Why is the cube's surface rendered as transparent?


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

enter image description here


Solution

  • --- 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);)

    enter image description here

    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

    1. To Disable backFaceCulling So every face is rendered - even though that makes everything slower.
    2. To reverse TheCulling algorighm with glFrontFace(GLenum mode) (you can set if Gl is supposed to render front faces or back faces). But this leads to the same problem perhaps if you have front and back side faces in your cube so the best solution is:
    3. Check the order of your vertices per face.As seen roughly on the website I mentioned, the order of vertices needs to be the same. So check them and fix the order.

    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.