Android上的OpenGL漫反射照明

时间:2016-09-22 18:01:57

标签: android opengl-es glsl shader

我有一个程序,我在其中创建立方体并更改它们。我这样做的一种方法是通过我的LightCube类中的着色器添加光照。我跟随this tutorial,一切正常。我的问题是为什么drawLight()方法是必要的?所有灯光都可以单独使用着色器实现,而无需调用drawLight。我可以看到drawLight创建了一个看似明亮的像素,但由于我处理的是小立方体,因此单个像素非常突出。它只是一种强调较大物体中阴影的方法,还是有一个我忽略的更大目的?

这是我的MyGLRenderer:

public class MyGLRenderer implements GLSurfaceView.Renderer {

private static final String TAG = "MyGLRenderer";
private Context context;
static MainActivity x = null;
private Cube mCube;
private TexturedCube tCube;
private LightCube lCube;

private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private float[] mRotationMatrix = new float[16];
private float mCubeRotation;
private static long mLastUpdateMillis;
private static final float CUBE_ROTATION_INCREMENT = 1.6f;

public static int limitx = 0;
public static int limity = 0;
long startTime = System.nanoTime();
int frames = 0;
static int output=0;

public static int cubeType = 1;
public static int blendSwitch = 1;
private float[] mLightModelMatrix = new float[16];
private final float[] mLightPosInModelSpace = new float[] {0.0f, 0.0f, 0.0f, 1.0f};

/** Used to hold the current position of the light in world space (after transformation via model matrix). */
public static final float[] mLightPosInWorldSpace = new float[4];

/** Used to hold the transformed position of the light in eye space (after transformation via modelview matrix) */
public static final float[] mLightPosInEyeSpace = new float[4];

/* The refresh rate, in frames per second. */
public static final int REFRESH_RATE_FPS = 60;
/* The duration, in milliseconds, of one frame. */
 static final float FRAME_TIME_MILLIS = TimeUnit.SECONDS.toMillis(1) / REFRESH_RATE_FPS;

public MyGLRenderer(Context context) {
    this.context=context;
}

public static int loadShader(int type, String shaderCode){
    //create a vertex shader type (GLES30.GL_VERTEX_SHADER)
    //or a fragment shader type (GLES30.GL_FRAGMENT_SHADER)
    int shader = GLES30.glCreateShader(type);

    //add the source code to the shader and compile it
    GLES30.glShaderSource(shader, shaderCode);
    GLES30.glCompileShader(shader);


    return shader;
}




public void onSurfaceCreated(GL10 unused, EGLConfig config) {
    // Set the background frame color
    GLES30.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    GLES30.glClearDepthf(1.0f);
   GLES30.glEnable(GLES30.GL_DEPTH_TEST);
    GLES30.glDepthFunc(GLES30.GL_LEQUAL);


    switch(blendSwitch) {

        case 1 :
            GLES30.glDisable(GLES20.GL_BLEND);
            break;

        case 2 :
            GLES30.glEnable(GLES20.GL_BLEND);
            GLES30.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE);
            //Enable Blending
            break;
    }

    mCube = new Cube();
    tCube = new TexturedCube();
    lCube = new LightCube();

    //cubeList.add(new Cube()); If you would like to generate new cubes via array
  //  cubeList.add(new Cube());

    TexturedCube.mTextureDataHandle = TexturedCube.loadTexture(context, R.drawable.app_icon_your_company);

}


public void logFPS() {
    frames++;
    if(System.nanoTime() - startTime >= 1000000000) {
       // Log.d("FPSCounter", "fps: " + frames); for testing - send info to log
        output=frames;
        frames = 0;
        startTime = System.nanoTime();
    }
}

public void onDrawFrame(GL10 unused) {

  //  mCube = cubeList.get(0);

    logFPS();

    if (x != null) {
        x.GLDisplay();
    }


    float[] scratch = new float[16];


    // Redraw background color
    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);


    for(float xshift=-1; xshift < limitx; xshift++) {
        for (float yshift = -1; yshift < limity; yshift++) {


            //Set the camera position
            Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -400, xshift, yshift, 0f, 0f, 1.0f, 0.0f);
            //setLookAtM(float[] rm, int rmOffset, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)
            //Defines a viewing transformation in terms of an eye point, a center of view, and an up vector.


            //setting up light conditions.

            Matrix.setIdentityM(mLightModelMatrix, 0);
            Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, -5.0f);
           // Matrix.rotateM(mLightModelMatrix, 0, mCubeRotation , 0.0f, 1.0f, 0.0f); -->incompatible with the update Cube Rotation? requires further examination
            Matrix.translateM(mLightModelMatrix, 0, 0.0f, 0.0f, 2.0f);
            Matrix.multiplyMV(mLightPosInWorldSpace, 0, mLightModelMatrix, 0, mLightPosInModelSpace, 0);
            Matrix.multiplyMV(mLightPosInEyeSpace, 0, mViewMatrix, 0, mLightPosInWorldSpace, 0);


            Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
            //multiplyMM(float[] result, int resultOffset, float[] lhs, int lhsOffset, float[] rhs, int rhsOffset)
            //Multiplies two 4x4 matrices together and stores the result in a third 4x4 matrix.


            //Create a rotation transformation
            long time = SystemClock.uptimeMillis() % 4000L;
            float angle = 0.090f * ((int) time);


            Matrix.setRotateM(mRotationMatrix, 0, mCubeRotation, 1.0f, 1.0f, -1.0f);

            //Calculate the projection and view transformation
            //Combine the rotation matrix with the projection and camera view
            //Note that the mMVPMatrix factor *must* be first in order
            //for the matrix multiplication product to be correct
            Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
            //multiplyMM(float[] result, int resultOffset, float[] lhs, int lhsOffset, float[] rhs, int rhsOffset)
            //Multiplies two 4x4 matrices together and stores the result in a third 4x4 matrix.


            switch(cubeType) {
                case 1:lCube.draw(scratch);
                   // drawLight();
                    break;

                case 2:tCube.draw(scratch);
                    break;
            }

            updateCubeRotation();



        }
    }

}

public void drawLight()
{
    LightCube.mPointProgramHandle = GLES20.glCreateProgram();
    GLES20.glAttachShader(LightCube.mPointProgramHandle, loadShader(GLES20.GL_VERTEX_SHADER, LightCube.pointVertexShader));
    GLES20.glAttachShader(LightCube.mPointProgramHandle, loadShader(GLES20.GL_FRAGMENT_SHADER, LightCube.pointFragmentShader));
    GLES20.glLinkProgram(LightCube.mPointProgramHandle);

    GLES20.glUseProgram(LightCube.mPointProgramHandle);
    final int pointMVPMatrixHandle = GLES20.glGetUniformLocation(LightCube.mPointProgramHandle, "uMVPMatrix");
    final int pointPositionHandle = GLES20.glGetAttribLocation(LightCube.mPointProgramHandle, "vPosition");

    // Pass in the position.
    GLES20.glVertexAttrib3f(pointPositionHandle, mLightPosInModelSpace[0], mLightPosInModelSpace[1], mLightPosInModelSpace[2]);

    // Since we are not using a buffer object, disable vertex arrays for this attribute.
    GLES20.glDisableVertexAttribArray(pointPositionHandle);

    // Pass in the transformation matrix.
    Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mLightModelMatrix, 0);
    Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0);
    GLES20.glUniformMatrix4fv(pointMVPMatrixHandle, 1, false, mMVPMatrix, 0);

    // Draw the point.
    GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1);
}


public void onSurfaceChanged(GL10 unused, int width, int height) {

    GLES30.glViewport(0, 0, width, height);

    Matrix.frustumM(mProjectionMatrix, 0, -1, 15, -20, 1, 100, 500);
    //frustumM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far)
    //the issue is one of depth - if you make it smaller than the cubes will float inwardly due to the nature of the frustrum

}

public static void checkGlError(String glOperation) {
    int error;
    while ((error = GLES30.glGetError()) != GLES30.GL_NO_ERROR) {
        Log.e(TAG, glOperation + ": glError " + error);
        throw new RuntimeException(glOperation + ": glError " + error);
    }
}


private void updateCubeRotation() {
    if (mLastUpdateMillis != 0) {
        float factor = (SystemClock.elapsedRealtime() - mLastUpdateMillis) / FRAME_TIME_MILLIS;
        mCubeRotation += CUBE_ROTATION_INCREMENT * factor;
    }
    mLastUpdateMillis = SystemClock.elapsedRealtime();

}


}

这是我的LightCube:     公共类LightCube {

/** 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 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;" +
                "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.000000000002 * distance * distance)));" +
                "v_Color = a_Color * diffuse;" +
                "gl_Position = uMVPMatrix * vPosition;" +
                "}";

/** Shader code for the fragment. */
private static final String FRAGMENT_SHADER_CODE =
                "precision mediump float;" +
                "varying vec4 v_Color;" +
                "void main() {" +
                "  gl_FragColor = v_Color;" +
                "}";


// Define a shader program for the position of light on screen.
final static String pointVertexShader =
                  "uniform mat4 uMVPMatrix;      \n"
                + "attribute vec4 vPosition;     \n"
                + "void main()                    \n"
                + "{                              \n"
                + "   gl_Position = uMVPMatrix * vPosition;   \n"
                + "   gl_PointSize = 5.0;         \n"
                + "}                              \n";

final static String pointFragmentShader =
                  "precision mediump float;       \n"
                + "void main()                    \n"
                + "{                              \n"
                + "   gl_FragColor = vec4(1.0,1.0, 1.0, 1.0);     \n"
                + "}                              \n";


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 mPointProgramHandle;




public LightCube() {
    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);


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


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

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

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

    // 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]);

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

    // Disable vertex arrays.
    GLES20.glDisableVertexAttribArray(mPositionHandle);
    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;
}
}

1 个答案:

答案 0 :(得分:1)

我认为教程中的drawLight点可以让你轻松看到灯光的位置。

这有助于您轻松确认其他灯光着色器是否正确照亮了对象的正确部分。如果您不喜欢它,请将其删除。