Android OpenGLES2.0(十六)——3D模型貼圖及光照處理(obj+mtl)
在Android OpenGLES2.0(十四)——Obj格式3D模型載入中實現了Obj格式的3D模型的載入,載入的是一個沒有貼圖,沒有光照處理的帽子,為了呈現出立體效果,“手動”加了光照,擁有貼圖的紋理及光照又該怎麼載入呢?
模型檔案
本篇部落格例子中載入的是一個卡通形象皮卡丘,資源是在網上隨便找的一個。載入出來如圖所示:
obj內容格式如下:
# Wavefront OBJ file
# Exported by Misfit Model 3D 1.3.8
# Thu Sep 27 20:02:52 2012
mtllib pikachu.mtl
# 191 Vertices
v 34.493484 75.31411 -39.308891
v 27.34606 45.516556 -47.155548
#...省略若干行
vt 0.859513 0.676464
vt 0.769048 0.0597
#...省略若干行
vn -0.068504 -0.433852 -0.898376
vn 0.422088 -0.855411 -0.30019
#...省略若干行
usemtl pikagen
o DrawCall_25
g DrawCall_25
f 2/1/1 1/2/2 3/3/3
f 1/4/4 2/5/5 4/6/6
#...省略若干行
usemtl pikagen
o DrawCall_262
g DrawCall_262
f 2/58/58 3/59/59 17/60/60
f 2/61/61 17/62/62 6/63/63
#...省略若干行
mtl檔案內容格式如下:
# Material file for pikachu.obj
newmtl eye
Ns 0
d 1
illum 2
Kd 0.8 0.8 0.8
Ks 0.0 0.0 0.0
Ka 0.2 0.2 0.2
map_Kd eye1.png
newmtl mouth
Ns 0
d 1
illum 2
Kd 0.8 0.8 0.8
Ks 0.0 0.0 0.0
Ka 0.2 0.2 0.2
map_Kd mouth1.png
newmtl pikagen
Ns 0
d 1
illum 2
Kd 0.8 0.8 0.8
Ks 0.0 0.0 0.0
Ka 0.2 0.2 0.2
map_Kd pikagen.png
關於Obj的內容格式,在上篇部落格中已經做了總結,本篇部落格中使用的obj,可以看到f後面的不再跟的是4個數字,而是f 2/58/58 3/59/59 17/60/60
這種樣子的三組數,每一組都表示為頂點座標索引/貼圖座標點索引/頂點法線索引,三個頂點組成一個三角形。而頭部的mtllib pikachu.mtl
則指明使用的材質庫。
而mtl格式檔案中,主要資料型別為:
newmtl name #name為材質名稱
Ns exponent #exponent指定材質的反射指數,定義了反射高光度
Ka r g b #環境光反射,g和b兩引數是可選的,如果只指定了r的值,則g和b的值都等於r的值
Kd r g b #漫反射
Ks r g b #鏡面光反射
# Ka Kd Ks 都還有其他兩種格式,可查閱其他資料:
#Kd spectral file.rfl factor
#Kd xyz x y z
map_Kd picture.png #固有紋理貼圖
map_Ka picture1.png #陰影紋理貼圖
map_Ks picture2.png #高光紋理貼圖
illum 2 #光照模型
#光照模型屬性如下:
#0. 色彩開,陰影色關
#1. 色彩開,陰影色開
#2. 高光開
#3. 反射開,光線追蹤開
#4. 透明: 玻璃開 反射:光線追蹤開
#5. 反射:菲涅爾衍射開,光線追蹤開
#6. 透明:折射開 反射:菲涅爾衍射關,光線追蹤開
#7. 透明:折射開 反射:菲涅爾衍射開,光線追蹤開
#8. 反射開,光線追蹤關
#9. 透明: 玻璃開 反射:光線追蹤關
#10. 投射陰影於不可見表面
模型及貼圖載入
模型載入和之前的模型載入大同小異,不同的是,這次我們需要將模型的貼圖座標、頂點法線也一起載入,並傳入到shader中。其他引數,有的自然也要取到。
模型載入以obj檔案為入口,解析obj檔案,從中獲取到mtl檔案相對路徑,然後解析mtl檔案。將材質庫拆分為諸多的單一材質。obj物件的 載入,根據具使用材質不同來分解為多個3D模型。具體載入過程如下:
建立儲存單個材質的類
public class MtlInfo {
//還有其他相關資訊,需要的時候一起新增進來
public String newmtl;
public float[] Ka=new float[3]; //陰影色
public float[] Kd=new float[3]; //固有色
public float[] Ks=new float[3]; //高光色
public float[] Ke=new float[3]; //
public float Ns; //shininess
public String map_Kd; //固有紋理貼圖
public String map_Ks; //高光紋理貼圖
public String map_Ka; //陰影紋理貼圖
//denotes the illumination model used by the material.
// illum = 1 indicates a flat material with no specular highlights,
// so the value of Ks is not used.
// illum = 2 denotes the presence of specular highlights,
// and so a specification for Ks is required.
public int illum;
}
建立儲存擁有單一材質的3D物件的類
public class Obj3D {
public FloatBuffer vert;
public int vertCount;
public FloatBuffer vertNorl;
public FloatBuffer vertTexture;
public MtlInfo mtl;
private ArrayList<Float> tempVert;
private ArrayList<Float> tempVertNorl;
public ArrayList<Float> tempVertTexture;
public int textureSMode;
public int textureTMode;
public void addVert(float d){
if(tempVert==null){
tempVert=new ArrayList<>();
}
tempVert.add(d);
}
public void addVertTexture(float d){
if(tempVertTexture==null){
tempVertTexture=new ArrayList<>();
}
tempVertTexture.add(d);
}
public void addVertNorl(float d){
if(tempVertNorl==null){
tempVertNorl=new ArrayList<>();
}
tempVertNorl.add(d);
}
public void dataLock(){
if(tempVert!=null){
setVert(tempVert);
tempVert.clear();
tempVert=null;
}
if(tempVertTexture!=null){
setVertTexture(tempVertTexture);
tempVertTexture.clear();
tempVertTexture=null;
}
if(tempVertNorl!=null){
setVertNorl(tempVertNorl);
tempVertNorl.clear();
tempVertNorl=null;
}
}
public void setVert(ArrayList<Float> data){
int size=data.size();
ByteBuffer buffer=ByteBuffer.allocateDirect(size*4);
buffer.order(ByteOrder.nativeOrder());
vert=buffer.asFloatBuffer();
for (int i=0;i<size;i++){
vert.put(data.get(i));
}
vert.position(0);
vertCount=size/3;
}
public void setVertNorl(ArrayList<Float> data){
int size=data.size();
ByteBuffer buffer=ByteBuffer.allocateDirect(size*4);
buffer.order(ByteOrder.nativeOrder());
vertNorl=buffer.asFloatBuffer();
for (int i=0;i<size;i++){
vertNorl.put(data.get(i));
}
vertNorl.position(0);
}
public void setVertTexture(ArrayList<Float> data){
int size=data.size();
ByteBuffer buffer=ByteBuffer.allocateDirect(size*4);
buffer.order(ByteOrder.nativeOrder());
vertTexture=buffer.asFloatBuffer();
for (int i=0;i<size;){
vertTexture.put(data.get(i));
i++;
vertTexture.put(data.get(i));
i++;
}
vertTexture.position(0);
}
}
實現材質庫的解析方法
public static HashMap<String,MtlInfo> readMtl(InputStream stream){
HashMap<String,MtlInfo> map=new HashMap<>();
try{
InputStreamReader isr=new InputStreamReader(stream);
BufferedReader br=new BufferedReader(isr);
String temps;
MtlInfo mtlInfo=new MtlInfo();
while((temps=br.readLine())!=null)
{
String[] tempsa=temps.split("[ ]+");
switch (tempsa[0].trim()){
case "newmtl": //材質
mtlInfo=new MtlInfo();
mtlInfo.newmtl=tempsa[1];
map.put(tempsa[1],mtlInfo);
break;
case "illum": //光照模型
mtlInfo.illum=Integer.parseInt(tempsa[1]);
break;
case "Kd":
read(tempsa,mtlInfo.Kd);
break;
case "Ka":
read(tempsa,mtlInfo.Ka);
break;
case "Ke":
read(tempsa,mtlInfo.Ke);
break;
case "Ks":
read(tempsa,mtlInfo.Ks);
break;
case "Ns":
mtlInfo.Ns=Float.parseFloat(tempsa[1]);
case "map_Kd":
mtlInfo.map_Kd=tempsa[1];
break;
}
}
}catch (Exception e){
e.printStackTrace();
}
return map;
}
private static void read(String[] value,ArrayList<Float> list){
for (int i=1;i<value.length;i++){
list.add(Float.parseFloat(value[i]));
}
}
private static void read(String[] value,float[] fv){
for (int i=1;i<value.length&&i<fv.length+1;i++){
fv[i-1]=Float.parseFloat(value[i]);
}
}
實現3D物件拆分解析的方法
public static List<Obj3D> readMultiObj(Context context,String file){
boolean isAssets;
ArrayList<Obj3D> data=new ArrayList<>();
ArrayList<Float> oVs=new ArrayList<Float>();//原始頂點座標列表
ArrayList<Float> oVNs=new ArrayList<>(); //原始頂點法線列表
ArrayList<Float> oVTs=new ArrayList<>(); //原始貼圖座標列表
HashMap<String,MtlInfo> mTls=null;
HashMap<String,Obj3D> mObjs=new HashMap<>();
Obj3D nowObj=null;
MtlInfo nowMtl=null;
try{
String parent;
InputStream inputStream;
if (file.startsWith("assets/")){
isAssets=true;
String path=file.substring(7);
parent=path.substring(0,path.lastIndexOf("/")+1);
inputStream=context.getAssets().open(path);
Log.e("obj",parent);
}else{
isAssets=false;
parent=file.substring(0,file.lastIndexOf("/")+1);
inputStream=new FileInputStream(file);
}
InputStreamReader isr=new InputStreamReader(inputStream);
BufferedReader br=new BufferedReader(isr);
String temps;
while((temps=br.readLine())!=null){
if("".equals(temps)){
}else{
String[] tempsa=temps.split("[ ]+");
switch (tempsa[0].trim()){
case "mtllib": //材質
InputStream stream;
if (isAssets){
stream=context.getAssets().open(parent+tempsa[1]);
}else{
stream=new FileInputStream(parent+tempsa[1]);
}
mTls=readMtl(stream);
break;
case "usemtl": //採用紋理
if(mTls!=null){
nowMtl=mTls.get(tempsa[1]);
}
if(mObjs.containsKey(tempsa[1])){
nowObj=mObjs.get(tempsa[1]);
}else{
nowObj=new Obj3D();
nowObj.mtl=nowMtl;
mObjs.put(tempsa[1],nowObj);
}
break;
case "v": //原始頂點
read(tempsa,oVs);
break;
case "vn": //原始頂點法線
read(tempsa,oVNs);
break;
case "vt":
read(tempsa,oVTs);
break;
case "f":
for (int i=1;i<tempsa.length;i++){
String[] fs=tempsa[i].split("/");
int index;
if(fs.length>0){
//頂點索引
index=Integer.parseInt(fs[0])-1;
nowObj.addVert(oVs.get(index*3));
nowObj.addVert(oVs.get(index*3+1));
nowObj.addVert(oVs.get(index*3+2));
}
if(fs.length>1){
//貼圖
index=Integer.parseInt(fs[1])-1;
nowObj.addVertTexture(oVTs.get(index*2));
nowObj.addVertTexture(oVTs.get(index*2+1));
}
if(fs.length>2){
//法線索引
index=Integer.parseInt(fs[2])-1;
nowObj.addVertNorl(oVNs.get(index*3));
nowObj.addVertNorl(oVNs.get(index*3+1));
nowObj.addVertNorl(oVNs.get(index*3+2));
}
}
break;
}
}
}
}catch (Exception e){
e.printStackTrace();
}
for (Map.Entry<String, Obj3D> stringObj3DEntry : mObjs.entrySet()) {
Obj3D obj = stringObj3DEntry.getValue();
data.add(obj);
obj.dataLock();
}
return data;
}
頂點著色器及片元著色器
頂點著色器
attribute vec3 vPosition;
attribute vec2 vCoord;
uniform mat4 vMatrix;
uniform vec3 vKa;
uniform vec3 vKd;
uniform vec3 vKs;
varying vec2 textureCoordinate;
attribute vec3 vNormal; //法向量
varying vec4 vDiffuse; //用於傳遞給片元著色器的散射光最終強度
varying vec4 vAmbient; //用於傳遞給片元著色器的環境光最終強度
varying vec4 vSpecular; //用於傳遞給片元著色器的鏡面光最終強度
void main(){
gl_Position = vMatrix*vec4(vPosition,1);
textureCoordinate = vCoord;
vec3 lightLocation=vec3(0.0,-200.0,-500.0); //光照位置
vec3 camera=vec3(0,200.0,0);
float shininess=10.0; //粗糙度,越小越光滑
vec3 newNormal=normalize((vMatrix*vec4(vNormal+vPosition,1)).xyz-(vMatrix*vec4(vPosition,1)).xyz);
vec3 vp=normalize(lightLocation-(vMatrix*vec4(vPosition,1)).xyz);
vDiffuse=vec4(vKd,1.0)*max(0.0,dot(newNormal,vp)); //計算散射光的最終強度
vec3 eye= normalize(camera-(vMatrix*vec4(vPosition,1)).xyz);
vec3 halfVector=normalize(vp+eye); //求視線與光線的半向量
float nDotViewHalfVector=dot(newNormal,halfVector); //法線與半向量的點積
float powerFactor=max(0.0,pow(nDotViewHalfVector,shininess)); //鏡面反射光強度因子
vSpecular=vec4(vKs,1.0)*powerFactor; //計算鏡面光的最終強度
vAmbient=vec4(vKa,1.0);
}
片元著色器
precision mediump float;
varying vec2 textureCoordinate;
uniform sampler2D vTexture;
varying vec4 vDiffuse; //接收從頂點著色器過來的散射光分量
varying vec4 vAmbient; //接收傳遞給片元著色器的環境光分量
varying vec4 vSpecular; //接收傳遞給片元著色器的鏡面光分量
void main() {
vec4 finalColor=texture2D(vTexture,textureCoordinate);
gl_FragColor=finalColor*vAmbient+finalColor*vSpecular+finalColor*vDiffuse;
}
啟動載入及渲染
完成了以上準備工作,就可以呼叫readMultiObj
方法,將obj檔案讀成一個或多個帶有各項引數的3D模型類,然後將每一個3D模型的引數傳入shader中,進而進行渲染:
List<Obj3D> model=ObjReader.readMultiObj(this,"assets/3dres/pikachu.obj");
List<ObjFilter2> filters=new ArrayList<>();
for (int i=0;i<model.size();i++){
ObjFilter2 f=new ObjFilter2(getResources());
f.setObj3D(model.get(i));
filters.add(f);
}
mGLView.setRenderer(new GLSurfaceView.Renderer() {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
for (ObjFilter2 f:filters){
f.create();
}
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
for (ObjFilter2 f:filters){
f.onSizeChanged(width, height);
float[] matrix= Gl2Utils.getOriginalMatrix();
Matrix.translateM(matrix,0,0,-0.3f,0);
Matrix.scaleM(matrix,0,0.008f,0.008f*width/height,0.008f);
f.setMatrix(matrix);
}
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
for (ObjFilter2 f:filters){
Matrix.rotateM(f.getMatrix(),0,0.3f,0,1,0);
f.draw();
}
}
});
mGLView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
OK,至此大功告成。
原始碼
所有的程式碼全部在一個專案中,託管在Github上——Android OpenGLES 2.0系列部落格的Demo
歡迎轉載,轉載請保留文章出處。湖廣午王的部落格[http://blog.csdn.net/junzia/article/details/58272305]
相關文章
- 圖片拼貼處理軟體Posterino for macMac
- Phong光照模型、Blinn-phong光照模型歸納模型
- 基於 HTML5 Canvas 的 3D 模型貼圖問題HTMLCanvas3D模型
- [computer graphics]簡單光照模型(Phong和Blinn-Phong)和明暗處理模型
- 傳統光照模型模型
- 圖片上傳及圖片處理
- Android圖片處理(Matrix,ColorMatrix)AndroidColorMatrix
- Android 圖片快取處理Android快取
- android 圓角圖片 處理Android
- Android 圖片高斯模糊處理Android
- WidsMob HDR Mac(光照渲染效果照片處理器)Mac
- WidsMob HDR Mac 光照渲染效果照片處理器Mac
- Android ImageView 圖片靠右,靠左處理AndroidView
- 使用SVG做模型貼圖的思路SVG模型
- android簡單的圖形特效處理Android特效
- WebGL多模型光照綜合例項Web模型
- android圖片處理,讓圖片變成圓形Android
- Android 中的轉場動畫及相容處理Android動畫
- PostgreSQL在何處處理 sql查詢之六十六SQL
- OpenGL 4.0 GLSL 採用平行光照模型模型
- Lambert漫反射光照模型歸納反射模型
- 【Filament】自定義Blinn Phong光照模型模型
- Android影象處理 - 高斯模糊的原理及實現Android
- Android中OpenGL濾鏡和RenderScript圖片處理Android
- Android處理圖片OOM的若干方法小結AndroidOOM
- 轉貼:ORA-04031故障分析處理
- android處理webserviceAndroidWeb
- python圖片處理類之~PIL.Image模組(ios android icon圖示自動生成處理)PythoniOSAndroid
- SpringMVC處理模型資料SpringMVC模型
- Android圖片處理:識別影象方向並顯示Android
- 「技美之路 第08篇」圖形 2.4 傳統經驗光照模型詳解模型
- Excel表格中的3D圖表簡介和圖表資料系列的處理方法Excel3D
- webpack圖片處理Web
- Thumbnailator處理圖片AI
- iOS 圖片處理iOS
- Unity3D 的物理渲染和光照模型Unity3D模型
- WebGL自學課程(12):光照模型與渲染方式Web模型
- Android高德地圖貼合圖片完成手繪地圖展示Android地圖