想在手机上实现透视变换的效果,可是不想加opencv,所以只能找opengl的方法,经过不停的寻找终于让我找到了外国一个大神写的算法,写入代码,一运行
效果非常好,除了有锯齿,然后发现网页不知道什么时候被我关闭了,找了半天找不到了,算了如果有人发现请告诉我,谢谢
接下来贴代码
public class TestActivity extends Activity implements GLSurfaceView.Renderer {
private GLSurfaceView view;
int[] status = new int[1];
int program;
FloatBuffer attributeBuffer;
FloatBuffer positionBuffer;
ShortBuffer indicesBuffer;
short[] indicesData;
float[] attributesData;
float[] positionData;
private int[] textureIds = new int[1];
int textureId;
int attributePosition;
int attributeRegion;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = new GLSurfaceView(this);
view.setEGLContextClientVersion(2);
view.setRenderer(this);
setContentView(view);
}
@Override
public void onPause() {
view.onPause();
super.onPause();
}
@Override
public void onResume() {
super.onResume();
view.onResume();
}
public void onDrawFrame(GL10 gl) {
GLES20.glClearColor(0f, 0f, 0f, 1f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
drawNonAffine(100, 0, 600, 150, 700, 400, 50, 500);
positionBuffer.position(0);
positionBuffer.put(positionData);
positionBuffer.position(0);
GLES20.glVertexAttribPointer(attributePosition, 2, GLES20.GL_FLOAT, false, 2 * 4, positionBuffer);
GLES20.glEnableVertexAttribArray(attributePosition);
attributeBuffer.position(0);
attributeBuffer.put(attributesData);
attributeBuffer.position(0);
GLES20.glVertexAttribPointer(attributeRegion, 3, GLES20.GL_FLOAT, false, 3 * 4, attributeBuffer);
GLES20.glEnableVertexAttribArray(attributeRegion);
indicesBuffer.position(0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT, indicesBuffer);
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
String vertexShaderSource =
"attribute vec2 a_Position;" +
"attribute vec3 a_Region;" +
"varying vec3 v_Region;" +
"uniform mat3 u_World;" +
"void main()" +
"{" +
" v_Region = a_Region;" +
" vec3 xyz = u_World * vec3(a_Position, 1);" +
" gl_Position = vec4(xyz.xy, 0, 1);" +
"}";
String fragmentShaderSource =
"precision mediump float;" +
"varying vec3 v_Region;\n" +
"uniform sampler2D u_TextureId;\n" +
"void main(){\n" +
" vec2 r = v_Region.xy / v_Region.z;\n"+
" vec4 out_color = texture2D(u_TextureId, r);\n"+
" gl_FragColor = out_color;"+
"}";
attributeBuffer = ByteBuffer.allocateDirect(3 * 4 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
positionBuffer = ByteBuffer.allocateDirect(2 * 4 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
attributesData = new float[3 * 4];
positionData = new float[2*4];
indicesBuffer = ByteBuffer.allocateDirect(6 * 2).order(ByteOrder.nativeOrder()).asShortBuffer();
indicesData = new short[] { 0, 1, 2, 2, 3, 0 };
indicesBuffer.position(0);
indicesBuffer.put(indicesData);
program = loadProgram(vertexShaderSource, fragmentShaderSource);
textureId = loadTexture();
GLES20.glUseProgram(program);
float width = view.getWidth();
float height = view.getHeight();
float world[] = new float[] {
2f / width, 0, 0,
0, 2f / height, 0,
-1f, -1f, 1
};
int uniformWorld = GLES20.glGetUniformLocation(program, "u_World");
int uniformTextureId = GLES20.glGetUniformLocation(program, "u_TextureId");
GLES20.glUniformMatrix3fv(uniformWorld, 1, false, world, 0);
GLES20.glUniform1i(uniformTextureId, 0);
attributePosition = GLES20.glGetAttribLocation(program, "a_Position");
attributeRegion = GLES20.glGetAttribLocation(program, "a_Region");
}
public void setFilters(int minFilter, int magFilter) {
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, minFilter);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, magFilter);
}
public void setWrapping(int wrapS, int wrapT) {
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, wrapS);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, wrapT);
}
public void drawNonAffine(float bottomLeftX, float bottomLeftY, float bottomRightX, float bottomRightY, float topRightX, float topRightY, float topLeftX, float topLeftY) {
float ax = topRightX - bottomLeftX;
float ay = topRightY - bottomLeftY;
float bx = topLeftX - bottomRightX;
float by = topLeftY - bottomRightY;
float cross = ax * by - ay * bx;
boolean rendered = false;
if (cross != 0) {
float cy = bottomLeftY - bottomRightY;
float cx = bottomLeftX - bottomRightX;
float s = (ax * cy - ay * cx) / cross;
if (s > 0 && s < 1) {
float t = (bx * cy - by * cx) / cross;
if (t > 0 && t < 1) {
//uv coordinates for texture
float lbx = 0; // left bottom x
float lby = 1; // left bottom y
float rbx = 1; //right bottom x
float rby = 1; //right bottom y
float rtx = 1; //right top x
float rty = 0; //right top y
float ltx = 0; //left top x
float lty = 0; //left top x
// float u0 = 0; // texture bottom left u x
//
// float v0 = 0; // texture bottom left v y
//
// float u2 = 1; // texture top right u x
//
// float v2 = 1; // texture top right v y
int bufferIndex = 0;
float q0 = 1 / (1 - t);
float q1 = 1 / (1 - s);
float q2 = 1 / t;
float q3 = 1 / s;
positionData[bufferIndex++] = bottomLeftX;
positionData[bufferIndex++] = bottomLeftY;
positionData[bufferIndex++] = bottomRightX;
positionData[bufferIndex++] = bottomRightY;
positionData[bufferIndex++] = topRightX;
positionData[bufferIndex++] = topRightY;
positionData[bufferIndex++] = topLeftX;
positionData[bufferIndex++] = topLeftY;
bufferIndex = 0;
attributesData[bufferIndex++] = lbx * q0;
attributesData[bufferIndex++] = lby * q0;
attributesData[bufferIndex++] = q0;
attributesData[bufferIndex++] = rbx * q1;
attributesData[bufferIndex++] = rby * q1;
attributesData[bufferIndex++] = q1;
attributesData[bufferIndex++] = rtx * q2;
attributesData[bufferIndex++] = rty * q2;
attributesData[bufferIndex++] = q2;
attributesData[bufferIndex++] = ltx * q3;
attributesData[bufferIndex++] = lty * q3;
attributesData[bufferIndex++] = q3;
rendered = true;
}
}
}
if (!rendered) {
throw new RuntimeException("Shape must be concave and vertices must be clockwise.");
}
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
private int loadTexture() {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.forest);
GLES20.glGenTextures(1, textureIds, 0);
int textureId = textureIds[0];
if (textureId == 0) {
throw new RuntimeException("Could not generate texture.");
}
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
setFilters(GLES20.GL_LINEAR, GLES20.GL_LINEAR);
setWrapping(GLES20.GL_CLAMP_TO_EDGE, GLES20.GL_CLAMP_TO_EDGE);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
bitmap.recycle();
return textureId;
}
private int loadProgram(String vertexShaderSource, String fragmentShaderSource) {
int id = GLES20.glCreateProgram();
int vertexShaderId = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderSource);
int fragmentShaderId = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderSource);
GLES20.glAttachShader(id, vertexShaderId);
GLES20.glAttachShader(id, fragmentShaderId);
GLES20.glLinkProgram(id);
GLES20.glDeleteShader(vertexShaderId);
GLES20.glDeleteShader(fragmentShaderId);
GLES20.glGetProgramiv(id, GLES20.GL_LINK_STATUS, status, 0);
if (status[0] == 0) {
String log = GLES20.glGetProgramInfoLog(id);
GLES20.glDeleteProgram(id);
throw new RuntimeException("Shader error:" + log);
}
return id;
}
private int loadShader(int type, String source) {
int id = GLES20.glCreateShader(type);
GLES20.glShaderSource(id, source);
GLES20.glCompileShader(id);
GLES20.glGetShaderiv(id, GLES20.GL_COMPILE_STATUS, status, 0);
if (status[0] == 0) {
String log = GLES20.glGetShaderInfoLog(id);
GLES20.glDeleteShader(id);
throw new RuntimeException("Shader error:" + log);
}
return id;
}
private void checkGlError(String op) {
int error;
if ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
throw new RuntimeException("GL error code: " + error + " for " + op + ".");
}
}
}
接下来贴图像校正效果
校正到
还有点畸形,这是我简单的调试了一下,坐标都是我加一点加一点推出来的,想很好必须找出4个点的精准坐标进行换算
贴代码
public void drawNonAffine(float bottomLeftX, float bottomLeftY, float bottomRightX, float bottomRightY, float topRightX, float topRightY, float topLeftX, float topLeftY) {
float ax = topRightX - bottomLeftX;
float ay = topRightY - bottomLeftY;
float bx = topLeftX - bottomRightX;
float by = topLeftY - bottomRightY;
float cross = ax * by - ay * bx;
boolean rendered = false;
if (cross != 0) {
float cy = bottomLeftY - bottomRightY;
float cx = bottomLeftX - bottomRightX;
float s = (ax * cy - ay * cx) / cross;
if (s > 0 && s < 1) {
float t = (bx * cy - by * cx) / cross;
if (t > 0 && t < 1) {
//uv coordinates for texture
float lbx = 0; // left bottom x
float lby = 1; // left bottom y
float rbx = 0.83f; //right bottom x
float rby = 1; //right bottom y
float rtx = 1; //right top x
float rty = 0; //right top y
float ltx = 0.17f; //left top x
float lty = 0.275f; //left top x
// float u0 = 0; // texture bottom left u x
//
// float v0 = 0; // texture bottom left v y
//
// float u2 = 1; // texture top right u x
//
// float v2 = 1; // texture top right v y
int bufferIndex = 0;
float q0 = 1 / (1 - t);
float q1 = 1 / (1 - s);
float q2 = 1 / t;
float q3 = 1 / s;
positionData[bufferIndex++] = bottomLeftX;
positionData[bufferIndex++] = bottomLeftY;
positionData[bufferIndex++] = bottomRightX;
positionData[bufferIndex++] = bottomRightY;
positionData[bufferIndex++] = topRightX;
positionData[bufferIndex++] = topRightY;
positionData[bufferIndex++] = topLeftX;
positionData[bufferIndex++] = topLeftY;
bufferIndex = 0;
attributesData[bufferIndex++] = lbx * q0;
attributesData[bufferIndex++] = lby * q0;
attributesData[bufferIndex++] = q0;
attributesData[bufferIndex++] = rbx * q1;
attributesData[bufferIndex++] = rby * q1;
attributesData[bufferIndex++] = q1;
attributesData[bufferIndex++] = rtx * q2;
attributesData[bufferIndex++] = rty * q2;
attributesData[bufferIndex++] = q2;
attributesData[bufferIndex++] = ltx * q3;
attributesData[bufferIndex++] = lty * q3;
attributesData[bufferIndex++] = q3;
rendered = true;
}
}
}
if (!rendered) {
throw new RuntimeException("Shape must be concave and vertices must be clockwise.");
}
}
我改的就是纹理坐标的8个点的数据,lbx,lby,rbx,rby,rtx,rty,ltx,lty
drawNonAffine(0, 0, 194, 0, 194, 257, 0, 257);
我传入的是图片的长宽,也可以自己设置一个矩形,只要不是不规则四边形就可以了
补上那位大神的链接
https://bitlush.com/blog/arbitrary-quadrilaterals-in-opengl-es-2-0
GitHub