Android OpenGL添加紋理

1 相關(guān)基礎(chǔ)

1.1 貼紋理原理簡單概述

其實貼紋理的原理非常簡單杯矩,就是給每個三角形貼上圖片即可。那么如何給三角形貼圖片呢袖外?我們知道史隆,既然是給三角形貼圖片曼验,那肯定就需要一張圖泌射,然后在這張圖片上指定三角形的三個頂點(diǎn)對應(yīng)這張圖的位置。這樣就可以準(zhǔn)確的為這個三角形貼好圖片了鬓照。

值得注意的是熔酷,三角形的三個頂點(diǎn)在圖片上的位置取值范圍為[0,1]。即以相對圖片的寬高比例來計算的豺裆。

在加載紋理圖片時拒秘,OpenGL為每張圖片分配好ID,將圖片緩存起來臭猜。在貼圖時躺酒,通過ID來查找圖片。

1.2 相關(guān)API

跟繪制三角形類似蔑歌,如果需要開啟貼紋理功能需要如下代碼:
gl.glEnable(GL10.GL_TEXTURE_2D);
對應(yīng)的關(guān)閉為:
gl.glDisable(GL10.GL_TEXTURE_2D);
前面1.1節(jié)中阴颖,我們提到:在加載紋理圖片時,OpenGL為每張圖片分配好ID丐膝,將圖片緩存起來。在貼圖時钾菊,通過ID來查找圖片帅矗。因此,在我們開始貼圖之前煞烫,需要為當(dāng)前模型綁定好紋理圖片的ID:

//根據(jù)ID綁定對應(yīng)的紋理
gl.glBindTexture(GL10.GL_TEXTURE_2D, model.getTextureIds()[0]);

上面代碼中浑此,我們看到,在Model實體類中滞详,通過getTextureIds函數(shù)來獲取ID數(shù)組凛俱,并取出數(shù)組的第一個數(shù)據(jù)紊馏。而Model類是我們自己自定義的實體類,顯然不可能在我們的自定義的實體類中“無中生有”出一個ID數(shù)組蒲犬。那么這個ID數(shù)組從哪里來朱监?

注意,紋理的ID是保存在一個int[]數(shù)組中原叮,數(shù)組的第一個元素即為ID赫编。

關(guān)于紋理對應(yīng)的ID,后面詳細(xì)說奋隶。我們繼續(xù)往下走擂送,在拿到紋理ID的情況下,如何繪制唯欣。首先你需要啟用紋理坐標(biāo)數(shù)組:
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
然后在繪制三角形之前嘹吨,將紋理坐標(biāo)數(shù)據(jù)設(shè)定好:
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, model.getTextureBuffer());
此時即可繪制紋理。當(dāng)然了境氢,我們現(xiàn)在只是大致講講蟀拷,更完整更詳細(xì)的內(nèi)容第二節(jié)談~。

1.3 pxy文件格式

由于我采用的模型的紋理坐標(biāo)數(shù)據(jù)是pxy格式产还。pxy格式里面保存的是浮點(diǎn)數(shù)的集合匹厘,即每4個字節(jié)為一個數(shù)據(jù)。每2個浮點(diǎn)數(shù)表示一個坐標(biāo)點(diǎn)脐区,每三個坐標(biāo)點(diǎn)對應(yīng)一個三角形在圖片中的紋理區(qū)域愈诚。

1.4 多個模型數(shù)據(jù)

為什么要提多個模型數(shù)據(jù)呢?我們知道牛隅,三維模型中炕柔,自然是包含各個角度的映像圖片。一張圖片往往很難包含整個模型的紋理信息媒佣。因此匕累,我們將一個3D模型分割成多個3D模型,每個模型對應(yīng)一張紋理圖片默伍。理論上說欢嘿,3張圖片即可包含整個模型紋理信息了,即也糊,將一個模型分割成3個3D模型炼蹦。當(dāng)然了,分割的越多狸剃,紋理圖片的構(gòu)造就越簡單(你也可以以一張360°全景紋理圖片掐隐,但是構(gòu)造這樣的圖片成本比較高)。
可能你會說钞馁,我們該如何分割模型虑省?每個模型的坐標(biāo)位置信息我們無法給它分割匿刮,因為坐標(biāo)位置數(shù)據(jù)是一個數(shù)組,是按照三角形頂點(diǎn)的順序指定的探颈。
請注意一點(diǎn)熟丸,我們只負(fù)責(zé)顯示模型,我們不管如何分割膝擂,分割這塊丟個模型的設(shè)計者虑啤!因為設(shè)計者可以通過相關(guān)的3D設(shè)計軟件輕松的分割。我們只需關(guān)注架馋,如何同時顯示多個3D模型狞山,并為每個3D模型貼好對應(yīng)的紋理即可。

2 代碼編寫

2.1 解析pxy文件

前面我們大致介紹了pxy文件格式叉寂,我們知道萍启,pxy保存的就是當(dāng)前stl文件中三角形頂點(diǎn)在紋理圖片上對應(yīng)的坐標(biāo)。每個頂點(diǎn)占2個浮點(diǎn)數(shù)(對應(yīng)x屏鳍、y)勘纯。那么我們的解析就非常簡單了,在STLReader類中钓瞭,添加如下函數(shù):

private void parseTexture(Model model, byte[] textureBytes) {
    int facetCount = model.getFacetCount();
    // 三角面?zhèn)€數(shù)有三個頂點(diǎn)驳遵,一個頂點(diǎn)對應(yīng)紋理二維坐標(biāo)
    float[] textures = new float[facetCount * 3 * 2];
    int textureOffset = 0;
    for (int i = 0; i < facetCount * 3; i++) {
        //第i個頂點(diǎn)對應(yīng)的紋理坐標(biāo)
        //tx和ty的取值范圍為[0,1],表示的坐標(biāo)位置是在紋理圖片上的對應(yīng)比例
        float tx = Util.byte4ToFloat(textureBytes, textureOffset);
        float ty = Util.byte4ToFloat(textureBytes, textureOffset + 4);

        textures[i * 2] = tx;
        //我們的pxy文件原點(diǎn)是在左下角,因此需要用1減去y坐標(biāo)值
        textures[i * 2 + 1] = 1 - ty;

        textureOffset += 8;
    }
    model.setTextures(textures);
}

同時山涡,我們需要在Model類中添加紋理相關(guān)數(shù)據(jù)屬性堤结,并且添加對應(yīng)的setter、getter函數(shù)鸭丛。代碼我就不貼出來了竞穷,后面我會上傳源碼。

現(xiàn)在我們編寫好了解析pxy紋理坐標(biāo)數(shù)據(jù)鳞溉,接下來就是把解析stl文件和pxy文件整合在一起的函數(shù)瘾带,在STLReader中添加:

public Model parseStlWithTexture(InputStream stlInput, InputStream textureInput) throws IOException {
    Model model = parseBinStl(stlInput);
    int facetCount = model.getFacetCount();
    // 三角面片有3個頂點(diǎn),一個頂點(diǎn)有2個坐標(biāo)軸數(shù)據(jù)熟菲,每個坐標(biāo)軸數(shù)據(jù)是float類型(4字節(jié))
    byte[] textureBytes = new byte[facetCount * 3 * 2 * 4];
    textureInput.read(textureBytes);// 將所有紋理坐標(biāo)讀出來
    parseTexture(model, textureBytes);
    return model;
}

此時看政,我們的STLReader類就可以通過parseStlWithTexture函數(shù)完美的將stl和pxy數(shù)據(jù)封裝到Model對象中了。

2.2 加載紋理圖片

前面我們提到了抄罕,OpenGL為每張紋理圖片生成一個ID帽衙。接下來我們看看如何將一個紋理圖片加載到OpenGL通道中,并且分配一個ID贞绵。首先,我們需要讀取紋理圖片恍飘,生成Bitmap對象榨崩。然后調(diào)用glGenTextures函數(shù)谴垫,生成ID,并將此ID保存到Model對象中母蛛。此時翩剪,我們已經(jīng)拿到了ID,但是這個ID并沒有綁定Bitmap對象彩郊。在將ID和Bitmap綁定之前前弯,需要調(diào)用glBindTexture函數(shù),將生成的ID綁定到紋理通道秫逝,并且通過glTexParameterf設(shè)定當(dāng)前綁定紋理的相關(guān)屬性恕出。 最后通過GLUtils.texImage2D函數(shù)將Bitmap對象與當(dāng)前紋理通道綁定,而當(dāng)前紋理通道已經(jīng)綁定好了ID违帆,從而達(dá)到了ID與紋理的間接綁定浙巫。以后使用紋理時,就可以直接通過ID來訪問刷后,無需直接訪問Bitmap對象了的畴。

private void loadTexture(GL10 gl, Model model, boolean isAssets) {
    Log.d("GLRenderer", "綁定紋理:" + model.getPictureName());
    Bitmap bitmap = null;
    try {
        // 打開圖片資源
        if (isAssets) {//如果是從assets中讀取
            bitmap = BitmapFactory.decodeStream(context.getAssets().open(model.getPictureName()));
        } else {//否則就是從SD卡里面讀取
            bitmap = BitmapFactory.decodeFile(model.getPictureName());
        }
        // 生成一個紋理對象,并將其ID保存到成員變量 texture 中
        int[] textures = new int[1];
        gl.glGenTextures(1, textures, 0);
        model.setTextureIds(textures);

        // 將生成的空紋理綁定到當(dāng)前2D紋理通道
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

        // 設(shè)置2D紋理通道當(dāng)前綁定的紋理的屬性
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
                GL10.GL_NEAREST);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
                GL10.GL_LINEAR);

        // 將bitmap應(yīng)用到2D紋理通道當(dāng)前綁定的紋理中
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {

        if (bitmap != null)
            bitmap.recycle();

    }
}
2.3 讀取多個模型數(shù)據(jù)

我們前面說過尝胆,將一個模型分割成多個模型丧裁,因此,我們需要讀取多個模型數(shù)據(jù)含衔,并保存起來煎娇。我們在此之前已經(jīng)學(xué)會了讀取一個模型數(shù)據(jù),那么讀取多個模型數(shù)據(jù)通過for循環(huán)即可抱慌。為了讀取上的方便逊桦,我們?yōu)閷⒚總€模型數(shù)據(jù)按序命名。另外抑进,我們知道强经,目前為止,我們的一個模型對應(yīng)三種格式文件:

pxy:三角形對應(yīng)的紋理坐標(biāo)
stl:三角網(wǎng)數(shù)據(jù)
jpg:紋理圖片

為了讀取上的方便寺渗,我們將同一個模型的這三個文件設(shè)為相同的名稱匿情,如:1.pxy
、1.stl信殊、1.jpg炬称。各個模型之間按序命名,格式如下圖:


命名格式

此時涡拘,我們就可以很輕松的讀取啦~玲躯。在GLRenderer
中:

private List<Model> models = new ArrayList<>();

public GLRenderer(Context context) {
    this.context = context;
    try {
        STLReader reader = new STLReader();
        for (int i = 1; i <= 6; i++) {
            Model model = reader.parserStlWithTextureInAssets(context, "chuwang/" + i);

            models.add(model);
        }

    } catch (IOException e) {
        e.printStackTrace();
    }
}

此時我們就完成了將所有的模型數(shù)據(jù)保存在List<Model>類型的models對象中。

2.4 開始繪制

前面所做的鋪墊已經(jīng)完成,接下來就是最后的繪制了跷车!相比上一篇的代碼棘利,我們只需修改onSurfaceCreatedonDrawFrame函數(shù)。其實大部分代碼都是相同的朽缴,只是我們通過for循環(huán)的方式善玫,將所有的模型繪制出來而已。
但是有個區(qū)別需要注意密强,就是我們需要獲取所有模型在xyz坐標(biāo)中的最大值最小值茅郎,以及所有模型加在一起后的中心點(diǎn)位置。前面我們只有一個模型或渤,很快就算好了系冗。多個模型我們也是很簡單,只需根據(jù)每個模型的在xyz坐標(biāo)中的最大值最小值計算即可劳坑,詳情請看我的附件源碼毕谴。
我們看看onSurfaceCreated函數(shù),onSurfaceCreated函數(shù)需要負(fù)責(zé)計算所有模型的中心點(diǎn)距芬、所有模型在xyz坐標(biāo)中的最大值最小值涝开,以及加載所有模型對應(yīng)的紋理圖片:

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    gl.glEnable(GL10.GL_DEPTH_TEST); // 啟用深度緩存
    gl.glClearColor(0f, 0f, 0f, 0f);// 設(shè)置深度緩存值
    gl.glDepthFunc(GL10.GL_LEQUAL); // 設(shè)置深度緩存比較函數(shù)
    gl.glShadeModel(GL10.GL_SMOOTH);// 設(shè)置陰影模式GL_SMOOTH


    //初始化相關(guān)數(shù)據(jù)
    initConfigData(gl);

}

private void initConfigData(GL10 gl) {
    float r = Util.getR(models);
    mScalef = 0.5f / r;
    mCenterPoint = Util.getCenter(models);

    //為每個模型綁定紋理
    for (Model model : models) {
        loadTexture(gl, model, true);
    }
}

再看看onDrawFrame函數(shù),onDrawFrame函數(shù)需要通過for循環(huán)的方式框仔,繪制出每個模型:

@Override
public void onDrawFrame(GL10 gl) {
    // 清除屏幕和深度緩存
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

    gl.glLoadIdentity();// 重置當(dāng)前的模型觀察矩陣


    //眼睛對著原點(diǎn)看
    GLU.gluLookAt(gl, eye.x, eye.y, eye.z, center.x,
            center.y, center.z, up.x, up.y, up.z);

    //為了能有立體感覺舀武,通過改變mDegree值,讓模型不斷旋轉(zhuǎn)
    gl.glRotatef(mDegree, 0, 1, 0);

    //將模型放縮到View剛好裝下
    gl.glScalef(mScalef, mScalef, mScalef);
    //把模型移動到原點(diǎn)
    gl.glTranslatef(-mCenterPoint.x, -mCenterPoint.y,
            -mCenterPoint.z);


    //===================begin==============================//
    for (Model model : models) {
        //開啟貼紋理功能
        gl.glEnable(GL10.GL_TEXTURE_2D);
        //根據(jù)ID綁定對應(yīng)的紋理
        gl.glBindTexture(GL10.GL_TEXTURE_2D, model.getTextureIds()[0]);
        //啟用相關(guān)功能
        gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

        //開始繪制
        gl.glNormalPointer(GL10.GL_FLOAT, 0, model.getVnormBuffer());
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, model.getVertBuffer());
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, model.getTextureBuffer());

        gl.glDrawArrays(GL10.GL_TRIANGLES, 0, model.getFacetCount() * 3);

        //關(guān)閉當(dāng)前模型貼紋理离斩,即將紋理id設(shè)置為0
        gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);

        //關(guān)閉對應(yīng)的功能
        gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
        gl.glDisable(GL10.GL_TEXTURE_2D);
    }
    //=====================end============================//
}

從代碼上也可以看出银舱,相比前幾篇文章,代碼的修改并不大跛梗。細(xì)心的童鞋會發(fā)現(xiàn)寻馏,我這里并沒有開啟光照、材料屬性核偿。主要是我們已經(jīng)貼好紋理了诚欠,并且默認(rèn)上模型會有光照效果。
最后看看效果吧漾岳,其實效果已經(jīng)在最開始已經(jīng)看過了轰绵,我們再看看

綁定紋理

https://github.com/changhaismile/OpenGLDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市尼荆,隨后出現(xiàn)的幾起案子左腔,更是在濱河造成了極大的恐慌,老刑警劉巖捅儒,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件液样,死亡現(xiàn)場離奇詭異振亮,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蓄愁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門双炕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人撮抓,你說我怎么就攤上這事∫》妫” “怎么了丹拯?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荸恕。 經(jīng)常有香客問我乖酬,道長,這世上最難降的妖魔是什么融求? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任咬像,我火速辦了婚禮,結(jié)果婚禮上生宛,老公的妹妹穿的比我還像新娘县昂。我一直安慰自己,他們只是感情好陷舅,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布倒彰。 她就那樣靜靜地躺著,像睡著了一般莱睁。 火紅的嫁衣襯著肌膚如雪待讳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天仰剿,我揣著相機(jī)與錄音创淡,去河邊找鬼。 笑死南吮,一個胖子當(dāng)著我的面吹牛琳彩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播旨袒,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼汁针,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了砚尽?” 一聲冷哼從身側(cè)響起施无,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎必孤,沒想到半個月后猾骡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瑞躺,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年兴想,在試婚紗的時候發(fā)現(xiàn)自己被綠了幢哨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡嫂便,死狀恐怖捞镰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情毙替,我是刑警寧澤岸售,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站厂画,受9級特大地震影響凸丸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袱院,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一屎慢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧忽洛,春花似錦腻惠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苍在,卻和暖如春绝页,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寂恬。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工续誉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人初肉。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓酷鸦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親牙咏。 傳聞我的和親對象是個殘疾皇子臼隔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

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

  • 轉(zhuǎn)載請注明出處:【huachao1001的簡書:http://www.reibang.com/users/0a7e...
    huachao1001閱讀 6,058評論 6 22
  • 版本記錄 前言 OpenGL 圖形庫項目中一直也沒用過,最近也想學(xué)著使用這個圖形庫妄壶,感覺還是很有意思摔握,也就自然想著...
    刀客傳奇閱讀 8,808評論 0 8
  • http://blog.csdn.net/wangdingqiaoit/article/details/51457...
    jerryhigh閱讀 5,314評論 0 8
  • 紋理(Textures) 我們已經(jīng)了解到,我們可以為每個頂點(diǎn)使用顏色來增加圖形的細(xì)節(jié)丁寄,從而創(chuàng)建出有趣的圖像氨淌。但是通...
    IceMJ閱讀 5,626評論 2 13
  • 一泊愧、紋理基礎(chǔ) 3D圖形渲染中最基本的操作就是對一個表面應(yīng)用紋理。紋理可以表現(xiàn)只從網(wǎng)格的幾何形狀無法得到的附加細(xì)節(jié)盛正。...
    cain_huang閱讀 8,715評論 0 7