效果如下:
画圆的难点是
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();
}
}