天天看点

Opengl ES系列学习--颜色

     本节我们来学习一下颜色的知识,在看完原作者讲解的颜色的知识的基础上,只要我们理解了,应该就能提取出重点,其实本节的重点就是下面这一句话。

Opengl ES系列学习--颜色

     所以我们要作的就是计算出物体颜色和光源颜色,然后把它们两个相乘,得到的就是目标颜色了。只有清晰的明确了目标,知道我们要干什么,这样效率才能更高,不至于被方向搞乱。本节我们实现的效果如下:

Opengl ES系列学习--颜色

     我们是仿照作者的实现,加了一个橙色的光源,而且在上一节的基础上,继续使用所有的纹理,作者是先去掉,后面的章节又加上的,我们索性就直接使用了。可以看到黑夜中,一个橙色的光源发出的光照射着这十个立方体,立方体本身都被照成了橙色。

     本节因为要画立方体和光源,两个属性不同,所以我们就把它们的着色器全部独立分开了;针对功能实现的不同,我们也把立方体抽取为ColorCube类,把光源抽取为ColorLight类,各自处理各自的逻辑,互不影响,这样即使出问题,也更容易查找分析。

     因为我们把功能抽取出来之后,Render渲染类倒时简单了多很,GlColorRender类非常简单,完整源码如下:

package com.opengl.learn.aric.color;

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

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES20.GL_DEPTH_BUFFER_BIT;
import static android.opengl.GLES20.GL_DEPTH_TEST;

public class GlColorRender implements GLSurfaceView.Renderer {
    private static final String TAG = GlColorRender.class.getSimpleName();
    private Context mContext;
    private int mWidth, mHeight;
    private ColorCube mCube;
    private ColorLight mLight;

    public GlColorRender(Context context) {
        mContext = context;
        mCube = new ColorCube(mContext);
        mLight = new ColorLight(mContext);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        mCube.onSurfaceCreated();
        mLight.onSurfaceCreated();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        mWidth = width;
        mHeight = height;
        mCube.onSurfaceChanged(gl, width, height);
        mLight.onSurfaceChanged(gl, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES32.glViewport(0, 0, mWidth, mHeight);
        GLES32.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        GLES32.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES32.glEnable(GL_DEPTH_TEST);

        mCube.onDrawFrame();
        mLight.onDrawFrame();
    }
}
           

     对象封装后,Render类的功能就非常清晰了,它只提供绘制的流程,具体的绘制功能由每个封装类自己实现,它当中也没有什么特殊的逻辑,我们就不再说明了。

     接下来我们看一下ColorLight,完整源码如下:

package com.opengl.learn.aric.color;

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 ColorLight {
    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[] mLightColorsData = {1.0f, 0.5f, 0.31f};

    private static final String TAG = ColorLight.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 ColorLight(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.glcolor_light_vertex, R.raw.glcolor_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(3, POSITION_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() {
        GLES32.glUseProgram(mLightProgram);
        GLES32.glEnableVertexAttribArray(3);
        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.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);
    }
}
           

     我们还是把光源定义为一个立方体,所以它还是继续沿用我们绘制立方体时的顶点坐标数据,正交投影矩阵Matrix.perspectiveM(projectionMatrix, 0, 45f, aspect, 1.0f, 100f)和上一节绘制立方体时一模一样,都使用相同的矩阵;摄像机的视图矩阵也和绘制立方体时一样,Matrix.setLookAtM(viewMatrix, 0, 0f, 10f, 20f, 0f, 0f, 0f, 0f, 1f, 0f),我们还是把摄像机放在Y轴10个单位,Z轴20个单位的地方去查看场景中心,相当于俯视;然后我们把光源沿X、Y、Z三个方向都缩小0.5倍,再把它沿Y轴旋转-60度,也就是朝左向旋转,光源就摆放好了。

     这里需要说明一下,我们在本节开始时说到,物体颜色乘以光源颜色得到最终的效果,这是指针对物体的,光源是不需要作这样的计算的,这个大家要考虑清楚。光源的顶点着色器源码如下:

#version 320 es
layout(location = 3) 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);
}
           

     定义光源的颜色为uLightColor,我们使用uniform类型的变量直接赋值,也就不用在经过顶点着色的中转了。

     再来看一下ColorCube,该类的完整源码如下:

package com.opengl.learn.aric.color;

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 ColorCube {
    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[] mLightColorsData = {1.0f, 0.5f, 0.31f};

    private static final String TAG = ColorCube.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 mProgramObject;
    private int uTextureContainer, containerTexture;
    private int uTextureFace, faceTexture;
    private int uMatrixLocation, uLightColor;
    private FloatBuffer mVerticesBuffer;
    private FloatBuffer mColorsBuffer;
    private FloatBuffer mTextureBuffer;
    private ShortBuffer mIndicesBuffer;
    private int mWidth, mHeight;
    private int mVAO, mVBO, mCBO, mTBO;
    private float[] uMatrix = 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];
    private float radius = 30f;
    private long startTime = 0;
    private float[][] directions = {
            {0f, 0.0f, -10f}, {2.3f, 1.7f, -15.0f}, {-2.5f, -0.2f, -20.5f}, {-0.8f, -1.65f, -12.3f}, {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 ColorCube(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);
    }

    public void onSurfaceCreated() {
        mProgramObject = OpenGLUtils.loadProgram(mContext, R.raw.glcolor_cube_vertex, R.raw.glcolor_cube_fragment);
        uTextureContainer = glGetUniformLocation(mProgramObject, "uTextureContainer");
        uTextureFace = glGetUniformLocation(mProgramObject, "uTextureFace");
        uMatrixLocation = glGetUniformLocation(mProgramObject, "uMatrix");
        uLightColor = glGetUniformLocation(mProgramObject, "uLightColor");
        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];
        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);
    }

    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.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, mLightColorsData, 0);

        GLES32.glBindVertexArray(mVAO);

        for (int i = 0; i < directions.length; i++) {
            setCubeMatrix(i);
            GLES32.glUniformMatrix4fv(uMatrixLocation, 1, false, uMatrix, 0);
            GLES32.glDrawArrays(GL_TRIANGLES, 0, mVerticesData.length);
        }

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

    private void setCubeMatrix(int i) {
        Matrix.setIdentityM(uMatrix, 0);
        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, 0f, 10f, 20f, 0f, 0f, 0f, 0f, 1f, 0f);

        Matrix.translateM(modelMatrix, 0, directions[i][0], directions[i][1], directions[i][2]);
        int[] rotate = {0, 0, 0};
        int remaind = i % 3;
        rotate[remaind] = 1;

        long degree = (now - startTime) / 100 * 5 * i;
        Matrix.rotateM(modelMatrix, 0, degree, rotate[0], rotate[1], rotate[2]);

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

     和上一节中绘制的立方体差别也比较小,因为十个立方体的位置排布的太紧,所以我调整了下directions向量中的值,让它们分散一些。因为最终的效果颜色是物体颜色乘以光源颜色,所以我们这里也要把光源颜色传递到着色器中,和绘制光源时一样,我们还是使用uniform变量直接定义它,其他的绘制的逻辑和上一节基本完全相同。

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

#version 320 es
layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec3 aColor;
layout(location = 2) in vec2 aTexCoord;
uniform mat4 uMatrix;

out vec3 outColor;
out vec2 outTexCoord;

void main()
{
    outColor = aColor;
    outTexCoord = aTexCoord;
    gl_Position = uMatrix * vec4(aPosition, 1.0);
}
           

     这里的outColor、aColor这两个变量其实已经没用了,可以直接删除掉;outTexCoord还是中转纹理坐标到片段着色器中,没什么好说的。

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

#version 320 es
precision mediump float;
out vec4 FragColor;
in vec3 outColor;
in vec2 outTexCoord;
uniform sampler2D uTextureContainer;
uniform sampler2D uTextureFace;
uniform vec3 uLightColor;

void main()
{
    vec4 color = mix(texture(uTextureContainer, outTexCoord), texture(uTextureFace, outTexCoord), 0.2);
    FragColor = color * vec4(uLightColor, 1.0);
}
           

     color变量还是两个纹理进行插值混合的结果,也就相当于物体本身的颜色;uLightColor就是光源颜色了,按照最开始的原理,将它们两个相乘就得到物体显示的最终效果了,简单吧。

     好了,本节的内容就这么多了,越往后看,大家会越发现,到后边的那些非常炫的效果,都是基于一些物理定理或者前人总结的原理上实现出来的,相当于我们就是把定理或者原理中的变量一个个计算出来,然后按照定理计算一下,就可以得到最终的效果了,必须要有这样的认识,我们才能有提纲挈领的认识和掌握。

     下一节我们继续学习基础光照的知识。