lexalenka
lexalenka

Reputation: 317

Lighting a textured object in OpenGL 2.0+

I’ve been developing a cube program that provides a number of cubes with desired qualities. However, whenever I try to light a textured cube, my cube becomes very dark. The lighting works well with a non-textured cube so I’m led to believe it’s done properly just as a simple textured cube without lighting works. There doesn’t seem to be significant documentation on how to solve this in OpenGL 2.0+ but there are a few things pertaining to older versions.

The following link offers information as to why my cube is behaving as it is, but I’m having trouble translating the solution to a newer version, especially within my shader code where I’m unsure if further changes should occur. I am using Android Studio 2.1.3 if that and its contained emulators would pose issues to the desired effect. If anyone could offer any advice, I’d greatly appreciate it. I have a separate (large) renderer that calls for the Cube to be drawn, let me know if that code would be beneficial as well in addition to my Cube. Below is my Cube:

public class TexturedLightCube {


/** Cube vertices */
private static final float VERTICES[] = {
        -0.3f, -0.3f, -0.3f, //top front right
        0.3f, -0.3f, -0.3f,  //bottom front right
        0.3f, 0.3f, -0.3f, //bottom front left
        -0.3f, 0.3f, -0.3f, //top front left
        -0.3f, -0.3f, 0.3f,  //top back right
        0.3f, -0.3f, 0.3f, //bottom back right
        0.3f, 0.3f, 0.3f,  //bottom back left
        -0.3f, 0.3f, 0.3f // top back left
};

/** Vertex colors. */
private static final float COLORS[] = {
        0.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 0.0f, 0.0f, 1.0f,
        1.0f, 1.0f, 0.0f, 1.0f,
        0.0f, 1.0f, 0.0f, 1.0f,
        0.0f, 0.0f, 1.0f, 1.0f,
        1.0f, 0.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f, 1.0f,
        0.0f, 1.0f, 1.0f, 1.0f,
};


/** Order to draw vertices as triangles. */
private static final byte INDICES[] = {
        0, 1, 3, 3, 1, 2, // Front face.
        0, 1, 4, 4, 5, 1, // Bottom face.
        1, 2, 5, 5, 6, 2, // Right face.
        2, 3, 6, 6, 7, 3, // Top face.
        3, 7, 4, 4, 3, 0, // Left face.
        4, 5, 7, 7, 6, 5, // Rear face.
};


private static final float TEXTURECOORDS[] =
        {

                0.0f, 1.0f, //left-bottom
                0.0f, 0.0f, //right bottom
                1.0f, 0.0f, //left top
                1.0f, 1.0f, //right top

                0.0f, 1.0f, //left-bottom
                0.0f, 0.0f, //right bottom
                1.0f, 0.0f, //left top
                1.0f, 1.0f, //right top


        };


private static final float NORMALS[] = {

        //set all normals to all light for testing
        1.0f, 1.0f, 1.0f,  //top front right
        1.0f, 0.0f, 1.0f,  //bottom front right
        0.0f, 0.0f, 1.0f,  //bottom front left
        0.0f, 1.0f, 1.0f,  //top front left
        1.0f, 1.0f, 0.0f,  //top back right
        1.0f, 0.0f, 0.0f,  //bottom back right
        0.0f, 0.0f, 0.0f,  //bottom back left
        0.0f, 1.0f, 0.0f  //top back left
};

static final int COORDS_PER_VERTEX = 3;

private static final int VALUES_PER_COLOR = 4;

/** Vertex size in bytes. */
final int VERTEX_STRIDE = COORDS_PER_VERTEX * 4;

/** Color size in bytes. */
private final int COLOR_STRIDE = VALUES_PER_COLOR * 4;

/** Shader code for the vertex. */
private static final String VERTEX_SHADER_CODE =
        "uniform mat4 uMVPMatrix;" +
        "uniform mat4 uMVMatrix;" +
        "uniform vec3 u_LightPos;" +
        "attribute vec4 vPosition;" +
        "attribute vec4 a_Color;" +
        "attribute vec3 a_Normal;" +
        "varying vec4 v_Color;" +
        "attribute vec2 a_TexCoordinate;" +
        "varying vec2 v_TexCoordinate;" +
        "void main() {" +
        "vec3 modelViewVertex = vec3(uMVMatrix * vPosition);"+
        "vec3 modelViewNormal = vec3(uMVMatrix * vec4(a_Normal, 0.0));" +
        "float distance = length(u_LightPos - modelViewVertex);" +
        "vec3 lightVector = normalize(u_LightPos - modelViewVertex);" +
        "float diffuse = max(dot(modelViewNormal, lightVector), 0.1);" +
        "diffuse = diffuse * (1.0/(1.0 + (0.00000000000002 * distance * distance)));" + //attenuation factor
        "v_Color = a_Color * a_Color * diffuse;" +
        "gl_Position = uMVPMatrix * vPosition;" +
        "v_TexCoordinate = a_TexCoordinate;" +
        "}";



/** Shader code for the fragment. */
private static final String FRAGMENT_SHADER_CODE =
        "precision mediump float;" +
                "varying vec4 v_Color;" +
                "uniform sampler2D u_Texture;"+ //The input texture
                "varying vec2 v_TexCoordinate;" +
                "void main() {" +
                "  gl_FragColor =  v_Color * texture2D(u_Texture, v_TexCoordinate) ;" +   //still works with just color
                "}";


private int mTextureUniformHandle; //Pass in texture.
private int mTextureCoordinateHandle; //Pass in model texture coordinate information.
private final int mTextureCoordinateDataSize = 2; //Size of texture coordinate data in elements
public static int mTextureDataHandle; //Handle to texturedata;
private final FloatBuffer mTextureBuffer; //Store model data in float buffer.
private final FloatBuffer mVertexBuffer;
private final FloatBuffer mColorBuffer;
private final FloatBuffer mNormalBuffer;
private final ByteBuffer mIndexBuffer;
private final int mProgram;
private final int mPositionHandle;
private final int mColorHandle;
private final int mMVPMatrixHandle;
private final int mNormalHandle;
public static int mLightPosHandle;
public final int mMVMatrixHandle;



public static int loadTexture(final Context context, final int resourceId) {
    //Get the texture from the Android resource directory
    final int[] textureHandle = new int[1];

    InputStream is = context.getResources().openRawResource(+ R.drawable.teneighty);
    Bitmap bitmap = null;
    try {
        //BitmapFactory is an Android graphics utility for images
        bitmap = BitmapFactory.decodeStream(is);

    } finally {
        //Always clear and close
        try {
            is.close();
            is = null;
        } catch (IOException e) {
        }
    }

    //Generate one texture pointer...
    GLES20.glGenTextures(1, textureHandle, 0);
    //and bind it to our array.
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);

    //Create Nearest Filtered Texture.
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

    //Accounting for different texture parameters.
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);

    //Use the Android GLUtils to specify a two-dimensional texture image from our bitmap.
    GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

    //Clean up
    bitmap.recycle();


    if (textureHandle[0] == 0)

    {
        throw new RuntimeException("Error loading texture");
    }

    return textureHandle[0];
}



public TexturedLightCube() {

    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(VERTICES.length * 4);
    byteBuffer.order(ByteOrder.nativeOrder());
    mVertexBuffer = byteBuffer.asFloatBuffer();
    mVertexBuffer.put(VERTICES);
    mVertexBuffer.position(0);


    byteBuffer = ByteBuffer.allocateDirect(COLORS.length * 4);
    byteBuffer.order(ByteOrder.nativeOrder());
    mColorBuffer = byteBuffer.asFloatBuffer();
    mColorBuffer.put(COLORS);
    mColorBuffer.position(0);

    byteBuffer = ByteBuffer.allocateDirect(NORMALS.length * 4);
    byteBuffer.order(ByteOrder.nativeOrder());
    mNormalBuffer = byteBuffer.asFloatBuffer();
    mNormalBuffer.put(NORMALS);
    mNormalBuffer.position(0);

    byteBuffer = ByteBuffer.allocateDirect(TEXTURECOORDS.length * 4);
    byteBuffer.order(ByteOrder.nativeOrder());
    mTextureBuffer = byteBuffer.asFloatBuffer();
    mTextureBuffer.put(TEXTURECOORDS);
    mTextureBuffer.position(0);


    mIndexBuffer = ByteBuffer.allocateDirect(INDICES.length);
    mIndexBuffer.put(INDICES);
    mIndexBuffer.position(0);

    mProgram = GLES20.glCreateProgram();
    GLES20.glAttachShader(mProgram, loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE));
    GLES20.glAttachShader(mProgram, loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE));
    GLES20.glLinkProgram(mProgram);


    mTextureDataHandle = GLES20.glGetUniformLocation(mProgram, "u_Texture");
    mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "a_TexCoordinate");
    mTextureUniformHandle = GLES20.glGetUniformLocation(mProgram, "u_Texture");
    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
    mMVMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVMatrix");
    mLightPosHandle = GLES20.glGetUniformLocation(mProgram, "u_LightPos");
    mNormalHandle = GLES20.glGetAttribLocation(mProgram, "a_Normal");
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
    mColorHandle = GLES20.glGetAttribLocation(mProgram, "a_Color");

}

/**
 * Encapsulates the OpenGL ES instructions for drawing this shape.
 *
 * @param mvpMatrix The Model View Project matrix in which to draw this shape
 */
public void draw(float[] mvpMatrix) {
    // Add program to OpenGL environment.
    GLES20.glUseProgram(mProgram);

    //set active texture unit to texture unit 0.
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);

    // Prepare the cube coordinate data.
    GLES20.glEnableVertexAttribArray(mPositionHandle);
    GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, VERTEX_STRIDE, mVertexBuffer);


    // Prepare the cube color data.
    GLES20.glEnableVertexAttribArray(mColorHandle);
    GLES20.glVertexAttribPointer(mColorHandle, 4, GLES20.GL_FLOAT, false, COLOR_STRIDE, mColorBuffer);

    //Will have the same size as Vertex as we are implementing per vertex lighting
    GLES20.glEnableVertexAttribArray(mNormalHandle);
    GLES20.glVertexAttribPointer(mNormalHandle, 3, GLES20.GL_FLOAT, false, VERTEX_STRIDE, mNormalBuffer);

    // Prepare the cube texture data.
    GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
    //Pass texture coordinate information.
    GLES20.glVertexAttribPointer(mTextureCoordinateHandle,4, GLES20.GL_FLOAT, false, mTextureCoordinateDataSize, mTextureBuffer);

    // Apply the projection and view transformation.
    GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

    GLES20.glUniform3f(LightCube.mLightPosHandle, MyGLRenderer.mLightPosInEyeSpace[0], MyGLRenderer.mLightPosInEyeSpace[1], MyGLRenderer.mLightPosInEyeSpace[2]);


    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glUniform1i(mTextureUniformHandle, 0);


    // Draw the cube.
    GLES20.glDrawElements(GLES20.GL_TRIANGLES, INDICES.length, GLES20.GL_UNSIGNED_BYTE, mIndexBuffer); //-removed indices-

    // Disable vertex arrays.
    GLES20.glDisableVertexAttribArray(mPositionHandle);
    GLES20.glDisableVertexAttribArray(mTextureCoordinateHandle);
    GLES20.glDisableVertexAttribArray(mColorHandle);
    GLES20.glDisableVertexAttribArray(mNormalHandle);


}

/** Loads the provided shader in the program. */
private static int loadShader(int type, String shaderCode){
    int shader = GLES20.glCreateShader(type);

    GLES20.glShaderSource(shader, shaderCode);
    GLES20.glCompileShader(shader);

    return shader;
}



 }

Upvotes: 1

Views: 589

Answers (1)

solidpixel
solidpixel

Reputation: 12159

  1. You're missing an ambient component to your lighting, which emulates second order (and higher) reflections you would get in real life, but can't get directly in a rasterizer.
  2. Not sure why you are squaring a_Color in your fragment shader. This will definitely make things darker because all values are between 0 and 1; e.g. 0.1^2 == 0.01.
  3. Remember that your dot product might be negative, so you want to clamp out negative diffuse components (e.g. no light intensity on surfaces which are facing away from the light).

Upvotes: 2

Related Questions