OpenGL ES点精灵、线段和三角形的绘制
点精灵的绘制
以下对shader的编译和程序链接进行了封装(更多细节请参考 OpenGL——三角形图元的绘制):
public class ShaderHelper {
private static final String TAG = "ShaderHelper";
public static int compileVertexShader(String shaderCode) {
return compileShader(GLES20.GL_VERTEX_SHADER, shaderCode);
}
public static int compileFragmentShader(String shaderCode) {
return compileShader(GLES20.GL_FRAGMENT_SHADER, shaderCode);
}
private static int compileShader(int type, String shaderCode) {
final int shaderObjectId = GLES20.glCreateShader(type);
if (shaderObjectId == 0) {
Log.e(TAG, "Could not create new shader.");
return 0;
}
GLES20.glShaderSource(shaderObjectId, shaderCode);
GLES20.glCompileShader(shaderObjectId);
final int[] compileStatus = new int[1];
GLES20.glGetShaderiv(shaderObjectId, GLES20.GL_COMPILE_STATUS,
compileStatus, 0);
if (compileStatus[0] == 0) {
// If it failed, delete the shader object.
GLES20.glDeleteShader(shaderObjectId);
Log.e(TAG, "Compilation of shader failed.");
return 0;
}
// Return the shader object ID.
return shaderObjectId;
}
public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
final int programObjectId = GLES20.glCreateProgram();
if (programObjectId == 0) {
Log.w(TAG, "Could not create new program");
return 0;
}
GLES20.glAttachShader(programObjectId, vertexShaderId);
GLES20.glAttachShader(programObjectId, fragmentShaderId);
// Link the two shaders together into a program.
GLES20.glLinkProgram(programObjectId);
// Get the link status.
final int[] linkStatus = new int[1];
GLES20.glGetProgramiv(programObjectId, GLES20.GL_LINK_STATUS,
linkStatus, 0);
// Print the program info log to the Android log output.
Log.v(TAG,
"Results of linking program:\n"
+ GLES20.glGetProgramInfoLog(programObjectId));
// Verify the link status.
if (linkStatus[0] == 0) {
// If it failed, delete the program object.
GLES20.glDeleteProgram(programObjectId);
Log.w(TAG, "Linking of program failed.");
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) {
GLES20.glValidateProgram(programObjectId);
final int[] validateStatus = new int[1];
GLES20.glGetProgramiv(programObjectId, GLES20.GL_VALIDATE_STATUS,
validateStatus, 0);
Log.v(TAG, "Results of validating program: " + validateStatus[0]
+ "\nLog:" + GLES20.glGetProgramInfoLog(programObjectId));
return validateStatus[0] != 0;
}
/**
* Helper function that compiles the shaders, links and validates the
* program, returning the program ID.
*/
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);
validateProgram(program);
return program;
}
}
public class ShaderProgram {
protected int mProgram = -1;
protected final int mWidth, mHeight;
protected Context mContext;
protected ShaderProgram(Context context, String vertex, String fragment, int width, int height) {
mContext = context;
if (!TextUtils.isEmpty(vertex) && !TextUtils.isEmpty(fragment)) {
mProgram = ShaderHelper.buildProgram(vertex, fragment);
}
mWidth = width;
mHeight = height;
}
public void useProgram() {
if (mProgram >= 0) {
GLES20.glUseProgram(mProgram);
}
}
public void release() {
if (mProgram >= 0) {
GLES20.glDeleteProgram(mProgram);
}
mProgram = -1;
}
创建一个PointProgram(为了测试,这里每次只提交一个point,不考虑性能的问题),opengles中并没有设置glPointSize的API,需要在shader里面设置内建变量 gl_PointSize 来设置点精灵的大小:
public class PointProgram extends ShaderProgram {
private static final int POSITION_COUNT = 2;
private static final int STRIDE = POSITION_COUNT * 4;
private final int mPositionLocation;
private final int mColorLocation;
private final int mPointSizeLocation;
private final FloatBuffer mFloatBuffer;
private final float[] mPositions = new float[2];
public PointProgram(Context context, int width, int height) {
super(context, VERTEX, FRAGMENT, width, height);
mPositionLocation = GLES20.glGetAttribLocation(mProgram, "a_Position");
mColorLocation = GLES20.glGetUniformLocation(mProgram, "u_Color");
mPointSizeLocation = GLES20.glGetUniformLocation(mProgram, "uPointSize");
mFloatBuffer = ByteBuffer
.allocateDirect(STRIDE)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
}
public void draw(PointF point, int color, float pointSize) {
useProgram();
mPositions[0] = transformX(point.x);
mPositions[1] = transformY(point.y);
mFloatBuffer.position(0);
mFloatBuffer.put(mPositions);
float r = Color.red(color) / 255f;
float g = Color.green(color) / 255f;
float b = Color.blue(color) / 255f;
float a = Color.alpha(color) / 255f;
GLES20.glUniform4f(mColorLocation, r, g, b, a);
mFloatBuffer.position(0);
GLES20.glVertexAttribPointer(mPositionLocation, 2, GLES20.GL_FLOAT, false, 0, mFloatBuffer);
GLES20.glEnableVertexAttribArray(mPositionLocation);
GLES20.glUniform1f(mPointSizeLocation, pointSize);
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1);
}
private static final String VERTEX = "attribute vec4 a_Position;\n" +
"uniform float uPointSize;\n" +
"void main() {\n" +
" gl_Position = a_Position;\n" +
" gl_PointSize = uPointSize;\n" +
"}";
private static final String FRAGMENT = "precision mediump float;\n" +
"\n" +
"uniform vec4 u_Color;\n" +
"\n" +
"void main() {\n" +
" gl_FragColor = u_Color;\n" +
"}";
绘制的调用:
public class myRenderer implements GLSurfaceView.Renderer {
private Context mContext;
private PointProgram mPointProgram = null;
private LineProgram mLineProgram = null;
private int mViewportWidth;
private int mViewportHeight;
public myRenderer(Context context){
mContext = context;
DisplayMetrics dm = context.getResources().getDisplayMetrics();
mViewportWidth = dm.widthPixels;
mViewportHeight = dm.heightPixels;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glViewport(0,0,mViewportWidth,mViewportHeight);
GLES20.glClearColor(0,0,0,0);
if (mPointProgram == null) {
mPointProgram = new PointProgram(mContext, mViewportWidth, mViewportHeight);
}
if(mLineProgram == null){
mLineProgram = new LineProgram(mContext, mViewportWidth, mViewportHeight);
}
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
}
@Override
public void onDrawFrame(GL10 gl) {
mPointProgram.draw(new PointF(mViewportWidth*0.5f,mViewportHeight*0.5f), Color.RED,50.f);
}
}
线段的绘制
创建一个LineProgram:
public class LineProgram extends ShaderProgram {
private static final int COORDS_PER_VERTEX = 2;
private static final int STRIDE = COORDS_PER_VERTEX * 4;
private static final int POSITION_COUNT = 2;
private final int mPositionLocation;
private final int mColorLocation;
public LineProgram(Context context, int width, int height) {
super(context, VERTEX, FRAGMENT, width, height);
mPositionLocation = GLES20.glGetAttribLocation(mProgram, "a_Position");
mColorLocation = GLES20.glGetUniformLocation(mProgram, "u_Color");
}
public void drawLines(PointF points[], int color, float linewidth) {
useProgram();
float[] pointpPosis = new float[points.length * POSITION_COUNT];
FloatBuffer pointsbuffer = ByteBuffer
.allocateDirect(STRIDE * points.length)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
for(int i = 0; i < points.length; i ++)
{
pointpPosis[i*2] = transformX(points[i].x);
pointpPosis[i*2+1] = transformY(points[i].y);
}
pointsbuffer.position(0);
pointsbuffer.put(pointpPosis);
pointsbuffer.position(0);
GLES20.glVertexAttribPointer(mPositionLocation, POSITION_COUNT, GLES20.GL_FLOAT,
false, STRIDE, pointsbuffer);
GLES20.glEnableVertexAttribArray(mPositionLocation);
float r = Color.red(color) / 255f;
float g = Color.green(color) / 255f;
float b = Color.blue(color) / 255f;
float a = Color.alpha(color) / 255f;
GLES20.glUniform4f(mColorLocation, r, g, b, a);
GLES20.glLineWidth(linewidth);
GLES20.glDrawArrays(GLES20.GL_LINES, 0, points.length);
GLES20.glDisableVertexAttribArray(mPositionLocation);
}
private static final String VERTEX = "attribute vec4 a_Position;\n" +
"\n" +
"void main() {\n" +
" gl_Position = a_Position;\n" +
"}";
private static final String FRAGMENT = "precision mediump float;\n" +
"\n" +
"uniform vec4 u_Color;\n" +
"\n" +
"void main() {\n" +
" gl_FragColor = u_Color;\n" +
"}";
}
调用绘制:
public void onDrawFrame(GL10 gl) {
PointF p1 = new PointF(mViewportWidth*0.2f,mViewportHeight*0.8f);
PointF p2 = new PointF(mViewportWidth*0.8f,mViewportHeight*0.4f);
PointF arr[] = {p1,p2};
mLineProgram.drawLines(arr,Color.WHITE,8.f);
}
三角形的绘制
创建一个triangleProgram:
public class TriangleProgram extends ShaderProgram {
private static final int COORDS_PER_VERTEX = 2;
private static final int STRIDE = COORDS_PER_VERTEX * 4;
private static final int POSITION_COUNT = 2;
private final int mPositionLocation;
private final int mColorLocation;
public TriangleProgram(Context context, int width, int height) {
super(context, VERTEX, FRAGMENT, width, height);
mPositionLocation = GLES20.glGetAttribLocation(mProgram, "a_Position");
mColorLocation = GLES20.glGetUniformLocation(mProgram, "u_Color");
}
public void drawTriangle(PointF points[], int color) {
useProgram();
float[] pointpPosis = new float[points.length * POSITION_COUNT];
FloatBuffer pointsbuffer = ByteBuffer
.allocateDirect(STRIDE * points.length)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
for(int i = 0; i < points.length; i ++)
{
pointpPosis[i*2] = transformX(points[i].x);
pointpPosis[i*2+1] = transformY(points[i].y);
}
pointsbuffer.position(0);
pointsbuffer.put(pointpPosis);
pointsbuffer.position(0);
GLES20.glVertexAttribPointer(mPositionLocation, POSITION_COUNT, GLES20.GL_FLOAT,
false, STRIDE, pointsbuffer);
GLES20.glEnableVertexAttribArray(mPositionLocation);
float r = Color.red(color) / 255f;
float g = Color.green(color) / 255f;
float b = Color.blue(color) / 255f;
float a = Color.alpha(color) / 255f;
GLES20.glUniform4f(mColorLocation, r, g, b, a);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, points.length);
GLES20.glDisableVertexAttribArray(mPositionLocation);
}
private static final String VERTEX = "attribute vec4 a_Position;\n" +
"\n" +
"void main() {\n" +
" gl_Position = a_Position;\n" +
"}";
private static final String FRAGMENT = "precision mediump float;\n" +
"\n" +
"uniform vec4 u_Color;\n" +
"\n" +
"void main() {\n" +
" gl_FragColor = u_Color;\n" +
"}";
绘制三角形:
p1 = new PointF(mViewportWidth*0.5f,mViewportHeight*0.2f);
p2 = new PointF(mViewportWidth*0.2f,mViewportHeight*0.7f);
PointF p3 = new PointF(mViewportWidth*0.8f,mViewportHeight*0.7f);
PointF triangleArr[] = {p1,p2,p3};
mTriangleProgram.drawTriangle(triangleArr,Color.BLUE);
最终输出展示:
源码地址:https://github.com/shadowcpp/OpenGL-ES/tree/master/openglesDemo