天天看点

Opengl ES系列学习--光照基础

     本节我们来学习一下光照基础的知识,理解了原作者的讲解,我们就应该能提炼出本节的重点就是冯氏光照模型。之前我们已经说过了,后面的效果实现都是把前人总结好的原理计算出来。冯氏光照模型介绍如下:

Opengl ES系列学习--光照基础

     上面的四张效果图看着就非常明显了,第一张是环境光,第二张是漫反射,第三张是镜面反射,第四张是所有效果的综合,也就是我们眼睛看到的效果。颜色的处理是在片段着色器中,分别把三部分反射因子相加,然后乘以物体本身的颜色就完成了,代码实现如下:

Opengl ES系列学习--光照基础

     本节我们最终实现的效果图如下:

Opengl ES系列学习--光照基础

     我们画了四个立方体,中间这个就是冯氏光照模式的最终效果,中间正上方的是单独镜面反射的效果,中间右上的环境光 + 漫反射的效果,左下角的是单独环境光的效果。我们把光源的颜色设置为白色,可以看到最终的效果才更接近实际情况,不会像单独环境光或者漫反射那样整体是一个颜色。

     我们再加上纹理看一下效果:

Opengl ES系列学习--光照基础

     有了纹理采样,细节更加突出,有点小兴奋。不过大家看看B站上那些Opengl作出来的游戏效果,就会发现,我们现在作的所有都是小菜,真的太菜了,但是也不要紧,当前的基础积累就是以后万丈高楼的根基。

     本节的效果有三个类:GlLightRender、LightCube、LightLight,GlLightRender类和上一节基本完全相同,还是提供绘制的流程;LightCube绘制立方体;LightLight绘制光源。我们先来看一下LightLight类,完整源码如下:

package com.opengl.learn.aric.light;

import android.content.Context;
import android.opengl.GLES32;
import android.opengl.Matrix;

import com.opengl.learn.OpenGLUtils;
import com.opengl.learn.R;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

import static android.opengl.GLES20.GL_ARRAY_BUFFER;
import static android.opengl.GLES20.GL_FLOAT;
import static android.opengl.GLES20.GL_STATIC_DRAW;
import static android.opengl.GLES20.GL_TRIANGLES;
import static android.opengl.GLES20.glGenBuffers;
import static android.opengl.GLES20.glGetUniformLocation;

public class LightLight {
    private final float[] mVerticesData =
            {
                    // back face
                    0.5f, 0.5f, -0.5f,     // (5) Top-right far
                    0.5f, -0.5f, -0.5f,      // (7) Bottom-right far
                    -0.5f, -0.5f, -0.5f,     // (6) Bottom-left far
                    -0.5f, -0.5f, -0.5f,     // (6) Bottom-left far
                    -0.5f, 0.5f, -0.5f,     // (4) Top-left far
                    0.5f, 0.5f, -0.5f,     // (5) Top-right far

                    // front face
                    -0.5f, 0.5f, 0.5f,     // (0) Top-left near
                    -0.5f, -0.5f, 0.5f,     // (2) Bottom-left near
                    0.5f, -0.5f, 0.5f,     // (3) Bottom-right near
                    0.5f, -0.5f, 0.5f,     // (3) Bottom-right near
                    0.5f, 0.5f, 0.5f,     // (1) Top-right near
                    -0.5f, 0.5f, 0.5f,     // (0) Top-left near

                    // left face
                    -0.5f, 0.5f, -0.5f,     // (4) Top-left far
                    -0.5f, -0.5f, -0.5f,     // (6) Bottom-left far
                    -0.5f, -0.5f, 0.5f,     // (2) Bottom-left near
                    -0.5f, -0.5f, 0.5f,     // (2) Bottom-left near
                    -0.5f, 0.5f, 0.5f,     // (0) Top-left near
                    -0.5f, 0.5f, -0.5f,     // (4) Top-left far

                    // right face
                    0.5f, 0.5f, 0.5f,     // (1) Top-right near
                    0.5f, -0.5f, 0.5f,     // (3) Bottom-right near
                    0.5f, -0.5f, -0.5f,      // (7) Bottom-right far
                    0.5f, -0.5f, -0.5f,      // (7) Bottom-right far
                    0.5f, 0.5f, -0.5f,     // (5) Top-right far
                    0.5f, 0.5f, 0.5f,     // (1) Top-right near

                    // bottom face
                    -0.5f, -0.5f, 0.5f,     // (2) Bottom-left near
                    -0.5f, -0.5f, -0.5f,     // (6) Bottom-left far
                    0.5f, -0.5f, -0.5f,      // (7) Bottom-right far
                    0.5f, -0.5f, -0.5f,      // (7) Bottom-right far
                    0.5f, -0.5f, 0.5f,     // (3) Bottom-right near
                    -0.5f, -0.5f, 0.5f,     // (2) Bottom-left near

                    // top face
                    -0.5f, 0.5f, -0.5f,     // (4) Top-left far
                    -0.5f, 0.5f, 0.5f,     // (0) Top-left near
                    0.5f, 0.5f, 0.5f,     // (1) Top-right near
                    0.5f, 0.5f, 0.5f,     // (1) Top-right near
                    0.5f, 0.5f, -0.5f,     // (5) Top-right far
                    -0.5f, 0.5f, -0.5f,     // (4) Top-left far

            };

    public static final float[] mLightColorsData = {1.0f, 1.0f, 1.0f};
    public static final float[] mLightPosData = {0f, 0f, 5.0f};

    private static final String TAG = LightLight.class.getSimpleName();
    private static final int BYTES_PER_FLOAT = 4;
    private static final int BYTES_PER_SHORT = 2;
    private static final int POSITION_COMPONENT_COUNT = 3;
    private static final int COLOR_COMPONENT_COUNT = 3;
    private static final int TEXTURE_COMPONENT_COUNT = 2;
    private static final int INDEX_COMPONENT_COUNT = 1;
    private static final int MATRIX_LENGHT = 16;
    private Context mContext;
    private int mLightProgram;
    private int uLightMatrixLocation, uLightColor;
    private FloatBuffer mVerticesBuffer;
    private FloatBuffer mColorsBuffer;
    private int mWidth, mHeight;
    private int mLightVAO, mLightVBO;
    private float[] uLightMatrix = new float[MATRIX_LENGHT];
    private float[] projectionMatrix = new float[MATRIX_LENGHT];
    private float[] modelMatrix = new float[MATRIX_LENGHT];
    private float[] viewMatrix = new float[MATRIX_LENGHT];

    public LightLight(Context context) {
        mContext = context;
        mVerticesBuffer = ByteBuffer.allocateDirect(mVerticesData.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();

        mVerticesBuffer.put(mVerticesData).position(0);
    }

    public void onSurfaceCreated() {
        mLightProgram = OpenGLUtils.loadProgram(mContext, R.raw.gllight_light_vertex, R.raw.gllight_light_fragment);
        uLightMatrixLocation = glGetUniformLocation(mLightProgram, "uMatrix");
        uLightColor = glGetUniformLocation(mLightProgram, "uLightColor");
        int[] array = new int[1];
        GLES32.glGenVertexArrays(1, array, 0);
        mLightVAO = array[0];
        array = new int[1];
        glGenBuffers(1, array, 0);
        mLightVBO = array[0];

        GLES32.glBindVertexArray(mLightVAO);

        mVerticesBuffer.position(0);
        GLES32.glBindBuffer(GL_ARRAY_BUFFER, mLightVBO);
        GLES32.glBufferData(GL_ARRAY_BUFFER, BYTES_PER_FLOAT * mVerticesData.length, mVerticesBuffer, GL_STATIC_DRAW);
        GLES32.glVertexAttribPointer(0, POSITION_COMPONENT_COUNT, GL_FLOAT, false, 0, 0);
        GLES32.glEnableVertexAttribArray(0);
    }

    public void onSurfaceChanged(GL10 gl, int width, int height) {
        mWidth = width;
        mHeight = height;
    }

    public void onDrawFrame() {
        GLES32.glUseProgram(mLightProgram);
        GLES32.glEnableVertexAttribArray(0);
        GLES32.glBindVertexArray(mLightVAO);
        setLightMatrix();
        GLES32.glUniformMatrix4fv(uLightMatrixLocation, 1, false, uLightMatrix, 0);
        GLES32.glUniform3fv(uLightColor, 1, mLightColorsData, 0);
        GLES32.glDrawArrays(GL_TRIANGLES, 0, mVerticesData.length);
        GLES32.glBindVertexArray(0);
        GLES32.glDisableVertexAttribArray(3);
    }

    private void setLightMatrix() {
        Matrix.setIdentityM(modelMatrix, 0);
        Matrix.setIdentityM(viewMatrix, 0);

        float aspect = (float) mWidth / (float) mHeight;
        Matrix.perspectiveM(projectionMatrix, 0, 45f, aspect, 1.0f, 100f);
        Matrix.setLookAtM(viewMatrix, 0, 0f, 10f, 20f, 0f, 0f, 0f, 0f, 1f, 0f);
        Matrix.translateM(modelMatrix, 0, mLightPosData[0], mLightPosData[1], mLightPosData[2]);
        Matrix.scaleM(modelMatrix, 0, 0.5f, 0.5f, 0.5f);
        Matrix.rotateM(modelMatrix, 0, -60, 0f, 1f, 0f);

        Matrix.multiplyMM(uLightMatrix, 0, viewMatrix, 0, modelMatrix, 0);
        Matrix.multiplyMM(uLightMatrix, 0, projectionMatrix, 0, uLightMatrix, 0);
    }
}
           

     因为在绘制立方体的时候,也要使用到光源的一些属性,所以我们把光源颜色mLightColorsData、光源位置mLightPosData都修饰为public static属性,在立方体的绘制中方便使用,光源颜色设置为白色,光源的透视投影矩阵、视图矩阵和绘制立方体的完全相同,我们把光源沿X、Y、Z三个轴都缩小一半,其他的就没有什么特殊的地方了。

     光源的顶点着色器源码如下:

#version 320 es
layout(location = 0) in vec3 aPosition;
uniform mat4 uMatrix;

void main()
{
    gl_Position = uMatrix * vec4(aPosition, 1.0);
}
           

     很简单,就是用计算完的矩阵和顶点位置相乘得到最终的顶点位置。片段着色器的源码如下:

#version 320 es
precision mediump float;
uniform vec3 uLightColor;
out vec4 FragColor;

void main()
{
    FragColor = vec4(uLightColor, 1f);
}
           

     片段着色器使用我们定义的mLightColorsData白色直接绘制。

     再来看一下LightCube,完整源码如下:

package com.opengl.learn.aric.light;

import android.content.Context;
import android.opengl.GLES32;
import android.opengl.Matrix;
import android.util.Log;

import com.opengl.learn.OpenGLUtils;
import com.opengl.learn.R;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

import static android.opengl.GLES20.GL_ARRAY_BUFFER;
import static android.opengl.GLES20.GL_FLOAT;
import static android.opengl.GLES20.GL_STATIC_DRAW;
import static android.opengl.GLES20.GL_TEXTURE0;
import static android.opengl.GLES20.GL_TEXTURE1;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.GL_TRIANGLES;
import static android.opengl.GLES20.glGenBuffers;
import static android.opengl.GLES20.glGetUniformLocation;

public class LightCube {
    private final float[] mVerticesData =
            {
                    // back face
                    0.5f, 0.5f, -0.5f,     // (5) Top-right far
                    0.5f, -0.5f, -0.5f,      // (7) Bottom-right far
                    -0.5f, -0.5f, -0.5f,     // (6) Bottom-left far
                    -0.5f, -0.5f, -0.5f,     // (6) Bottom-left far
                    -0.5f, 0.5f, -0.5f,     // (4) Top-left far
                    0.5f, 0.5f, -0.5f,     // (5) Top-right far

                    // front face
                    -0.5f, 0.5f, 0.5f,     // (0) Top-left near
                    -0.5f, -0.5f, 0.5f,     // (2) Bottom-left near
                    0.5f, -0.5f, 0.5f,     // (3) Bottom-right near
                    0.5f, -0.5f, 0.5f,     // (3) Bottom-right near
                    0.5f, 0.5f, 0.5f,     // (1) Top-right near
                    -0.5f, 0.5f, 0.5f,     // (0) Top-left near

                    // left face
                    -0.5f, 0.5f, -0.5f,     // (4) Top-left far
                    -0.5f, -0.5f, -0.5f,     // (6) Bottom-left far
                    -0.5f, -0.5f, 0.5f,     // (2) Bottom-left near
                    -0.5f, -0.5f, 0.5f,     // (2) Bottom-left near
                    -0.5f, 0.5f, 0.5f,     // (0) Top-left near
                    -0.5f, 0.5f, -0.5f,     // (4) Top-left far

                    // right face
                    0.5f, 0.5f, 0.5f,     // (1) Top-right near
                    0.5f, -0.5f, 0.5f,     // (3) Bottom-right near
                    0.5f, -0.5f, -0.5f,      // (7) Bottom-right far
                    0.5f, -0.5f, -0.5f,      // (7) Bottom-right far
                    0.5f, 0.5f, -0.5f,     // (5) Top-right far
                    0.5f, 0.5f, 0.5f,     // (1) Top-right near

                    // bottom face
                    -0.5f, -0.5f, 0.5f,     // (2) Bottom-left near
                    -0.5f, -0.5f, -0.5f,     // (6) Bottom-left far
                    0.5f, -0.5f, -0.5f,      // (7) Bottom-right far
                    0.5f, -0.5f, -0.5f,      // (7) Bottom-right far
                    0.5f, -0.5f, 0.5f,     // (3) Bottom-right near
                    -0.5f, -0.5f, 0.5f,     // (2) Bottom-left near

                    // top face
                    -0.5f, 0.5f, -0.5f,     // (4) Top-left far
                    -0.5f, 0.5f, 0.5f,     // (0) Top-left near
                    0.5f, 0.5f, 0.5f,     // (1) Top-right near
                    0.5f, 0.5f, 0.5f,     // (1) Top-right near
                    0.5f, 0.5f, -0.5f,     // (5) Top-right far
                    -0.5f, 0.5f, -0.5f,     // (4) Top-left far

            };

    private final float[] mColorsData =
            {
                    1.0f, 0.0f, 0.0f,
                    0.0f, 1.0f, 0.0f,
                    0.0f, 0.0f, 1.0f,
                    1.0f, 1.0f, 0.0f,
            };

    private final float[] mTextureData =
            {
                    0.0f, 0.0f,
                    0.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 0.0f,
                    0.0f, 0.0f,

                    0.0f, 0.0f,
                    0.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 0.0f,
                    0.0f, 0.0f,

                    0.0f, 0.0f,
                    0.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 0.0f,
                    0.0f, 0.0f,

                    0.0f, 0.0f,
                    0.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 0.0f,
                    0.0f, 0.0f,

                    0.0f, 0.0f,
                    0.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 0.0f,
                    0.0f, 0.0f,

                    0.0f, 0.0f,
                    0.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 0.0f,
                    0.0f, 0.0f,
            };

    private final float[] mNormalData =
            {
                    0.0f, 0.0f, -1.0f,
                    0.0f, 0.0f, -1.0f,
                    0.0f, 0.0f, -1.0f,
                    0.0f, 0.0f, -1.0f,
                    0.0f, 0.0f, -1.0f,
                    0.0f, 0.0f, -1.0f,

                    0.0f, 0.0f, 1.0f,
                    0.0f, 0.0f, 1.0f,
                    0.0f, 0.0f, 1.0f,
                    0.0f, 0.0f, 1.0f,
                    0.0f, 0.0f, 1.0f,
                    0.0f, 0.0f, 1.0f,

                    -1.0f, 0.0f, 0.0f,
                    -1.0f, 0.0f, 0.0f,
                    -1.0f, 0.0f, 0.0f,
                    -1.0f, 0.0f, 0.0f,
                    -1.0f, 0.0f, 0.0f,
                    -1.0f, 0.0f, 0.0f,

                    1.0f, 0.0f, 0.0f,
                    1.0f, 0.0f, 0.0f,
                    1.0f, 0.0f, 0.0f,
                    1.0f, 0.0f, 0.0f,
                    1.0f, 0.0f, 0.0f,
                    1.0f, 0.0f, 0.0f,

                    0.0f, -1.0f, 0.0f,
                    0.0f, -1.0f, 0.0f,
                    0.0f, -1.0f, 0.0f,
                    0.0f, -1.0f, 0.0f,
                    0.0f, -1.0f, 0.0f,
                    0.0f, -1.0f, 0.0f,

                    0.0f, 1.0f, 0.0f,
                    0.0f, 1.0f, 0.0f,
                    0.0f, 1.0f, 0.0f,
                    0.0f, 1.0f, 0.0f,
                    0.0f, 1.0f, 0.0f,
                    0.0f, 1.0f, 0.0f,
            };

    private final float[] mEyePosData = {0f, 2f, 20f};

    private static final String TAG = LightCube.class.getSimpleName();
    private static final int BYTES_PER_FLOAT = 4;
    private static final int BYTES_PER_SHORT = 2;
    private static final int POSITION_COMPONENT_COUNT = 3;
    private static final int COLOR_COMPONENT_COUNT = 3;
    private static final int TEXTURE_COMPONENT_COUNT = 2;
    private static final int INDEX_COMPONENT_COUNT = 1;
    private static final int NORMAL_COMPONENT_COUNT = 3;
    private static final int MATRIX_LENGHT = 16;
    private Context mContext;
    private int mProgramObject;
    private int uTextureContainer, containerTexture;
    private int uTextureFace, faceTexture;
    private int uModelMatrix, uViewMatrix, uProjectionMatrix, uLightColor, uLightPos, uViewPos, uIndex;
    private FloatBuffer mVerticesBuffer;
    private FloatBuffer mColorsBuffer;
    private FloatBuffer mTextureBuffer;
    private FloatBuffer mNormalBuffer;
    private ShortBuffer mIndicesBuffer;
    private int mWidth, mHeight;
    private int mVAO, mVBO, mCBO, mTBO, mNBO;
    private float[] modelMatrix = new float[MATRIX_LENGHT];
    private float[] viewMatrix = new float[MATRIX_LENGHT];
    private float[] projectionMatrix = new float[MATRIX_LENGHT];
    private float radius = 30f;
    private long startTime = 0;
    private float[][] directions = {
            {0f, 0.0f, -10f}, {4.3f, 4.7f, -15.0f}, {-4.5f, -10.2f, -20.5f}, {0f, 4.2f, -20.5f}, {3.24f, -3.4f, -15.5f},
            {-3.1f, 2.0f, -7.5f}, {4.47f, -2.0f, -5.5f}, {-4.5f, 2.40f, -9.5f}, {5f, 0.2f, -20.5f}, {-5.43f, -2.3f, -8.5f},
    };

    public LightCube(Context context) {
        mContext = context;
        mVerticesBuffer = ByteBuffer.allocateDirect(mVerticesData.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();

        mVerticesBuffer.put(mVerticesData).position(0);

        mColorsBuffer = ByteBuffer.allocateDirect(mColorsData.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        mColorsBuffer.put(mColorsData).position(0);

        mTextureBuffer = ByteBuffer.allocateDirect(mTextureData.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        mTextureBuffer.put(mTextureData).position(0);

        mNormalBuffer = ByteBuffer.allocateDirect(mNormalData.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        mNormalBuffer.put(mNormalData).position(0);
    }

    public void onSurfaceCreated() {
        mProgramObject = OpenGLUtils.loadProgram(mContext, R.raw.gllight_cube_vertex, R.raw.gllight_cube_fragment);
        uTextureContainer = glGetUniformLocation(mProgramObject, "uTextureContainer");
        uTextureFace = glGetUniformLocation(mProgramObject, "uTextureFace");
        uModelMatrix = glGetUniformLocation(mProgramObject, "uModelMatrix");
        uViewMatrix = glGetUniformLocation(mProgramObject, "uViewMatrix");
        uProjectionMatrix = glGetUniformLocation(mProgramObject, "uProjectionMatrix");
        uLightColor = glGetUniformLocation(mProgramObject, "uLightColor");
        uLightPos = glGetUniformLocation(mProgramObject, "uLightPos");
        uViewPos = glGetUniformLocation(mProgramObject, "uViewPos");
        uIndex = glGetUniformLocation(mProgramObject, "uIndex");
        int[] array = new int[1];
        GLES32.glGenVertexArrays(1, array, 0);
        mVAO = array[0];
        array = new int[4];
        glGenBuffers(4, array, 0);
        mVBO = array[0];
        mCBO = array[1];
        mTBO = array[2];
        mNBO = array[3];
        Log.e(TAG, "onSurfaceCreated, " + mProgramObject + ", uTexture: " + uTextureContainer + ", uTextureFace: " + uTextureFace);
        containerTexture = OpenGLUtils.loadTexture(mContext, R.mipmap.container);
        faceTexture = OpenGLUtils.loadTexture(mContext, R.mipmap.awesomeface);

        GLES32.glBindVertexArray(mVAO);

        mVerticesBuffer.position(0);
        GLES32.glBindBuffer(GL_ARRAY_BUFFER, mVBO);
        GLES32.glBufferData(GL_ARRAY_BUFFER, BYTES_PER_FLOAT * mVerticesData.length, mVerticesBuffer, GL_STATIC_DRAW);
        GLES32.glVertexAttribPointer(0, POSITION_COMPONENT_COUNT, GL_FLOAT, false, 0, 0);
        GLES32.glEnableVertexAttribArray(0);

        mTextureBuffer.position(0);
        GLES32.glBindBuffer(GL_ARRAY_BUFFER, mTBO);
        GLES32.glBufferData(GL_ARRAY_BUFFER, BYTES_PER_FLOAT * mTextureData.length, mTextureBuffer, GL_STATIC_DRAW);
        GLES32.glVertexAttribPointer(2, TEXTURE_COMPONENT_COUNT, GL_FLOAT, false, 0, 0);
        GLES32.glEnableVertexAttribArray(2);

        mNormalBuffer.position(0);
        GLES32.glBindBuffer(GL_ARRAY_BUFFER, mNBO);
        GLES32.glBufferData(GL_ARRAY_BUFFER, BYTES_PER_FLOAT * mNormalData.length, mNormalBuffer, GL_STATIC_DRAW);
        GLES32.glVertexAttribPointer(3, NORMAL_COMPONENT_COUNT, GL_FLOAT, false, 0, 0);
        GLES32.glEnableVertexAttribArray(3);
    }

    public void onSurfaceChanged(GL10 gl, int width, int height) {
        mWidth = width;
        mHeight = height;
    }

    public void onDrawFrame() {
        if (startTime == 0) {
            startTime = System.currentTimeMillis();
        }

        GLES32.glUseProgram(mProgramObject);
        GLES32.glEnableVertexAttribArray(0);
        GLES32.glEnableVertexAttribArray(1);
        GLES32.glEnableVertexAttribArray(2);
        GLES32.glEnableVertexAttribArray(3);

        GLES32.glActiveTexture(GL_TEXTURE0);
        GLES32.glBindTexture(GL_TEXTURE_2D, containerTexture);
        GLES32.glUniform1i(uTextureContainer, 0);

        GLES32.glActiveTexture(GL_TEXTURE1);
        GLES32.glBindTexture(GL_TEXTURE_2D, faceTexture);
        GLES32.glUniform1i(uTextureFace, 1);

        GLES32.glUniform3fv(uLightColor, 1, LightLight.mLightColorsData, 0);
        GLES32.glUniform3fv(uLightPos, 1, LightLight.mLightPosData, 0);
        GLES32.glUniform3fv(uViewPos, 1, mEyePosData, 0);

        GLES32.glBindVertexArray(mVAO);

        for (int i = 0; i < 4; i++) {
            setCubeMatrix(i);
            GLES32.glUniformMatrix4fv(uModelMatrix, 1, false, modelMatrix, 0);
            GLES32.glUniformMatrix4fv(uViewMatrix, 1, false, viewMatrix, 0);
            GLES32.glUniformMatrix4fv(uProjectionMatrix, 1, false, projectionMatrix, 0);
            GLES32.glUniform1i(uIndex, i);
            GLES32.glDrawArrays(GL_TRIANGLES, 0, mVerticesData.length);
        }

        GLES32.glBindVertexArray(0);
        GLES32.glDisableVertexAttribArray(0);
        GLES32.glDisableVertexAttribArray(1);
        GLES32.glDisableVertexAttribArray(2);
        GLES32.glDisableVertexAttribArray(3);
    }

    private void setCubeMatrix(int i) {
        Matrix.setIdentityM(modelMatrix, 0);
        Matrix.setIdentityM(viewMatrix, 0);
        Matrix.setIdentityM(projectionMatrix, 0);

        float aspect = (float) mWidth / (float) mHeight;
        Matrix.perspectiveM(projectionMatrix, 0, 45f, aspect, 1.0f, 100f);

        long now = System.currentTimeMillis();
        float camX = (float) (Math.sin((now - startTime) / 1000f) * radius);
        float camZ = (float) (Math.cos((now - startTime) / 1000f) * radius);
        Matrix.setLookAtM(viewMatrix, 0, mEyePosData[0], mEyePosData[1], mEyePosData[2], 0f, 0f, 0f, 0f, 1f, 0f);

        Matrix.translateM(modelMatrix, 0, directions[i][0], directions[i][1], directions[i][2]);

        float scale = i == 0 ? 4.5f : 3f;
        Matrix.scaleM(modelMatrix, 0, scale, scale, scale);
    }
}
           

     在onDrawFrame方法中,我们执行glDrawArrays四次,画出上图中的四个立方体,画立方体时传入的uLightColor、uLightPos都是前面我们在LightLight类中定义的公有成员变量;另外我们为了在片段着色器中区分出四个立方体,单独定义了一个uniform int uIndex变量,等下我们在片段着色器中就可以看到。该类中也没有其他需要重点说明的东西了。

     接着看一下立方体的顶点着色器,完整源码如下:

#version 320 es
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec3 aColor;
layout(location = 2) in vec2 aTexCoord;
layout(location = 3) in vec3 aNormal;
uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjectionMatrix;

out vec3 outColor;
out vec2 outTexCoord;
out vec3 outNormal;
out vec3 outFragPos;

void main()
{
    outColor = aColor;
    outTexCoord = aTexCoord;
    outNormal = aNormal;
    outFragPos = vec3(uModelMatrix * vec4(aPosition, 1.0f));
    gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aPosition, 1.0);
}
           

     和上一节不同,因为我们要单独使用modelMatrix,这样就不能在CPU中计算完,把结果矩阵传进来,所以我们把modelMatrix、viewMatrix、projectionMatrix全部传进来,在GPU中进行计算,还是记住顺序从右往左!!另外我们在顶点属性中新增了法线aNormal,法线变量需要传入到片段着色器计算使用;还计算出片段坐标outFragPos,也是要传递到片段着色器计算使用。

     再来看一下立方体的片段着色器,源码如下:

#version 320 es
precision mediump float;
out vec4 FragColor;
in vec3 outColor;
in vec2 outTexCoord;
in vec3 outNormal;
in vec3 outFragPos;
uniform sampler2D uTextureContainer;
uniform sampler2D uTextureFace;
uniform vec3 uLightColor;
uniform vec3 uLightPos;
uniform vec3 uViewPos;
uniform int uIndex;

vec4 getAmbientFactor() {
    float ambientStrength = 0.2f;
    vec4 ambient = vec4(ambientStrength * uLightColor, 1.0f);
    return ambient;
}

vec4 getDiffuseFactor() {
    vec3 normal = normalize(outNormal);
    vec3 lightDir = normalize(uLightPos - outFragPos);
    float diff = max(dot(normal, lightDir), 0.0);
    vec3 diffuse = diff * uLightColor;
    return vec4(diffuse, 1.0f);
}

vec4 getSpecularFactor() {
    float specularStrength = 0.5f;
    vec3 viewDir = normalize(uViewPos - outFragPos);
    vec3 lightDir = normalize(uLightPos - outFragPos);
    vec3 reflectDir = reflect(-lightDir, outNormal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0f), 32.0f);
    vec3 specular = specularStrength * spec * uLightColor;
    return vec4(specular, 1.0f);
}

void main() {
    vec4 objectColor = mix(texture(uTextureContainer, outTexCoord), texture(uTextureFace, outTexCoord), 0.2);
//    objectColor = vec4(1.0f, 0.5f, 0.31f, 1.0f);

    vec4 ambient = getAmbientFactor();
    vec4 diffuse = getDiffuseFactor();
    vec4 specular = getSpecularFactor();

    if (uIndex == 0) {
        FragColor = (ambient + diffuse + specular) * objectColor;
    } else if (uIndex == 1) {
        FragColor = (ambient + diffuse) * objectColor;
    } else if (uIndex == 2) {
        FragColor = (ambient) * objectColor;
    } else if (uIndex == 3) {
        FragColor = (specular) * objectColor;
    }
}
           

     片段着色器也是Opengl产生各种炫丽效果的地方,可以看看,我们的片段着色器中的逻辑也越来越复杂了。第一个点需要说明就是pow函数,最后的参数不能传32,这样在着色器编译时,会调用pow带有int类型参数的函数,但是根本没有这样的函数定义,所以会导致编译报错,正确的应该使用32.0f!

     所有自定义的函数需要写在main函数的上面,否则也会因为引用不到导致编译报错。getAmbientFactor函数是计算环境光照因子的,这里的float ambientStrength = 0.2f取值没有任何参考,只是我们的一个估值而已,用它乘以光照颜色就可以得到环境光照因子了;getDiffuseFactor函数是计算漫反射因子的,具体的计算过程大家可以参看原作者的讲解,已经非常清楚了,以后的实现也可以抛开原理,直接把对应的值传进来,按照顺序进行计算就可以了;getSpecularFactor函数是计算镜面光反射因子的,该函数的每一步也就是原原本本的计算思路了,只需要把对应的变量得到传进来就可以了;最后把三个因子相加,再乘以物体颜色就得到最终结果了(FragColor = (ambient + diffuse + specular) * objectColor)。

     好,本节的内容到这里就结束了,下一节我们继续学习材质内容。