#1 opengl学习资料:
(1.1)百度huachao1001,选择他的专栏,可以看到几篇很好的入门文章!
#2 入门demo:http://www.jianshu.com/p/442682fda917
再配上这个入门文章,http://blog.csdn.net/jason0539/article/details/9164885
上面两样东西可以让你很清晰地入门!
入门之前先看一下View,SurfaceView,GLSurfaceView的概念及区别:http://blog.csdn.net/androidwubo/article/details/60469183
#3 开始绘制第一个图形:三角形
demo及原理: http://blog.csdn.net/huachao1001/article/details/52044602
#4 绘制一个3d图形,而且让这个3d图形一直会动!(从网上随便下载一个.stl文件,存到手机sd卡中,然后读取这个.stl文件绘出对应的3d图!)
demo参考处: http://blog.csdn.net/huachao1001/article/details/52054334
自己的代码:
(4.1)布局文件不需要、清单文件中读sd卡权限;
(4.2)Model类:
package com.example.y81022671.a15_opengl_third;
import java.nio.FloatBuffer;
/**
* Created by y81022671 on 2017/7/4.
*/
public class Model {
private int facetCount;
private float[] verts;
private float[] vnorms;
private short[] remarks;
private FloatBuffer vertBuffer;
private FloatBuffer vnormBuffer;
float maxX;
float minX;
float maxY;
float minY;
float maxZ;
float minZ;
public Point getCenterPoint() {
float cx = minX + (maxX - minX) / 2;
float cy = minY + (maxY - minY) / 2;
float cz = minZ + (maxZ - minZ) / 2;
return new Point(cx, cy, cz);
}
public float getR() {
float dx = (maxX - minX);
float dy = (maxY - minY);
float dz = (maxZ - minZ);
float max = dx;
if (dy > max) {
max = dy;
}
if (dz > max) {
max = dz;
}
return max;
}
public float[] getVerts(float[] verts) {
return verts;
}
public void setVerts(float[] verts) {
this.verts = verts;
vertBuffer = Util.floatToBuffer(verts);
}
public float[] getVnorms(float[] vnorms) {
return vnorms;
}
public void setVnorms(float[] vnorms) {
this.vnorms = vnorms;
this.vnormBuffer = Util.floatToBuffer(vnorms);
}
public int getFacetCount() {
return facetCount;
}
public void setFacetCount(int facetCount) {
this.facetCount = facetCount;
}
public short[] getRemarks() {
return remarks;
}
public void setRemarks(short[] remarks) {
this.remarks = remarks;
}
public FloatBuffer getVertBuffer() {
return vertBuffer;
}
public FloatBuffer getVnormBuffer() {
return vnormBuffer;
}
}
(4.3)Util类:
package com.example.y81022671.a15_opengl_third;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
/**
* Created by y81022671 on 2017/7/4.
*/
public class Util {
public static FloatBuffer floatToBuffer(float[] a) {
ByteBuffer bb = ByteBuffer.allocateDirect(a.length * 4);
bb.order(ByteOrder.nativeOrder());
FloatBuffer buffer = bb.asFloatBuffer();
buffer.put(a);
buffer.position(0);
return buffer;
}
//根据stl文件的第81-第84这四个字节,求出该stl文件中有多少个三角面的个数!
public static int byte4ToInt(byte[] bytes, int offset) {
int b3 = bytes[offset + 3] & 0xFF;
int b2 = bytes[offset + 2] & 0xFF;
int b1 = bytes[offset + 1] & 0xFF;
int b0 = bytes[offset + 0] & 0xFF;
return (b3 << 24) | (b2 << 16) | (b1 << 8) | (b0);
}
//拿到bytes字节数组,从offset这个下标开始,数4个字节,然后将这4个字节转化成1个float类型的数值!
public static float byte4ToFloat(byte[] bytes, int offset) {
return Float.intBitsToFloat(byte4ToInt(bytes, offset));
}
public static int byte2ToShort(byte[] bytes, int offset) {
int b1 = bytes[offset + 1] & 0xFF;
int b0 = bytes[offset + 0] & 0xFF;
return (short) ((b1 << 8) | b0);
}
}
(4.4)STLReader类:
package com.example.y81022671.a15_opengl_third;
import android.content.Context;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by y81022671 on 2017/7/4.
*/
public class STLReader {
private StlLoadListener stlLoadListener = new StlLoadListener() {
@Override
public void onstart() {
System.out.println("StlLoadListener onstart");
}
@Override
public void onLoading(int cur, int total) {
System.out.println("StlLoadListener onLoading");
}
@Override
public void onFinished() {
System.out.println("StlLoadListener onFinished");
}
@Override
public void onFailure(Exception e) {
System.out.println("StlLoadListener onFailure");
}
};
public Model parserBinStlInSDCard(String path) throws IOException {
File file = new File(path);
FileInputStream fis = new FileInputStream(file);
return parserBinStl(fis);
}
public Model parserBinStlInAssets(Context context, String fileName) throws IOException {
InputStream is = context.getAssets().open(fileName);
return parserBinStl(is);
}
//根据二进制的stl文件读出它的相关数据!即解析二进制的stl文件
public Model parserBinStl(InputStream in) throws IOException {
if (null != stlLoadListener) {
stlLoadListener.onstart();
}
Model model = new Model();
in.skip(80);
byte[] bytes = new byte[4];
in.read(bytes);
int facetCount = Util.byte4ToInt(bytes, 0);
model.setFacetCount(facetCount);
if (facetCount == 0) {
in.close();
return model;
}
byte[] facetBytes = new byte[50 * facetCount];
in.read(facetBytes);
in.close();
//定义一个新的方法解析后面那些关于三角面的字节
parseModel(model, facetBytes);
if (null != stlLoadListener) {
stlLoadListener.onFinished();
}
return model;
}
//定义的一个新的方法,它的作用是:解析stl二进制文件的后面那些字节(所有三角面信息的那些字节!)
private void parseModel(Model model, byte[] facetBytes) {
int facetCount = model.getFacetCount();
float[] verts = new float[facetCount * 3 * 3];
float[] vnorms = new float[facetCount * 3 * 3];
short[] remarks = new short[facetCount];
int stlOffset = 0;
try {
for (int i = 0; i < facetCount; i++) {
if (null != stlLoadListener) {
stlLoadListener.onLoading(i, facetCount);
}
for (int j = 0; j < 4; j++) {
float x = Util.byte4ToFloat(facetBytes, stlOffset + 0);
float y = Util.byte4ToFloat(facetBytes, stlOffset + 4);
float z = Util.byte4ToFloat(facetBytes, stlOffset + 8);
stlOffset = stlOffset + 12;
if (j == 0) {
vnorms[i * 9 + 0] = x;
vnorms[i * 9 + 1] = y;
vnorms[i * 9 + 2] = z;
vnorms[i * 9 + 3] = x;
vnorms[i * 9 + 4] = y;
vnorms[i * 9 + 5] = z;
vnorms[i * 9 + 6] = x;
vnorms[i * 9 + 7] = y;
vnorms[i * 9 + 8] = z;
} else {
verts[i * 9 + (j - 1) * 3 + 0] = x;
verts[i * 9 + (j - 1) * 3 + 1] = y;
verts[i * 9 + (j - 1) * 3 + 2] = z;
}
if (i == 0 && j == 1) {
model.minX = model.maxX = x;
model.minY = model.maxY = y;
model.minZ = model.maxZ = z;
} else {
model.minX = Math.min(model.minX, x);
model.minY = Math.min(model.minY, y);
model.minZ = Math.min(model.minZ, z);
model.maxX = Math.max(model.maxX, x);
model.maxY = Math.max(model. maxY, y);
model.maxZ = Math.max(model.maxZ, z);
}
}
//stl文件的注释说明部分!
short r = (short) Util.byte2ToShort(facetBytes, facetCount);
stlOffset = stlOffset + 2;
remarks[i] = r;
}
} catch (Exception e) {
if (null != stlLoadListener) {
stlLoadListener.onFailure(e);
} else {
e.printStackTrace();
}
}
model.setVerts(verts);
model.setVnorms(vnorms);
model.setRemarks(remarks);
}
public static interface StlLoadListener {
void onstart();
void onLoading(int cur, int total);
void onFinished();
void onFailure(Exception e);
}
}
(4.5)MyRenderer类:
package com.example.y81022671.a15_opengl_third;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.os.Environment;
import java.io.File;
import java.io.IOException;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
/**
* Created by y81022671 on 2017/7/5.
*/
public class MyRenderer implements GLSurfaceView.Renderer {
private Model model;
private float mScalef = 1;
private Point mCenterPoint;
private Point eye = new Point(0, 0, -3);
private Point up = new Point(0, 1, 0);
private Point center = new Point(0, 0, 0);
private float mDegree = 0;
public MyRenderer(Context context) {
try {
String name0 = Environment.getExternalStorageDirectory() + "00";
String name1 = Environment.getExternalStorageDirectory() + File.pathSeparator + "smile.stl";
String name = new File(Environment.getExternalStorageDirectory() + File.separator + "smile.stl").getAbsolutePath();
String name2 = new File(Environment.getExternalStorageDirectory() + File.separator + "smile.txt").getName();
//model = new STLReader().parserBinStlInAssets(context, name);
//model = new STLReader().parserBinStlInAssets(context, name);
model = new STLReader().parserBinStlInSDCard(name);
} catch (IOException e) {
e.printStackTrace();
}
}
public void rotate(float degree) {
mDegree = degree;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glClearDepthf(1.0f);
gl.glDepthFunc(GL10.GL_LEQUAL);
gl.glShadeModel(GL10.GL_SMOOTH);
float r = model.getR();
mScalef = 0.5f / r;
mCenterPoint = model.getCenterPoint();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 45.0f, ((float) width / height), 1f, 100f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
GLU.gluLookAt(gl, eye.x, eye.y, eye.z, center.x, center.y, center.z, up.x, up.y, up.z);
gl.glRotatef(mDegree, 0, 1, 0);
gl.glScalef(mScalef, mScalef, mScalef);
gl.glTranslatef(-mCenterPoint.x, -mCenterPoint.y, -mCenterPoint.z);
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glNormalPointer(GL10.GL_FLOAT, 0, model.getVnormBuffer());
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, model.getVertBuffer());
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, model.getFacetCount() * 3);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}
}
(4.6) MainActivity类:
package com.example.y81022671.a15_opengl_third;
import android.app.ActivityManager;
import android.content.pm.ConfigurationInfo;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private boolean isSupportsEs2;
private GLSurfaceView glView;
private MyRenderer myRenderer;
private float rotateDegreen = 0;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
rotate(rotateDegreen);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
checkSupported();
if (isSupportsEs2) {
glView = new GLSurfaceView(this);
myRenderer = new MyRenderer(this);
glView.setRenderer(myRenderer);
setContentView(glView);
} else {
setContentView(R.layout.activity_main);
Toast.makeText(this, "当前设备不支持OpenGL ES 2.0!", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onResume() {
super.onResume();
if (null != glView) {
glView.onResume();
new Thread() {
@Override
public void run() {
while (true) {
try {
sleep(100);
rotateDegreen += 5;
handler.sendEmptyMessage(0x001);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
@Override
protected void onPause() {
super.onPause();
if (null != glView) {
glView.onPause();
}
}
private void rotate(float rotateDegreen) {
myRenderer.rotate(rotateDegreen);
glView.invalidate();
}
private void checkSupported() {
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
isSupportsEs2 = configurationInfo.reqGlEsVersion >= 0x2000;
boolean isEmulator = Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1
&& (Build.FINGERPRINT.startsWith("generic")
|| Build.FINGERPRINT.startsWith("unknown")
|| Build.MODEL.contains("google_sdk")
|| Build.MODEL.contains("Emulator")
|| Build.MODEL.contains("Android SDK built for x86"));
isSupportsEs2 = isSupportsEs2 || isEmulator;
}
}
#5