OpenGL_ES_画圆

效果如下:
这里写图片描述

画圆的难点是

1,openGL的坐标系和手机实际的坐标系是不相同的
2,OpenGL ES只能画点,线,三角形,如何实现圆呢

问题一

解决方式:是使用正交投影,实现OpenGL坐标转变成手机设备坐标

问题二

解决方式:我们三角形扇来实现如下图所示

这里写图片描述

如图我们可以根据圆心到圆边画足够多的三角形 这样就可以形成圆形了(参考圆周率的计算方式)

代码如下

/**
 * 画圆
*/
public class Demo3Activity extends AppCompatActivity {

private GLSurfaceView glSurfaceView;
private boolean isSetRender;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //判断是否支持OpenGL ES 2.0
    if (!isSupportES()){
        //不支持
        return;
    }
    //支持
    glSurfaceView = new GLSurfaceView(this);
    //设置es的版本是2.0
    glSurfaceView.setEGLContextClientVersion(2);
    glSurfaceView.setRenderer(new MyRender());
    isSetRender = true;
    setContentView(glSurfaceView);

}

class MyRender implements GLSurfaceView.Renderer{
    private final int POSITION_CONMONENT_COUNT = 2;
    private final int FLOAT_PER_BYTE = 4;
    private final FloatBuffer floatBuffer;
    private final String A_POSITION = "a_Position";
    private final String U_COLOr = "u_Color";
    private final String U_MATRIX = "u_Matrix";
    private int aPositionLocation;
    private int uColorLocation;
    private int uMatrixLocation;
    private final float[] vertexs;
    float[] proMatrix = new float[16];

    public MyRender(){
        //首先定义圆需要32个顶点,但是圆的闭合的 换句话说就是圆的首部和尾部是重合 所以圆一共需要33个点 多一个点用来和首部重合 实现闭合
        //这里设置圆心为中心(0,0)
        vertexs = new float[33*POSITION_CONMONENT_COUNT];
        //设置圆的半径
        float radius = 0.5f;
        //获取屏幕的宽高比
        DisplayMetrics displayMetrics = Demo3Activity.this.getResources().getDisplayMetrics();
        int offer =0 ;
        for (int i=0;i<32;i++){
            vertexs[offer++] = (float) (radius*Math.cos(i*2*Math.PI/32));
            vertexs[offer++] = (float) (radius*Math.sin(i*2*Math.PI/32));
        }
        vertexs[offer++] = (float) (radius*Math.cos(0));
        vertexs[offer++] = (float) (radius*Math.sin(0));

        //完成圆的顶点数据的
        floatBuffer = ByteBuffer.allocateDirect(vertexs.length * FLOAT_PER_BYTE)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer().put(vertexs);
    }

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
        GLES20.glClearColor(1.0f,1.0f,1.0f,1.0f);
        int programID = ShaderHelp.buildProgram(TextResourceRead.readTextFileFromResource(Demo3Activity.this,
                R.raw.demo3_vertex), TextResourceRead.readTextFileFromResource(Demo3Activity.this, R.raw.demo2_fragment));
        if (programID==0){
            return;
        }

        aPositionLocation =GLES20.glGetAttribLocation(programID,A_POSITION);
        uColorLocation = GLES20.glGetUniformLocation(programID,U_COLOr);
        uMatrixLocation = GLES20.glGetUniformLocation(programID,U_MATRIX);
        //这个让本地缓存从0开始读取数据 是非常重要的 如果不写的话会出现显示混乱
        floatBuffer.position(0);
        GLES20.glVertexAttribPointer(aPositionLocation,POSITION_CONMONENT_COUNT,GLES20.GL_FLOAT,false,0,floatBuffer);
        GLES20.glEnableVertexAttribArray(aPositionLocation);
        GLES20.glUseProgram(programID);



    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int i, int i1) {
        float aspectRatio = (float) i>(float) i1?(float) i/(float) i1:(float) i1/(float) i;
        if (i<i1){
            //宽大于高
            Matrix.orthoM(proMatrix,0,-1,1,-aspectRatio,aspectRatio,-1,1);
        }else {
            //宽小于高
            Matrix.orthoM(proMatrix,0,-aspectRatio,aspectRatio,-1,1,-1,1);
        }
    }

    @Override
    public void onDrawFrame(GL10 gl10) {

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glUniformMatrix4fv(uMatrixLocation,1,false,proMatrix,0);
        GLES20.glUniform4f(uColorLocation,1.0f,0f,0f,1.0f);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN,0,66);

    }
}


@Override
protected void onResume() {
    super.onResume();
    if (isSetRender){
        glSurfaceView.onResume();
    }
}

@Override
protected void onPause() {
    super.onPause();
    if (isSetRender){
        glSurfaceView.onPause();
    }
}

/**
 * 判断是否支持 Es2.0
 *
 * @return true 支持2.0
 */
public boolean isSupportES() {
    ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    ConfigurationInfo deviceConfigurationInfo = activityManager.getDeviceConfigurationInfo();
//        return deviceConfigurationInfo.reqGlEsVersion>=0x20000; 这段代码只能在真机上运行不能在模拟器上运行,为了兼容模拟器,应该  写如下的代码
    return deviceConfigurationInfo.reqGlEsVersion >= 0x20000
            || (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")));

}
}

顶点着色器 demo3_vertex.glsl

attribute vec4 a_Position;
uniform mat4 u_Matrix;
void main() {
gl_Position = u_Matrix*a_Position;
}

片段着色器 demo2_fragment.glsl

precision mediump float;
uniform vec4 u_Color;
void main() {
gl_FragColor = u_Color;
}

shaderHelp 类

public class ShaderHelp {
/**
 * 编译顶点着色器
 * @param shaderCode 着色器代码
 * @return
 */
public static int compileVertexShader(String shaderCode){
   return compileShader(GLES20.GL_VERTEX_SHADER,shaderCode);
}

/**
 * 编译片段着色器
 * @param shaderCode 着色器代码
 * @return
 */
public static int compileFragmentShader(String shaderCode){
    return compileShader(GLES20.GL_FRAGMENT_SHADER,shaderCode);
}

/**
 * 编译着色器
 * @param type 着色器类型
 * @param shaderCode 着色器代码
 * @return 成功创建的着色器对象的引用或者0代表着创建或者编译失败
 */
public static int compileShader(int type,String shaderCode){
    //根据着色器类型  创建着色器 返回着色器的引用ID
    int shaderID = GLES20.glCreateShader(type);
    if (shaderID==0){
        //0代表着创建失败
        LogUtil.log_d("着色器创建失败");
        return 0;
    }
    //如果创建着色器成功的话,将传入的着色器代码传入到创建的着色器中
    GLES20.glShaderSource(shaderID,shaderCode);
    //编译着色器
    GLES20.glCompileShader(shaderID);
    //获取编译状态 判断编译结果是否正确
    int[] compileStatus = new int[1];
    GLES20.glGetShaderiv(shaderID, GLES20.GL_COMPILE_STATUS,compileStatus,0);
    //打印编译结果
    LogUtil.log_d("编译结果是:shadercode==\n"+shaderCode+"\n"+"结果:"+GLES20.glGetShaderInfoLog(shaderID)+"\n");
    //判断编译结果
    if (compileStatus[0]==0){
        //删除着色器对象
        GLES20.glDeleteShader(shaderID);
        LogUtil.log_d("编译失败");
        return 0;
    }
    //此时代表着编译成功
    return shaderID;
}

/**
 * 连接片段着色器和顶点着色器 成为一个程序program
 * @param vertexShaderID 顶点的着色器id
 * @param fragmentShaderID 片段着色器id
 * @return 返回程序id 0代表着程序创建或者连接失败
 */
public static int linkProgram(int vertexShaderID,int fragmentShaderID){
    //创建程序
    int programID = GLES20.glCreateProgram();
    if (programID==0){
        LogUtil.log_d("着色器程序创建失败");
        return 0;
    }
    //创建成功
    //将顶点着色器和片段着色器连接到程序上
    GLES20.glAttachShader(programID,vertexShaderID);
    GLES20.glAttachShader(programID,fragmentShaderID);
    //链接程序
    GLES20.glLinkProgram(programID);
    //获取着色器链接程序的结果
    int[] linkstatus = new int[1];
    GLES20.glGetProgramiv(programID, GLES20.GL_LINK_STATUS,linkstatus,0);
    if (linkstatus[0]==0){
        //链接失败
        LogUtil.log_d("程序链接失败:"+GLES20.glGetProgramInfoLog(programID)+"\n");
        return 0;
    }
    //链接成功
    return programID;
}

/**
 * 通过顶点着色器代码和片段着色器代码 建立程序
 * @param vertexShaderSource 顶点着色器
 * @param fragmentShaderSource 片段着色器
 * @return  建立代码的id
 */
public static int buildProgram(String vertexShaderSource,String fragmentShaderSource){
    int vertexShaderID = compileVertexShader(vertexShaderSource);
    int fragmentShaderID = compileFragmentShader(fragmentShaderSource);
    int programID;
    programID = linkProgram(vertexShaderID,fragmentShaderID);
    validateProgram(programID);
    return programID;
}

/**
 * 验证该程序是否是高效的
 * @param programID
 * @return
 */
public static boolean validateProgram(int programID){
    GLES20.glValidateProgram(programID);
    int[] validateStatus = new int[1];
    GLES20.glGetProgramiv(programID, GLES20.GL_VALIDATE_STATUS,validateStatus,0);
    LogUtil.log_d("程序是否高效的验证结果是:"+validateStatus[0]+"\n返回信息是"+GLES20.glGetProgramInfoLog(programID));
    return validateStatus[0]!=0;
}
}

TextResourceRead类

/**
 * Created by WangKunKun on 2018/7/16
 * 注解:读取资源文件中的着色器
**/
public class TextResourceRead {

public static String readTextFileFromResource(Context context, int resourceID) {
    StringBuilder stringBuilder = new StringBuilder();
    try {
        InputStream inputStream = context.getResources().openRawResource(resourceID);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String nextLine;
        while ((nextLine = bufferedReader.readLine()) != null) {
            stringBuilder.append(nextLine);
            stringBuilder.append("\n");
        }
    } catch (IOException e) {
        throw new RuntimeException("无法打开资源:"+resourceID,e);
    } catch (Resources.NotFoundException r) {
        throw new RuntimeException("找不到资源:"+resourceID,r);
    }
    return stringBuilder.toString();
}

}

猜你喜欢

转载自blog.csdn.net/wkk_ly/article/details/81198779