在图片渲染的时候,之前使用的顶点坐标是占满整个屏幕的归一化坐标
//顶点坐标
static float vertexData[] = { // in counterclockwise order:
-1f, -1f, 0.0f, // bottom left
1f, -1f, 0.0f, // bottom right
-1f, 1f, 0.0f, // top left
1f, 1f, 0.0f, // top right
};
这样就导致了如下图所示的的问题
所以我们应该根据屏幕宽高和图片宽高对应的比例算出正确的位置:
上面我们得到的( ?)是不在归一化坐标范围内的,为了能使OpenGL
正确的渲染,我们就需要把(?)以及其他边统一转换到归一化坐标内,这个操作就是正交投影
使用正交投影,不管物体多远多近,物体看起来总是形状、大小比例相同的。
在OpenGLES里面使用投影矩阵:
vertex_shader_m.glsl
attribute vec4 av_Position;//顶点位置
attribute vec2 af_Position;//纹理位置
varying vec2 v_texPo;//纹理位置 与fragment_shader交互
uniform mat4 u_Matrix; //投影矩阵
void main() {
v_texPo = af_Position;
gl_Position = av_Position * u_Matrix;
}
在使用的时候:
//投影矩阵
private int uMatrix;
private float[] matrix = new float[4 * 4];
//1. 获取投影矩阵
uMatrix = GLES20.glGetUniformLocation(program, "u_Matrix");
//2. 计算值
public void onSurfaceChanged(int width, int height) {
if (width > height) {
float x = width / ((float) height / bitmap.getHeight() * bitmap.getWidth());
Matrix.orthoM(matrix, 0, -x, x, -1, 1, -1, 1);
} else {
float y = height / ((float) width / bitmap.getWidth() * bitmap.getHeight());
Matrix.orthoM(matrix, 0, -1, 1, -y, y, -1, 1);
}
}
//3. 赋值
GLES20.glUniformMatrix4fv(uMatrix, 1, false, matrix, 0);
主要代码如下:BitmapTexture.java
import android.content.Context;
import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
//纹理 根据坐标系映射
public class BitmapTexture {
//顶点坐标
static float vertexData[] = { // in counterclockwise order:
-1f, -1f, 0.0f, // bottom left
1f, -1f, 0.0f, // bottom right
-1f, 1f, 0.0f, // top left
1f, 1f, 0.0f, // top right
};
//纹理坐标 对应顶点坐标 与之映射
static float textureData[] = { // in counterclockwise order:
0f, 1f, 0.0f, // bottom left
1f, 1f, 0.0f, // bottom right
0f, 0f, 0.0f, // top left
1f, 0f, 0.0f, // top right
};
//每一次取点的时候取几个点
static final int COORDS_PER_VERTEX = 3;
private final int vertexCount = vertexData.length / COORDS_PER_VERTEX;
//每一次取的总的点 大小
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
private Context context;
//位置
private FloatBuffer vertexBuffer;
//纹理
private FloatBuffer textureBuffer;
private int program;
private int avPosition;
//纹理位置
private int afPosition;
//正交投影
private int uMatrix;
private float[] matrix = new float[4 * 4];
//需要渲染的纹理id
private int imageTextureId;
private Bitmap bitmap;
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
public BitmapTexture(Context context) {
this.context = context;
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);
textureBuffer = ByteBuffer.allocateDirect(textureData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(textureData);
textureBuffer.position(0);
}
public void onSurfaceCreated() {
String vertexSource = ShaderUtil.readRawTxt(context, R.raw.vertex_shader_m);
String fragmentSource = ShaderUtil.readRawTxt(context, R.raw.fragment_shader);
program = ShaderUtil.createProgram(vertexSource, fragmentSource);
if (program > 0) {
//获取顶点坐标字段
avPosition = GLES20.glGetAttribLocation(program, "av_Position");
//获取纹理坐标字段
afPosition = GLES20.glGetAttribLocation(program, "af_Position");
uMatrix = GLES20.glGetUniformLocation(program, "u_Matrix");
imageTextureId = createImageTexture();
}
}
public void onSurfaceChanged(int width, int height) {
if (width > height) {
float x = width / ((float) height / bitmap.getHeight() * bitmap.getWidth());
Matrix.orthoM(matrix, 0, -x, x, -1, 1, -1, 1);
} else {
float y = height / ((float) width / bitmap.getWidth() * bitmap.getHeight());
Matrix.orthoM(matrix, 0, -1, 1, -y, y, -1, 1);
}
}
public void draw() {
//使用程序
GLES20.glUseProgram(program);
GLES20.glUniformMatrix4fv(uMatrix, 1, false, matrix, 0);
//绑定渲染纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imageTextureId);
GLES20.glEnableVertexAttribArray(avPosition);
GLES20.glEnableVertexAttribArray(afPosition);
//设置顶点位置值
GLES20.glVertexAttribPointer(avPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
//设置纹理位置值
GLES20.glVertexAttribPointer(afPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureBuffer);
//绘制 GLES20.GL_TRIANGLE_STRIP:复用坐标
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
GLES20.glDisableVertexAttribArray(avPosition);
GLES20.glDisableVertexAttribArray(afPosition);
//解绑纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
private int createImageTexture() {
int[] textureIds = new int[1];
//创建纹理
GLES20.glGenTextures(1, textureIds, 0);
if (textureIds[0] == 0) {
return 0;
}
//绑定纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
//环绕(超出纹理坐标范围) (s==x t==y GL_REPEAT 重复)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
//过滤(纹理像素映射到坐标点) (缩小、放大:GL_LINEAR线性)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
//测试图片
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
//解绑纹理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
return textureIds[0];
}
}
注意: 在使用FBO
GLES20.glTexImage2D
分配内存大小的时候,需要根据横竖屏来设置值。不然计算出来的值和渲染的宽高不一样,渲染就会出现变形。
public void onSurfaceChanged(int width, int height) {
if (width > height) {
float x = width / ((float) height / bitmap.getHeight() * bitmap.getWidth());
Matrix.orthoM(matrix, 0, -x, x, -1, 1, -1, 1);
} else {
float y = height / ((float) width / bitmap.getWidth() * bitmap.getHeight());
Matrix.orthoM(matrix, 0, -1, 1, -y, y, -1, 1);
}
if (fboId != 0) {
GLES20.glDeleteFramebuffers(1, new int[]{fboId}, 0);
GLES20.glDeleteTextures(1, new int[]{imageTextureId}, 0);
}
createFBO(width, height);
}
private void createFBO(int w, int h) {
if (bitmap == null) {
throw new IllegalArgumentException("bitmap is null");
}
//1. 创建FBO
int[] fbos = new int[1];
GLES20.glGenFramebuffers(1, fbos, 0);
fboId = fbos[0];
//2. 绑定FBO
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
//3. 创建FBO纹理
fboTextureId = createTexture();
//4. 把纹理绑定到FBO
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, fboTextureId, 0);
//5. 设置FBO分配内存大小
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, w, h,
0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
//6. 检测是否绑定从成功
if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER)
!= GLES20.GL_FRAMEBUFFER_COMPLETE) {
Log.e("zzz", "glFramebufferTexture2D error");
}
//7. 解绑纹理和FBO
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
}