OpenGL之光源

OpenGL之基礎(chǔ)
OpenGL之繪制簡單形狀
OpenGL之顏色
OpenGL之調(diào)整屏幕寬高比
OpenGL之三維
OpenGL之紋理
OpenGL之構(gòu)建簡單物體
OpenGL之觸控反饋
OpenGL之粒子
OpenGL之天空盒
OpenGL之地形

光源分類

通彻R。可以把不同的光源分為下面幾種

光源 說明
環(huán)境光(Ambient light) 環(huán)境光看上去來自四面八方,場景中的一切被照亮的程度都一樣叙淌。這近似于我們從大的、平等的光源獲取的光照,比如天空
方向光(Directional light) 方向光看上去似乎來自一個方向,光源好像處于極其遠的地方捉貌。這與我們從太陽或月亮獲取的光照相似
點光(Point light) 點光看上去是從附近某處投射的光亮贯要,而且光的密度隨著距離而減少暖侨。這適于表示近處的光源,其把它們的光投射到四面八方崇渗,像一個燈泡或蠟燭一樣
聚光(Spot light) 聚光與點光類似字逗,只是加了一個限制,只能向一個特定的方向投射宅广。這是我們從手電筒或者聚光燈所獲得的光照類型

把光線在物體表面反射的方式分為兩類:

反射方式 說明
漫反射(Diffuse reflection) 漫反射是指光線平等地向所有方向蔓延葫掉,適于表示沒有拋光表面的材質(zhì),比如地毯或外面的混凝土墻
鏡面反射( Specular reflection) 鏡面反射在某個特定的方向上反射更加強烈跟狱,適于被拋光的或者閃亮的材質(zhì)俭厚,比如光滑的金屬或者剛剛打過蠟的汽車

朗伯體反射實現(xiàn)方向光

朗伯體反射描述了這樣一個表面,它會反射所有方向上打到它的光線驶臊,以使它在任何觀察點上看起來都是一樣的挪挤。它的外觀只依賴于它與光源所處的方位及其距離

使用一個水平表面和單個的、不隨距離而減弱的方向光源关翎,因此,唯一有影響的就是這個表面相對于該光源所處的方位

直接面對光源的表面 與光源成一定角度的表面
image-20211223141607462.png
image-20211223141630129.png

我們可以看到表面垂直正對光源時扛门,在這個角度,它捕捉并反射盡可能多的光線纵寝;表面相對光源旋轉(zhuǎn)了45度時论寨,這個表面反射的光線與旋轉(zhuǎn)角的余弦有關(guān)

要計算出一個表面接收了多少光線,我們所需要做的就是計算出它直接面向光源時接收了多少光線,再把結(jié)果乘以那個角度的余弦值

計算高度圖的方位

在給高度圖添加朗伯體反射之前政基,需要某個方法獲知其表面的方位是什么贞铣。因為高度圖不是一個水平的表面,我們需要計算高度圖上每個點的方位沮明≡樱可以用表面法線(surface normal)表示方位,它是一個特殊類型的向量荐健,它垂直于表面酱畅,且有一個值為1的單位長度

因為表面法線是應(yīng)用于表面的而不是點,在計算每個點的法線時江场,把這個點的鄰接點合并在一起創(chuàng)建一個平面纺酸。我們將用兩個向量來表示這個平面:一個從右側(cè)的點指向左側(cè)的點,另外一個從上面的點指向下面的點址否。如果我們計算這兩個向量的叉積餐蔬,就能得到一個垂直于這個平面的向量,然后佑附,我們可以歸一化那個向量以得到中間點的表面法線樊诺,為什么使用從右向左的向量,而不是從左向右呢?因為我們想要這個表面法線指向上方音同,離開高度圖词爬,所以我們用叉積的右手規(guī)則計算出每個向量需要指向的方向


image-20211223144206780.png

我們假定每個點占用一個單位部分,x值向右增加权均,z值向下增加顿膨。其上邊、左邊叽赊、右邊和下邊的點的高度分別是0.2恋沃、0.1、0.1和0.1必指。要計算從右向左的向量囊咏,我們用右邊的點減去左邊的點得到值為(-2,0,0)的向量,再用上邊和下邊的點做同樣的計算得到值為(0,-0.1,2)的向量取劫。一旦有了這兩個向量,我們就可以計算它們的叉積得到值為(0,4,0.2)的向量研侣,我們再把這個向量歸一化得到值為(0,0.9988,0.05) 的表面法線

給高度圖加入法線向量

public class Heightmap {
    // 法線向量占的位數(shù)
    private static final int NORMAL_COMPONENT_COUNT = 3;
    private static final int TOTAL_COMPONENT_COUNT = NORMAL_COMPONENT_COUNT + POSITION_COMPONENT_COUNT;
    private static final int STRIDE = TOTAL_COMPONENT_COUNT * BYTES_PER_FLOAT;
    private int mNumOfIndex;
    private VertexBuffer mVertexBuffer;
    private IndexBuffer mIndexBuffer;
    private int mWidth;
    private int mHeight;

    public Heightmap(Bitmap bitmap) {
        mWidth = bitmap.getWidth();
        mHeight = bitmap.getHeight();
        initVertexBuffer(bitmap);
        initIndexBuffer();
    }


    /**
     * 綁定著色器中 attribute 數(shù)據(jù)谱邪,包括位置和法線向量
     *
     * @param heightmapProgram
     */
    public void bindData(HeightmapProgram heightmapProgram) {
        int offset = 0;
        mVertexBuffer.setVertexAttribPointer(
                offset,
                heightmapProgram.getAPosition(),
                POSITION_COMPONENT_COUNT,
                STRIDE
        );

        offset += POSITION_COMPONENT_COUNT * BYTES_PER_FLOAT;
        mVertexBuffer.setVertexAttribPointer(
                offset,
                heightmapProgram.getANormal(),
                NORMAL_COMPONENT_COUNT,
                STRIDE
        );
    }

    public void draw() {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer.getBufferId());
        glDrawElements(GL_TRIANGLES, mNumOfIndex, GL_UNSIGNED_SHORT, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    /**
     * 初始化索引Buffer
     */
    private void initIndexBuffer() {
        mNumOfIndex = numOfIndex();
        short[] indexData = new short[mNumOfIndex];
        int offset = 0;
        for (short row = 0; row < mHeight - 1; row++) {
            for (short col = 0; col < mWidth - 1; col++) {
                short topLeft = (short) (row * mWidth + col);
                short topRight = (short) (topLeft + 1);
                short bottomLeft = (short) ((row + 1) * mWidth + col);
                short bottomRight = (short) (bottomLeft + 1);

                indexData[offset++] = topLeft;
                indexData[offset++] = bottomLeft;
                indexData[offset++] = topRight;

                indexData[offset++] = topRight;
                indexData[offset++] = bottomLeft;
                indexData[offset++] = bottomRight;
            }
        }
        mIndexBuffer = new IndexBuffer(indexData);
    }

    /**
     * 初始化頂點Buffer,加上法向向量
     *
     * @param bitmap
     */
    private void initVertexBuffer(Bitmap bitmap) {
        float[] vertexData = new float[mWidth * mHeight * TOTAL_COMPONENT_COUNT];
        int offset = 0;
        int[] pixels = new int[mWidth * mHeight];
        bitmap.getPixels(pixels, 0, mWidth, 0, 0, mWidth, mHeight);
        for (int row = 0; row < mHeight; row++) {
            for (int col = 0; col < mWidth; col++) {

                // 點的位置
                Point point = getPoint(pixels, row, col);
                vertexData[offset++] = point.getX();
                vertexData[offset++] = point.getY();
                vertexData[offset++] = point.getZ();

                //點的上下左右
                Point top = getPoint(pixels, row - 1, col);
                Point left = getPoint(pixels, row, col - 1);
                Point right = getPoint(pixels, row, col + 1);
                Point bottom = getPoint(pixels, row + 1, col);

                // 得到法向向量
                Vector rightToLeft = Vector.vectorBetween(right, left);
                Vector topToBottom = Vector.vectorBetween(top, bottom);
                Vector normal = rightToLeft.crossProduct(topToBottom).normalize();

                // 將法向向量寫入頂點數(shù)據(jù)數(shù)組中
                vertexData[offset++] = normal.getX();
                vertexData[offset++] = normal.getY();
                vertexData[offset++] = normal.getZ();
            }
        }
        mVertexBuffer = new VertexBuffer(vertexData);
    }

    /**
     * 獲取pixels像素中的某個像素的三維空間位置
     *
     * @param pixels
     * @param row
     * @param col
     * @return
     */
    private Point getPoint(int[] pixels, int row, int col) {
        float x = (col / (float) (mWidth - 1)) - 0.5f;
        float z = (row / (float) (mHeight - 1)) - 0.5f;

        row = clamp(row, 0, mHeight - 1);
        col = clamp(col, 0, mWidth - 1);
        int pixel = pixels[row * mWidth + col];
        float y = Color.red(pixel) / 255f;

        return new Point(x, y, z);
    }

    private int clamp(int value, int min, int max) {
        return Math.min(max, Math.max(value, min));
    }

    /**
     * 返回高度圖的索引數(shù)目
     * 相鄰四個像素點形成1個正方形即2個三角形庶诡,即6個頂點惦银,整個bitmap形成(mWidth - 1) * (mHeight - 1) 個正方形
     *
     * @return
     */
    private int numOfIndex() {
        return (mWidth - 1) * (mHeight - 1) * 2 * 3;
    }
}

給著色器加入方向光

頂點著色器

uniform mat4 u_Matrix;
uniform mat4 u_MVMatrix;
uniform mat4 u_IT_MVMatrix;

uniform vec4 u_PointLightPositions[3];
uniform vec3 u_PointLightColors[3];

vec3 materialColor;
vec4 eyeSpacePosition;
vec3 eyeSpaceNormal;

attribute vec4 a_Position;
varying vec3 v_Color;

uniform vec3 u_VectorToLight;
attribute vec3 a_Normal;

vec3 getAmbientLighting();
vec3 getDirectionalLighting();
vec3 getPointLighting();

void main()
{
    materialColor = mix(vec3(0.180, 0.467, 0.153),
    vec3(0.660, 0.670, 0.680),
    a_Position.y);

    eyeSpacePosition=u_MVMatrix*a_Position;
    eyeSpaceNormal=normalize(vec3(u_IT_MVMatrix*vec4(a_Normal, 0.0)));

    v_Color=getAmbientLighting();
    v_Color+=getDirectionalLighting();
    v_Color+=getPointLighting();

    gl_Position = u_Matrix * a_Position;
}

vec3 getAmbientLighting(){
    return materialColor*0.1;
}

vec3 getDirectionalLighting(){
    return materialColor*0.3*max(dot(eyeSpaceNormal, u_VectorToLight), 0.0);
}

vec3 getPointLighting(){
    vec3 lightingSum=vec3(0.0);
    for (int i=0;i<3;i++){
        vec3 toPointLight=vec3(u_PointLightPositions[i])-vec3(eyeSpacePosition);
        float diatance=length(toPointLight);
        toPointLight=normalize(toPointLight);

        float cos=max(dot(toPointLight, eyeSpaceNormal), 0.0);
        lightingSum+=materialColor*u_PointLightColors[i]*5.0*cos/diatance;
    }
    return lightingSum;
}

變量說明

變量 說明
u_Matrix 模型視圖投影矩陣
u_MVMatrix 表示模型視圖矩陣,位置與其相乘,就會將位置信息裝換到眼空間中
u_IT_MVMatrix 表示模型視圖矩陣倒置矩陣的轉(zhuǎn)置矩陣扯俱,平面法線與之相乘书蚪,在進行歸一化,可以取消縮放的影響
u_PointLightPositions 點光源的位置迅栅,在眼空間中
u_PointLightColors 點光源顏色
u_VectorToLight 存儲方向光源的歸一化向量殊校,在眼空間中
a_Position 頂點位置信息
a_Normal 高度圖法線
v_Color 最終片段的顏色信息,因為片段著色器未做任何改動读存,直接將該值賦值給了最終片段的顏色信息
materialColor 山脈顏色为流,由mix函數(shù)根據(jù)山脈高度計算得到
eyeSpacePosition 頂點在眼空間中的位置,由a_Position和u_MVMatrix 進行轉(zhuǎn)換得到
eyeSpaceNormal 在眼空間中的高度圖法線让簿,由a_Normal 和u_IT_MVMatrix 進行轉(zhuǎn)換并歸一化得到

方法說明

方法 說明
getAmbientLighting() 獲取環(huán)境光敬察。其值設(shè)為山脈顏色的0.1,將全局都提亮一些
getDirectionalLighting() 獲取方向光尔当。相當(dāng)于太陽莲祸,其光源值定為materialColor*0.3,通過計算指向光源的向量與表面法線的點積椭迎,來計算表面與光線之間夾角的余弦值锐帜。它的工作原理是,當(dāng)兩個向量都是歸一化的向量時侠碧,那兩個向量的點積就是它們之間夾角的余弦抹估,為了避免出現(xiàn)負(fù)的結(jié)果,用max()把最小余弦值限制為0弄兜,然后药蜻,應(yīng)用這個光線,把當(dāng)前頂點的顏色與余弦值相乘替饿。余弦值在0和1之間语泽,因此,最終的顏色將是處于黑色和原色之間的某個顏色
getPointLighting() 獲取點光视卢。對于當(dāng)前頂點踱卵,循環(huán)計算每個點光,得到當(dāng)前頂點到點光的向量并歸一化据过,然后計算夾角余弦惋砂,再除以距離,并把其結(jié)果加人lightingSum绳锅,就是所有點光對當(dāng)前頂點的光照值了西饵,乘以5是為了放到效果,使其更明亮

更新著色器封裝代碼

public class HeightmapProgram extends BaseProgram {
    private static final String U_MV_MATRIX = "u_MVMatrix";
    private static final String U_IT_MV_MATRIX = "u_IT_MVMatrix";

    private static final String U_VECTOR_TO_LIGHT = "u_VectorToLight";

    private static final String U_POINT_LIGHT_POSITIONS = "u_PointLightPositions";
    private static final String U_POINT_LIGHT_COLORS = "u_PointLightColors";
    private static final String A_NORMAL = "a_Normal";

    private final int mUMatrix;
    private final int mUVectorToLight;
    private final int mUMVMatrix;
    private final int mUITMVMatrix;
    private final int mUPointLightPositions;
    private final int mUPointLightColors;

    private final int mAPosition;
    private final int mANormal;

    public HeightmapProgram(Context context) {
        super(context, R.raw.light9_heightmap_vertex, R.raw.particles8_heightmap_fragment);
        mUMatrix = glGetUniformLocation(mProgram, U_MATRIX);
        mUMVMatrix = glGetUniformLocation(mProgram, U_MV_MATRIX);
        mUITMVMatrix = glGetUniformLocation(mProgram, U_IT_MV_MATRIX);

        mUVectorToLight = glGetUniformLocation(mProgram, U_VECTOR_TO_LIGHT);

        mUPointLightPositions = glGetUniformLocation(mProgram, U_POINT_LIGHT_POSITIONS);
        mUPointLightColors = glGetUniformLocation(mProgram, U_POINT_LIGHT_COLORS);

        mAPosition = glGetAttribLocation(mProgram, A_POSITION);
        mANormal = glGetAttribLocation(mProgram, A_NORMAL);
    }

    public void setUniforms(float[] mvMatrix, float[] it_MVMatrix, float[] matrix,
                            float[] vectorToLight, float[] pointLightPositions, float[] pointLightColors) {
        glUniformMatrix4fv(mUMVMatrix, 1, false, mvMatrix, 0);
        glUniformMatrix4fv(mUITMVMatrix, 1, false, it_MVMatrix, 0);
        glUniformMatrix4fv(mUMatrix, 1, false, matrix, 0);
        glUniform3fv(mUVectorToLight, 1, vectorToLight, 0);
        glUniform4fv(mUPointLightPositions, 3, pointLightPositions, 0);
        glUniform3fv(mUPointLightColors, 3, pointLightColors, 0);
    }

    public int getAPosition() {
        return mAPosition;
    }

    public int getANormal() {
        return mANormal;
    }
}

更新渲染器代碼

public class MyRenderer implements GLSurfaceView.Renderer, ITouchRenderer {
    private static final String TAG = "MyRenderer";
    /*private static final int[] SKY_ID = new int[]{
            R.drawable.left, R.drawable.right,
            R.drawable.bottom, R.drawable.top,
            R.drawable.front, R.drawable.back,
    };*/
    private static final int[] SKY_ID = new int[]{
            R.drawable.night_left, R.drawable.night_right,
            R.drawable.night_bottom, R.drawable.night_top,
            R.drawable.night_front, R.drawable.night_back,
    };
    private Context mContext;
    private float[] modelMatrix = new float[16];
    private float[] viewMatrix = new float[16];
    private float[] viewMatrixForSkybox = new float[16];
    private float[] projectionMatrix = new float[16];
    private float[] tempMatrix = new float[16];
    private float[] modelViewProjectionMatrix = new float[16];

    private float[] modelViewMatrix = new float[16];
    private float[] it_modelViewMatrix = new float[16];

    private HeightmapProgram mHeightmapProgram;
    private Heightmap mHeightmap;

    private ParticlesProgram mParticlesProgram;
    private ParticleSystem mParticleSystem;
    private ParticleShooter mRedParticleShooter;
    private ParticleShooter mGreenParticleShooter;
    private ParticleShooter mBlueParticleShooter;
    // 控制粒子的角度
    private float mAngleVarianceInDegree = 30f;
    // 控制粒子的速度
    private float mSpeedVariance = 1f;
    private long mGlobalStartTime;

    private SkyBox mSkyBox;
    private SkyProgram mSkyProgram;
    private int mTextureId;
    private int mCubeTextureId;
    private float xRotate;
    private float yRotate;
    // 方向光源鳞芙,指向太陽
    private final float[] vectorToLight = {0.3f, 0.35f, -0.89f, 0f};
    // 點光源位置
    private final float[] pointLightPositions = new float[]{
            -1f, 1f, 0f, 1f,
            0f, 1f, 0f, 1f,
            1f, 1f, 0f, 1f
    };
    // 點光源顏色
    private final float[] pointLightColors = new float[]{
            1f, 0.2f, 0.02f,
            0.02f, 0.25f, 0.02f,
            0.02f, 0.2f, 1f
    };

    public MyRenderer(Context context) {
        mContext = context;
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
        glClearColor(0f, 0f, 0f, 0f);
        glEnable(GLES20.GL_DEPTH_TEST);
        glEnable(GLES20.GL_CULL_FACE);

        // 初始化高度圖
        mHeightmapProgram = new HeightmapProgram(mContext);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 4;
        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.heightmap, options);
        mHeightmap = new Heightmap(bitmap);

        // 粒子著色器程序
        mParticlesProgram = new ParticlesProgram(mContext);
        // 粒子系統(tǒng)眷柔,maxParticleCount如果太小的話期虾,可能只能看到比較新的粒子
        mParticleSystem = new ParticleSystem(10000);
        // 粒子方向
        com.test.opengl.light9.geometry.bean.Vector particleDirection = new Vector(0f, 0.5f, 0f);
        // 粒子發(fā)射器
        mRedParticleShooter = new ParticleShooter(new Point(-1f, 0f, 0f), particleDirection, Color.RED, mAngleVarianceInDegree, mSpeedVariance);
        mGreenParticleShooter = new ParticleShooter(new com.test.opengl.light9.geometry.bean.Point(0f, 0f, 0f), particleDirection, Color.GREEN, mAngleVarianceInDegree, mSpeedVariance);
        mBlueParticleShooter = new ParticleShooter(new Point(1f, 0f, 0f), particleDirection, Color.BLUE, mAngleVarianceInDegree, mSpeedVariance);
        // 粒子系統(tǒng)啟動的時間
        mGlobalStartTime = System.nanoTime();
        // 使用圖片的樣式繪制粒子
        mTextureId = com.test.opengl.light9.util.TextureHelper.loadTexture(mContext, R.drawable.particle_texture);

        mSkyProgram = new SkyProgram(mContext);
        mSkyBox = new SkyBox();
        mCubeTextureId = TextureHelper.loadCubeTexture(mContext, SKY_ID);
    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
        glViewport(0, 0, width, height);
        MatrixHelper.perspectiveM(projectionMatrix, 45, (float) width / height, 1, 10);
        // 更新視圖矩陣
        updateViewMatrix();
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        drawSky();
        drawHeightmap();
        drawParticles();
    }

    private void drawHeightmap() {
        // 更新矩陣
        setIdentityM(modelMatrix, 0);
        // 用模型矩陣使高度圖在x和z方向上變寬100倍,而在y方向上只變高10倍
        // 著色器中的顏色插值依賴于頂點所在位置的y值驯嘱,這不會擾亂镶苞,因為在頂點著色器中,設(shè)置v_Color的時間是在我們把它與矩陣相乘之前
        Matrix.scaleM(modelMatrix, 0, 80f, 30f, 80f);
        updateMvpMatrix();

        mHeightmapProgram.useProgram();
        final float[] vectorToLightInEyeSpace = new float[4];
        final float[] pointPositionsInEyeSpace = new float[12];
        Matrix.multiplyMV(vectorToLightInEyeSpace, 0, viewMatrix, 0, vectorToLight, 0);
        Matrix.multiplyMV(pointPositionsInEyeSpace, 0, viewMatrix, 0, pointLightPositions, 0);
        Matrix.multiplyMV(pointPositionsInEyeSpace, 4, viewMatrix, 0, pointLightPositions, 4);
        Matrix.multiplyMV(pointPositionsInEyeSpace, 8, viewMatrix, 0, pointLightPositions, 8);

        mHeightmapProgram.setUniforms(modelViewMatrix, it_modelViewMatrix, modelViewProjectionMatrix,
                vectorToLightInEyeSpace, pointPositionsInEyeSpace, pointLightColors);

        mHeightmap.bindData(mHeightmapProgram);
        mHeightmap.draw();
    }

    private void drawParticles() {
        // 關(guān)閉深度測試的寫操作
        glDepthMask(false);

        // 創(chuàng)建并添加粒子
        float currentTime = (System.nanoTime() - mGlobalStartTime) / 1000000000f;
        mRedParticleShooter.addParticle(mParticleSystem, currentTime, 5);
        mGreenParticleShooter.addParticle(mParticleSystem, currentTime, 5);
        mBlueParticleShooter.addParticle(mParticleSystem, currentTime, 5);

        // 更新矩陣
        setIdentityM(modelMatrix, 0);
        updateMvpMatrix();

        // 累計混合技術(shù)鞠评,粒子越多茂蚓,就越亮
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ONE);
        mParticlesProgram.useProgram();
        // 設(shè)置uniform的值
        mParticlesProgram.setUniforms(modelViewProjectionMatrix, currentTime, mTextureId);
        // to do ,注釋掉mParticleShooter.bindData(mParticlesProgram);模擬器就不會掛
        // 綁定粒子系統(tǒng)數(shù)據(jù)
        mParticleSystem.bindData(mParticlesProgram);
        // 繪制粒子系統(tǒng)的所有粒子
        mParticleSystem.draw();
        // 一次繪制完成后,關(guān)閉累計混合技術(shù)
        glDisable(GL_BLEND);
        glDepthMask(true);
    }

    private void drawSky() {
        // 深度測試算法改為小于等于谢澈,讓天空盒被繪制出來
        glDepthFunc(GL_LEQUAL);

        // 更新矩陣
        setIdentityM(modelMatrix, 0);
        updateMvpMatrixForSky();

        mSkyProgram.useProgram();
        mSkyProgram.setUniforms(modelViewProjectionMatrix, mCubeTextureId);
        mSkyBox.bindData(mSkyProgram);
        mSkyBox.draw();
        // 深度測試算法改回去煌贴,以免影響其他物體的繪制
        glDepthFunc(GL_LESS);
    }

    /**
     * 更新高度圖和粒子的模型視圖投影矩陣
     */
    private void updateMvpMatrix() {
        // 得到視圖模型矩陣
        Matrix.multiplyMM(modelViewMatrix, 0, viewMatrix, 0, modelMatrix, 0);
        // 視圖模型矩陣的反轉(zhuǎn)矩陣
        Matrix.invertM(tempMatrix, 0, modelViewMatrix, 0);
        // 視圖模型矩陣的反轉(zhuǎn)矩陣的轉(zhuǎn)置矩陣
        Matrix.transposeM(it_modelViewMatrix, 0, tempMatrix, 0);
        Matrix.multiplyMM(modelViewProjectionMatrix, 0, projectionMatrix, 0, modelViewMatrix, 0);
    }

    /**
     * 更新天空盒的模型視圖投影矩陣
     */
    private void updateMvpMatrixForSky() {
        Matrix.multiplyMM(tempMatrix, 0, viewMatrixForSkybox, 0, modelMatrix, 0);
        multiplyMM(modelViewProjectionMatrix, 0, projectionMatrix, 0, tempMatrix, 0);
    }

    /**
     * 處理拖拽事件
     *
     * @param deltaX
     * @param deltaY
     */
    public void handleDrag(float deltaX, float deltaY) {
        Log.d(TAG, "handleDrag: deltaX=" + deltaX + " deltaY=" + deltaY);
        xRotate += deltaX / 16f;
        yRotate += deltaY / 16f;
        if (yRotate > 90) {
            yRotate = 90;
        }
        if (yRotate < -90) {
            yRotate = -90;
        }
        // 更新視圖矩陣
        updateViewMatrix();
    }

    @Override
    public void handlePress(float normalizedX, float normalizedY) {

    }

    /**
     * 更新視圖矩陣,包括viewMatrix和viewMatrixForSkybox
     * 高度圖和粒子的視圖矩陣viewMatrix锥忿,代表相機牛郑,它應(yīng)用于所有的物體
     * 天空盒視圖矩陣viewMatrixForSkybox,只表示旋轉(zhuǎn)
     */
    private void updateViewMatrix() {
        setIdentityM(viewMatrix, 0);
        Matrix.rotateM(viewMatrix, 0, -yRotate, 1f, 0f, 0f);
        Matrix.rotateM(viewMatrix, 0, -xRotate, 0f, 1f, 0f);

        // 使用 viewMatrixForSkybox 旋轉(zhuǎn)天空盒
        System.arraycopy(viewMatrix, 0, viewMatrixForSkybox, 0, viewMatrix.length);
        // 使用 viewMatrix 一起旋轉(zhuǎn)和平移高度圖和粒子
        translateM(viewMatrix, 0, 0, -1.5f, -5f);
    }

}

效果

光線.gif
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敬鬓,一起剝皮案震驚了整個濱河市淹朋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钉答,老刑警劉巖础芍,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異数尿,居然都是意外死亡仑性,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門右蹦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诊杆,“玉大人,你說我怎么就攤上這事何陆〕啃冢” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵贷盲,是天一觀的道長淘这。 經(jīng)常有香客問我,道長巩剖,這世上最難降的妖魔是什么铝穷? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮佳魔,結(jié)果婚禮上曙聂,老公的妹妹穿的比我還像新娘。我一直安慰自己吃引,他們只是感情好筹陵,可當(dāng)我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著镊尺,像睡著了一般朦佩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上庐氮,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天语稠,我揣著相機與錄音,去河邊找鬼弄砍。 笑死仙畦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的音婶。 我是一名探鬼主播慨畸,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼衣式!你這毒婦竟也來了寸士?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤碴卧,失蹤者是張志新(化名)和其女友劉穎弱卡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體住册,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡婶博,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了荧飞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凡人。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖垢箕,靈堂內(nèi)的尸體忽然破棺而出划栓,到底是詐尸還是另有隱情,我是刑警寧澤条获,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布忠荞,位于F島的核電站,受9級特大地震影響帅掘,放射性物質(zhì)發(fā)生泄漏委煤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一修档、第九天 我趴在偏房一處隱蔽的房頂上張望碧绞。 院中可真熱鬧,春花似錦吱窝、人聲如沸讥邻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兴使。三九已至系宜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間发魄,已是汗流浹背盹牧。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留励幼,地道東北人汰寓。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像苹粟,于是被迫代替她去往敵國和親有滑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容