OpenGL ES 3. OBJ file rendering

Hello, next I will introduce OpenGL ES 3. OBJ file rendering.

The previous section introduces the OBJ file and its text structure. Next, it introduces how to load the OBJ file into memory and render it through OpenGL.

1. OBJ file parsing class

Because OBJ text files are stored according to certain rules (see the previous section for details), we first introduce the loading and parsing of OBJ files, and the LoadedObjectVertexNormalTexture class used to render objects after loading. This paper introduces loading vertex coordinates, triangle faces, texture coordinates and other information.

import android.opengl.GLES30;

//Loaded object - only carrying vertex information, random color
public class LoadedObjectVertexNormalTexture
{	
	int mProgram;//Custom render pipeline shader program id  
    int muMVPMatrixHandle;//Total transformation matrix reference
    int muMMatrixHandle;//Transformation matrix of position and rotation
    int maPositionHandle; //Vertex position attribute reference  
    int maNormalHandle; //Vertex normal vector attribute reference  
    int maLightLocationHandle;//Light location attribute reference  
    int maCameraHandle; //Camera position property reference 
    int maTexCoorHandle; //Vertex texture coordinate attribute reference  
    String mVertexShader;//Vertex shader code script    	 
    String mFragmentShader;//Chip shader code script    
	
    int vCount=0;  
    
	FloatBuffer   mVertexBuffer;//Vertex coordinate data buffer  
	FloatBuffer   mNormalBuffer;//Vertex normal vector data buffer
	FloatBuffer   mTexCoorBuffer;//Vertex texture coordinate data buffer
    
    public LoadedObjectVertexNormalTexture(MySurfaceView mv,float[] vertices,float[] normals,float texCoors[])
    {//constructor 	
    	//Initialize vertex coordinate, normal vector, texture coordinate data
    	initVertexData(vertices,normals,texCoors);
    	//Initialize shader      
    	initShader(mv);
    }
    
	//Method of initializing vertex coordinate, normal vector and texture coordinate data
    public void initVertexData(float[] vertices,float[] normals,float texCoors[])
    {
    	//Initialization of vertex coordinate data============================
    	vCount=vertices.length/3;   
		
        //Create vertex coordinate data buffer
        //vertices.length*4 is because an integer has four bytes
        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
        vbb.order(ByteOrder.nativeOrder());//Set byte order
        mVertexBuffer = vbb.asFloatBuffer();//Convert to Float buffer
        mVertexBuffer.put(vertices);//Put vertex coordinate data into buffer
        mVertexBuffer.position(0);//Set buffer start position
        //Special note: because the byte order of different platforms is different, the data unit is not byte and must pass ByteBuffer
        //The key to the transformation is to set the nativeOrder() through ByteOrder, otherwise there may be problems
        //Initialization of vertex coordinate data============================
        
        //Initialization of vertex normal vector data============================  
        ByteBuffer cbb = ByteBuffer.allocateDirect(normals.length*4);
        cbb.order(ByteOrder.nativeOrder());//Set byte order
        mNormalBuffer = cbb.asFloatBuffer();//Convert to Float buffer
        mNormalBuffer.put(normals);//Put vertex normal vector data into buffer
        mNormalBuffer.position(0);//Set buffer start position
        //Special note: because the byte order of different platforms is different, the data unit is not byte and must pass ByteBuffer
        //The key to the transformation is to set the nativeOrder() through ByteOrder, otherwise there may be problems
        //Initialization of vertex shading data============================
        
        //Initialization of vertex texture coordinate data============================  
        ByteBuffer tbb = ByteBuffer.allocateDirect(texCoors.length*4);
        tbb.order(ByteOrder.nativeOrder());//Set byte order
        mTexCoorBuffer = tbb.asFloatBuffer();//Convert to Float buffer
        mTexCoorBuffer.put(texCoors);//Put vertex texture coordinate data into buffer
        mTexCoorBuffer.position(0);//Set buffer start position
        //Special note: because the byte order of different platforms is different, the data unit is not byte and must pass ByteBuffer
        //The key to the transformation is to set the nativeOrder() through ByteOrder, otherwise there may be problems
        //Initialization of vertex texture coordinate data============================
    }

    
    //Method of drawing loading object
    public void drawSelf(int texId)
    {        
    	 //Make use of a set of shader programs
    	 GLES30.glUseProgram(mProgram);
         //Pass the final transform matrix into the shader program
         GLES30.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(), 0); 
         //Pass position, rotation transform matrix into shader program
         GLES30.glUniformMatrix4fv(muMMatrixHandle, 1, false, MatrixState.getMMatrix(), 0);   
         //Pass the light position into the shader program   
         GLES30.glUniform3fv(maLightLocationHandle, 1, MatrixState.lightPositionFB);
         //Pass camera position into shader program   
         GLES30.glUniform3fv(maCameraHandle, 1, MatrixState.cameraFB);
         // Passing vertex position data into the rendering pipeline
         GLES30.glVertexAttribPointer (maPositionHandle, 3, GLES30.GL_FLOAT, 
         		false, 3*4, mVertexBuffer);       
         //Passing vertex normal vector data into the rendering pipeline
         GLES30.glVertexAttribPointer (maNormalHandle, 3, GLES30.GL_FLOAT, 
         		false, 3*4, mNormalBuffer);   
         //Passing vertex texture coordinate data into the rendering pipeline
         GLES30.glVertexAttribPointer (maTexCoorHandle, 2, GLES30.GL_FLOAT, 
         		false, 2*4, mTexCoorBuffer);
         //Enable vertex position, normal vector, texture coordinate data array
         GLES30.glEnableVertexAttribArray(maPositionHandle);  
         GLES30.glEnableVertexAttribArray(maNormalHandle);  
         GLES30.glEnableVertexAttribArray(maTexCoorHandle); //Enable texture coordinate data array
         //Binding texture
         GLES30.glActiveTexture(GLES30.GL_TEXTURE0);//Enable texture 0
         GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texId);//Binding texture
         //Drawing loaded objects
         GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vCount); 
    }
}

 

Specific loading tool class LoadUtil: alv is the list of original vertex coordinates -- load directly from obj file, alvResult is the list of result vertex coordinates -- organize by face, alt is the list of original texture coordinates, altResult is the list of texture coordinate results, aln is the list of original normal vectors, and alnResult is the list of normal vector results.

public class LoadUtil 
{		
	//Loading objects with vertex information from obj file
    public static LoadedObjectVertexNormalTexture loadFromFile
    (String fname, Resources r,MySurfaceView mv)
    {
    	//References to objects after loading
    	LoadedObjectVertexNormalTexture lo=null;
    	//Original vertex coordinate list -- load directly from obj file
    	ArrayList<Float> alv=new ArrayList<Float>();
    	//Result vertex coordinate list -- organized by face
    	ArrayList<Float> alvResult=new ArrayList<Float>(); 
    	//Original texture coordinates list
    	ArrayList<Float> alt=new ArrayList<Float>();  
    	//Texture coordinate result list
    	ArrayList<Float> altResult=new ArrayList<Float>();  
    	//Original normal vector list
    	ArrayList<Float> aln=new ArrayList<Float>();    
    	//List of normal vector results
    	ArrayList<Float> alnResult=new ArrayList<Float>();    
    	
    	try
    	{
    		InputStream in=r.getAssets().open(fname);
    		InputStreamReader isr=new InputStreamReader(in);
    		BufferedReader br=new BufferedReader(isr);
    		String temps=null;
    		
    		//Scan the file, and execute different processing logic according to different line types
		    while((temps=br.readLine())!=null) 
		    {//Read a line of text
		    	
		    	String[] tempsa=temps.split("[ ]+");//Split text lines with spaces
		      	if(tempsa[0].trim().equals("v"))
		      	{//This line is vertex coordinate line
		      	    //If it is a vertex coordinate row, the XYZ coordinates of this vertex are extracted and added to the original vertex coordinate list
		      		alv.add(Float.parseFloat(tempsa[1]));
		      		alv.add(Float.parseFloat(tempsa[2]));
		      		alv.add(Float.parseFloat(tempsa[3]));
		      	}
		      	else if(tempsa[0].trim().equals("vt"))
		      	{//This is the texture coordinate line
		      		//If it is a texture coordinate line, extract the ST coordinate and add it to the original texture coordinate list
		      		alt.add(Float.parseFloat(tempsa[1]));
		      		alt.add(1-Float.parseFloat(tempsa[2])); 
		      	}
		      	else if(tempsa[0].trim().equals("vn"))
		      	{//This line is a normal vector line
		      		//If it is a texture coordinate line, extract the ST coordinate and add it to the original texture coordinate list
		      		aln.add(Float.parseFloat(tempsa[1]));//Put in the aln list
		      		aln.add(Float.parseFloat(tempsa[2])); //Put in the aln list
		      		aln.add(Float.parseFloat(tempsa[3])); //Put in the aln list
		      	}
		      	else if(tempsa[0].trim().equals("f")) 
		      	{//This behavior is triangular face     		
		      		//Calculate the index of the 0th vertex and get the XYZ coordinates of the vertex	      		
		      		int index=Integer.parseInt(tempsa[1].split("/")[0])-1;
		      		float x0=alv.get(3*index);
		      		float y0=alv.get(3*index+1);
		      		float z0=alv.get(3*index+2);
		      		alvResult.add(x0);
		      		alvResult.add(y0);
		      		alvResult.add(z0);		
		      		
		      	    //Calculate the index of the first vertex and get the XYZ three coordinates of this vertex	  
		      		index=Integer.parseInt(tempsa[2].split("/")[0])-1;
		      		float x1=alv.get(3*index);
		      		float y1=alv.get(3*index+1);
		      		float z1=alv.get(3*index+2);
		      		alvResult.add(x1);
		      		alvResult.add(y1);
		      		alvResult.add(z1);
		      		
		      	    //Calculate the index of the second vertex and get the XYZ coordinates of the vertex	
		      		index=Integer.parseInt(tempsa[3].split("/")[0])-1;
		      		float x2=alv.get(3*index);
		      		float y2=alv.get(3*index+1);
		      		float z2=alv.get(3*index+2);
		      		alvResult.add(x2);
		      		alvResult.add(y2); 
		      		alvResult.add(z2);			      		
		      		
		      		//Organize texture coordinates into the resulting texture coordinates list
		      		//Texture coordinates of vertex 0
		      		int indexTex=Integer.parseInt(tempsa[1].split("/")[1])-1;
		      		altResult.add(alt.get(indexTex*2));
		      		altResult.add(alt.get(indexTex*2+1));
		      	    //Texture coordinates of the first vertex
		      		indexTex=Integer.parseInt(tempsa[2].split("/")[1])-1;
		      		altResult.add(alt.get(indexTex*2));
		      		altResult.add(alt.get(indexTex*2+1));
		      	    //Texture coordinates of the second vertex
		      		indexTex=Integer.parseInt(tempsa[3].split("/")[1])-1;
		      		altResult.add(alt.get(indexTex*2));
		      		altResult.add(alt.get(indexTex*2+1));		      		
		      		
		      	    //=================================================
		      		//Calculate the normal vector index of the 0th vertex
		      		int indexN=Integer.parseInt(tempsa[1].split("/")[2])-1;//Get normal vector number
		      		float nx0=aln.get(3*indexN);//Get x value of normal vector
		      		float ny0=aln.get(3*indexN+1);//Get y value of normal vector
		      		float nz0=aln.get(3*indexN+2);//Get z value of normal vector
		      		alnResult.add(nx0);//Put in the alnResult list
		      		alnResult.add(ny0);//Put in the alnResult list
		      		alnResult.add(nz0);	//Put in the alnResult list	
		      		
		      	    //Calculate the index of the first vertex and get the XYZ three coordinates of this vertex	  
		      		indexN=Integer.parseInt(tempsa[2].split("/")[2])-1;
		      		float nx1=aln.get(3*indexN);
		      		float ny1=aln.get(3*indexN+1);
		      		float nz1=aln.get(3*indexN+2);
		      		alnResult.add(nx1);
		      		alnResult.add(ny1);
		      		alnResult.add(nz1);
		      		
		      	    //Calculate the index of the second vertex and get the XYZ coordinates of the vertex	
		      		indexN=Integer.parseInt(tempsa[3].split("/")[2])-1;
		      		float nx2=aln.get(3*indexN);
		      		float ny2=aln.get(3*indexN+1);
		      		float nz2=aln.get(3*indexN+2);
		      		alnResult.add(nx2);
		      		alnResult.add(ny2); 
		      		alnResult.add(nz2);	
		      	}		      		
		    } 
		    
		    //Generate vertex array
		    int size=alvResult.size();
		    float[] vXYZ=new float[size];
		    for(int i=0;i<size;i++)
		    {
		    	vXYZ[i]=alvResult.get(i);
		    } 
		    
		    //Generate texture array
		    size=altResult.size();
		    float[] tST=new float[size];
		    for(int i=0;i<size;i++)
		    {
		    	tST[i]=altResult.get(i);
		    }
		    
		    //Generate normal vector array
		    size=alnResult.size();//Get the size of the normal vector list
		    float[] nXYZ=new float[size];//Create an array of normal vectors
		    for(int i=0;i<size;i++)
		    {
		    	nXYZ[i]=alnResult.get(i);//Store the normal vector value in the array
		    }
		    
		    //Create a load object object
		    lo=new LoadedObjectVertexNormalTexture(mv,vXYZ,nXYZ,tST);
    	}
    	catch(Exception e)
    	{
    		Log.d("load error", "load error");
    		e.printStackTrace();
    	}    	
    	return lo;//Returns a reference to the created object object
    }
}

 

2. Describes the MySurfaceView class used to render the entire 3D scene: customize the rendererscenerer, and set the rendering mode to render rendermode ﹐ continuously.

class MySurfaceView extends GLSurfaceView 
{
	private final float TOUCH_SCALE_FACTOR = 180.0f/320;//Angular scaling
    private SceneRenderer mRenderer;//Scene renderer    
    
    private float mPreviousY;//Last touch Y coordinate
    private float mPreviousX;//Last touch position X coordinate
    
    int textureId;//System assigned texture id
	
	public MySurfaceView(Context context) {
        super(context);
        this.setEGLContextClientVersion(3); //Set to use OPENGL ES3.0
        mRenderer = new SceneRenderer();	//Create scene renderer
        setRenderer(mRenderer);				//Set up the renderer		        
        setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//Set render mode to active   
    }
	
	//Touch event callback method
    @Override 
    public boolean onTouchEvent(MotionEvent e) 
    {
        float y = e.getY();
        float x = e.getX();
        switch (e.getAction()) {
        case MotionEvent.ACTION_MOVE:
            float dy = y - mPreviousY;//Calculate stylus Y displacement
            float dx = x - mPreviousX;//Calculate stylus X displacement
            mRenderer.yAngle += dx * TOUCH_SCALE_FACTOR;//Set rotation angle along y axis
            mRenderer.xAngle+= dy * TOUCH_SCALE_FACTOR;//Set rotation angle along x axis
            requestRender();//Redraw
        }
        mPreviousY = y;//Record the position of the stylus
        mPreviousX = x;//Record the position of the stylus
        return true;
    }

	private class SceneRenderer implements GLSurfaceView.Renderer 
    {  
		float yAngle;//Angle of rotation about Y axis
    	float xAngle; //Angle of rotation about X axis
    	//Objects loaded from the specified obj file
		LoadedObjectVertexNormalTexture lovo;
    	
        public void onDrawFrame(GL10 gl) 
        { 
        	//Clear depth buffer and color buffer
            GLES30.glClear( GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_COLOR_BUFFER_BIT);

            //Coordinate system extrapolation
            MatrixState.pushMatrix();
            MatrixState.translate(0, -16f, -60f);
            //Rotate around Y-axis and X-axis
            MatrixState.rotate(yAngle, 0, 1, 0);
            MatrixState.rotate(xAngle, 1, 0, 0);
            
            //If the loaded object is not empty, draw the object
            if(lovo!=null)
            {
            	lovo.drawSelf(textureId);
            }   
            MatrixState.popMatrix();                  
        }  

        public void onSurfaceChanged(GL10 gl, int width, int height) {
            //Set window size and location 
        	GLES30.glViewport(0, 0, width, height); 
        	//Calculate the aspect ratio of GLSurfaceView
            float ratio = (float) width / height;
            //Call this method to calculate and generate perspective projection matrix
            MatrixState.setProjectFrustum(-ratio, ratio, -1, 1, 2, 100);
            //Call this method to generate camera 9 parameter position matrix
            MatrixState.setCamera(0,0,0,0f,0f,-1f,0f,1.0f,0.0f);
        }

        public void onSurfaceCreated(GL10 gl, EGLConfig config) 
        {
            //Set screen background color RGBA
            GLES30.glClearColor(0.0f,0.0f,0.0f,1.0f);    
            //Open depth detection
            GLES30.glEnable(GLES30.GL_DEPTH_TEST);
            //Turn on back clipping   
            GLES30.glEnable(GLES30.GL_CULL_FACE);
            //Initialize transformation matrix
            MatrixState.setInitStack();
            //Initialize light location
            MatrixState.setLightLocation(40, 10, 20);
            //Load objects to draw
            lovo=LoadUtil.loadFromFile("ch_t.obj", MySurfaceView.this.getResources(),MySurfaceView.this);
            //Load texture map
            textureId=initTexture(R.drawable.ghxp);
        }
    }
  	public int initTexture(int drawableId)//textureId
	{
		//Generate texture ID
		int[] textures = new int[1];
		GLES30.glGenTextures (
				1,          //Number of texture IDs generated
				textures,   //Array of texture IDS
				0           //Offset
		);    
		int textureId=textures[0];    
		GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId);
		GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER,GLES30.GL_NEAREST);
		GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MAG_FILTER,GLES30.GL_LINEAR);
		GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S,GLES30.GL_REPEAT);
		GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T,GLES30.GL_REPEAT);
        
        //Load picture through input stream ==================== begin===================
        InputStream is = this.getResources().openRawResource(drawableId);
        Bitmap bitmapTmp;
        try {
        	bitmapTmp = BitmapFactory.decodeStream(is);
        } 
        finally {
            try {
                is.close();
            } 
            catch(IOException e) {
                e.printStackTrace();
            }
        }
        //Load picture through input stream ==================== end===================== 
	   	GLUtils.texImage2D (
	    		GLES30.GL_TEXTURE_2D, //Texture type
	     		0, 
	     		GLUtils.getInternalFormat(bitmapTmp), 
	     		bitmapTmp, //texture image
	     		GLUtils.getType(bitmapTmp), 
	     		0 //Texture border size
	     );
	    bitmapTmp.recycle(); 		  //Release image after texture loading
        return textureId;
	}
}

Implementation of the onSurfaceCreated method, in which an object of the LoadedObjectVertexNormalTexture class is created after the transformation matrix is initialized.

 

3. Example rendering:

 

Finally, welcome to exchange and study together: wechat: liaosy666; QQ:2209115372 .

 

Published 56 original articles· Zan Zan 65. 20000 visitors+
Private letter follow

Keywords: Attribute Android angular

Added by TalonFinsky on Wed, 08 Apr 2020 11:26:19 +0300