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去做。
网友评论