如何在OpenGl ES 中实现绘画圆 与条带,以及实现点索引:
ok、二话不说上代码,
首先是ShaderUtil.java文件:
package com.example.sample_5_9_mine;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import android.content.res.Resources;
import android.opengl.GLES20;
import android.util.Log;
public class ShaderUtil {
public static int loadShader(int shaderType,String source){
int shader = GLES20.glCreateShader(shaderType);
if(shader != 0){
GLES20.glShaderSource(shader,source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled,0);
if(compiled[0] == 0){
Log.e("ES20_ERROR:","Could not compiled shader");
Log.e("ES20_ERRROR",GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
public static int createProgram(String vertexSource,String fragmentSource){
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,vertexSource);
if(vertexShader == 0){
return 0;
}
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentSource);
if(pixelShader == 0){
return 0;
}
int program = GLES20.glCreateProgram();
if(program!=0){
GLES20.glAttachShader(program,vertexShader);
checkGlError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachError");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus,0);
if(linkStatus[0] != GLES20.GL_TRUE){
Log.e("ES_ERROR","Could not lined");
Log.e("ES_ERROR",GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
public static void checkGlError(String op){
int error;
while((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR ){
Log.e("ES20_ERROR",op+" :glError");
throw new RuntimeException(op + ":glError");
}
}
public static String loadFromAssetsFile(String fname,Resources r){
String result = null;
try{
InputStream in = r.getAssets().open(fname);
int ch = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while( (ch=in.read()) != -1 ){
baos.write(ch);
}
byte[] buff = baos.toByteArray();
baos.close();
in.close();
result = new String(buff,"UTF-8");
result = result.replaceAll("\\r\\n", "\n");
}
catch(Exception e){
e.printStackTrace();
}
return result;
}
}
Constant.java文件:
package com.example.sample_5_9_mine;
public class Constant {
public static final float UNIT_SIZE = 1f;
public static float ratio;
}
MatrixState.java文件:
package com.example.sample_5_9_mine;
import java.nio.ByteBuffer;
import android.opengl.Matrix;
public class MatrixState {
private static float[] mProjMatrix = new float[16];
private static float[] mVMatrix = new float[16];
private static float[] currMatrix ;
static float[][] mStack = new float[10][16];
static int stackTop = -1;
public static void setInitStack(){
currMatrix = new float[16];
Matrix.setRotateM(currMatrix, 0, 0, 1, 0, 0);
}
public static void pushMatrix(){
stackTop++;
for(int i=0;i<16;i++){
mStack[stackTop][i] = currMatrix[i];
}
}
public static void popMatrix(){
for(int i=0;i<16;i++){
currMatrix[i] = mStack[stackTop][i];
}
stackTop--;
}
public static void translate(float x, float y, float z){
Matrix.translateM(currMatrix, 0, x, y, z);
}
static ByteBuffer llbb = ByteBuffer.allocateDirect( 3 * 4);
static float[] cameraLocation = new float[3];
public static void setCamera(
float cx, float cy, float cz,
float tx, float ty, float tz,
float upx, float upy, float upz){
Matrix.setLookAtM(mVMatrix, 0, cx, cy, cz, tx, ty, tz, upx, upy, upz);
}
public static void setProjectFrustum(
float left, float right,float bottom,
float top, float near, float far){
Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);
}
public static void setProjectOrtho(
float left, float right,float bottom,
float top, float near, float far){
Matrix.orthoM(mProjMatrix, 0, left, right, bottom, top, near, far);
}
static float[] mMVPMatrix = new float[16];
public static float[] getFinalMatrix(){
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, currMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
return mMVPMatrix;
}
public static float[] getMMatrix(){
return currMatrix;
}
}
Belt.java文件:
package com.example.sample_5_9_mine;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.opengl.GLES20;
public class Belt {
int mProgram;
int muMVPMatrixHandle;
int maPositionHandle;
int maColorHandle;
String mVertexShader;
String mFragmentShader;
FloatBuffer mVertexBuffer;
FloatBuffer mColorBuffer;
private ByteBuffer mIndexBuffer;
int vCount = 0;
int iCount = 0;
public Belt(MySurfaceView mv){
initVertexData();
initShader(mv);
}
public void initVertexData(){
int n = 6;
vCount = 2 * (n + 1);
float angdegBegin = -90;
float angdegEnd = 90;
float angdegSpan = (angdegEnd - angdegBegin) / n;
float[] vertices = new float[vCount * 3];
int count = 0;
for(float angdeg = angdegBegin;angdeg <= angdegEnd;angdeg += angdegSpan){
double angrad = Math.toRadians(angdeg);
vertices[count++] = (float) (-0.6f * Constant.UNIT_SIZE * Math
.sin(angrad));// 顶点坐标
vertices[count++] = (float) (0.6f * Constant.UNIT_SIZE * Math
.cos(angrad));
vertices[count++] = 0;
// 当前点
vertices[count++] = (float) (-Constant.UNIT_SIZE * Math.sin(angrad));// 顶点坐标
vertices[count++] = (float) (Constant.UNIT_SIZE * Math.cos(angrad));
vertices[count++] = 0;
}
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
mVertexBuffer = vbb.asFloatBuffer();
mVertexBuffer.put(vertices);
mVertexBuffer.position(0);
iCount = vCount;
byte indices[] = new byte[iCount];
for(int i = 0; i< iCount;i++){
indices[i] = (byte) i;
}
mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
mIndexBuffer.put(indices);
mIndexBuffer.position(0);
count = 0;
float colors[] = new float[vCount * 4];
for(int i= 0; i<colors.length;i+=8){
colors[count++] = 1;
colors[count++] = 1;
colors[count++] = 1;
colors[count++] = 0;
colors[count++] = 0;
colors[count++] = 1;
colors[count++] = 1;
colors[count++] = 0;
}
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);
cbb.order(ByteOrder.nativeOrder());
mColorBuffer = cbb.asFloatBuffer();
mColorBuffer.put(colors);
mColorBuffer.position(0);
}
public void initShader(MySurfaceView mv) {
mVertexShader = ShaderUtil.loadFromAssetsFile("vertex.sh",
mv.getResources());
mFragmentShader = ShaderUtil.loadFromAssetsFile("frag.sh",
mv.getResources());
mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
maColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
}
public void drawSelf() {
GLES20.glUseProgram(mProgram);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false,
MatrixState.getFinalMatrix(), 0);
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT,
false, 3 * 4, mVertexBuffer);
GLES20.glVertexAttribPointer(maColorHandle, 4, GLES20.GL_FLOAT, false,
4 * 4, mColorBuffer);
GLES20.glEnableVertexAttribArray(maPositionHandle);
GLES20.glEnableVertexAttribArray(maColorHandle);
GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, iCount,
GLES20.GL_UNSIGNED_BYTE, mIndexBuffer);
}
}
Circle.java文件:
package com.example.sample_5_9_mine;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.opengl.GLES20;
public class Circle {
int mProgram;
int muMVPMatrixHandle;
int maPositionHandle;
int maColorHandle;
String mVertexShader;
String mFragmentShader;
FloatBuffer mVertexBuffer;
FloatBuffer mColorBuffer;
private ByteBuffer mIndexBuffer;
int vCount = 0;
int iCount = 0;
public Circle(MySurfaceView mv){
initVertexData();
initShader(mv);
}
public void initVertexData(){
int n = 10;
//参考Belt.java文件终于知道为什么这里是加2不是加1了!
//原因如下:作为条带,分成6份,需要在第七份上加一组边,
//Circle也是这样啊,表面上第一条边和最后一条边重合了,
//实际上在for循环时,重合的第一条边和最后一天都会画了一次~
vCount = n + 2;
float angdegSpan = 360.0f / n;
float[] vertices = new float[vCount * 3];
int count = 0;
vertices[count++] = 0;
vertices[count++] = 0;
vertices[count++] = 0;
for(float angdeg = 0;Math.ceil(angdeg) <= 360;angdeg+=angdegSpan){
double angrad = Math.toRadians(angdeg);
vertices[count++] = (float) (-Constant.UNIT_SIZE * Math.sin(angrad));
vertices[count++] = (float) ( Constant.UNIT_SIZE * Math.cos(angrad));
vertices[count++] = 0;
}
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());
mVertexBuffer = vbb.asFloatBuffer();
mVertexBuffer.put(vertices);
mVertexBuffer.position(0);
iCount = vCount;
byte indices[] = new byte[iCount];
for(int i = 0;i<iCount;i++){
indices[i] = (byte)i;
}
mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
mIndexBuffer.put(indices);
mIndexBuffer.position(0);
count = 0;
//r g b alpha
float colors[] = new float[vCount*4];
colors[count++] = 1;
colors[count++] = 1;
colors[count++] = 1;
colors[count++] = 0;
//因为这里是一次上色四个值!
for(int i = 4;i<colors.length; i+=4){
colors[count++] = 0;
colors[count++] = 1;
colors[count++] = 0;
colors[count++] = 0;
}
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
cbb.order(ByteOrder.nativeOrder());
mColorBuffer = cbb.asFloatBuffer();
mColorBuffer.put(colors);
mColorBuffer.position(0);
}
public void initShader(MySurfaceView mv){
mVertexShader = ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
mFragmentShader = ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());
mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
maColorHandle = GLES20.glGetAttribLocation(mProgram,"aColor");
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram,"uMVPMatrix");
}
public void drawSelf(){
GLES20.glUseProgram(mProgram);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, MatrixState.getFinalMatrix(),0);
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT,false,
3*4 , mVertexBuffer);
GLES20.glVertexAttribPointer(maColorHandle, 4, GLES20.GL_FLOAT, false,
4*4, mColorBuffer);
GLES20.glEnableVertexAttribArray(maPositionHandle);
GLES20.glEnableVertexAttribArray(maColorHandle);
GLES20.glDrawElements(GLES20.GL_TRIANGLE_FAN, iCount,GLES20.GL_UNSIGNED_BYTE,
mIndexBuffer);
}
}
MySurfaceView.java文件:
package com.example.sample_5_9_mine;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
class MySurfaceView extends GLSurfaceView
{
private SceneRenderer mRenderer;//场景渲染器
public MySurfaceView(Context context) {
super(context);
this.setEGLContextClientVersion(2); //设置使用OPENGL ES2.0
mRenderer = new SceneRenderer(); //创建场景渲染器
setRenderer(mRenderer); //设置渲染器
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//设置渲染模式为主动渲染
}
private class SceneRenderer implements GLSurfaceView.Renderer
{
Belt belt;//条状物
Circle circle;//圆
public void onDrawFrame(GL10 gl)
{
//清除深度缓冲与颜色缓冲
GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
//保护现场
MatrixState.pushMatrix();
//绘制条状物
MatrixState.pushMatrix();
MatrixState.translate(-1.3f, 0, 0);//沿x方向平移
belt.drawSelf();
MatrixState.popMatrix();
//绘制圆
MatrixState.pushMatrix();
MatrixState.translate(1.3f, 0, 0);//沿x方向平移
circle.drawSelf();
MatrixState.popMatrix();
//恢复现场
MatrixState.popMatrix();
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
//设置视窗大小及位置
GLES20.glViewport(0, 0, width, height);
//计算GLSurfaceView的宽高比
Constant.ratio = (float) width / height;
// 调用此方法计算产生透视投影矩阵
MatrixState.setProjectFrustum(-Constant.ratio, Constant.ratio, -1, 1, 20, 100);
// 调用此方法产生摄像机9参数位置矩阵
MatrixState.setCamera(0, 8f, 30, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
//初始化变换矩阵
MatrixState.setInitStack();
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//设置屏幕背景色RGBA
GLES20.glClearColor(0.5f,0.5f,0.5f, 1.0f);
//创建圆对象
circle=new Circle(MySurfaceView.this);
//创建条状物对象
belt=new Belt(MySurfaceView.this);
//打开深度检测
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
//打开背面剪裁
GLES20.glEnable(GLES20.GL_CULL_FACE);
}
}
}
MainActivity.java文件:
package com.example.sample_5_9_mine;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity {
private MySurfaceView mGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置为全屏
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 设置为横屏模式
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// 初始化GLSurfaceView
mGLSurfaceView = new MySurfaceView(this);
// 切换到主界面
setContentView(mGLSurfaceView);
mGLSurfaceView.requestFocus();// 获取焦点
mGLSurfaceView.setFocusableInTouchMode(true);// 设置为可触控
}
@Override
protected void onResume() {
super.onResume();
mGLSurfaceView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mGLSurfaceView.onPause();
}
}
frag.sh文件:
precision mediump float;
varying vec4 vColor; //接收从顶点着色器过来的参数
varying vec3 vPosition;//接收从顶点着色器过来的顶点位置
void main() {
gl_FragColor = vColor;//给此片元颜色值
}
vertex.sh文件:
uniform mat4 uMVPMatrix; //总变换矩阵
attribute vec3 aPosition; //顶点位置
attribute vec4 aColor; //顶点颜色
varying vec4 vColor; //用于传递给片元着色器的变量
void main() {
gl_Position = uMVPMatrix * vec4(aPosition,1); //根据总变换矩阵计算此次绘制此顶点位置
vColor = aColor;//将接收的颜色传递给片元着色器
}