1.Android增強(qiáng)現(xiàn)實(shí)(一)-AR的三種方式(展示篇)
2.Android增強(qiáng)現(xiàn)實(shí)(二)-支持拖拽控制進(jìn)度和伸縮的VrGifView
3.Android增強(qiáng)現(xiàn)實(shí)(三)-3D模型展示器
前言
前段時(shí)間研究了一下增強(qiáng)現(xiàn)實(shí)在Android端的實(shí)現(xiàn)随常,目前大體分為兩種,全景立體圖(GIF和全景圖)和3D模型圖豆混。這篇博客主要講一下關(guān)于3D模型的展示方式吧映砖。
使用方式
1.Add it in your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the dependency
dependencies {
compile 'com.github.sdfdzx:VRShow:v1.0.2'
}
XML and Java
<com.study.xuan.stlshow.widget.STLView
android:id="@+id/stl"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
java
//讀取STL文件類
STLViewBuilder
.init(STLView stlView)
.Reader(ISTLReader reader)
.Byte(byte[] bytes)
.File(File file)
.Assets(Context context, String fileName)
.InputStream(InputStream inputStream)
.build();
//基礎(chǔ)使用方法
STLViewBuilder.init(mStl).Assets(this, "bai.stl").build();
mStl.setTouch(true);//是否可以觸摸
mStl.setScale(true);//是否可以縮放
mStl.setRotate(true);//是否可以拖拽
mStl.setSensor(true);//是否支持陀螺儀
//stl文件讀取過程中的回調(diào)
mStl.setOnReadCallBack(new OnReadCallBack() {
@Override
public void onStart() {}
@Override
public void onReading(int cur, int total) {}
@Override
public void onFinish() {}
});
技術(shù)分析
對(duì)于3D模型的渲染其實(shí)對(duì)于平常的應(yīng)用平臺(tái)其實(shí)涉及的還是比較少的往扔,在游戲平臺(tái)應(yīng)用廣泛颠悬,我無意中在京東看到過這樣的功能
起先我平常對(duì)于這種效果接觸的比較少,還不太清楚怎么實(shí)現(xiàn)力穗,后來才了解到關(guān)于OpenGL的相關(guān)知識(shí)才了解到這種實(shí)現(xiàn)方式其實(shí)是利用OpenGL和GLSurfaceView進(jìn)行實(shí)現(xiàn)佑惠。
大概了解了實(shí)現(xiàn)可行性朋腋,我們來看一下需求:
1.支持渲染3D模型
2.支持單指拖拽
3.支持雙指縮放
4.支持陀螺儀
5.支持讀取時(shí)的異步回調(diào)
對(duì)于這個(gè)的實(shí)現(xiàn)方式首先要了解這幾個(gè)知識(shí)點(diǎn):
1.3D模型,STL文件格式
2.OpenGL相關(guān)知識(shí)
3.GLSurfaceView的使用
3D模型膜楷,STL文件格式
其實(shí)對(duì)于3D模型的渲染旭咽,這里其實(shí)要明白的就是我們要做的就是兩步:
1.3D模型數(shù)據(jù)文件->模型數(shù)據(jù)(異步讀取文件過程)
2.模型數(shù)據(jù)->模型展示(渲染展示過程)
這里只涉及STL文件格式的3D模型數(shù)據(jù),不同的文件格式赌厅,讀取文件的格式也不一樣穷绵,我目前就實(shí)現(xiàn)了STL文件格式的,那么問題來了特愿,何為STL文件仲墨,為什么要了解STL文件勾缭?
我們其實(shí)沒必要了解那么深入,這里引入百度百科的介紹其實(shí)已經(jīng)夠我們進(jìn)行了解目养;
STL是用三角網(wǎng)格來表現(xiàn)3D CAD模型的一種文件格式俩由。
可能這樣我們理解還是比較困難,那么再加一張圖
上圖可以看到是一個(gè)由STL文件描述的貓癌蚁,就是由一個(gè)個(gè)小的三角形構(gòu)成的幻梯,所以說STL描述的就是構(gòu)成這個(gè)3D模型所用的所有的三角形的相關(guān)數(shù)據(jù)。
那么我們就需要了解一下STL文件是怎么描述三角形數(shù)據(jù)的努释。
STL文件分為兩種格式碘梢,一種是ASCII明碼格式,另一種是二進(jìn)制格式伐蒂。
ASCII明碼格式:(以下引自百度百科)
ASCII碼格式的STL文件逐行給出三角面片的幾何信息煞躬,每一行以1個(gè)或2個(gè)關(guān)鍵字開頭。
在STL文件中的三角面片的信息單元 facet 是一個(gè)帶矢量方向的三角面片逸邦,STL三維模型就是由一系列這樣的三角面片構(gòu)成恩沛。
整個(gè)STL文件的首行給出了文件路徑及文件名。
在一個(gè) STL文件中缕减,每一個(gè)facet由7 行數(shù)據(jù)組成复唤,
facet normal 是三角面片指向?qū)嶓w外部的法矢量坐標(biāo),
outer loop 說明隨后的3行數(shù)據(jù)分別是三角面片的3個(gè)頂點(diǎn)坐標(biāo)烛卧,3頂點(diǎn)沿指向?qū)嶓w外部的法矢量方向逆時(shí)針排列。
明碼://字符段意義
solidfilenamestl//文件路徑及文件名
facetnormalxyz//三角面片法向量的3個(gè)分量值
outerloop
vertexxyz//三角面片第一個(gè)頂點(diǎn)坐標(biāo)
vertexxyz//三角面片第二個(gè)頂點(diǎn)坐標(biāo)
vertexxyz//三角面片第三個(gè)頂點(diǎn)坐標(biāo)
endloop
endfacet//完成一個(gè)三角面片定義
......//其他facet
endsolidfilenamestl//整個(gè)STL文件定義結(jié)束
看到上面的介紹,其實(shí)不難發(fā)現(xiàn),其實(shí)對(duì)于ASCII碼格式的STL文件我們需要怎么讀取哪妓局?其實(shí)很簡單总放,有固定的字段表示文件的開始和結(jié)束,有固定的字段表示一個(gè)三角的開始和結(jié)束好爬,固定每個(gè)三角形由7行數(shù)據(jù)構(gòu)成局雄,固定每一行表示的含義,這所有的都是固定的存炮,一個(gè)for循環(huán)炬搭,按照文件的格式讀取即可。
二進(jìn)制格式:(以下引自百度百科)
二進(jìn)制STL文件用固定的字節(jié)數(shù)來給出三角面片的幾何信息穆桂。
文件起始的80個(gè)字節(jié)是文件頭宫盔,用于存貯文件名;
緊接著用 4 個(gè)字節(jié)的整數(shù)來描述模型的三角面片個(gè)數(shù)享完,
后面逐個(gè)給出每個(gè)三角面片的幾何信息灼芭。每個(gè)三角面片占用固定的50個(gè)字節(jié),依次是:
3個(gè)4字節(jié)浮點(diǎn)數(shù)(角面片的法矢量)
3個(gè)4字節(jié)浮點(diǎn)數(shù)(1個(gè)頂點(diǎn)的坐標(biāo))
3個(gè)4字節(jié)浮點(diǎn)數(shù)(2個(gè)頂點(diǎn)的坐標(biāo))
3個(gè)4字節(jié)浮點(diǎn)數(shù)(3個(gè)頂點(diǎn)的坐標(biāo))個(gè)
三角面片的最后2個(gè)字節(jié)用來描述三角面片的屬性信息般又。
一個(gè)完整二進(jìn)制STL文件的大小為三角形面片數(shù)乘以 50再加上84個(gè)字節(jié)彼绷。
UINT8//Header//文件頭
UINT32//Numberoftriangles//三角面片數(shù)量
//foreachtriangle(每個(gè)三角面片中)
REAL32[3]//Normalvector//法線矢量
REAL32[3]//Vertex1//頂點(diǎn)1坐標(biāo)
REAL32[3]//Vertex2//頂點(diǎn)2坐標(biāo)
REAL32[3]//Vertex3//頂點(diǎn)3坐標(biāo)
UINT16//Attributebytecountend//文件屬性統(tǒng)計(jì)
其實(shí)讀取方法和上面的相似巍佑,只不過上面的是操作文件的行,這里就是操作字節(jié)數(shù)了寄悯,可以看到每個(gè)三角面占用的字節(jié)數(shù)固定萤衰,固定的字節(jié)數(shù)內(nèi)數(shù)據(jù)依次占用固定的字節(jié)數(shù),所以還是一個(gè)for循環(huán)猜旬,按照字節(jié)的格式讀取即可脆栋。
OpenGL相關(guān)知識(shí)
OpenGL的相關(guān)知識(shí)怎么說哪,很多渲染過程中的相關(guān)api我也沒搞懂昔馋,這里只說幾個(gè)我們實(shí)現(xiàn)過程中需要了解的吧(具體網(wǎng)上資料很多筹吐,這方面我反正是個(gè)小白,就不充胖子了)秘遏。
1.glTranslatef(x,y,z)
2.glRotatef(angle,x,y,z)
3.glScalef(x,y,z)
看到字面意思就很好理解吧丘薛,平移,旋轉(zhuǎn)邦危,縮放洋侨,有api就好說了,剩下的就是我們將我們觸摸得到的量轉(zhuǎn)化成這里面的數(shù)值就行倦蚪。
GLSurfaceView的使用
GLSurfaceView是Android一個(gè)專門處理3D模型的的View希坚,他的基本用法和平常的View沒什么差異,唯一需要注意的就是需要調(diào)用setRenderer()
傳入一個(gè)Renderer
對(duì)象陵且。理解起來也比較容易裁僧,GLSurfaceView其實(shí)就是一個(gè)View,也就是一個(gè)展示的視圖,而控制展示的也就是Renderer
對(duì)象了慕购。Renderer其實(shí)是一個(gè)接口聊疲,對(duì)應(yīng)有三個(gè)方法需要我們實(shí)現(xiàn),onSurfaceCreated對(duì)應(yīng)視圖創(chuàng)建時(shí)調(diào)用沪悲,onSurfaceChanged對(duì)應(yīng)視圖改變時(shí)調(diào)用获洲,onDrawFrame對(duì)應(yīng)視圖繪制時(shí)調(diào)用。
public interface Renderer {
void onSurfaceCreated(GL10 gl, EGLConfig config);
void onSurfaceChanged(GL10 gl, int width, int height);
void onDrawFrame(GL10 gl);
}
對(duì)應(yīng)配合上面OpenGL的相關(guān)知識(shí)殿如,其實(shí)大概的實(shí)現(xiàn)過程已經(jīng)有個(gè)雛形了贡珊。
關(guān)鍵代碼
1.讀取STL文件(這里以ASCII格式為例)
這里我是定義了一個(gè)讀取的接口ISTLReader
public interface ISTLReader {
public STLModel parserBinStl(byte[] bytes);
public STLModel parserAsciiStl(byte[] bytes);
public void setCallBack(OnReadListener listener);
}
可以通過STLViewBuilder.Reader(ISTLReader reader)方法自己實(shí)現(xiàn)。
我默認(rèn)實(shí)現(xiàn)的STLReader這里只放上對(duì)于ASCII格式文件讀取的偽代碼吧涉馁。
public STLModel parserAsciiStl(byte[] bytes) {
...
String stlText = new String(bytes);
String[] stlLines = stlText.split("\n");
vertext_size = (stlLines.length - 2) / 7;
...
for (int i = 0; i < stlLines.length; i++) {
String string = stlLines[i].trim();
if (string.startsWith("facet normal ")) {
string = string.replaceFirst("facet normal ", "");
String[] normalValue = string.split(" ");
for (int n = 0; n < 3; n++) {
...
}
}
if (string.startsWith("vertex ")) {
string = string.replaceFirst("vertex ", "");
String[] vertexValue = string.split(" ");
...
}
...
}
...
}
這里可以看到我是將byte[]轉(zhuǎn)為了String门岔,接著就通過固定的格式來進(jìn)行讀取,偽代碼在上面烤送,便于理解讀取過程固歪,可以看到,基本的就是通過對(duì)行數(shù),startsWith牢裳,split等對(duì)字符串處理的函數(shù)進(jìn)行讀取的逢防,讀取規(guī)則其實(shí)可以仿照上面對(duì)于STL文件格式的介紹。
2.自定義Renderer渲染
自定義Renderer其實(shí)主要是對(duì)于OPenGL函數(shù)的調(diào)用蒲讯,由于我對(duì)于這塊也不是特別了解忘朝,我是在別人的基礎(chǔ)上進(jìn)行了一定的修改,里面參數(shù)的修改影響的就是渲染效果判帮。而對(duì)于我們要實(shí)現(xiàn)的關(guān)于旋轉(zhuǎn)縮放的函數(shù)其實(shí)就比較基礎(chǔ)了,這里我還加入了關(guān)于縮放范圍的控制局嘁。
gl.glRotatef(angleX, 0, 1, 0);
gl.glRotatef(angleY, 1, 0, 0);
gl.glPopMatrix();
scale_rember = scale_now * scale;
if (scaleRange) {
if (scale_rember > SCALE_MAX) {
scale_rember = SCALE_MAX;
}
if (scale_rember < SCALE_MIN) {
scale_rember = SCALE_MIN;
}
}
gl.glScalef(scale_rember, scale_rember, scale_rember);
3.手勢監(jiān)聽
其實(shí)對(duì)于縮放和旋轉(zhuǎn)的處理和前一篇Android增強(qiáng)現(xiàn)實(shí)(二)-支持拖拽控制進(jìn)度和伸縮的VrGifView的處理大同小異,具體大家可以看前一篇博客晦墙,而對(duì)于陀螺儀的處理其實(shí)也比較簡單悦昵,只不過用的比較少所以比較陌生,步驟也不比較固定晌畅。
private void initSensor() {
sensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
gyroscopeSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
sensorEventListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
if (timestamp != 0) {
final float dT = (sensorEvent.timestamp - timestamp) * NS2S;
stlRenderer.angleX += sensorEvent.values[0] * dT * 180.0f % 360.0f;
stlRenderer.angleY += sensorEvent.values[1] * dT * 180.0f % 360.0f;
stlRenderer.requestRedraw();
requestRender();
}
timestamp = sensorEvent.timestamp;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
sensorManager.registerListener(sensorEventListener, gyroscopeSensor, SensorManager
.SENSOR_DELAY_GAME);
}
總結(jié)
其實(shí)相較于前一篇的對(duì)于GIF圖的處理但指,這里技術(shù)上的考慮不是特別多,主要是對(duì)于3D文件STL格式的學(xué)習(xí)抗楔,OpenGL基礎(chǔ)知識(shí)的學(xué)習(xí)棋凳,還有陀螺儀傳感器使用的學(xué)習(xí)。
這里對(duì)于STL文件的讀取還有Renderer中OpenGL的使用參考學(xué)習(xí)了以下資料连躏,大家感興趣的可以去查看學(xué)習(xí):
1.一個(gè)不錯(cuò)的STL解析器剩岳,支持貼紋理,坐標(biāo)系等
2.Android OpenGL入門系列入热,一個(gè)不錯(cuò)的系列入門文章
3.Android OpenGL入門系列