最通俗的安卓OpenGL教學08——正交投影、矩陣變換
阿新 • • 發佈:2020-12-22
技術標籤:OpenGL ES
在上幾節,我們渲染得到到的圖片紋理很明顯是被拉昇了的,這裡我們就要利用正交投影,用矩陣變換來對座標進行重新計算,使用了正交投影后,不管物體多遠多近,物體看起來總是形狀、大小相同的。
在OpenGL中要改變頂點座標的範圍,可以用矩形來重新計算,最後再歸一化就可以。
正交投影的使用步驟如下:
- 在頂點著色器中新增矩陣
- 根據圖形寬高和螢幕寬高計算需要顯示的範圍
- 使用矩陣進行變換
先看下效果:
1. 在頂點著色器中新增矩陣
attribute vec4 vPosition;
attribute vec2 fPosition;
varying vec2 ft_Position;
uniform mat4 u_Matrix;
void main(){
gl_Position = vPosition * u_Matrix;
ft_Position = fPosition;
}
2. 根據圖形寬高和螢幕寬高計算需要顯示的範圍
@Override
public void onSurfaceChanged(int width, int height) {
//設定視窗大小
GLES20.glViewport(0, 0, width, height);
//只獲取一下要載入圖片的大小,不載入
BitmapFactory. Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(mContext.getResources(), R.drawable.nobb, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
if (width > height) {
Matrix.orthoM(matrix, 0, -width / ((height / imageHeight * 1f) * imageWidth * 1f), width / ((height / imageHeight * 1f) * imageWidth * 1f), -1f, 1f, -1f, 1f);
} else {
Matrix.orthoM(matrix, 0, -1, 1, -height / ((width / imageWidth * 1f) * imageHeight * 1f), height / ((width / imageWidth * 1f) * imageHeight * 1f), -1f, 1f);
}
}
3. 使用矩陣進行變換
GLES20.glUniformMatrix4fv(u_matrix, 1, false, matrix, 0);
3. 完整程式碼
package com.york.media.opengl.demo.bitmap;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import com.york.media.opengl.R;
import com.york.media.opengl.egl.YGLSurfaceView;
import com.york.media.opengl.egl.YShaderUtil;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
/**
* author : York
* date : 2020/12/20 17:06
* desc : 加入正交投影
*/
public class YBitmapOrthogonalRender implements YGLSurfaceView.YGLRender {
private final Context mContext;
private final FloatBuffer vertexBuffer;
private final FloatBuffer fragmentBuffer;
private int program;
private int vPosition;
private int fPosition;
private int[] textureIds;
private int u_matrix;
private final float[] matrix = new float[16];
public YBitmapOrthogonalRender(Context context) {
this.mContext = context;
//頂點座標
float[] vertexData = {
-1f, -1f,
1f, -1f,
-1f, 1f,
1f, 1f
};
//讀取頂點座標
vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertexData);
vertexBuffer.position(0);
//紋理座標
float[] fragmentData = {
0f, 1f,
1f, 1f,
0f, 0f,
1f, 0f
};
//讀取紋理座標
fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(fragmentData);
fragmentBuffer.position(0);
}
@Override
public void onSurfaceCreated() {
//載入頂點著色器 shader
String vertexSource = YShaderUtil.getRawResource(mContext, R.raw.screen_vert_matrix);
//載入片元著色器 shader
String fragmentSource = YShaderUtil.getRawResource(mContext, R.raw.screen_frag);
//獲取源程式
program = YShaderUtil.createProgram(vertexSource, fragmentSource);
//從渲染程式中得到著頂點色器中的屬性
vPosition = GLES20.glGetAttribLocation(program, "vPosition");
//從渲染程式中得到片元著色器中的屬性
fPosition = GLES20.glGetAttribLocation(program, "fPosition");
//從渲染程式中得到投影的屬性
u_matrix = GLES20.glGetUniformLocation(program, "u_Matrix");
//建立 1個紋理,放入到 int [] textureIds, 一共有 30多個 紋理
textureIds = new int[1];
GLES20.glGenTextures(1, textureIds, 0);//第三個引數是指從哪兒開始取
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
//設定環繞方式
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);
//設定過濾方式
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);
}
@Override
public void onSurfaceChanged(int width, int height) {
//設定視窗大小
GLES20.glViewport(0, 0, width, height);
//只獲取一下要載入圖片的大小,不載入
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(mContext.getResources(), R.drawable.nobb, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
if (width > height) {
Matrix.orthoM(matrix, 0, -width / ((height / imageHeight * 1f) * imageWidth * 1f), width / ((height / imageHeight * 1f) * imageWidth * 1f), -1f, 1f, -1f, 1f);
} else {
Matrix.orthoM(matrix, 0, -1, 1, -height / ((width / imageWidth * 1f) * imageHeight * 1f), height / ((width / imageWidth * 1f) * imageHeight * 1f), -1f, 1f);
}
}
@Override
public void onDrawFrame() {
//清除螢幕,此處用的是紅色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1f, 0f, 0f, 1f);
//使用著色器源程式
GLES20.glUseProgram(program);
GLES20.glUniformMatrix4fv(u_matrix, 1, false, matrix, 0);
//使能頂點屬性陣列,使之有效
GLES20.glEnableVertexAttribArray(vPosition);
//使能之後,為頂點屬性賦值,繫結頂點座標
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8, vertexBuffer);
//使能片元屬性陣列,使之有效
GLES20.glEnableVertexAttribArray(fPosition);
//使能之後,為片元屬性賦值,繫結紋理座標
GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8, fragmentBuffer);
//啟用紋理 0號
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
//繫結 2D紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[0]);
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.nobb);
//設定圖片 bitmap
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();//用完及時回收啊
//繪製圖形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
//解綁 2D紋理
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
}
嗯,看: