(五)加载obj和mtl

2019-11-25  本文已影响0人  SunnyDay_ab5f

1.shader

public static String vs =
            "uniform mat4 uMatrix;\n" +
            "uniform vec3 uCamera;\n" +
            "uniform vec3 uLight;\n" +
            "uniform vec3 vKa;\n" +
            "uniform vec3 vKd;\n" +
            "uniform vec3 vKs;\n" +
            "attribute vec4 vPosition;\n" +
            "attribute vec4 vNormal;\n" +
            "varying vec4 vSpecular;\n" +
            "varying vec4 vDiffuse;\n" +
            "varying vec4 vAmbient;\n" +
            "void main(){\n" +
            "float shininess=3.0;\n" +
            "gl_Position=uMatrix*vPosition;\n" +
            "vec4 initAmbient=vec4(vKa,1.0);\n" +
            "vec4 initDiffuse=vec4(vKd,1.0);\n" +
            "vec4 initSpecular=vec4(vKs,1.0);\n" +
            "vAmbient=initAmbient;\n" +
            "vec4 _vNormal=normalize(vNormal);\n" +
            "vec3 eye=uCamera-vPosition.xyz;\n" +
            "eye=normalize(eye);\n" +
            "vec3 vp=uLight-vPosition.xyz;\n" +
            "vp=normalize(vp);\n" +
            "float nDotViewPosition=max(0.0,dot(_vNormal.xyz,vp));\n" +
            "vDiffuse=initDiffuse*nDotViewPosition;\n" +
            "vec3 halfVector=normalize(vp+eye);\n" +
            "float nDotViewHalfVector=dot(_vNormal.xyz,halfVector);\n" +
            "float powerFactor=max(0.0,pow(nDotViewHalfVector,shininess));\n" +
            "vSpecular=initSpecular*powerFactor;\n" +
            "}";


    public static String fs =
            "precision mediump float;\n" +
            "varying vec4 vSpecular;\n" +
            "varying vec4 vDiffuse;\n" +
            "varying vec4 vAmbient; \n" +
            "void main(){\n" +
            "vec4 finalColor=vec4(1.0,1.0,1.0,1.0);\n" +   //这里如果写1.0f 在opengles 2.0 可以编译的过去,在3.0shader会报错
            "gl_FragColor=finalColor*vSpecular+finalColor*vDiffuse+finalColor*vAmbient;\n" +
            "}";

这部分代码我是在网上找的。

2.Renderer

import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;

import java.util.List;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MyGLRenderer implements GLSurfaceView.Renderer {

    private Triangle mTriangle;
    private String filePath;

    private final float[] vPMatrix = new float[16];
    private final float[] projectionMatrix = new float[16];
    private final float[] viewMatrix = new float[16];
    private float[] rotationMatrix = new float[16];



    public volatile float mAngle;
    private ObjFileReader reader;
    private List<Triangle> triangles;

    public float getAngle() {
        return mAngle;
    }

    public void setAngle(float angle) {
        mAngle = angle;
    }

    public void setPath(String filePath) {
        this.filePath = filePath;
    }

   //代码没有对加载是否成功做判断 在项目中要做好判断和log打印。
    public static int loadShader(int type, String shaderCode){

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);
        return shader;
    }

    private static final String TAG = "MyGLRenderer";
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // Set the background frame color
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        // initialize a triangle
        String objPath = filePath + "patrick.obj";
        String mtlPath = filePath + "patrick.mtl";
        reader = new ObjFileReader();
        reader.readMtlFile(mtlPath);
        triangles = reader.readObjFile(objPath);
        for (int i = 0; i < triangles.size(); i++) {
            triangles.get(i).init2();
        }
    }

    public void onDrawFrame(GL10 unused) {
        // Redraw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        // Set the camera position (View matrix)
//        Matrix.setLookAtM(viewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        Matrix.setLookAtM(viewMatrix, 0, 0, 0, 4, 0f, 0f, 0f, 0f, 1.0f, 0.0f);//upY 方向是正方向 upz则看不到


        // Calculate the projection and view transformation
        Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0);

        float[] scratch = new float[16];

        // Create a rotation for the triangle
        // long time = SystemClock.uptimeMillis() % 4000L;
        // float angle = 0.090f * ((int) time);
        Matrix.setRotateM(rotationMatrix, 0, mAngle, 0, 1.0f, 0);

        // Combine the rotation matrix with the projection and camera view
        // Note that the vPMatrix factor *must be first* in order
        // for the matrix multiplication product to be correct.
        Matrix.multiplyMM(scratch, 0, vPMatrix, 0, rotationMatrix, 0);

//        // Draw triangle
        for (int i = 0; i < triangles.size(); i++) {
            triangles.get(i).draw2(scratch);
        }
    }

    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        float ratio = (float) width / height;

        // this projection matrix is applied to object coordinates
        // in the onDrawFrame() method
//        Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
        Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 1, 100);//100 这个值要尽可能的大一些,不然模型可能会无法全部显示
    }
}

3.Triangle

import android.opengl.GLES20;
import android.util.Log;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;

public class Triangle {
    private int mProgram;
    private FloatBuffer vertexBuffer;
    // Use to access and set the view transformation
    private int vPMatrixHandle;

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;
    private int positionHandle;
    private int colorHandle;
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
    public MtlInfo mtlInfo;//材质纹理信息
    public List<Float> vertexList = new ArrayList<>();
    public List<Float> normalList = new ArrayList<>();
    private int vertexCount;
    private int normalCount;
    private FloatBuffer normalBuffer;
    private int kaHandle;
    private int kdHandle;
    private int ksHandle;
    private int normalHandle;
    private int mCameraHandle;
    private int mLightHandle;
    private float [] fvArray;
    private float[] fvnArray;
    private float[] lightBuffer = new float[]{0f,0f,1000f};//光源
    private float[] cameraBuffer = new float[]{0f,0f,4f};//相机
    private static final String TAG = "Triangle";
    public void init() {
        //加载顶点数据
        fvArray = new float[vertexList.size()];
        for (int i = 0; i < vertexList.size(); i++) {
            fvArray[i] = vertexList.get(i);
        }
        vertexCount = fvArray.length / 3;
        ByteBuffer bb = ByteBuffer.allocateDirect(fvArray.length * 4);//一个三角形3个点,一个点由3个浮点数表示 一个浮点数有4个字节 3*3*4
        //使用设备硬件的本机字节顺序
        bb.order(ByteOrder.nativeOrder());
        //从ByteBuffer创建一个浮点缓冲区
        vertexBuffer = bb.asFloatBuffer();
        //将坐标添加到FloatBuffer
        vertexBuffer.put(fvArray);
        //清除数据缓存
        vertexList.clear();
        fvArray=null;
        //设置缓冲区以读取第一个坐标
        vertexBuffer.position(0);

        //加载法线向量数据
        fvnArray = new float[normalList.size()];
        normalCount = fvnArray.length / 3;
        ByteBuffer nbb = ByteBuffer.allocateDirect(fvnArray.length * 4);//一个三角形3个点,一个点由3个浮点数表示 一个浮点数有4个字节 3*3*4
        //使用设备硬件的本机字节顺序
        nbb.order(ByteOrder.nativeOrder());
        //从ByteBuffer创建一个浮点缓冲区
        normalBuffer = nbb.asFloatBuffer();
        //将坐标添加到FloatBuffer
        normalBuffer.put(fvnArray);
        //设置缓冲区以读取第一个坐标
        normalBuffer.position(0);

        int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
                MyShader.vs);
        int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
                MyShader.fs);

        //创建空的OpenGL ES程序
        mProgram = GLES20.glCreateProgram();
        // 将顶点着色器添加到程序中
        GLES20.glAttachShader(mProgram, vertexShader);

        // 将片段着色器添加到程序中
        GLES20.glAttachShader(mProgram, fragmentShader);
        // 创建OpenGL ES程序可执行文件
        GLES20.glLinkProgram(mProgram);
    }

    public void draw2(float[] mvpMatrix) {
        // 将程序添加到OpenGL ES环境
        GLES20.glUseProgram(mProgram);
        vPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMatrix");
       // 将投影和视图转换传递到着色器
        GLES20.glUniformMatrix4fv(vPMatrixHandle, 1, false, mvpMatrix, 0);

        //相机位置句柄
        mCameraHandle =GLES20.glGetUniformLocation(mProgram,"uCamera");
        //灯光位置句柄
        mLightHandle =GLES20.glGetUniformLocation(mProgram,"uLight");
        kaHandle = GLES20.glGetUniformLocation(mProgram, "vKa");
        kdHandle = GLES20.glGetUniformLocation(mProgram, "vKd");
        ksHandle = GLES20.glGetUniformLocation(mProgram, "vKs");

        GLES20.glUniform3fv(mCameraHandle,1,cameraBuffer,0);
        GLES20.glUniform3fv(mLightHandle,1,lightBuffer,0);
        GLES20.glUniform3fv(kaHandle,1,mtlInfo.Ka,0);
        GLES20.glUniform3fv(kdHandle,1,mtlInfo.Kd,0);
        GLES20.glUniform3fv(ksHandle,1,mtlInfo.Ks,0);

//        mHCoord =GLES20.glGetAttribLocation(mProgram,"vCoord");
//        mHTexture =GLES20.glGetUniformLocation(mProgram,"vTexture");

       //获取顶点着色器的vPosition成员的句柄
        positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        //三角形顶点法线句柄
        normalHandle = GLES20.glGetAttribLocation(mProgram, "vNormal");


//        GLES20.glEnableVertexAttribArray(mHCoord);
//        GLES20.glVertexAttribPointer(mHCoord, 2, GLES20.GL_FLOAT, false, 0, vertTexture);
        // 启用三角形顶点的句柄
        GLES20.glEnableVertexAttribArray(positionHandle);
        // 启用三角形顶点法线的句柄
        GLES20.glEnableVertexAttribArray(normalHandle);

        // 准备三角坐标数据
        GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);

        GLES20.glVertexAttribPointer(normalHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, normalBuffer);
     
        //画三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);

        //禁用顶点数组
        GLES20.glDisableVertexAttribArray(positionHandle);
        GLES20.glDisableVertexAttribArray(normalHandle);
        GLES20.glUseProgram(0);
    }

}

因为项目中没有贴图,所以没有加载贴图,代码是demo版的,只供参考。

3.MyGLSurfaceView

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;

public class MyGLSurfaceView extends GLSurfaceView {
    private final MyGLRenderer renderer;

    public MyGLSurfaceView(Context context){
        super(context);

        // Create an OpenGL ES 2.0 context
        setEGLContextClientVersion(2);

        renderer = new MyGLRenderer();
        String filePath = context.getExternalCacheDir() + "/";
        renderer.setPath(filePath);
        // Set the Renderer for drawing on the GLSurfaceView
        //注意在设置Renderer之前要在glthread线城中解析完数据加载到内存中
        setRenderer(renderer);
        //不会实时绘制模型,调用requestRender()才会绘制
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }


    private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
    private float previousX;
    private float previousY;

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        // MotionEvent reports input details from the touch screen
        // and other input controls. In this case, you are only
        // interested in events where the touch position changed.

        float x = e.getX();
        float y = e.getY();

        switch (e.getAction()) {
            case MotionEvent.ACTION_MOVE:

                float dx = x - previousX;
                float dy = y - previousY;

                // reverse direction of rotation above the mid-line
                if (y > getHeight() / 2) {
                    dx = dx * -1 ;
                }

                // reverse direction of rotation to left of the mid-line
                if (x < getWidth() / 2) {
                    dy = dy * -1 ;
                }

                renderer.setAngle(
                        renderer.getAngle() +
                                ((dx + dy) * TOUCH_SCALE_FACTOR));
                requestRender();
        }

        previousX = x;
        previousY = y;
        return true;
    }
}

//效果如下图:


image.png

没有贴图看不清楚 。。!

这个模型还是比较小的,顶点数不多,如果是大模型,一百万个顶点甚至更多的时候,加载会非常耗时,不适合在项目中使用,提个建议,模型解析用c去做,会提升很多效率,亲测同一个模型,用java解析需要2—3f分钟,c的话只要几秒钟。顶点数据,法线数据,推荐用vbo去做。

上一篇下一篇

猜你喜欢

热点阅读