1
votes

I need a shader that create a diffuse light that is only affected by the angle between the normal and the light source. My current shader that is affected by both distance and angle looks like this:

private final String vertexShaderCode2 =
        "uniform mat4 uMVPMatrix;" +
        "uniform mat4 uMVMatrix;" +
        "uniform vec3 uLightPos;" +
        "attribute vec4 vPosition;" +
        "attribute vec4 aColor;" +
        "attribute vec3 aNormal;" +

        "varying vec4 vColor;" +
        "void main() {" +
        "   vec3 modelViewVertex = vec3(uMVMatrix * vPosition); " +
        "   vec3 modelViewNormal = vec3(uMVMatrix * vec4(aNormal, 0.0));" +
        "   float distance = length(uLightPos - modelViewVertex);" +
        "   vec3 lightVector = normalize(uLightPos - modelViewVertex);" +
        "   float diffuse = max(dot(modelViewNormal, lightVector), 0.1);" +
        "   diffuse = diffuse * (1.0 / (1.0 + (0.25 * distance * distance)));" +
        "   vColor = aColor * diffuse;" +

        "  gl_Position = uMVPMatrix * vPosition;" +

        "}";

private final String fragmentShaderCode =
        "precision mediump float;" +
        "varying vec4 vColor;" +
        "void main() {" +
        "  gl_FragColor = vColor;" +
        "}";

Here is code for populating the shaders:

    // Calculate position of the light.
    Matrix.setIdentityM(mLightModelMatrix, 0);
    Matrix.rotateM(mLightModelMatrix, 0, LightAngleInDegrees, 0.0f, 1.0f, 1.0f);
    Matrix.translateM(mLightModelMatrix, 0, 0.0f, 100.0f, -10.0f);
    Matrix.multiplyMV(mLightPosInWorldSpace, 0, mLightModelMatrix, 0, mLightPosInModelSpace, 0);
    Matrix.multiplyMV(mLightPosInEyeSpace, 0, mViewMatrix, 0, mLightPosInWorldSpace, 0);

    // Player
    Matrix.setIdentityM(mModelMatrix, 0);
    Matrix.translateM(mModelMatrix, 0, py, px, pz);
    Matrix.rotateM(mModelMatrix, 0, mAngle, 0, 0, 1.0f);
    Matrix.scaleM(mModelMatrix, 0, scalef, scalef, scalef * 2);
    Matrix.multiplyMM(mMVMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);
    Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVMatrix, 0);
    cube.draw(mMVPMatrix, mMVMatrix, mLightPosInWorldSpace);

And finally, the cube.draw method:

public void draw(float[] mvpMatrix, float[] mvMatrix, float[] mLightPosInWorldSpace) {
    ...
    // Pass in the light position in eye space.
    GLES20.glUniform3f(LightPosHandle, mLightPosInWorldSpace[0], mLightPosInWorldSpace[1], mLightPosInWorldSpace[2]);
    ...
}

If I leave the following line out of the Shader:

diffuse = diffuse * (1.0 / (1.0 + (0.25 * distance * distance)));

Then, distance is not accounted for but a side effect is that rotating the cube is not affecting the color of the cube sides, like if the light source is following the cube around. LightAngleInDegrees (light) and mAngle (cube) is two separate variables.

1
Your shaders look fine to me. Besides the light, does everything else render the way you would expect it to when you move around?Andrew Williamson
Yes, everything else seems to render like expected. I guess I mix up some matrices so that the shader does not actually get the angle between the normal and the light position, only a "relative" light position that is constant. I can't figure out what is wrong.Plarsen
Oh, pass in mLightPosInWorldSpace, not mLightPosInEyeSpace.Andrew Williamson

1 Answers

0
votes

The light position for diffuse calculations needs to be done in world space. That means you need to pass in mLightPosInWorldSpace, not mLightPosInEyeSpace.

public void draw(float[] mvpMatrix, float[] mvMatrix, float[] mLightPosInEyeSpace) {
    ...
    // Pass in the light position in world space.
    GLES20.glUniform3f(LightPosHandle, mLightPosInWorldSpace[0], mLightPosInWorldSpace[1], mLightPosInWorldSpace[2]);
    ...
}

Good thing you named your variables so clearly, otherwise I would not have spotted that.