capacity:容量
allocate:分配
Coord:坐标
vertex:顶点
Square:平面
Program:程序
Compile:编译
font:字体
pname:工程名称
Validate:验证
Uniform:均匀
ByteBuffer和FloatBuffer是java.nio包下的类
ByteBuffer bb=ByteBuffer.allocate(capacity);//参数是一个容量
ByteBuffer bb2=ByteBuffer.allocateDirect(capacity);//这个方法在参与I/O操作性能会更好
called unimplemented OpenGL ES API 报错。。
坐标系:安卓中的坐标系在横屏的时候会变形,变粗,所以在横屏的时候需要处理摄像机。
屏幕的左上角是(-1.0,1.0)横向向右为X轴正向,纵向向下为Y轴负向,其范围都是从 -1到 +1。
我们在该类中定义的float类型的数据并不能直接被opengl使用,float[]是属于虚拟机环境的,而Opengl作为本地系统库
直接运行在硬件上,所以我们需要将float[] 转化为FloatBuffer以使数据可以被opengl使用
//如下浮点数组代表的是一个三角形的顶点坐标
float[] vertex={0.1f,0.1f,
0.1f,0.1f,
0.1f,0.1f};
//浮点缓冲
FloatBuffer fb=FloatBuffer.allocate(int capacity);
//把顶点坐标添加到FloatBuffer中,并把位置0作为开始位置。
fb.put(vertex);
fb.position(0);
着色器语言(shading language),由C/C++编写
如下是顶点shader
attribute vec4 a_Position;
void main()
{
gl_Position = a_Position;
}
gl_Position即opengl定义的顶点的坐标,我们目的就是通过这个来告诉opengl我们的顶点数据。
vec4是着色器语言中的向量类型的一种,包含了四个浮点数的向量
如下是面shader
precision mediump float;
uniform vec4 u_Color;
void main()
{
gl_FragColor = u_Color;
}
传入一个颜色信息。这里注意一下 上面顶点着色器的 限定符 attribute 和 uniform 。attribute 一般用于每个顶点
各不相同的量,如顶点位置等,后者一般用于同一组顶点组成的相同的量,如光源位置,一组颜色等。
编写shader的帮助类ShaderHelper和TextResourceReader,可以直接使用。
在使用的时候通过TextResourceReader的readTextFileFromResource(Context context,int resourceId)方法返回shader字符串,然后再用ShaderHelper的buildProgram(String vertexShaderSource,String fragmentShaderSource)返回的内容直接使用。
ShaderHelper主要功能是
1,读取shader文本内容
2,编译shader
3,将顶点shader和面shader链接成program
4,验证program
TextResourceReader主要功能是
1,把shader源代码通过流的转换返回我们需要的shader字符串。
public class ShaderHelper {
private static final String TAG = "ShaderHelper";
/**
* 加载并编译顶点shader,返回得到的opengl id
* @param shaderCode
* @return
*/
public static int compileVertexShader(String shaderCode) {
return compileShader(GL_VERTEX_SHADER, shaderCode);
}
/**
* 加载并编译片段shader,返回opengl id
* @param shaderCode
* @return
*/
public static int compileFragmentShader(String shaderCode) {
return compileShader(GL_FRAGMENT_SHADER, shaderCode);
}
/**
* 加载并编译着色器,返回opengl id
* @param type
* @param shaderCode
* @return
*/
private static int compileShader(int type, String shaderCode) {
// 建立新的着色器对象
final int shaderObjectId = glCreateShader(type);
if (shaderObjectId == 0) {
if (LoggerConfig.ON) {
Log.w(TAG, "不能创建新的着色器.");
}
return 0;
}
// 传递着色器资源代码.
glShaderSource(shaderObjectId, shaderCode);
//编译着色器
glCompileShader(shaderObjectId);
// 获取编译的状态
final int[] compileStatus = new int[1];
glGetShaderiv(shaderObjectId, GL_COMPILE_STATUS,
compileStatus, 0);
if (LoggerConfig.ON) {
//打印log
Log.v(TAG, "代码编译结果:" + "\n" + shaderCode
+ "\n:" + glGetShaderInfoLog(shaderObjectId));
}
// 确认编译的状态
if (compileStatus[0] == 0) {
// 如果编译失败,则删除该对象
glDeleteShader(shaderObjectId);
if (LoggerConfig.ON) {
Log.w(TAG, "编译失败!.");
}
return 0;
}
// 返回着色器的opengl id
return shaderObjectId;
}
/**
* 链接顶点着色器和片段着色器成一个program
* 并返回这个pragram的opengl id
* @param vertexShaderId
* @param fragmentShaderId
* @return
*/
public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
// 新建一个program对象
final int programObjectId = glCreateProgram();
if (programObjectId == 0) {
if (LoggerConfig.ON) {
Log.w(TAG, "不能新建一个 program");
}
return 0;
}
//把点shader添加到program中
glAttachShader(programObjectId, vertexShaderId);
//把shader添加到program中
glAttachShader(programObjectId, fragmentShaderId);
//将两个着色器连接成一个program
glLinkProgram(programObjectId);
// 获取连接状态
final int[] linkStatus = new int[1];
glGetProgramiv(programObjectId, GL_LINK_STATUS,linkStatus, 0);
if (LoggerConfig.ON) {
// Print the program info log to the Android log output.
Log.v(
TAG,
"Results of linking program:\n"
+ glGetProgramInfoLog(programObjectId));
}
// 验证连接状态
if (linkStatus[0] == 0) {
// 如果失败就删除
glDeleteProgram(programObjectId);
if (LoggerConfig.ON) {
Log.w(TAG, "连接 program 失败!.");
}
return 0;
}
// Return the program object ID.
return programObjectId;
}
/**
* Validates an OpenGL program. Should only be called when developing the
* application.
*/
public static boolean validateProgram(int programObjectId) {
glValidateProgram(programObjectId);
final int[] validateStatus = new int[1];
glGetProgramiv(programObjectId, GL_VALIDATE_STATUS,
validateStatus, 0);
Log.v(TAG, "Results of validating program: " + validateStatus[0]
+ "\nLog:" + glGetProgramInfoLog(programObjectId));
return validateStatus[0] != 0;
}
/**
* /**
* 编译,连接 ,返回 program 的 ID
* @param vertexShaderSource
* @param fragmentShaderSource
* @return
*/
public static int buildProgram(String vertexShaderSource,
String fragmentShaderSource) {
int program;
// Compile the shaders.
int vertexShader = compileVertexShader(vertexShaderSource);
int fragmentShader = compileFragmentShader(fragmentShaderSource);
// Link them into a shader program.
program = linkProgram(vertexShader, fragmentShader);
if (LoggerConfig.ON) {
validateProgram(program);
}
return program;
}
}
如下工具类TextResourceReader是把shader源代码通过流的转换返回我们需要的shader字符串。
public class TextResourceReader {
public static String readTextFileFromResource(Context context,
int resourceId) {
StringBuilder body = new StringBuilder();
try {
InputStream inputStream =
context.getResources().openRawResource(resourceId);
InputStreamReader inputStreamReader =
new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String nextLine;
while ((nextLine = bufferedReader.readLine()) != null) {
body.append(nextLine);
body.append('\n');
}
} catch (IOException e) {
throw new RuntimeException(
"Could not open resource: " + resourceId, e);
} catch (Resources.NotFoundException nfe) {
throw new RuntimeException("Resource not found: " + resourceId, nfe);
}
return body.toString();
}
}
方便打印的工具类
public class LoggerConfig {
public static final boolean ON = true;
}
图形类
public class Square2 {
private Context context;
/*------------------第一步: 修改顶点数据-------------------------*/
//矩形(其实是菱形)顶点坐标 ,按逆时针
static float squareCoords[] = { -0.25f, 0.5f , // top left
0.75f, 0.5f , // top right
-0.75f, -0.5f , // bottom left
0.25f, -0.5f }; // bottom right
//float类型的字节数,用于给FloatBuffer指定容量时使用
private static final int BYTES_PER_FLOAT = 4;
// 数组中每个顶点的坐标数
static final int COORDS_PER_VERTEX = 2;
/*------------------第二步: 修改顶点个数-------------------------*/
private static final int POSITION_COMPONENT_COUNT = 4;
//要把定义的float坐标数组转化成FloatBuffer类型,因为它是直接运行在硬件上的,不是java虚拟机。
private FloatBuffer vertexBuffer;
//A_POSITION是顶点shader的变量名,U_COLOR是片段shader的变量名 ,它们用于匹配shader文件中的变量。
private static final String A_POSITION = "a_Position";
private static final String U_COLOR = "u_Color";
//------------获得program的ID的含义类似的
private int uColorLocation;
private int aPositionLocation;
private int program;//保存program的id
//矩形的构造方法
public Square2(Context context) {
this.context = context;
vertexBuffer = ByteBuffer
.allocateDirect(squareCoords.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
// 把坐标数组加入FloatBuffer中,并且从0下标开始读取
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);
getProgram();
//----------第三步: 获取这两个ID ,通过匹配shader文件中的变量来获取
uColorLocation = GLES20.glGetUniformLocation(program, U_COLOR);
aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
//---------第四步: 传入数据
//GLES20.glVertexAttribPointer(int indx, int size, int type, boolean normalized, int stride, Buffer ptr)
//int index:???
//int size:每个顶点的坐标数
//int type:类型,用GLES20.GL_FLOAT
//boolean normalized:???是否统一化
//int stride:???跨度
//Buffer ptr:承载坐标数组转换成的FloatBuffer。
GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false, 0, vertexBuffer);
//使面变成顶点数组???
GLES20.glEnableVertexAttribArray(aPositionLocation);
}
//获取program,
private void getProgram(){
//获取顶点shader文本
String vertexShaderSource = TextResourceReader
.readTextFileFromResource(context, R.raw.simple_vertex_shader);
//获取片段shader文本
String fragmentShaderSource = TextResourceReader
.readTextFileFromResource(context, R.raw.simple_fragment_shader);
//获取program的id
program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
//使用该program
GLES20.glUseProgram(program);
}
public void draw(){
GLES20.glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);
//绘制模式
//GLES20.glDrawArrays(int mode, int first, int count)
//int mode:绘制模式, GLES20.GL_TRIANGLE_STRIP:将传入的顶点按照顺序依次连接进行绘制
//int first:从数组的哪个位置开始
//int count:绘制的个数
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, POSITION_COMPONENT_COUNT);
}
}
Render类
public class MyRender implements Renderer {
private Context context;
public MyRender(Context context){
this.context = context;
}
//定义三角形对象
Triangle triangle;
Square2 square;
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
Log.w("MyRender","onSurfaceCreated");
// TODO Auto-generated method stub
//First:设置清空屏幕用的颜色,前三个参数对应红绿蓝,最后一个对应alpha
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
// triangle = new Triangle(context);
square = new Square2(context);
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
Log.w("MyRender","onSurfaceChanged");
// TODO Auto-generated method stub
//Second:设置视口尺寸,即告诉opengl可以用来渲染的surface大小
glViewport(0,0,width,height);
}
public void onDrawFrame(GL10 gl) {
Log.w("MyRender","onDrawFrame");
// TODO Auto-generated method stub
//Third:清空屏幕,擦除屏幕上所有的颜色,并用之前glClearColor定义的颜色填充整个屏幕
glClear(GL_COLOR_BUFFER_BIT);
square.draw();
}
}
Activity
public class MainActivity extends Activity {
private GLSurfaceView glSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
glSurfaceView = new GLSurfaceView(this);
glSurfaceView.setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
glSurfaceView.setRenderer(new MyRender(this));
setContentView(glSurfaceView);
}
}
手机的屏幕和普通的OpenGL的坐标系不一样,因为手机横竖屏转换的时候会拉伸,
它的坐标系是Y轴向上为正,向下为负,X轴向右为正,X轴向左为负。
坐标范围是
左上角(-1.0,1.0);
右上角(1.0,1.0);
左下角(-1.0,-1.0);
右下角(1.0,-1.0);
一个正方形按照Z字形画出两个三角形,重合的点可以复用前提是在draw的时候使用GLES20.GL_TRIANGLE_STRIP模式,在画图之前需要指定顶点数量,每个顶点所占有的坐标数(坐标是从数组中依次获取,必须把float数组转换成FloatBuffer类型,OpenGL是直接在硬件中进行的,不是虚拟机)。如果使用的是不复用的模式,那么一个矩形需要指定6对坐标,也就是画出两个三角形。
画圆