3d建模 android - CSDN
精华内容
参与话题
  • Android加载三维模型

    千次阅读 2019-02-22 16:09:17
    这几天在Android项目中需要加载三维模型,找了多种方法,最后决定使用jpct引擎。话不多说,上代码。 一、代码解析 首先创建一个活动MainActivity,活动布局如下: 包括五个button,一个ImageView作为背景,一个...

    这几天在Android项目中需要加载三维模型,找了多种方法,最后决定使用jpct引擎。话不多说,上代码。

    一、代码解析

    首先创建一个活动MainActivity,活动布局如下:

    包括五个button,一个ImageView作为背景,一个GLSurfaceView显示模型,布局代码如下:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/screen"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/colorBg"/>
        <android.opengl.GLSurfaceView
            android:id="@+id/surfaceView"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="180dip"
            android:orientation="vertical">
        <Button
            android:id="@+id/btnLoadModel"
            android:layout_width="match_parent"
            android:layout_height="60dip"
            android:text="loadModel"/>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="60dip"
                android:orientation="horizontal">
                <Button
                    android:id="@+id/btnLeft"
                    android:layout_width="180dip"
                    android:layout_height="60dip"
                    android:text="left"/>
                <Button
                    android:id="@+id/btnRight"
                    android:layout_width="180dip"
                    android:layout_height="60dip"
                    android:text="right"/>
            </LinearLayout>
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="60dip"
                android:orientation="horizontal">
                <Button
                    android:id="@+id/btnTop"
                    android:layout_width="180dip"
                    android:layout_height="60dip"
                    android:text="top"/>
                <Button
                    android:id="@+id/btnDown"
                    android:layout_width="180dip"
                    android:layout_height="60dip"
                    android:text="down"/>
            </LinearLayout>
        </LinearLayout>
    
    </RelativeLayout>

    新建RenderView.class用于加载模型和显示处理:(如果有Unity或者其他游戏引擎的开发经验的话,下面的代码非常好理解。)

    public class RenderView implements GLSurfaceView.Renderer{
    
        public World myWorld;
        private FrameBuffer frameBuffer;
        private Object3D selectedObj;
    
        public RenderView(Context context) {
            myWorld = new World();
            myWorld.setAmbientLight(25, 25, 25);
    
            Light light = new Light(myWorld);
            light.setIntensity(250, 250, 250);
            light.setPosition(new SimpleVector(0, 0, -15));
    
            Camera cam = myWorld.getCamera();
            cam.setFOVLimits(0.1f,2.0f);
            cam.setFOV(1.08f);
            cam.setYFOV(1.92f);
            cam.setClippingPlanes(0f,2000f);
            System.out.println(cam.getFOV());
            System.out.println(cam.getYFOV());
            System.out.println(cam.getPosition());
            String[] names=Config.getParameterNames();
            for(String i:names){
                System.out.println(i);
            }
    
        }
    
        public void onSurfaceChanged(GL10 gl, int w, int h) {
            if (frameBuffer != null) {
                frameBuffer.dispose();
            }
            frameBuffer = new FrameBuffer(gl, w, h);
        }
    
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            gl.glClearColor(1.0f,1.0f,1.0f,0.3f);
        }
    
        public void onDrawFrame(GL10 gl) {
            frameBuffer.clear(Color.TRANSPARENT);
            myWorld.renderScene(frameBuffer);
            myWorld.draw(frameBuffer);
    
            frameBuffer.display();
        }
    
    
        public void addObject(Context context) {
            Object3D newObject = null;
            try {
                createTextures(context);
                Object3D[] objectsArray2 = Loader.loadOBJ(context.getResources().getAssets().open("policecar.obj"), context.getResources()
                        .getAssets().open("policecar.mtl"), 1f);
                newObject = Object3D.mergeAll(objectsArray2);
                newObject.setTexture("policecar_texture");
                newObject.setOrigin(new SimpleVector(0, 0, 300));
                newObject.rotateZ(3.1415926f);
                newObject.setName("policecar.obj");
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            newObject.strip();
            newObject.build();
            Message msg=new Message();
            msg.what=MainActivity.MSG_LOAD_MODEL_SUC;
            msg.obj=newObject;
            MainActivity.handler.sendMessage(msg);
            selectedObj=newObject;
        }
        public void applyTranslation(float incX, float incY, float incZ) {
            if (this.selectedObj != null) {
    
                SimpleVector objOrigin = this.selectedObj.getOrigin();
                SimpleVector currentPoition=this.selectedObj.getTransformedCenter();
                System.out.println(currentPoition);
    
                this.selectedObj.translate(incX, incY, incZ);
            }
        }
        private void createTextures(Context context) {
    
            Bitmap bitmap=BitmapFactory.decodeResource(context.getResources(),R.mipmap.policecar);
            Texture texture = new Texture(bitmap);
            if(!TextureManager.getInstance().containsTexture("policecar_texture")){
                TextureManager.getInstance().addTexture("policecar_texture", texture);
            }
        }
    }

     myWorld是当前的场景,所有的物体都在当前场景中,FrameBuffer是显示缓存,selectedObj是当前选择的物体。下面我们来详细解释这些代码。

    public RenderView(Context context) {
            myWorld = new World();
            myWorld.setAmbientLight(25, 25, 25);
    
            Light light = new Light(myWorld);
            light.setIntensity(250, 250, 250);
            light.setPosition(new SimpleVector(0, 0, -15));
    
            Camera cam = myWorld.getCamera();
            cam.setFOVLimits(0.1f,2.0f);
            cam.setFOV(1.08f);
            cam.setYFOV(1.92f);
            cam.setClippingPlanes(0f,2000f);
            System.out.println(cam.getFOV());
            System.out.println(cam.getYFOV());
            System.out.println(cam.getPosition());
            String[] names=Config.getParameterNames();
            for(String i:names){
                System.out.println(i);
            }
    
        }

    RenderView是类的 构造函数,定义了场景中的一些关键要素,例如摄像机,光照等。myWorld.setAmbientLight设置全局光,Light是当前场景中的光线设置,light.setIntensity设置光照强度,light.setPosition设置光源位置,此处光源是点光源。cam是场景中的摄像机,cam.setFOVLimits设置摄像机的视场角范围,此处设置为0.1-2.0。摄像机的X和Y方向的视场角可以分别设置。cam.setClippingPlanes设置相机的视距,此处为0-2000。

    public void onSurfaceChanged(GL10 gl, int w, int h) {
            if (frameBuffer != null) {
                frameBuffer.dispose();
            }
            frameBuffer = new FrameBuffer(gl, w, h);
        }
    
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            gl.glClearColor(1.0f,1.0f,1.0f,0.3f);
        }
    
        public void onDrawFrame(GL10 gl) {
            frameBuffer.clear(Color.TRANSPARENT);
            myWorld.renderScene(frameBuffer);
            myWorld.draw(frameBuffer);
    
            frameBuffer.display();
        }

    这几个函数是用于定义GLSurfaceView创建时和刷新时的函数,gl.glClearColor定义了SurfaceView刷新颜色。frameBuffer.clear定义了frameBuffer刷新颜色。

    public void addObject(Context context) {
            Object3D newObject = null;
            try {
                createTextures(context);
                Object3D[] objectsArray2 = Loader.loadOBJ(context.getResources().getAssets().open("policecar.obj"), context.getResources()
                        .getAssets().open("policecar.mtl"), 1f);
                newObject = Object3D.mergeAll(objectsArray2);
                newObject.setTexture("policecar_texture");
                newObject.setOrigin(new SimpleVector(0, 0, 300));
                newObject.rotateZ(3.1415926f);
                newObject.setName("policecar.obj");
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            newObject.strip();
            newObject.build();
            Message msg=new Message();
            msg.what=MainActivity.MSG_LOAD_MODEL_SUC;
            msg.obj=newObject;
            MainActivity.handler.sendMessage(msg);
            selectedObj=newObject;
        }

    addObject用于向场景中添加物体,loadOBJ函数采用的是二进制流的形式加载模型数据。加载OBJ格式的模型需要三个文件,分别是模型.obj,模型.mtl以及模型的贴图文件,Obj文件是模型的面信息,mtl文件定义了模型贴图与面位置信息。

    public void applyTranslation(float incX, float incY, float incZ) {
            if (this.selectedObj != null) {
    
                SimpleVector objOrigin = this.selectedObj.getOrigin();
                SimpleVector currentPoition=this.selectedObj.getTransformedCenter();
                System.out.println(currentPoition);
    
                this.selectedObj.translate(incX, incY, incZ);
            }
        }

    此函数用于模型的位移,其中object.translate()函数定义了模型的位移,translate将会将模型位移到当前世界坐标分别加上函数参数之后的坐标。也就是新的坐标=旧坐标+参数。getTransfromedCenter获取当前物体的当前世界坐标。、

     

    下面解析MainActivity中的代码

    public class MainActivity extends AppCompatActivity{
        public final static int MSG_LOAD_MODEL_SUC=0;
    
    
        private GLSurfaceView myGLView;
        private RenderView myRenderer;
        private Button btnLoad;
        private Button btnLeft;
        private Button btnRight;
        private Button btnTop;
        private Button btnDown;
    
        private Thread threadLoadModel;
        public static Handler handler;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            handler=new Handler(){
                @Override
                public void handleMessage(Message msg){
                    switch (msg.what){
                        case MSG_LOAD_MODEL_SUC:
                            Toast.makeText(MainActivity.this, "模型加载成功", Toast.LENGTH_SHORT).show();
                            Object3D object3D=(Object3D) msg.obj;
                            myRenderer.myWorld.addObject(object3D);
                            break;
                    }
                }
            };
            btnLoad=findViewById(R.id.btnLoadModel);
            btnLeft=findViewById(R.id.btnLeft);
            btnRight=findViewById(R.id.btnRight);
            btnTop=findViewById(R.id.btnTop);
            btnDown=findViewById(R.id.btnDown);
    
            btnLoad.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this, "开始加载模型", Toast.LENGTH_SHORT).show();
                    threadLoadModel.start();
                }
            });
            btnLeft.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    myRenderer.applyTranslation(-10,0,0);
                }
            });
            btnRight.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    myRenderer.applyTranslation(10,0,0);
                }
            });
            btnTop.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    myRenderer.applyTranslation(0,-10,0);
                }
            });
    
            btnDown.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    myRenderer.applyTranslation(0,10,0);
                }
            });
    
            myGLView = (GLSurfaceView) this.findViewById(R.id.surfaceView);
            myGLView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
            myGLView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
            myGLView.setZOrderOnTop(true);
            myRenderer = new RenderView(this);
            myGLView.setRenderer(myRenderer);
    
            threadLoadModel=new Thread(new Runnable() {
                @Override
                public void run() {
                    myRenderer.addObject(MainActivity.this);
                }
            });
    
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            myGLView.onPause();
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            myGLView.onResume();
        }
    }

    有关View的代码就不多做解析。具体解释一下模型的加载和GLView的设置。

    为了使加载模型的过程中程序不堵塞住,开辟一个子线程进行模型解析。模型解析之后,向主线程发送消息,提醒主线程将模型添加到场景中,即myRenderer.myWorld.addObjct()。同时也为了避免模型过大导致加载过程失败导致的程序崩溃。

    myGLView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
            myGLView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
            myGLView.setZOrderOnTop(true);

    这段代码将GLSurfaceView的刷新设置为透明并将View设置到所有View的最顶层。

    二、jpct的世界坐标系

    jpct中的正世界坐标系格式如下,以GlSurfaceView中心为原点,垂直于屏幕向内为z轴正方向,平行于屏幕向右为X轴正方向,向下为Y轴正方向,与Android设备的屏幕xy正方向相一致。

     jpct下载地址:http://www.jpct.net/

    项目下载地址:https://download.csdn.net/download/njnutzhou/10968296

    展开全文
  • Android 3d 球形建模

    千次阅读 2012-06-26 18:20:36
    opengl里的对球形的建模:首先要把球形表式由点组成的模型。现在的问题是:怎么把这个球用点集进行表示。 代码如下: Java代码 final int UNIT_SIZE=10000;  ArrayList alVertix=new ArrayList...

    opengl里的对球形的建模:首先要把球形表式由点组成的模型。现在的问题是:怎么把这个球用点集进行表示。

    代码如下:

    Java代码 复制代码 收藏代码
    1. final int UNIT_SIZE=10000
    2. ArrayList<Integer> alVertix=new ArrayList<Integer>();//存放顶点坐标的ArrayList 
    3. final int angleSpan=18;//将球进行单位切分的角度 
    4.    for(int vAngle=-90;vAngle<=90;vAngle=vAngle+angleSpan){//垂直方向angleSpan度一份 
    5.     for(int hAngle=0;hAngle<360;hAngle=hAngle+angleSpan)//水平方向angleSpan度一份 
    6.     {//纵向横向各到一个角度后计算对应的此点在球面上的坐标 
    7.         double xozLength=scale*UNIT_SIZE*Math.cos(Math.toRadians(vAngle)); 
    8.         int x=(int)(xozLength*Math.cos(Math.toRadians(hAngle))); 
    9.         int z=(int)(xozLength*Math.sin(Math.toRadians(hAngle))); 
    10.         int y=(int)(scale*UNIT_SIZE*Math.sin(Math.toRadians(vAngle))); 
    11.         //将计算出来的XYZ坐标加入存放顶点坐标的ArrayList 
    12.         alVertix.add(x);alVertix.add(y);alVertix.add(z); 
    13.     } 
    14.    }     
        	final int UNIT_SIZE=10000;
        	ArrayList<Integer> alVertix=new ArrayList<Integer>();//存放顶点坐标的ArrayList
        	final int angleSpan=18;//将球进行单位切分的角度
            for(int vAngle=-90;vAngle<=90;vAngle=vAngle+angleSpan){//垂直方向angleSpan度一份
            	for(int hAngle=0;hAngle<360;hAngle=hAngle+angleSpan)//水平方向angleSpan度一份
            	{//纵向横向各到一个角度后计算对应的此点在球面上的坐标
            		double xozLength=scale*UNIT_SIZE*Math.cos(Math.toRadians(vAngle));
            		int x=(int)(xozLength*Math.cos(Math.toRadians(hAngle)));
            		int z=(int)(xozLength*Math.sin(Math.toRadians(hAngle)));
            		int y=(int)(scale*UNIT_SIZE*Math.sin(Math.toRadians(vAngle)));
            		//将计算出来的XYZ坐标加入存放顶点坐标的ArrayList
            		alVertix.add(x);alVertix.add(y);alVertix.add(z);
            	}
            } 	

    讲解:

    1. 首先把球想成由很多的半径不一样的圆所组成的。把球想像成由很多平形于x,z平面的圆所组成的。

    2. x,z轴的圆的角度是由0-360,y轴的变动由-90到90。

    3. 在java里,三角函数要用弧度计算,而我们平实一般都是角度来计算。其实不管三角函数用的是角度和弧度,其都是度量的是角的大小,所以对于同一个角,其三角函数的值都是一样的(不管是用角度计算,还是用弧度计算)。所以sin(x)就不要注重其x是角度还是弧度,只要关心,其是由对边/斜边(前题是直角三角形)。

    4. 如果对于计算球上每个点的坐标,不太理解,可以在笛卡尔级坐标系里,画一个点,再把这个点进行映射到三个坐标轴上去,就可以了。

    注:角度和弧度的理解:http://hi.baidu.com/kent_edwin/blog/item/9425f0029e06967e3812bb28.html


    展开全文
  • Android 3D模型展示

    千次阅读 2018-09-19 14:12:11
    开发工具:Unity3d 2017.3 Android Studio 3. Visual studio 2017 开发语言 : C# , java   3D场景,本人做的是一个简单的3D海岛场景,里面有山脉,椰子树,草 岛周围是海,海水可以动,虽然效果不明显。 ...

    开发工具:Unity3d 2017.3       Android Studio 3.       Visual studio 2017

    开发语言 : C#  , java

     

    3D场景,本人做的是一个简单的3D海岛场景,里面有山脉,椰子树,草 岛周围是海,海水可以动,虽然效果不明显。

    下面是单指拖动,双指放大缩小的脚本代码

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class effect2 : MonoBehaviour {

            private float speed = 0.1f;
            private int isforward;//标记摄像机的移动方向
            //记录两个手指的旧位置
            private Vector2 oposition1 = new Vector2();
            private Vector2 oposition2 = new Vector2();

            Vector2 m_screenPos = new Vector2(); //记录手指触碰的位置

            //用于判断是否放大
            bool isEnlarge(Vector2 oP1, Vector2 oP2, Vector2 nP1, Vector2 nP2)
            {
                //函数传入上一次触摸两点的位置与本次触摸两点的位置计算出用户的手势
                float leng1 = Mathf.Sqrt((oP1.x - oP2.x) * (oP1.x - oP2.x) + (oP1.y - oP2.y) * (oP1.y - oP2.y));
                float leng2 = Mathf.Sqrt((nP1.x - nP2.x) * (nP1.x - nP2.x) + (nP1.y - nP2.y) * (nP1.y - nP2.y));
                if (leng1 < leng2)
                {
                    //放大手势
                    return true;
                }
                else
                {
                    //缩小手势
                    return false;
                }
            }

            void Start()
            {
                Input.multiTouchEnabled = true;//开启多点触碰
            }

            void Update()
            {
                if (Input.touchCount <= 0)
                    return;
                if (Input.touchCount == 1) //单点触碰移动摄像机
                {
                    if (Input.touches[0].phase == TouchPhase.Began)
                        m_screenPos = Input.touches[0].position;   //记录手指刚触碰的位置
                    if (Input.touches[0].phase == TouchPhase.Moved) //手指在屏幕上移动,移动摄像机
                    {
                        transform.Translate(new Vector3(Input.touches[0].deltaPosition.x * Time.deltaTime, Input.touches[0].deltaPosition.y * Time.deltaTime, 0));
                    }
                }

                else if (Input.touchCount > 1)//多点触碰
                {
                    //记录两个手指的位置
                    Vector2 nposition1 = new Vector2();
                    Vector2 nposition2 = new Vector2();

                    //记录手指的每帧移动距离
                    Vector2 deltaDis1 = new Vector2();
                    Vector2 deltaDis2 = new Vector2();

                    for (int i = 0; i < 2; i++)
                    {
                        Touch touch = Input.touches[i];
                        if (touch.phase == TouchPhase.Ended)
                            break;
                        if (touch.phase == TouchPhase.Moved) //手指在移动
                        {

                            if (i == 0)
                            {
                                nposition1 = touch.position;
                                deltaDis1 = touch.deltaPosition;
                            }
                            else
                            {
                                nposition2 = touch.position;
                                deltaDis2 = touch.deltaPosition;

                                if (isEnlarge(oposition1, oposition2, nposition1, nposition2)) //判断手势伸缩从而进行摄像机前后移动参数缩放效果
                                    isforward = 1;
                                else
                                    isforward = -1;
                            }
                            //记录旧的触摸位置
                            oposition1 = nposition1;
                            oposition2 = nposition2;
                        }
                        //移动摄像机
                        Camera.main.transform.Translate(isforward * Vector3.forward * Time.deltaTime * (Mathf.Abs(deltaDis2.x + deltaDis1.x) + Mathf.Abs(deltaDis1.y + deltaDis2.y)));
                    }
                }
            }
    }

     

    下面是效果截图:

     

     

     

     

     

    下面是本人的github链接,整个工程都在里面,有兴趣的读者可以自行下载:

    https://github.com/jiang-congcong/3D-Scene

    展开全文
  • Android OpenGLES2.0(十四)——Obj格式3D模型加载

    万次阅读 热门讨论 2017-01-10 02:10:52
    自然是通过其他工具类似于Maya、3DMax等3D建模工具,做好模型导出来,然后用OpenGLES加载导出的模型文件。模型的加载大同小异,本篇博客是以Obj格式的3D模型为例。 模型文件 本篇博客例子中加载的是一个帽子,

    在博主《OpenGLES系列》文章中,最开始的几篇讲的就是OpenGL世界中各种形体的构建,但是那些形体都是规则的简单形体,遇到复杂的形体,比如说一个人、一朵花,怎么办呢?自然是通过其他工具类似于Maya、3DMax等3D建模工具,做好模型导出来,然后用OpenGLES加载导出的模型文件。模型的加载大同小异,本篇博客是以Obj格式的3D模型为例。

    模型文件

    本篇博客例子中加载的是一个帽子,资源是在网上随便找的一个。加载出来如图所示:
    这里写图片描述
    格式如下:

    # File exported by ZBrush version 4.2
    # www.zbrush.com
    #Vertex Count 4898
    #Face Count 4848
    #Auto scale x=0.211538 y=0.211538 z=0.211538
    #Auto offset x=-0.000000 y=-0.412507 z=-0.000000
    v -0.62500745 3.93329608 0.0000001
    v -0.00002446 3.32622414 1.33471741
    v 1.47657442 2.55452877 1.37523436
    v -1.01934254 3.90772931 0.00000007
    ...省略若干行...
    g default
    f 990 991 987 986
    f 991 874 873 987
    f 972 971 991 990
    f 971 55 874 991
    f 987 992 988 986
    ...省略若干行

    加载这个模型文件前,我们需要先知道这些数据代表的是什么。针对这个文件,#号开头的,是描述模型文件的相关信息。以v开头的,表示的是顶点坐标。以f开头的,表示一个面,后面跟的四个值是索引。一个v,后面的三个数,代表一个点的xyz,4个点组成了一个四边形。
    为什么是4个点?不是说在OpenGLES中基本几何是三角形么?这样问就有点尴尬了,因为模型文件是我在网上随便下的,自己选的模型,跪着也要加载出来。有什么关系,一个四边形不就是两个三角形么。
    这个模型文件只有v、f两类数据,但是一个炫酷的模型,往往是包含很多数据的,主要的数据类型如下:

    1. 顶点数据(Vertex data):

      • v 几何体顶点(Geometric vertices)
      • vt 贴图坐标点(Texture vertices)
      • vn 顶点法线(Vertex normals)
      • vp 参数空格顶点 (Parameter space vertices)
    2. 自由形态曲线(Free-form curve)/表面属性(surface attributes):

      • deg 度(Degree)
      • bmat 基础矩阵(Basis matrix)
      • step 步尺寸(Step size)
      • cstype 曲线或表面类型 (Curve or surface type)
    3. 元素(Elements):

      • p 点(Point)
      • l 线(Line)
      • f 面(Face)
      • curv 曲线(Curve)
      • curv2 2D曲线(2D curve)
      • surf 表面(Surface)
    4. 自由形态曲线(Free-form curve)/表面主体陈述(surface body statements):

      • parm 参数值(Parameter values )
      • trim 外部修剪循环(Outer trimming loop)
      • hole 内部整修循环(Inner trimming loop)
      • scrv 特殊曲线(Special curve)
      • sp 特殊的点(Special point)
      • end 结束陈述(End statement)
    5. 自由形态表面之间的连接(Connectivity between free-form surfaces):

      • con 连接 (Connect)
    6. 成组(Grouping):

      • g 组名称(Group name)
      • s 光滑组(Smoothing group)
      • mg 合并组(Merging group)
      • o 对象名称(Object name)
    7. 显示(Display)/渲染属性(render attributes):

      • bevel 导角插值(Bevel interpolation)
      • c_interp 颜色插值(Color interpolation)
      • d_interp 溶解插值(Dissolve interpolation)
      • lod 细节层次(Level of detail)
      • usemtl 材质名称(Material name)
      • mtllib 材质库(Material library)
      • shadow_obj 投射阴影(Shadow casting)
      • trace_obj 光线跟踪(Ray tracing)
      • ctech 曲线近似技术(Curve approximation technique)
      • stech 表面近似技术 (Surface approximation technique)

    模型加载

    知道了模型文件的内容和格式,加载起来就不是什么问题了:

    public class ObjReader {
    
        public static void read(InputStream stream,Obj3D obj3D){
            ArrayList<Float> alv=new ArrayList<Float>();//原始顶点坐标列表
            ArrayList<Float> alvResult=new ArrayList<Float>();//结果顶点坐标列表
            ArrayList<Float> norlArr=new ArrayList<>();
            float[] ab=new float[3],bc=new float[3],norl=new float[3];
            try{
                InputStreamReader isr=new InputStreamReader(stream);
                BufferedReader br=new BufferedReader(isr);
                String temps=null;
                while((temps=br.readLine())!=null)
                {
                    String[] tempsa=temps.split("[ ]+");
                    if(tempsa[0].trim().equals("v")) {//此行为顶点坐标
                        alv.add(Float.parseFloat(tempsa[1]));
                        alv.add(Float.parseFloat(tempsa[2]));
                        alv.add(Float.parseFloat(tempsa[3]));
                    }  else if(tempsa[0].trim().equals("f")) {//此行为三角形面
                        int a=Integer.parseInt(tempsa[1])-1;
                        int b=Integer.parseInt(tempsa[2])-1;
                        int c=Integer.parseInt(tempsa[3])-1;
                        int d=Integer.parseInt(tempsa[4])-1;
                        //abc和acd两个三角形组成的四边形
    
                        alvResult.add(alv.get(a*3));
                        alvResult.add(alv.get(a*3+1));
                        alvResult.add(alv.get(a*3+2));
                        alvResult.add(alv.get(b*3));
                        alvResult.add(alv.get(b*3+1));
                        alvResult.add(alv.get(b*3+2));
                        alvResult.add(alv.get(c*3));
                        alvResult.add(alv.get(c*3+1));
                        alvResult.add(alv.get(c*3+2));
    
                        alvResult.add(alv.get(a*3));
                        alvResult.add(alv.get(a*3+1));
                        alvResult.add(alv.get(a*3+2));
                        alvResult.add(alv.get(c*3));
                        alvResult.add(alv.get(c*3+1));
                        alvResult.add(alv.get(c*3+2));
                        alvResult.add(alv.get(d*3));
                        alvResult.add(alv.get(d*3+1));
                        alvResult.add(alv.get(d*3+2));
    
                        //这里也是因为下载模型文件的坑。下了个出了顶点和面啥也没有的模型文件
                        //为了有3d效果,给它加个光照,自己计算顶点法线
                        //用面法向量策略。按理说点法向量更适合这种光滑的3D模型,但是计算起来太复杂了,so
                        //既然主要讲3D模型加载,就先用面法向量策略来吧
                        //通常3D模型里面会包含法向量信息的。
                        //法向量的计算,ABC三个空间点,他们的法向量为向量AB与向量BC的外积,所以有:
                        for (int i=0;i<3;i++){
                            ab[i]=alv.get(a*3+i)-alv.get(b*3+i);
                            bc[i]=alv.get(b*3+i)-alv.get(c*3+i);
                        }
                        norl[0]=ab[1]*bc[2]-ab[2]*bc[1];
                        norl[1]=ab[2]*bc[0]-ab[0]*bc[2];
                        norl[2]=ab[0]*bc[1]-ab[1]*bc[0];
    
                        //上面两个三角形,传入了6个顶点,这里循环6次,简单粗暴
                        for (int i=0;i<6;i++){
                            norlArr.add(norl[0]);
                            norlArr.add(norl[1]);
                            norlArr.add(norl[2]);
                        }
                    }
                }
    
                //这些就是比较熟悉的了,一切都为了能够把数据给GPU
                int size=alvResult.size();
                float[] vXYZ=new float[size];
                for(int i=0;i<size;i++){
                    vXYZ[i]=alvResult.get(i);
                }
                ByteBuffer byteBuffer=ByteBuffer.allocateDirect(4*size);
                byteBuffer.order(ByteOrder.nativeOrder());
                obj3D.vert=byteBuffer.asFloatBuffer();
                obj3D.vert.put(vXYZ);
                obj3D.vert.position(0);
                obj3D.vertCount=size/3;
                int vbSize=norlArr.size();
                float[] vbArr=new float[size];
                for(int i=0;i<size;i++){
                    vbArr[i]=norlArr.get(i);
                }
                ByteBuffer vb=ByteBuffer.allocateDirect(4*vbSize);
                vb.order(ByteOrder.nativeOrder());
                obj3D.vertNorl=vb.asFloatBuffer();
                obj3D.vertNorl.put(vbArr);
                obj3D.vertNorl.position(0);
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    
        public static class Obj3D{
            public FloatBuffer vert;
            public int vertCount;
            public FloatBuffer vertNorl;
        }
    }

    模型渲染

    模型的渲染,和之前绘制各种形体也差不多了,往GPU传数据就不用说了,为了让3D模型呈现出立体效果,示例中,增加了简单而不靠谱的光照。所以看得出来,虽然加载出来有立体效果,但是能看到比较明显的网格。当然,光照不是本篇博客的重点,在后续博客里面再详细讨论下光照的问题。
    顶点Shader为:

    attribute vec3 vPosition;
    attribute vec2 vCoord;
    uniform mat4 vMatrix;
    
    varying vec2 textureCoordinate;
    
    attribute vec3 vNormal;         //法向量
    varying vec4 vDiffuse;          //用于传递给片元着色器的散射光最终强度
    
    
    //返回散射光强度
    vec4 pointLight(vec3 normal,vec3 lightLocation,vec4 lightDiffuse){
        //变换后的法向量
        vec3 newTarget=normalize((vMatrix*vec4(normal+vPosition,1)).xyz-(vMatrix*vec4(vPosition,1)).xyz);
        //表面点与光源的方向向量
        vec3 vp=normalize(lightLocation-(vMatrix*vec4(vPosition,1)).xyz);
        return lightDiffuse*max(0.0,dot(newTarget,vp));
    }
    
    void main(){
        gl_Position = vMatrix*vec4(vPosition,1);
        textureCoordinate = vCoord;
    
       vec4 at=vec4(1.0,1.0,1.0,1.0);   //光照强度
       vec3 pos=vec3(50.0,200.0,50.0);      //光照位置
       vDiffuse=pointLight(vNormal,pos,at);
    }

    片元Shader:

    precision mediump float;
    varying vec2 textureCoordinate;
    uniform sampler2D vTexture;
    varying vec4 vDiffuse;//接收从顶点着色器过来的散射光分量
    void main() {
        vec4 finalColor=vec4(1.0);
        //给此片元颜色值
        gl_FragColor=finalColor*vDiffuse+finalColor*vec4(0.15,0.15,0.15,1.0);
    }

    着色器中散射光强度的计算,是根据散射光的公式来的,光照公式在Android OpenGLES2.0(一)——了解OpenGLES2.0光照中有讲到。

    编译着色器,linkProgram,传入从Obj文件读取的值,然后和渲染一个立方体一样,渲染出模型就OK了。

    源码

    所有的代码全部在一个项目中,托管在Github上——Android OpenGLES 2.0系列博客的Demo


    欢迎转载,转载请保留文章出处。湖广午王的博客[http://blog.csdn.net/junzia/article/details/54300202]


    展开全文
  • 8款超级好用的3D建模软件上下篇

    千次阅读 2020-05-14 12:52:52
    做出一个好作品,不但要靠自身的技巧水平,选择适合自己的3D建模软件也是一个很重要的因素。 所以小编今天就给大家安利8款好用的3D建模软件。无论你是一个0基础的初阶学者,还是想提升自己水平的进阶从业者/爱好者...
  • 3D建模操作详细步骤

    2020-07-06 17:25:01
    第一步:3D建模是学3D的关键,也是初学者最重要的一步,首先要在CAD中将平面图画出来,然后保存,下一步需要。如图,以客厅为例  第二步:将保存好的CAD图导入到3D中,确保放在(0,0)座标附近,不然会造成后续的...
  • 一个基于OpenGL ES的简单易用的3D模型展示框架。自动分类解析STL、OBJ、3DS等模型文件,支持对模型进行旋转和缩放等操作。
  • 另外,如果你想面部运动或进一步的操作,那么最好使用专业的3D建模服务,并呈现艺术家对人脸三维建模。除了使用自定义的3D模型,你可以添加其他功能的3D人脸,如特定的情绪,表情,头发,眉毛和其他类似的东西,如...
  • 如今,对于提供沉浸式虚拟现实或增强现实体验的Android... 您准备好花几个月的时间学习如何与3D建模程序(例如Blender或Maya)一起使用吗? 如果不是这样,则应考虑使用Google Poly ,这是一个在线存储库,其中包含...
  • Android OpenGL显示任意3D模型文件

    千次阅读 2019-08-08 06:55:04
    虽然标题是说显示任意3D文件,但是本文主要是以STL格式文件为例。其他的格式本质上都是一样的,只是解析部分的代码不同而已。接下来我们开始学习~ 1 STL文件 它是标准的3D文件格式,一般3D打印机都是支持打印STL...
  •  今天要给大家介绍的是如何实现可旋转的汽车3D模型。  先看实现效果  这只是静态图,实际上,这个模型是可以根据手势进行旋转的,效果还可以。  下面我说一下实现的原理。首先,这种3D模型的旋转效果...
  • Android OpenGL ES显示3D模型

    千次阅读 2016-12-14 09:34:12
    这篇文章我们来来看如何将一个STL文件显示出来,把STL文件显示出来,那么我们就可以显示任意的3D模型了。下面是显示一把狙击枪的效果图:  什么是STL文件网上的解释是这样的:.stl 文件是在计算机图形应用系统中,...
  • 有个需求,在Android手机上进行人体穴位的查找,用到3d人体模型,这模型是用unity3d做比较好,还是用其他的呢,因为需要的是要对模型可以控制放大缩小旋转,这点我查过用OpenGL ES渲染应该可以做出来,也试过demo。...
  • 这是opengl es 2.0的演示。它基本上是一个android应用程序,带有一个3d引擎,可以加载wavefront obj、stl和dae文件。
  • Android下实现STL模型3D渲染

    千次阅读 2018-10-15 21:49:37
    Android下实现STL模型3D渲染 STL文件 STL文件是在计算机图形应用系统中,用于表示三角形网格的一种文件格式,是最多快速原型系统所应用的标准文件类型。 STL文件有两种:一种是ASCII明码格式,另一种是二进制...
  • 请问各位大神,怎么在android工程中展示3d模型,并且能够控制它,我原先是想用u3d做的,不过u3d好像都是做游戏比较多,而且打开的时候比较慢,还会有u3d的logo,控制模型运动好像也比较麻烦,除了用u3d之外还有没有...
  • 3D开发-模型创建

    2019-06-15 17:33:25
    3D建模可以使用3D建模软件建模贴图纹理。3D/CAD软件有PROE、AutodeskCAD、UG、Solidworks、KeyShot等建模软件。 PhotoScan 淘宝商家代做 www.taobao.com CAD建模软件 ...
  • 近年来随着三维成像技术的发展,3D人脸建模并不算是一件新鲜事,iphone-X,华为mate20 PRO,都可以进行三维人脸重建,但是以上两台设备都是主要依靠3D模组来进行三维人脸重建的,我们有没办法通过普...
  • 3D绘制中进行的纹理贴图也很简单,与设置顶点颜色的步骤相似,只要三步,步骤如下: Step1:设置启用贴图坐标数组; Step2:设置贴图坐标的数组信息; Step3:调用GL10的glBindTexture(int target, int texture...
  • 最新版本:Android高级进阶十二 在Android上使用3D 引擎(JPCT-AE)构建立方体  上面好多文章都是介绍使用Android原生opengl接口的,使用起来很是麻烦,代码量冗余臃肿,所以walfred设想是否有几款封装好了的...
1 2 3 4 5 ... 20
收藏数 3,604
精华内容 1,441
热门标签
关键字:

3d建模 android