精华内容
下载资源
问答
  • 今天,我们将学习如何使用Blender生成OBJ文件,以及如何使用javascript将数据解析为WebGL可以呈现的内容。 我们将首先解析一个多维数据集,然后再尝试一个动漫风格的海盗女孩模型。 ObjLoader源码 class ObjLoader{ ...

    作者: DSLMing
    时间: 2019.10.18

    参考
    FunWithWebGL2 011-Parsing.OBJ

    解析.OBJ文件

    今天,我们将学习如何使用Blender生成OBJ文件,以及如何使用javascript将数据解析为WebGL可以呈现的内容。 我们将首先解析一个多维数据集,然后再尝试一个动漫风格的海盗女孩模型。

    ObjLoader源码

    class ObjLoader{
    	static domToMesh(meshName,elmID,flipYUV){
    		var d = ObjLoader.parseFromDom(elmID,flipYUV);
    		return gl.fCreateMeshVAO(meshName,d[0],d[1],d[2],d[3],3);
    	}
    
    	static parseFromDom(elmID,flipYUV){ return ObjLoader.parseObjText(document.getElementById(elmID).innerHTML,flipYUV); }
    
    	static parseObjText(txt,flipYUV){
    		txt = txt.trim() + "\n"; //add newline to be able to access last line in the for loop
    
        // Line text from obj file,文本的每一行
        let line
        //Line split into an array,一行被分割的若干元素由数组表示
        let item
        // Itm split into an array, used for faced decoding,将其拆分为数组,用于面对式解码
    		let	ary
    		let i
        // 用于计算缓存数组的索引
        let ind
        // 确定人脸是否为四边形
    		let	isQuad = false
        // Cache Dictionary key = itm array element, val = final index of the vertice
        // 高速缓存字典键= itm数组元素,val =顶点的最终索引
        let aCache = []
        // 从obj读取的缓存顶点数组
    		let	cVert = []
        // 从obj读取的缓存法线数组
    		let	cNorm = []
        // Cache UV array
    		let cUV = []
        // 最终索引排序顶点数组
    		let fVert = []
        // 最终的法线数组
    		let	fNorm = []
        // Final Index Sorted UV array
    		let fUV = []
        // Final Sorted index array
    		let fIndex = []
        // Final count of unique vertices
    		let	fIndexCnt = 0
    		let posA = 0
    		let posB = txt.indexOf("\n",0);
    
    		while(posB > posA){
    			line = txt.substring(posA,posB).trim();
    			switch(line.charAt(0)){
    				//......................................................
    				// Cache Vertex Data for Index processing when going through face data
    				// Sample Data (x,y,z)
    				// v -1.000000 1.000000 1.000000
    				// vt 0.000000 0.666667
    				// vn 0.000000 0.000000 -1.000000
    				case "v":
    					itm = line.split(" "); itm.shift();
    					switch(line.charAt(1)){
    						case " ": cVert.push(parseFloat(itm[0]) , parseFloat(itm[1]) , parseFloat(itm[2]) ); break;		//VERTEX
    						case "t": cUV.push( parseFloat(itm[0]) , parseFloat(itm[1]) );	break;							//UV
    						case "n": cNorm.push( parseFloat(itm[0]) , parseFloat(itm[1]) , parseFloat(itm[2]) ); break;	//NORMAL
    					}
    				break;
    
    				//......................................................
    				// Process face data
    				// 所有索引值都从1开始,但是javascript数组索引从0开始。因此需要始终从索引中减去1来进行匹配
    				// Sample Data [Vertex Index, UV Index, Normal Index], Each line is a triangle or quad.
    				// 样品数据[顶点索引, 纹理索引, 法线信息],每条线是三角形或四边形。
    				// f 1/1/1 2/2/1 3/3/1 4/4/1
    				// f 34/41/36 34/41/35 34/41/36
    				// f 34//36 34//35 34//36
    				case "f":
    					itm = line.split(" ");
    					itm.shift();
    					isQuad = false;
    
    					for(i=0; i < itm.length; i++){
    						//--------------------------------
    						//In the event the face is a quad
    						if(i == 3 && !isQuad){
    							i = 2; //Last vertex in the first triangle is the start of the 2nd triangle in a quad.
    							isQuad = true;
    						}
    
    						//--------------------------------
    						//Has this vertex data been processed?
    						if(itm[i] in aCache){
    							fIndex.push( aCache[itm[i]] ); //it has, add its index to the list.
    						}else{
    							//New Unique vertex data, Process it.
    							ary = itm[i].split("/");
    
    							//Parse Vertex Data and save final version ordred correctly by index
    							ind = (parseInt(ary[0])-1) * 3;
    							fVert.push( cVert[ind] , cVert[ind+1] , cVert[ind+2] );
    
    							//Parse Normal Data and save final version ordered correctly by index
    							ind = (parseInt(ary[2])-1) * 3;
    							fNorm.push( cNorm[ind] , cNorm[ind+1] , cNorm[ind+2] );
    
    							//Parse Texture Data if available and save final version ordered correctly by index
    							if(ary[1] != ""){
    								ind = (parseInt(ary[1])-1) * 2;
    								fUV.push( cUV[ind] ,
    									(!flipYUV)? cUV[ind+1] : 1-cUV[ind+1]
    								);
    							}
    
    							//Cache the vertex item value and its new index.
    							//The idea is to create an index for each unique set of vertex data base on the face data
    							//So when the same item is found, just add the index value without duplicating vertex,normal and texture.
    							aCache[ itm[i] ] = fIndexCnt;
    							fIndex.push(fIndexCnt);
    							fIndexCnt++;
    						}
    
    						//--------------------------------
    						//In a quad, the last vertex of the second triangle is the first vertex in the first triangle.
    						if(i == 3 && isQuad) fIndex.push( aCache[itm[0]] );
    					}
    				break;
    			}
    
    			//Get Ready to parse the next line of the obj data.
    			posA = posB+1;
    			posB = txt.indexOf("\n",posA);
    		}
    		return [fIndex,fVert,fNorm,fUV];
    	}
    }//cls
    
    展开全文
  • Android OpenGL 解析obj文件绘制3D模型

    千次阅读 2019-01-16 10:38:08
    * 解析obj文件时,数据类型由开头的首字母决定 * switch(首字母){ * case "v": * 顶点坐标数据 * break; * case "vn": * 法向量坐标数据 * break; * case "vt": * 纹理坐标数据 * break; * case ...

    绘制3D模型一般我们都会用到OpenGL里面的一些api,看看如何使用这些api绘制3的模型。

    使用OpenGl绘制3D模型我主要用两种方式:

    1:glDrawArrays(int mode, int first,int count)  

    参数1:有三种取值

    1.GL_TRIANGLES:每三个顶之间绘制三角形,之间不连接

    2.GL_TRIANGLE_FAN:以V0V1V2,V0V2V3,V0V3V4,……的形式绘制三角形

    3.GL_TRIANGLE_STRIP:顺序在每三个顶点之间均绘制三角形。这个方法可以保证从相同的方向上所有三角形均被绘制。以V0V1V2,V1V2V3,V2V3V4……的形式绘制三角形

    参数2:从数组缓存中的哪一位开始绘制,一般都定义为0

    参数3:顶点的数量

    2:glDrawElements(int mode,int count,int type,java.nio.Buffer indices)

    mode——指明被渲染的是哪种图元,被允许的符号常量有GL_POINTS,GL_LINE_STRIP,GL_LINE_LOOP,GL_LINES,GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN和GL_TRIANGLES

    count——指明被渲染的元素个数。

    type——指明索引指的类型,不是GL_UNSIGNED_BYTE就是GL_UNSIGNED_SHORT。

    indices——指明存储索引的位置

     

    第一种 glDrawArrays 绘制3D模型,主要分为两大部分。第一步,先将3D模型的3维数组坐标写好。第二步,将GLSurfaceView的渲染器配置好,将坐标数组转为FloatBuffer,直接开始绘制就OK。使用这种方式绘制3D模型,一定要将做表数组写好,坐标数组的元素顺序与后面你使用的绘制类型有关,我建议可以根据obj模型文件来确定数组。我的数组是直接从obj模型文件中提取的。

    /**
     * 解析3D Obj 文件
     * 解析obj文件时,数据类型由开头的首字母决定
     * switch(首字母){
     *     case "v":
     *      顶点坐标数据
     *     break; 
     *     case "vn":
     *      法向量坐标数据
     *     break; 
     *     case "vt":
     *      纹理坐标数据
     *     break; 
     *     case "usemtl":
     *      材质
     *     break; 
     *     case "f":
     *      索引
     *     break;
     * }
     */
    public class ObjLoaderUtil {
        // 存放解析出来的顶点的坐标
        private ArrayList<ObjVertexs> mVertexArrayList;
        //存放解析出来面的索引
        private ArrayList<Integer> mIndexArrayList;
        //存放解析出来的法线坐标
        private ArrayList<ObjVertexs> mNormalArrayList;
        //存放解析出来的法线索引
        private ArrayList<Integer> mIndexNormalArrayList;
        //存放3D模型的顶点坐标数据
        public float[] mSurfaceFloat;
        //存放顶点坐标的floatbuffer
        public FloatBuffer mVertexFloatBuffer;
        //顶点坐标索引buffer
        public FloatBuffer mIndexVertexBuffer;
        //存放法线坐标的floatbuffer
        public FloatBuffer mNormalFloatBuffer;
    
    
        public ObjLoaderUtil() {
            mVertexArrayList = new ArrayList();
            mIndexArrayList = new ArrayList<>();
            mNormalArrayList = new ArrayList<>();
            mIndexNormalArrayList = new ArrayList<>();
        }
    
        /**
         * 将obj文件中的顶点坐标与面索引解析出来,存放集合中
         *
         * @param mContext
         * @throws IOException
         */
        public void load(Context mContext) throws IOException {
            //获取assets文件夹下的obj文件的数据输入流
            InputStream mInputStream = mContext.getClass().getClassLoader().getResourceAsStream("assets/Lion_OBJ.obj");
            InputStreamReader mInputStreamReader = new InputStreamReader(mInputStream);
            BufferedReader mBufferedReader = new BufferedReader(mInputStreamReader);
            String temps = null;
            //利用buffer读取流将obj文件的内容读取出来存放在temps
            while ((temps = mBufferedReader.readLine()) != null) {
                String[] temp = temps.split(" ");
    //            Log.d("tag", "文件信息" + temps);
                if (temp[0].trim().equals("v")) {
                    mVertexArrayList.add(new ObjVertexs(Float.valueOf(temp[1]), Float.valueOf(temp[2]), Float.valueOf(temp[3])));
                }
                if (temp[0].trim().equals("vn")) {
                    mNormalArrayList.add(new ObjVertexs(Float.valueOf(temp[1]), Float.valueOf(temp[2]), Float.valueOf(temp[3])));
                }
    //            如果读取到的首元素时"f",则表示读取到的数据是面索引数据
                if (temp[0].trim().equals("f")) {
                    if (temp[1].indexOf("/") == -1) {
                        for (int i = 1; i < temp.length; i++) {
                            mIndexArrayList.add(Integer.valueOf(temp[i]));
                        }
                    } else {
                        for (int i = 1; i < temp.length; i++) {
                            mIndexArrayList.add(Integer.valueOf(temp[i].split("/")[0]));
                        }
                    }
                }
    
            }
    
            mSurfaceFloat = getSurfaceFloat(mVertexArrayList, mIndexArrayList);
            mVertexFloatBuffer = makeFloatBuffer(mSurfaceFloat);
            mNormalFloatBuffer = makeFloatBuffer(getSurfaceFloat(mNormalArrayList, mIndexNormalArrayList));
        }
    
        /**
         * 将解析出来的顶点与索引组合起来,形成一个新的float数组,用于绘制3D模型,并将其返回
         *
         * @param mObjVertexs 存放坐标点的集合
         * @param mIntegers   存放索引的集合
         * @return
         */
        public float[] getSurfaceFloat(ArrayList<ObjVertexs> mObjVertexs, ArrayList<Integer> mIntegers) {
            float[][] mFloats = new float[mIntegers.size()][3];
            float[] surfaceFloat = new float[mIntegers.size() * 3];
            for (int i = 0; i < mIntegers.size(); i++) {
                mFloats[i][0] = mObjVertexs.get(mIntegers.get(i) - 1).x;
                mFloats[i][1] = mObjVertexs.get(mIntegers.get(i) - 1).y;
                mFloats[i][2] = mObjVertexs.get(mIntegers.get(i) - 1).z;
            }
            int i = 0;
            for (float[] floats : mFloats) {
                for (float v : floats) {
                    surfaceFloat[i++] = v;
                }
            }
            return surfaceFloat;
        }
    
        /**
         * 将存放3D模型的float数组,转换为floatbuffer
         *
         * @param mFloats
         * @return
         */
        public FloatBuffer makeFloatBuffer(float[] mFloats) {
            //为float数组分配缓存空间,一个float大小为4个字节
            ByteBuffer mByteBuffer = ByteBuffer.allocateDirect(mFloats.length * 4);
            //规定缓存区的字节顺序为本机字节顺序
            mByteBuffer.order(ByteOrder.nativeOrder());
            //将bytebuffer转换为floatbuffer
            FloatBuffer loatBuffer = mByteBuffer.asFloatBuffer();
            //将float数组填充到floatbuffe中,完成转换
            loatBuffer.put(mFloats);
            //规定缓存区的起始索引
            loatBuffer.position(0);
            return loatBuffer;
        }
    }
    
    /**
     * 继承GLSurfaceView,实现Renderer接口,开始绘制
     */
    public class GlModeView extends GLSurfaceView implements GLSurfaceView.Renderer, GestureDetector.OnGestureListener {
        //obj文件解析类
        private ObjLoaderUtil mObjLoaderUtil;
        //手势工具类
        private GestureDetectorCompat mCompat;
        //模型沿x,y轴旋转的角度
        private float xrot, yrot;
    
        public GlModeView(Context context, AttributeSet attrs) throws IOException {
            super(context, attrs);
            setRenderer(this);
            mObjLoaderUtil = new ObjLoaderUtil();
            mObjLoaderUtil.load(context);
            mCompat = new GestureDetectorCompat(context, this);
        }
    
        @Override
        public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
            //设置清屏色
            gl10.glClearColor(0.1f, 1f, 1f, 0);
            //用当前光线参数计算顶点颜色。否则仅仅简单将当前颜色与每个顶点关联。
            gl10.glEnable(GL10.GL_LIGHTING);
            //启用服务器端GL功能,允许(apply)引入的颜色与颜色缓冲区中的值进行逻辑运算
            gl10.glEnable(GL10.GL_BLEND);
            //这一行启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。
            gl10.glShadeModel(GL10.GL_SMOOTH);
            //当前活动纹理单元为二维纹理
            gl10.glEnable(GL10.GL_TEXTURE_2D);
            //做深度比较和更新深度缓存。值得注意的是即使深度缓冲区存在并且深度mask不是0,如果深度测试禁用的话,深度缓冲区也无法更新。
            gl10.glEnable(GL10.GL_DEPTH_TEST);
            // 法向量被计算为单位向量
            gl10.glEnable(GL10.GL_NORMALIZE);
            //启用面部剔除功能
            gl10.glEnable(GL10.GL_CULL_FACE);
            //指定哪些面不绘制
            gl10.glCullFace(GL10.GL_BACK);
            //开启定义多边形的正面和背面
            gl10.glEnable(GL10.GL_CULL_FACE);
            //改变每个顶点的散射和环境反射材质,可以使用颜色材质。
            gl10.glEnable(GL10.GL_COLOR_MATERIAL);
            // 开启抗锯齿
            gl10.glEnable(GL10.GL_POINT_SMOOTH);
            gl10.glHint(GL10.GL_POINT_SMOOTH_HINT, GL10.GL_NICEST);
            gl10.glEnable(GL10.GL_LINE_SMOOTH);
            gl10.glHint(GL10.GL_LINE_SMOOTH_HINT, GL10.GL_NICEST);
            gl10.glEnable(GL10.GL_POLYGON_OFFSET_FILL);
            gl10.glHint(GL10.GL_POLYGON_SMOOTH_HINT, GL10.GL_NICEST);
            //定义多边形的正面和背面,在一个完全由不透明的密闭surface组成的场景中,多边形的背面永远不会被看到。剔除这些不能显示出来的面可以加速渲染器渲染图像的时间。
            gl10.glFrontFace(GL10.GL_CCW);
            //指明深度缓冲区的清理值
            gl10.glClearDepthf(1.0f);
            gl10.glDepthFunc(GL10.GL_LEQUAL);
        }
    
        @Override
        public void onSurfaceChanged(GL10 gl10, int i, int i1) {
            //选择投影矩阵
            gl10.glMatrixMode(GL10.GL_PROJECTION);
            gl10.glViewport(0, 0, i, i1);
            gl10.glLoadIdentity();
            float x = (float) i / i1;
            gl10.glFrustumf(-x, x, -1, 1, 5, 200);
            GLU.gluLookAt(gl10, 0, 0, 50, 0, 0, 0, 0, 1, 0);
            //选择模型观察矩阵
            gl10.glMatrixMode(GL10.GL_MODELVIEW);
            gl10.glLoadIdentity();
        }
    
        @Override
        public void onDrawFrame(GL10 gl10) {
            //清除屏幕和深度缓存。
            gl10.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            gl10.glLoadIdentity();
            //设置顶点坐标buffer
            gl10.glVertexPointer(3, GL10.GL_FLOAT, 0,
                    mObjLoaderUtil.mVertexFloatBuffer);
            //启用顶点数组
            gl10.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            //沿X轴旋转
            gl10.glRotatef(xrot, 1, 0, 0);
            //沿Y轴旋转
            gl10.glRotatef(yrot, 0, 1, 0);
    
            gl10.glColor4f(1f, 0f, 0f, 0);
            //模型如果四边形化,i就是加4,模型如果三角化,i就是加3
            for (int i = 0; i <= (mObjLoaderUtil.mSurfaceFloat.length / 3 - 4); i += 4) {
                gl10.glDrawArrays(GL10.GL_TRIANGLE_FAN, i, 4);
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
                case MotionEvent.ACTION_UP:
                    getParent().requestDisallowInterceptTouchEvent(false);
                    break;
            }
            return mCompat.onTouchEvent(event);
        }
    
        @Override
        public boolean onDown(MotionEvent motionEvent) {
            return true;
        }
    
        @Override
        public void onShowPress(MotionEvent motionEvent) {
    
        }
    
        @Override
        public boolean onSingleTapUp(MotionEvent motionEvent) {
            return false;
        }
    
        @Override
        public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            xrot -= v;
            yrot -= v1;
            return true;
        }
    
        @Override
        public void onLongPress(MotionEvent motionEvent) {
    
        }
    
        @Override
        public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            return false;
        }
    }
    

    第二种 glDrawElements绘制3D模型,使用这种方式绘制3D模型,需要将顶点坐标数据准备好,及顶点索引数组准备好。就ok。

    这两个数组也是直接从obj文件中解析。相对上面的方式而言,就简单一点。

    /**
     * 解析obj文件
     */
    public class ObjUtils {
        //存放顶点坐标
        private ArrayList<Point> mVertexArrayList;
        public float[] mVertexFloats;
        public FloatBuffer mVertexFloatBuffer;
        //存放顶点索引
        private ArrayList<Short> mIndexArrayList;
        public short[] mIndexShorts;
        public ShortBuffer mIndexShortBuffer;
        //存放读取的obj文件的信息
        private String temps;
    
    
        public ObjUtils() {
            mVertexArrayList = new ArrayList<>();
            mIndexArrayList = new ArrayList<>();
         }
    
        /**
         * 加载obj文件数据
         */
        public void loadObj(Context context, String path) throws IOException {
            //读取city.obj文件
            InputStream inputStream = context.getClass().getClassLoader().getResourceAsStream(path);
            InputStreamReader mInputStreamReader = new InputStreamReader(inputStream);
            BufferedReader mReader = new BufferedReader(mInputStreamReader);
            //如果读取的文本内容不为空,则一直读取,否则停止
            while ((temps = mReader.readLine()) != null) {
                String[] temp = temps.split(" ");
                //以空格为分割符,将读取的一行temps分裂为数组
                //如果读取到的首元素时"v",则表示读取到的数据是顶点坐标数据
                if (temp[0].trim().equals("v")) {
                    mVertexArrayList.add(new Point(Float.valueOf(temp[1]), Float.valueOf(temp[2]), Float.valueOf(temp[3])));
                }
    //            如果读取到的首元素时"f",则表示读取到的数据是面索引数据
                if (temp[0].trim().equals("f")) {
                    if (temp[1].indexOf("/") == -1) {
                        for (int i = 1; i < temp.length; i++) {
                            int t = Integer.valueOf(temp[i]);
                            mIndexArrayList.add((short) t);
                        }
                    } else {
                        for (int i = 1; i < temp.length; i++) {
                            int t = Integer.valueOf(temp[i].split("/")[0]) - 1;
                            mIndexArrayList.add((short) t);
                        }
                    }
                }
    
            }
            mReader.close();
            mVertexFloats = getVertex(mVertexArrayList);
            mIndexShorts = getIndex(mIndexArrayList);
            mVertexFloatBuffer = makeFloatBuffer(mVertexFloats);
            mIndexShortBuffer = getBufferFromArray(mIndexShorts);
             }
    
         /**
         * 获取顶点数组
         */
        private float[] getVertex(ArrayList<Point> pointArrayList) {
            float[][] mFloats = new float[pointArrayList.size()][3];
            float[] surfaceFloat = new float[pointArrayList.size() * 3];
            for (int i = 0; i < pointArrayList.size(); i++) {
                mFloats[i][0] = pointArrayList.get(i).x;
                mFloats[i][1] = pointArrayList.get(i).y;
                mFloats[i][2] = pointArrayList.get(i).z;
            }
            int i = 0;
            for (float[] floats : mFloats) {
                for (float v : floats) {
                    surfaceFloat[i++] = v;
                }
            }
            return surfaceFloat;
        }
    
        /**
         * 获取索引数组
         */
        private short[] getIndex(ArrayList<Short> integerArrayList) {
            short[] mFloats = new short[integerArrayList.size()];
            for (int i = 0; i < integerArrayList.size(); i++) {
                mFloats[i] = integerArrayList.get(i);
            }
            return mFloats;
        }
    
        /**
         * 将float数组转换为buffer
         */
        private FloatBuffer makeFloatBuffer(float[] floats) {
            ByteBuffer mByteBuffer = ByteBuffer.allocateDirect(floats.length * 4);
            mByteBuffer.order(ByteOrder.nativeOrder());
            FloatBuffer floatBuffer = mByteBuffer.asFloatBuffer();
            floatBuffer.put(floats);
            floatBuffer.position(0);
            return floatBuffer;
        }
    
        private static ShortBuffer getBufferFromArray(short[] array) {
            ByteBuffer buffer = ByteBuffer.allocateDirect(array.length * 2);
            buffer.order(ByteOrder.nativeOrder());
            ShortBuffer shortBuffer = buffer.asShortBuffer();
            shortBuffer.put(array);
            shortBuffer.position(0);
            return shortBuffer;
        }
    }
    
    public class ModeView extends GLSurfaceView implements GLSurfaceView.Renderer, GestureDetector.OnGestureListener {
        private ObjUtils mObjUtils;
        private GestureDetectorCompat mCompat;
        private float xrot, yrot;
        private FloatBuffer colorBuffer;
    
        public ModeView(Context context, AttributeSet attrs) throws IOException {
            super(context, attrs);
            mObjUtils = new ObjUtils();
            setRenderer(this);
            mObjUtils.loadObj(context, "assets/mode.obj");
            mCompat = new GestureDetectorCompat(context, this);
        }
    
        @Override
        public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
            //设置清屏色
            gl10.glClearColor(1f, 1f, 1f, 0);
            //用当前光线参数计算顶点颜色。否则仅仅简单将当前颜色与每个顶点关联。
            gl10.glEnable(GL10.GL_LIGHTING);
            //启用服务器端GL功能,允许(apply)引入的颜色与颜色缓冲区中的值进行逻辑运算
            gl10.glEnable(GL10.GL_BLEND);
            //这一行启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。
            gl10.glShadeModel(GL10.GL_SMOOTH);
            //当前活动纹理单元为二维纹理
            gl10.glEnable(GL10.GL_TEXTURE_2D);
            //做深度比较和更新深度缓存。值得注意的是即使深度缓冲区存在并且深度mask不是0,如果深度测试禁用的话,深度缓冲区也无法更新。
            gl10.glEnable(GL10.GL_DEPTH_TEST);
            // 法向量被计算为单位向量
            gl10.glEnable(GL10.GL_NORMALIZE);
            //启用面部剔除功能
            gl10.glEnable(GL10.GL_CULL_FACE);
            //指定哪些面不绘制
            gl10.glCullFace(GL10.GL_BACK);
            //开启定义多边形的正面和背面
            gl10.glEnable(GL10.GL_CULL_FACE);
            //改变每个顶点的散射和环境反射材质,可以使用颜色材质。
            gl10.glEnable(GL10.GL_COLOR_MATERIAL);
            // 开启抗锯齿
            gl10.glEnable(GL10.GL_POINT_SMOOTH);
            gl10.glHint(GL10.GL_POINT_SMOOTH_HINT, GL10.GL_NICEST);
            gl10.glEnable(GL10.GL_LINE_SMOOTH);
            gl10.glHint(GL10.GL_LINE_SMOOTH_HINT, GL10.GL_NICEST);
            gl10.glEnable(GL10.GL_POLYGON_OFFSET_FILL);
            gl10.glHint(GL10.GL_POLYGON_SMOOTH_HINT, GL10.GL_NICEST);
            //定义多边形的正面和背面,在一个完全由不透明的密闭surface组成的场景中,多边形的背面永远不会被看到。剔除这些不能显示出来的面可以加速渲染器渲染图像的时间。
            gl10.glFrontFace(GL10.GL_CCW);
            //指明深度缓冲区的清理值
            gl10.glClearDepthf(1.0f);
            gl10.glDepthFunc(GL10.GL_LEQUAL);
        }
    
        @Override
        public void onSurfaceChanged(GL10 gl10, int i, int i1) {
            //选择投影矩阵
            gl10.glMatrixMode(GL10.GL_PROJECTION);
            gl10.glViewport(0, 0, i, i1);
            gl10.glLoadIdentity();
            float x = (float) i / i1;
            gl10.glFrustumf(-x, x, -1, 1, 4, 200);
            GLU.gluLookAt(gl10, 0, 0, 60, 0, 0, 0, 0, 1, 0);
            //选择模型观察矩阵
            gl10.glMatrixMode(GL10.GL_MODELVIEW);
            gl10.glLoadIdentity();
        }
    
        @Override
        public void onDrawFrame(GL10 gl10) {
            //启用顶点数组
            gl10.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            gl10.glEnableClientState(GL10.GL_COLOR_ARRAY);
            //清除屏幕和深度缓存。
            gl10.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
            gl10.glLoadIdentity();
            //设置顶点坐标buffer
            gl10.glVertexPointer(3, GL10.GL_FLOAT, 0,
                    mObjUtils.mVertexFloatBuffer);
            gl10.glTranslatex(0, -20, 0);
            gl10.glRotatef(xrot, 1, 0, 0);
            gl10.glRotatef(yrot, 0, 1, 0);
            gl10.glPushMatrix();
            gl10.glDrawElements(GL10.GL_TRIANGLES, mObjUtils.mIndexShorts.length,
                    GL10.GL_UNSIGNED_SHORT, mObjUtils.mIndexShortBuffer);
            gl10.glDisableClientState(GL10.GL_COLOR_ARRAY);
            //关闭点坐标管道
            gl10.glDisableClientState(GL10.GL_VERTEX_ARRAY);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
                case MotionEvent.ACTION_UP:
                    getParent().requestDisallowInterceptTouchEvent(false);
                    break;
            }
            return mCompat.onTouchEvent(event);
        }
    
        @Override
        public boolean onDown(MotionEvent motionEvent) {
            return true;
        }
    
        @Override
        public void onShowPress(MotionEvent motionEvent) {
    
        }
    
        @Override
        public boolean onSingleTapUp(MotionEvent motionEvent) {
            return false;
        }
    
        @Override
        public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            xrot -= v;
            yrot -= v1;
            return true;
        }
    
        @Override
        public void onLongPress(MotionEvent motionEvent) {
    
        }
    
        @Override
        public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            return false;
        }
    }
    

    这两种方式绘制3D模型的主要区别就是在 onDrawFrame这个方法中。

    一:

    for (int i = 0; i <= (mObjLoaderUtil.mSurfaceFloat.length / 3 - 4); i += 4) { gl10.glDrawArrays(GL10.GL_TRIANGLE_FAN, i, 4); }

    二:

    gl10.glDrawElements(GL10.GL_TRIANGLES, mObjUtils.mIndexShorts.length, GL10.GL_UNSIGNED_SHORT, mObjUtils.mIndexShortBuffer);

    在使用OpenGl绘制3D模型前,一定要把这两种方法研究下。

    欢迎各方大佬,指点评论!!!!!!

     

    展开全文
  • 此压缩包内为带纹理的obj文件,用win10自带3D查看器便可打开,解析见:https://blog.csdn.net/qq_41102371/article/details/108880797
  • OBJ文件格式解析

    2020-11-27 19:45:49
    OBJ文件格式解析 OBJ文件作为一种标准的3D模型文件格式,例如CAD。文件其中主要存储了模型 的顶点、线和面等元素,还包括法向量和纹理贴图等。 主要元素 v 几何顶点 vt 贴图坐标 vn 顶点法向量(或者说三角形mesh的...

    OBJ文件格式解析

    OBJ文件作为一种标准的3D模型文件格式,例如CAD。文件其中主要存储了模型 的顶点、线和面等元素,还包括法向量和纹理贴图等。

    主要元素

    v 几何顶点
    vt 贴图坐标
    vn 顶点法向量(或者说三角形mesh的法向量)
    l
    f
    mg 合并组(Merging group)
    o 对象名称(Object name)
    s 光滑组(Smoothing group)
    g 组名称(Group name)
    usemtl 材质名称(Material name)
    mtllib 材质库(Material library)

    各元素的格式举例

    f Vertex1/Texture1/Normal1 Vertex2/Texture2/Normal2 Vertex3/Texture3/Normal3
    属于这个面的顶点、纹理坐标和法向量的索引号,整数,也就是以上顶点、纹理坐标和法向量的顺序。
    s off 关闭光滑组
    mtllib master.mtl 导入材质库
    usemtl initialShadingGroup initialShadingGroup表示使用的材质。
    “g pCube1"表示组,这里的成组与Maya中的成组不一样,这里的成组是指把"g pCube1"后出现的面都结合到一起,组成一个整的多边形几何体。
    OBJ文件不包含面的颜色定义信息,不过可以引用材质库,材质库信息储存在一个后缀是”.mtl"的独立文件中。关键字"mtllib"即材质库的意思。 材质库中包含材质的漫射(diffuse),环境(ambient),光泽(specular)的RGB(红绿蓝)的定义值,以及反射(specularity),折射(refraction),透明度(transparency)等其它特征。

    参考链接

    3D中的OBJ文件格式详解
    【ZT】3D中的OBJ文件格式详解
    OBJ文件格式简介
    OBJ文件格式内幕详解

    展开全文
  • obj文件解析

    千次阅读 2017-04-10 14:43:01
    OBJ文件 -- 基本结构 OBJ文件不需要任何文件头(File Header),尽管经常使用几行文件信息的注释作为文件的开头。 OBJ文件由一行行文本组成,注释行以一个“井”号(#)为开头,空格和空行可以随意加到文件中以增加...

    OBJ文件 -- 基本结构  

    OBJ文件不需要任何文件头(File Header),尽管经常使用几行文件信息的注释作为文件的开头。  

    OBJ文件由一行行文本组成,注释行以一个“井”号(#)为开头,空格和空行可以随意加到文件中以增加文件的可读性。

    有字的行都由一两个标记字母也就是关键字(Keyword)开头,关键字可以说明这一行是什么样的数据。多行可以逻辑地连接在一起表示一行,方法是在每一行最后添加一个连接符(\)。  注意连接符(\)后面不能出现空格或tab格,否则将导致文件出错。 下列关键字可以在OBJ文件使用。 

     在这个列表中, 关键字根据数据类型排列,每个关键字有一段简短描述。 

    顶点数据(Vertex data): 

     v 几何体顶点 (Geometric vertices) 

    vt 贴图坐标点 (Texture vertices) 

    vn 顶点法线 (Vertex normals)  

    vp 参数空格顶点 (Parameter space vertices)  

    自由形态曲线(Free-form curve)/表面属性(surface attributes): 

    deg 度 (Degree)  

    bmat 基础矩阵 (Basis matrix) 

    step 步尺寸 (Step size)  

    cstype 曲线或表面类型 (Curve or surface type) 

    元素(Elements):  

    p 点 (Point) 

    l 线 (Line) 

    f 面 (Face) 

    curv 曲线 (Curve) 

    curv2 2D曲线 (2D curve) 

    surf 表面 (Surface)  

    自由形态曲线(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)  

    自由形态表面之间的连接(Connectivity between free-form surfaces):  

    con 连接 (Connect) 成组(Grouping): 

    g 组名称 (Group name) 

    s 光滑组 (Smoothing group)

     mg 合并组 (Merging group) 

    o 对象名称 (Object name) 

     显示(Display)/渲染属性(render attributes):  

    evel 导角插值 (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) 

    展开全文
  • Unity功能实现——解析OBJ模型文件

    千次阅读 2018-12-25 11:09:58
    Unity功能实现——解析OBJ模型文件 1、OBJFile类 用于存储读取obj文件的g、v、f g对应gameobject.name v对应mesh.vertices f对应mesh.triangles 注意unity对于mesh点索引的起始点是0不是1 using System.Collections....
  • OBJ文件解析代码

    2012-03-25 13:52:31
    OBJ文件解析代码 使用opengl c++
  • opengl解析obj模型文件

    千次阅读 2017-07-26 15:34:37
    obj模型文件格式 # This file uses centimeters as units for non-parametric coordinates. mtllib Cube.mtl g default v -0.500000 -0.500000 0.500000 #顶点坐标 v 0.500000 -0.500000 0.500000 v -0.500000 0....
  • OBJ文件解析之OBJ的整体结构

    万次阅读 2016-09-09 23:13:37
    在windows下c语言编程的过程中,你有没有好奇过obj文件的格式呢?以及后续的 静态库,动态链接库.lib文件的格式,在这里我将带领大家 做一下 力所能及的简单分析, 在此过程中做好以下准备工作: 1.用VS创建一个...
  • obj格式解析

    千次阅读 2020-10-15 19:36:24
    最近处理一些网格渲染的时候,需要解析Obj文件,从Free3D上随便找了个免费的人体obj模型解析测试一波 国际惯例,参考博客: 本文所使用的从Free3D下载的模型 .obj文件格式与.mtl文件格式 详解3D中的obj文件格式 3D中...
  • 1.首先vs 2013建立工程生成obj文件,如下图。 2.打开CMD命令行模式,用工具dumpbin执行以下命令对test.obj进行解析。 dumpbin /all test.obj > test.txt obj解析信息会保存在test.txt文件中。 obj文件它...
  • 三维模型obj文件解析

    千次阅读 2020-09-30 11:36:56
    3D模型obj文件解析,构造一个obj立方体并进行纹理映射实例讲解
  • Unity中解析加载Obj文件(C#)

    千次阅读 2018-05-17 13:53:14
    Obj文件格式: mtllib master.mtl v -0.500000 -0.500000 0.500000 v 0.500000 -0.500000 0.500000 v -0.500000 0.500000 0.500000 v 0.500000 0.500000 0.500000 v -0.500000 0.500000 -0.500000 v 0.50000....
  • Obj文件解析相关bug心得

    千次阅读 2015-11-14 10:02:58
    OBJ文件读入的好几个bug,实习期间遇到的问题。
  • [OpenGL] obj文件解析

    千次阅读 2013-01-24 11:27:15
    OBJ文件是一种标准的3D模型文件格式,很适合用于3D软件模型之间的互导。比如在3dsMax或LightWave中建了一个模型,想把它调到Maya里面渲染或动画,导出OBJ文件就是一种很好的选择。目前几乎所有知名的3D软件都支持OBJ...
  • 网格模型obj文件及其纹理解析

    万次阅读 2017-04-28 09:36:53
    最近在学习obj文件格式,上网查了些资料,很难找到比较全面的文章,尤其是对.mtl文件的说明甚少。今天把最近搜索的资料整合了一下。 常见到的*.obj文件有两种:第一种是基于COFF(Common Object File ...
  • 至于发生静态链接的时机是在编译器编译生成OBJ文件之后,在链接器链接 各个obj文件之时,在运行生成程序之前,详细的过程你可以去参考《程序员的自我修养》,在这里我只是引证一下静态链接的发生时存在的: ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 113,947
精华内容 45,578
关键字:

解析obj文件