《OpenGL從入門(mén)到放棄03 》相機(jī)和視圖

之前文章:

《OpenGL從入門(mén)到放棄01 》一些基本概念
《OpenGL從入門(mén)到放棄02 》GLSurfaceView和Renderer


上一篇文章我們看到三角形形狀跟預(yù)期有點(diǎn)不一樣


這是OpenGL坐標(biāo)系的問(wèn)題,對(duì)坐標(biāo)系不明白可以看第一節(jié)蛛碌,這一節(jié)我們通過(guò)投影和相機(jī)視圖來(lái)解決這個(gè)問(wèn)題。

先來(lái)個(gè)概念,之后再上代碼

相機(jī)

生活中我們拍照没炒,你站的高度猾昆,拿相機(jī)的位置匆骗,姿勢(shì)不同,拍出來(lái)的照片也就不一樣
在Android OpenGLES程序中配紫,我們可以通過(guò)以下方法來(lái)進(jìn)行相機(jī)設(shè)置:

Matrix.setLookAtM (float[] rm,      //接收相機(jī)變換矩陣
                int rmOffset,       //變換矩陣的起始位置(偏移量)
                float eyeX,float eyeY, float eyeZ,   //相機(jī)位置
                float centerX,float centerY,float centerZ,  //觀測(cè)點(diǎn)位置
                float upX,float upY,float upZ)  //up向量在xyz上的分量

相機(jī)位置:相機(jī)的位置是比較好理解的,就是相機(jī)在3D空間里面的坐標(biāo)點(diǎn)午阵,

相機(jī)觀察方向:相機(jī)的觀察方向躺孝,對(duì)應(yīng)center向量,表示的是相機(jī)鏡頭的朝向底桂,你可以朝前拍括细、朝后拍、也可以朝左朝右戚啥,或者其他的方向奋单。

相機(jī)UP方向:相機(jī)的UP方向,可以理解為相機(jī)頂端指向的方向猫十。比如你把相機(jī)斜著拿著览濒,拍出來(lái)的照片就是斜著的呆盖,你倒著拿著,拍出來(lái)的就是倒著的贷笛。

如圖:center的X,Y,Z表示相機(jī)的正方向的方向向量应又,up的X,Y,Z表示相機(jī)的上方向的方向向量。因?yàn)榭臻g中想要確定一個(gè)具體朝向乏苦,必須要有兩組向量株扛,所以此函數(shù)需要一個(gè)點(diǎn)和兩組向量來(lái)確定相機(jī)的位置和具體朝向。

投影

使用OpenGl繪制的3D圖形汇荐,需要展示在移動(dòng)端2D設(shè)備上洞就,這就需要投影。
OpenGl ES中有兩種投影方式:一種是正交投影掀淘,一種是透視投影

正交投影

投影物體的大小不會(huì)隨觀察點(diǎn)的遠(yuǎn)近而發(fā)生變化旬蟋,我們可以使用下面方法來(lái)執(zhí)行正交投影

Matrix.orthoM (float[] m,           //接收正交投影的變換矩陣
            int mOffset,        //變換矩陣的起始位置(偏移量)
            float left,         //相對(duì)觀察點(diǎn)近面的左邊距
            float right,        //相對(duì)觀察點(diǎn)近面的右邊距
            float bottom,       //相對(duì)觀察點(diǎn)近面的下邊距
            float top,          //相對(duì)觀察點(diǎn)近面的上邊距
            float near,         //相對(duì)觀察點(diǎn)近面距離
            float far)          //相對(duì)觀察點(diǎn)遠(yuǎn)面距離

透視投影

隨觀察點(diǎn)的距離變化而變化,觀察點(diǎn)越遠(yuǎn)革娄,視圖越小倾贰,反之越大,我們可以通過(guò)如下方法來(lái)設(shè)置透視投影:

Matrix.frustumM (float[] m,         //接收透視投影的變換矩陣
            int mOffset,        //變換矩陣的起始位置(偏移量)
            float left,         //相對(duì)觀察點(diǎn)近面的左邊距
            float right,        //相對(duì)觀察點(diǎn)近面的右邊距
            float bottom,       //相對(duì)觀察點(diǎn)近面的下邊距
            float top,          //相對(duì)觀察點(diǎn)近面的上邊距
            float near,         //相對(duì)觀察點(diǎn)近面距離
            float far)          //相對(duì)觀察點(diǎn)遠(yuǎn)面距離

只有繪制在近平面和遠(yuǎn)平面之間的內(nèi)容拦惋,才能被用戶看到

轉(zhuǎn)換矩陣(變換矩陣)

轉(zhuǎn)換矩陣用來(lái)做什么的呢匆浙?
是否記得上面我們繪制的圖形坐標(biāo)需要轉(zhuǎn)換為OpenGl中能處理的小端字節(jié)序(LittleEdian)
沒(méi)錯(cuò),轉(zhuǎn)換矩陣就是用來(lái)將數(shù)據(jù)轉(zhuǎn)為OpenGl ES可用的數(shù)據(jù)字節(jié)厕妖,我們將相機(jī)視圖矩陣和投影矩陣相乘吞彤,便得到一個(gè)轉(zhuǎn)換矩陣,然后我們?cè)賹⒋司仃噦鹘o頂點(diǎn)著色器叹放,具體使用方法及參數(shù)說(shuō)明如下:

Matrix.multiplyMM (float[] result, //接收相乘結(jié)果
            int resultOffset,  //接收矩陣的起始位置(偏移量)
            float[] lhs,       //左矩陣
            int lhsOffset,     //左矩陣的起始位置(偏移量)
            float[] rhs,       //右矩陣
            int rhsOffset)     //右矩陣的起始位置(偏移量)

下面簡(jiǎn)單講解下如何使用投影和相機(jī)視圖來(lái)實(shí)現(xiàn)矩陣變換并傳遞給頂點(diǎn)著色器

實(shí)戰(zhàn)

1饰恕、定義一個(gè)投影矩陣、相機(jī)矩陣井仰、變換矩陣

private final float[] mMVPMatrix = new float[16];      //變換矩陣(投影矩陣*相機(jī)矩陣的結(jié)果埋嵌,最終要傳遞給頂點(diǎn)著色器)
private final float[] mProjectionMatrix = new float[16]; //投影矩陣
private final float[] mViewMatrix = new float[16]; //相機(jī)位置矩陣

2、onSurfaceChanged 中進(jìn)行設(shè)置

public void onSurfaceChanged(GL10 gl, int width, int height) {
        // 設(shè)置繪圖的窗口(可以理解成在畫(huà)布上劃出一塊區(qū)域來(lái)畫(huà)圖)
        GLES20.glViewport(0,0,width,height);
        /**投影和相機(jī)視圖相關(guān)**/
        float ratio = (float) width / height;
        //設(shè)置正交投影
//        Matrix.orthoM (mProjectionMatrix, 0, -ratio, ratio, -1, 2, 3, 7);
        Matrix.orthoM (mProjectionMatrix, 0,0,width, height, 0, -width, width);//這個(gè)投影會(huì)跟屏幕坐標(biāo)關(guān)聯(lián)上
        //設(shè)置透視投影(觀察點(diǎn)越遠(yuǎn)俱恶,視圖越斜⑧隆),這個(gè)投影矩陣被應(yīng)用于對(duì)象坐標(biāo)在onDrawFrame()方法中
//        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 2, 3, 7);

        //設(shè)置相機(jī)位置
        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 7f, 0f, 0f, 0f, 0f, 1.0f, 1.0f);
        //計(jì)算變換矩陣,最終mMVPMatrix要傳遞給頂點(diǎn)著色器
        Matrix.multiplyMM(mMVPMatrix,0,mProjectionMatrix,0,mViewMatrix,0);

    }

至此就得到了一個(gè)變換矩陣 mMVPMatrix合是,最終需要傳遞給頂點(diǎn)著色器

3了罪、修改三角形代碼

在上一節(jié)代碼的基礎(chǔ)上修改

1、修改頂點(diǎn)著色器

// 頂點(diǎn)著色器的腳本
    String vertexShaderCode =
            "uniform mat4 uMVPMatrix;" +         //接收傳入的轉(zhuǎn)換矩陣
            " attribute vec4 vPosition;" +      //接收傳入的頂點(diǎn)
                    " void main() {" +
                        "  gl_Position = uMVPMatrix * vPosition;" +  //矩陣變換計(jì)算之后的位置
                    " }";

2聪全、增加變量

    //變換矩陣句柄
    private int mMVPMatrixHandle;
    //變換矩陣泊藕,提供set方法
    private float[] mvpMatrix = new float[16];
    public void setMvpMatrix(float[] mvpMatrix) {
        this.mvpMatrix = mvpMatrix;
    }

3、修改draw方法

draw方法主要是將我們的數(shù)據(jù)傳遞給著色器难礼,既然頂點(diǎn)著色器增加了矩陣變量娃圆,那么我們需要把變換矩陣傳遞給頂點(diǎn)著色器

        // 獲取變換矩陣的句柄
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        // 將投影和視圖轉(zhuǎn)換傳遞給著色器玫锋,可以理解為給頂點(diǎn)著色器中uMVPMatrix這個(gè)變量賦值為mvpMatrix
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

4、在onDrawFrame中繪制

    mGlTriangle02.setMvpMatrix(mMVPMatrix);
    mGlTriangle02.draw();

mGlTriangle02在onSurfaceCreated中new出來(lái)讼呢,然后就可以看到效果了

image.png

看起來(lái)差不多了撩鹿,可以看到頂點(diǎn)位置不變的情況下,使用投影跟相機(jī)視圖確實(shí)能改變2d效果悦屏。

最后貼下三角形的全部代碼

public class GLTriangle02{

    // 頂點(diǎn)著色器的腳本
    String vertexShaderCode =
            "uniform mat4 uMVPMatrix;" +         //接收傳入的轉(zhuǎn)換矩陣
            " attribute vec4 vPosition;" +      //接收傳入的頂點(diǎn)
                    " void main() {" +
                        "  gl_Position = uMVPMatrix * vPosition;" +  //矩陣變換計(jì)算之后的位置
                    " }";

    // 片元著色器的腳本
    String fragmentShaderCode =
            " precision mediump float;" +  // 聲明float類(lèi)型的精度為中等(精度越高越耗資源)
                    " uniform vec4 vColor;" +       // 接收傳入的顏色
                    " void main() {" +
                    "     gl_FragColor = vColor;" +  // 給此片元的填充色
                    " }";

    private FloatBuffer vertexBuffer;  //頂點(diǎn)坐標(biāo)數(shù)據(jù)要轉(zhuǎn)化成FloatBuffer格式


    // 數(shù)組中每3個(gè)值作為一個(gè)坐標(biāo)點(diǎn)
    static final int COORDS_PER_VERTEX = 3;
    //三角形的坐標(biāo)數(shù)組
    static float triangleCoords[] = {
            0.0f, 0.5f, 0.0f, // top
            -0.5f, -0.5f, 0.0f, // bottom left
            0.5f, -0.5f, 0.0f  // bottom right
    };

    //頂點(diǎn)個(gè)數(shù)节沦,計(jì)算得出
    private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
    //一個(gè)頂點(diǎn)有3個(gè)float,一個(gè)float是4個(gè)字節(jié)础爬,所以一個(gè)頂點(diǎn)要12字節(jié)
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per mVertex

    //三角形的顏色數(shù)組甫贯,rgba
    private float[] mColor = {
            0.0f, 1.0f, 0.0f, 1.0f,
    };

    //當(dāng)前繪制的頂點(diǎn)位置句柄
    private int vPositionHandle;
    //片元著色器顏色句柄
    private int vColorHandle;
    //變換矩陣句柄
    private int mMVPMatrixHandle;
    //這個(gè)可以理解為一個(gè)OpenGL程序句柄
    private final int mProgram;

    //變換矩陣,提供set方法
    private float[] mvpMatrix = new float[16];
    public void setMvpMatrix(float[] mvpMatrix) {
        this.mvpMatrix = mvpMatrix;
    }


    public GLTriangle02() {
        /** 1幕帆、數(shù)據(jù)轉(zhuǎn)換获搏,頂點(diǎn)坐標(biāo)數(shù)據(jù)float類(lèi)型轉(zhuǎn)換成OpenGL格式FloatBuffer赖条,int和short同理*/
        vertexBuffer = GLUtil.floatArray2FloatBuffer(triangleCoords);

        /** 2失乾、加載編譯頂點(diǎn)著色器和片元著色器*/
        int vertexShader = GLUtil.loadShader(GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
        int fragmentShader = GLUtil.loadShader(GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);

        /** 3、創(chuàng)建空的OpenGL ES程序纬乍,并把著色器添加進(jìn)去*/
        mProgram = GLES20.glCreateProgram();

        // 添加頂點(diǎn)著色器到程序中
        GLES20.glAttachShader(mProgram, vertexShader);

        // 添加片段著色器到程序中
        GLES20.glAttachShader(mProgram, fragmentShader);

        /** 4碱茁、鏈接程序*/
        GLES20.glLinkProgram(mProgram);

    }


    public void draw() {

        // 將程序添加到OpenGL ES環(huán)境
        GLES20.glUseProgram(mProgram);

        /***1.獲取句柄*/
        // 獲取頂點(diǎn)著色器的位置的句柄(這里可以理解為當(dāng)前繪制的頂點(diǎn)位置)
        vPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        // 獲取片段著色器的vColor句柄
        vColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
        // 獲取變換矩陣的句柄
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

        /**2.設(shè)置數(shù)據(jù)*/
        // 啟用頂點(diǎn)屬性,最后對(duì)應(yīng)禁用
        GLES20.glEnableVertexAttribArray(vPositionHandle);

        //準(zhǔn)備三角形坐標(biāo)數(shù)據(jù)
        GLES20.glVertexAttribPointer(vPositionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);
        // 設(shè)置繪制三角形的顏色仿贬,給vColor 這個(gè)變量賦值
        GLES20.glUniform4fv(vColorHandle, 1, mColor, 0);
        // 將投影和視圖轉(zhuǎn)換傳遞給著色器纽竣,可以理解為給uMVPMatrix這個(gè)變量賦值為mvpMatrix
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);

        /** 3.繪制三角形,三個(gè)頂點(diǎn)*/
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

        // 禁用頂點(diǎn)數(shù)組(好像不禁用也沒(méi)啥問(wèn)題)
        GLES20.glDisableVertexAttribArray(vPositionHandle);
    }
}

到這里我們對(duì)繪制三角形的步驟已經(jīng)有一個(gè)基本的了解茧泪,那么如何畫(huà)一個(gè)長(zhǎng)方形呢蜓氨?其實(shí)跟畫(huà)三角形差不多,下一節(jié)我們就學(xué)習(xí)一下如何繪制一個(gè)長(zhǎng)方形队伟,敬請(qǐng)期待穴吹。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市嗜侮,隨后出現(xiàn)的幾起案子港令,更是在濱河造成了極大的恐慌,老刑警劉巖锈颗,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件顷霹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡击吱,警方通過(guò)查閱死者的電腦和手機(jī)淋淀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)覆醇,“玉大人绅喉,你說(shuō)我怎么就攤上這事渠鸽。” “怎么了柴罐?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵徽缚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我革屠,道長(zhǎng)凿试,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任似芝,我火速辦了婚禮那婉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘党瓮。我一直安慰自己详炬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布寞奸。 她就那樣靜靜地躺著呛谜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪枪萄。 梳的紋絲不亂的頭發(fā)上隐岛,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音瓷翻,去河邊找鬼聚凹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛齐帚,可吹牛的內(nèi)容都是我干的妒牙。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼对妄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼湘今!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起饥伊,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤象浑,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后琅豆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體愉豺,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年茫因,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蚪拦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驰贷,靈堂內(nèi)的尸體忽然破棺而出盛嘿,到底是詐尸還是另有隱情,我是刑警寧澤括袒,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布次兆,位于F島的核電站,受9級(jí)特大地震影響锹锰,放射性物質(zhì)發(fā)生泄漏芥炭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一恃慧、第九天 我趴在偏房一處隱蔽的房頂上張望园蝠。 院中可真熱鬧,春花似錦痢士、人聲如沸彪薛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)善延。三九已至,卻和暖如春褥蚯,著一層夾襖步出監(jiān)牢的瞬間挚冤,已是汗流浹背况增。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工赞庶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人澳骤。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓歧强,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親为肮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子摊册,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

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