[Opengl Android] Rendering an obj model on Android

tool: eclipse

1. Obtain an obj file and analyze the content

3D yoyo net Click to open the link, you can download a lot of model files, use 3dmax to open and export the external format file obj to get an obj model

Change the format to txt to see the data content inside

Some model files will also have mlt files

Please go to https://www.douban.com/note/142379570/ for detailed data explanation

Second, get the data in the obj file

One is to analyze the data by yourself, please go to https://blog.csdn.net/xiaxl/article/details/77048507

Provides the analysis method of mlt and obj models, probably by traversing the data of each line and storing it in the array in the analysis format, and finally getting several vertices, normal vectors, and maps one-to-one corresponding arrays

The second is that I was too lazy, so I started to work hard to find the bag.

The first is obj2opengl.pl, a library that can automatically analyze the obj model through the command line and then export it into a .h file, which will generate an equal number of vertices, texture and normal arrays

Please go to http://maider.blog.sohu.com/281704711.html

However, .h cannot be used directly in the android project (of course, the data in .h can be copied directly to a new class dedicated to storing data, but it is ok when the data is small, for example, a cube may only have dozens of lines of data , but complex models will be more difficult, and a large number of float arrays need to be imported. The blog in the link above also talks about the solution)

Or use jni to parse c++ files

BUT is lazy or chooses to find bags diligently

Then org.obj2openjl learn about https://github.com/miffels/org.obj2openjl

mainly

RawOpenGLModel openGLModel = new Obj2OpenJL().convert("file");
OpenGLModelData openGLModelData = openGLModel.normalize().center().getDataForGLDrawElements();

float[] Vertices = openGLModelData.getVertices();
float[] Normals = openGLModelData.getNormals();
float[] TexCoords = openGLModelData.getTextureCoordinates();

Then put your obj file under the assets file.

At this time, the parsed data has been assigned to the above array and we can start rendering


3. Opengl rendering in Android

Recommend <<Opengl ES 2 for Android>> this book, you will learn more about the rendering process of opengl in Android, camera settings, projection settings

There are also some books in this area that I have packaged together https://download.csdn.net/download/qq_35263780/10366356


Let's talk about my own little project.


The first is the main class of Activity

Get OpenGL Read version information to check whether it is supported Get an OpenGL view class in which to start rendering things in the renderer class

glSurfaceView = new GLSurfaceView(this);
final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 20000;
		
		if (supportsEs2){
			glSurfaceView.setEGLContextClientVersion(2);
			try {
				glSurfaceView.setRenderer(new FirstOpenGLProjectRender(this));
			} catch (Exception e) {
				// TODO auto-generated catch block
				e.printStackTrace ();
			}
setContentView(glSurfaceView);

In the Activity class, there are also some events that deal with the life cycle of the android activity. Similar frameworks are as follows

public class FirstOpenGLProjectActivity extends AppCompatActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate();
	}

	@Override
	protected void onPause(){
		super.onPause();
		
	}
	
	@Override
	protected void onResume(){
		super.onResume();	
	}
}

Create a renderer class (inherit Renderer)


There should also be some functions that deal with the lifecycle

include

public void onSurfaceCreated(GL10 gl, EGLConfig config)

public void onSurfaceChanged(GL10 gl, int width, int height)

public void onDrawFrame(GL10 gl)

So the name suggests that one is called before rendering, the other is called when the screen changes, and the other is called at all times.

A bit like a function mechanism like start() update() in js

The rotation and scaling dynamic effects (projection matrix transformation) are probably all placed in onSurfaceChanged (please correct me if it is wrong)

Then next we need to get our array information and pass it to the underlying opengl rendering

	private final FloatBuffer VertexBuffer;
	private final FloatBuffer NormalBuffer;
	private final FloatBuffer TexureBuffer;
        private int VertexCount;
	   InputStream it = null;
		try {
			it = context.getAssets().open("obj1.obj");
		} catch (IOException e) {
			e.printStackTrace ();
		}
		RawOpenGLModel openGLModel = new Obj2OpenJL().convert(it);
		OpenGLModelData openGLModelData = openGLModel.normalize().center().getDataForGLDrawArrays();			

		
		float[] Vertices = openGLModelData.getVertices();
		VertexCount = Vertices.length / 3;
		VertexBuffer = ByteBuffer.allocateDirect(Vertices.length * 4).order(ByteOrder.nativeOrder())
	            .asFloatBuffer();
		VertexBuffer.put(Vertices);	
		VertexBuffer.position(0);//Read from 0
		
		float[] Normals = openGLModelData.getNormals();
		NormalBuffer = ByteBuffer.allocateDirect(Normals.length * 4).order(ByteOrder
                .nativeOrder()).asFloatBuffer();
        NormalBuffer.put(Normals);
        NormalBuffer.position(0);
		
		float[] TexCoords = openGLModelData.getTextureCoordinates();
        TexureBuffer = ByteBuffer.allocateDirect(TexCoords.length * 4).order(ByteOrder
                .nativeOrder()).asFloatBuffer();
        TexureBuffer.put(TexCoords);
        TexureBuffer.position(0);
		  

The first piece of code above should be seen at the beginning of this blog. We directly analyzed the

obj1.obj

and through

openGLModelData.getVertices()

and so on to get three arrays

You should have also noticed that we pass the obtained data information to a variable of type FloatBuffer, which is related to the mechanism of opengl. We can only pass this information to the underlying shader through this data flow, so as to output the required rendering. Vertex and Color Information

For details, please refer to Section 2.4 of <<Opengl ES 2 for Android>>

So when it comes to shaders

How do we create shaders in our project?

Create a new project file in this folder and save it as a glsl file vertex is vertex shader fragment is fragment shader

Hence the name, the vertex shader must have

gl_Position = whatever;

Fragment shader must have

gl_FragColor = whatever;

Then these two files are compiled with the syntax of C language, and there is probably only one void main() function in a simple shader.

Then the type of the variable and the way to pass the value

Please go to https://www.cnblogs.com/salam/archive/2016/01/08/5113572.html

You must be more serious than me


However, because opengl is such a language mechanism, bugs will not report errors, and it is not particularly easy to find (the Android application has been flashing back for a week, but I have not found the reason and always thought that the mechanism was wrong), but also because I just started to understand the shader. It's not very deep, and there will be a lot of strange problems at the beginning. For example, I just didn't understand the normal vector and light, so the data of gl_FlagColor is not right. I don't know where the bug is. Looking at other people's code, it gradually improved a little bit, and finally shipped a complete small instance is also a joy, so gl_position and gl_FlagColor need to correspond to the amount of data. The data of the vector defines the color a few times), if it is shipped out and does not flash back, it will be black, that is

Did you misspell the variable name in your shader somewhere?

Is the type definition in your shader correct? When is mat3 and when is vec4

Is there any problem with reading the type data?

Is your rendering order correct?

or something

If you read books and tutorials, you will basically have the first problem, hahaha (it's me

But there should be a way to compile shaders now.

So Amway https://www.jianshu.com/p/8687a040eb48

[Probably Amway has a lot of blogs, although I am lazy on the one hand, but it really benefits me a lot. Reading these blogs is definitely more useful than reading mine... Don't waste your time looking for information haha]

And I think it's funny

I hope I can experience this feeling in the future hahaha

pull back

I got the shader code on

vertex shader

uniform mat4 u_Matrix;//Transformation matrix  
uniform mat4 u_MVPMatrix; //Total transformation matrix  


attribute vec3 a_Position;
attribute vec3 a_Normal;
attribute vec2 a_TextureCoord;


varying vec3 v_FragPosition;
varying vec3 v_Normal;
varying vec2 v_TextureCoord;   		

void main()                    
{
    v_TextureCoord = a_TextureCoord;  
    v_Normal = a_Normal;      
    v_FragPosition = vec3(u_Matrix * vec4(a_Position,1.0));
                
    gl_Position = u_MVPMatrix *  vec4(a_Position,1);

}   

fragment shader

precision mediump float; 							

varying vec3 v_FragPosition;
varying vec3 v_Normal;
varying vec2 v_TextureCoord; //Texture coordinate variable variable two bits

uniform sampler2D u_Texture; //Texture sampler, representing a texture        	   								
uniform vec3 u_LightLocation;
  
void main()                    		
{    
    vec3 normal = normalize(v_Normal);
    vec3 lightDir = normalize(u_LightLocation-v_FragPosition);
    float factor = max(0.0, dot(normal, lightDir));
    vec4 diffuse = factor * vec4(1.0,1.0,1.0,1.0);                         	
    gl_FragColor = (diffuse + vec4(0.6,0.6,0.6,1))*texture2D(u_Texture, vec2(v_TextureCoord.s,v_TextureCoord.t));                              		
}

To sum up, the variables of the attribute and uniform types need to be defined in the renderer class and need to be passed from them (floatbuffer), similar to attribute values, and the variables of the Varying class are the data that needs to be passed between the two shaders. For example, I define a vertex shader

varying vec3 v_Normal;

That must also be in the fragment shader

varying vec3 v_Normal;

This value can be obtained from a variable of type attribute

v_Normal = a_Normal;    

After writing the shader code, compile and link it in the renderer class

Call this method in onSurfaceCreated

public void UseProgram(){
		 String vertexShaderSource = TextResourceReader
				 .readTextFileFromResource(context, R.raw.simple_vertex_shader); //Connect to glsl file simple_vertex_shader
		 String fragmentShaderSource = TextResourceReader
				 .readTextFileFromResource(context, R.raw.simple_fragment_shader);//Connect to the glsl file simple_fragment_shader
		 int vertexShader = ShaderHelper.compileVertexShader(vertexShaderSource);
		 int fragmentShader = ShaderHelper.compileFragmentShader(fragmentShaderSource);
		 //connect the shaders
		 program = ShaderHelper.linkProgram(vertexShader, fragmentShader); //Connect vertex shader and fragment shader to program
		
		 if(LoggerConfig.ON){
			 ShaderHelper.validateProgram(program);
		 }
		 // use the program defined here when drawing anything to the screen
		 glUseProgram(program);
	}

The next step is to pass the data stream we obtained earlier to the shader

define variable

	private int aPositionHandle;

Get attribute location in shader and assign to variable

aPositionHandle = glGetAttribLocation(program,"a_Position"); //Get the attribute location assignment location number in simple_vertex_shader

Connect with the data stream to start reading data

glVertexAttribPointer(aPositionHandle,3,GL_FLOAT,
				 false, 0, VertexBuffer);
glVertexAttribPointer(int index, int size, int type, boolean normalized, int stride, Buffer ptr)

They are (attribute position; how many components a vertex has; data type; default integer data; tell Opengl where to go, read data in the buffer vertexData to find the data corresponding to aPositionLocation)

finally

glEnableVertexAttribArray(aPositionHandle); //Enable

So far our shader has all the vertex data

The data acquisition of other attributes is also similar

But the texture map of the material needs to be initialized separately


	//initialize texture
	public void initTexture() {
	    int[] textures = new int[1];//Generate texture ID
	    glGenTextures(
	        1,//The number of texture ids generated
	        textures,//Array of texture ids
	        0//Offset
	        );
	    textureId = textures[0];//Get the generated texture id
	    glBindTexture(GL_TEXTURE_2D, textureId);//Bind texture Id
	    glTexParameterf(GL_TEXTURE_2D,//Set the MIN sampling method
	    GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	    glTexParameterf(GL_TEXTURE_2D,//Set the MAG sampling method
	        GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	    glTexParameterf(GL_TEXTURE_2D,//Set the S-axis stretching method
	        GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	    glTexParameterf(GL_TEXTURE_2D, //Set the T axis stretching method
	        GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	    // load image via input stream
	    InputStream is = context.getResources().openRawResource(//Create a stream that only wants texture images
	        R.drawable.t4);
	    Bitmap bitmapTmp;
	    try {bitmapTmp = BitmapFactory.decodeStream(is);}//Load image content from the stream
	    finally{try{is.close();}catch(IOException e){e.printStackTrace();}}
	    GLUtils.texImage2D(//Actually load texture into video memory
	        GL_TEXTURE_2D,//Texture type
	        0,//The level of the texture, 0 represents the basic image layer, which can be understood as a direct map
	        bitmapTmp,//texture image
	        0//Texture border size
	        );
	    bitmapTmp.recycle();//Release the texture map in the memory after the texture is loaded successfully, otherwise it will cause a memory crash when there are too many textures
	}
texture
		 initTexture();
		 glUniform1i(uTextureHandle, 0);//Corresponding to texture unit 0
		 
		 glActiveTexture(GL_TEXTURE0 + 0);
		 glBindTexture(GL_TEXTURE_2D, textureId);

For details, please go to https://blog.csdn.net/sz66cm/article/details/54317272

After assigning all the obtained data to the shader, you can see

[Only added texture without light]

【Add light】


The last source

https://download.csdn.net/download/qq_35263780/10366532






Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324642824&siteId=291194637