第五章 OpenGL ES 基礎(chǔ)-透視投影矩陣與正交投影矩陣
第一章 OpenGL ES 基礎(chǔ)-屏幕、紋理、頂點坐標(biāo)
第二章 OpenGL ES 基礎(chǔ)-GLSL語法簡單總結(jié)
第三章 OpenGL ES 基礎(chǔ)-GLSL渲染紋理
第四章 OpenGL ES 基礎(chǔ)-位移、縮放、旋轉(zhuǎn)原理
第五章 OpenGL ES 基礎(chǔ)-透視投影矩陣與正交投影矩陣
什么是透視投影?
模型都是3D的,但屏幕是2D的。如何將3D空間投影到2D平面,還能保持深度的視覺效果?在OpenGL中,采用透視投影矩陣作用頂點來實現(xiàn),即完成縮放、選擇、位移之后,進行透視投影的操作。
投影矩陣原理
投影矩陣用于投影變換,投影變換是三維場景中的物體正確渲染到二維屏幕的重要過程之一。在透視矩陣中,有幾個重要元素:視場角、成像設(shè)備的寬高比、場景中能看到的最近距離以及最遠(yuǎn)距離,通過這幾個參數(shù)可以定義一個視錐體對象,從而模擬人眼或者相機的在三維空間中的成像原理,通常有這個幾個值就可以構(gòu)造一個4x4的矩陣,通過OpenGL提供的接口設(shè)置即可。參數(shù)(near=n, far=f, left=l, right=r, top=t, bottom=b),對應(yīng)矩陣如下:
假設(shè)下圖是Z方向投影到屏幕的立體圖如下 ,根據(jù)上面矩陣參數(shù)可以推到為圖的對應(yīng)參數(shù)
側(cè)視圖看
根據(jù)上圖推出對應(yīng)參數(shù)技術(shù)
- 1.aspect:屏幕寬高比m_screenWidth / m_screenHeight
- 2.n:近截面z軸坐標(biāo)
- 3.f :遠(yuǎn)截面z軸坐標(biāo)
- 4.fov角度
- 5.我們可以求出t的長度t=n*tan(fov/2)
- 6.t = n * Math.tan(fov / 2);// n*tan(fov / 2)=T(勾股定理,計算直角邊)
- 7.b = -t;
- 8.r = aspect * t;
- 9.l= -r;
上面推到的對應(yīng)矩陣參數(shù),為什么要做這樣矩陣,視錐體視圖進一步的介紹,更加深刻認(rèn)識和理解還是圖解,下面透視投影進行點位和對應(yīng)參數(shù),如下:
對稱攝像機在相機空間中 YZ 平面上的視錐體視圖。請注意,頂部(t)、底部(b)和近裁剪面距離(n)都決定了垂直視野角(θ)。遠(yuǎn)處平面上的 Y 范圍是通過比率 f/n 計算的,因為對于原點/近處三角形和原點/遠(yuǎn)處三角形來說,這些角度都是相同的。由下圖可以計算相關(guān)點位
對稱攝像機在相機空間中 XZ 平面上的視錐體視圖。與圖1幾乎相同,只是使用左側(cè)(l)和右側(cè)(r)的視錐體范圍。
t = n * Math.tan(fov / 2);
b = -t;
r = aspect * t;
l= -r;
通過上面知識和原理,就可以實現(xiàn)對應(yīng)代碼生成4x4的矩陣
-
const double DEG2RAD = 3.14159265 / 180;
:該行定義了一個常量 DEG2RAD,用于將角度轉(zhuǎn)換為弧度 -
float tangent = tanf(fFov / 2.f * DEG2RAD);
:在這行中,fFov 表示視場角(Field of View),它通常以角度表示。首先,將 fFov 視場角轉(zhuǎn)換為弧度,并取其一半。
m3dLoadIdentity44對應(yīng)的初始化可以查看這篇-OpenGL 位移、縮放、旋轉(zhuǎn)原理
void m3dMakePerspectiveMatrix(M3DMatrix44f mProjection, float fFov, float fAspect, float zMin, float zMax)
{
m3dLoadIdentity44(mProjection); // Fastest way to get most valid values already in place
const double DEG2RAD = 3.14159265 / 180;
float tangent = tanf(fFov / 2.f * DEG2RAD); // tangent of half fovY
float height = zMin * tangent; // half height of near plane
float width = height * fAspect; // half width of near plane
float l = -width, r = width, b = -height, t = height, n = zMin, f = zMax;
// params: left, right, bottom, top, near, far
mProjection[0] = 2 * n / (r - l);
mProjection[5] = 2 * n / (t - b);
mProjection[8] = (r + l) / (r - l);
mProjection[9] = (t + b) / (t - b);
mProjection[10] = -(f + n) / (f - n);
mProjection[11] = -1;
mProjection[14] = -(2 * f * n) / (f - n);
mProjection[15] = 0;
}
正交投影矩陣原理
正交投影矩陣中 x 軸方向的縮放值
正交投影矩陣(Orthographic Projection Matrix)相較于透視投影矩陣具有以下特點:
-
等比例縮放:正交投影矩陣將物體等比例地投影到屏幕上,不會因為距離遠(yuǎn)近而導(dǎo)致物體大小的變化,保持了物體在觀察者視野中的大小一致性。
-
平行投影:所有的光線都是平行的,與觀察者的位置無關(guān)。這意味著從不同位置看到的物體大小和形狀保持不變。
-
沒有透視效果:正交投影不會出現(xiàn)透視效果,即物體遠(yuǎn)近大小差異不會隨著距離改變而產(chǎn)生。
-
簡單計算:相對于透視投影,正交投影的計算比較簡單,只需要定義視圖空間的邊界值即可,不涉及復(fù)雜的透視變換公式。
結(jié)合圖,我們知道,正交投影其實就是將目標(biāo)視域體[L,B,N]至[R,T,F]的區(qū)域
度量到規(guī)則視域體[-1,-1,0]至[1,1,1]的區(qū)域中,請仔細(xì)理解這句話,這意味著,我可以將很大的一個區(qū)域映射到的規(guī)則視域體cvv中,也可以將很小的區(qū)域映射到使視域體中,而這個區(qū)域的具體的大小是由L,B,N 至R,T,F的區(qū)域大小決定的。
在正交投影矩陣中,通常不需要考慮將 x 軸方向的縮放值乘以 near 或者 far 的情況。在一個簡單的正交投影矩陣中,x、y、z 三個軸的縮放比例是相同的,因此只需根據(jù)視圖空間的邊界值計算出寬度和高度的倒數(shù)(r_width 和 r_height),然后分別乘以一個恰當(dāng)?shù)南禂?shù)即可。
由于正交投影矩陣的特性是保持物體在觀察者視野中等比例地縮放,并且是平行投影,因此在計算 x、y、z 軸的縮放值時,并不需要考慮 near 或 far 參數(shù)對縮放的影響,這兩個參數(shù)主要用于定義裁剪空間的深度范圍,而非影響各個軸的縮放比例。
因此,在正交投影矩陣中,一般會根據(jù)邊界值計算出寬度和高度的倒數(shù),然后根據(jù)需要乘以適當(dāng)?shù)南禂?shù)來確定每個軸的縮放比例,而不涉及 near 或 far 對縮放值的影響。
由上圖推出對應(yīng)代碼生成4x4的矩陣
void EffectMatrix4::orthoM(float m[16], int mOffset,
float left, float right, float bottom, float top,
float near, float far) {
float r_width = 1.0f / (right - left);
float r_height = 1.0f / (top - bottom);
float r_depth = 1.0f / (far - near);
float x = 2.0f * (r_width);
float y = 2.0f * (r_height);
float z = -2.0f * (r_depth);
float tx = -(right + left) * r_width;
float ty = -(top + bottom) * r_height;
float tz = -(far + near) * r_depth;
m[mOffset + 0] = x;
m[mOffset + 5] = y;
m[mOffset +10] = z;
m[mOffset +12] = tx;
m[mOffset +13] = ty;
m[mOffset +14] = tz;
m[mOffset +15] = 1.0f;
m[mOffset + 1] = 0.0f;
m[mOffset + 2] = 0.0f;
m[mOffset + 3] = 0.0f;
m[mOffset + 4] = 0.0f;
m[mOffset + 6] = 0.0f;
m[mOffset + 7] = 0.0f;
m[mOffset + 8] = 0.0f;
m[mOffset + 9] = 0.0f;
m[mOffset + 11] = 0.0f;
}
正交投影矩陣用法
正交投影矩陣作用,為了適配2D畫面到渲染屏幕上等比縮放不變形
案例1:
播放視頻,畫面寬高,是如何視頻寬高匹配屏幕寬高?
分析:頂點坐標(biāo)四個點分別在[-1,1]之間,視頻播放正常要左右占滿,要么上下占滿,那個有一遍頂點要進行寬高比
用法 : vertexData
頂點坐標(biāo), vertexSource
頂點著色器, u_Matrix*av_Position
,對頂點進行矩陣操作使用正交投影
-
screenRatio = (float) screenWidth / screenHeight;
:表示屏幕縱橫比(寬高比)的值。 -
videoRatio = (float) videoWidth / videoHeight;
: 表示視頻縱橫比(寬高比)的值。 -
videoRatio > screenRatio
: 這個條件的滿足意味著視頻的橫向跨度相對于其縱向跨度更大,或者說視頻是橫向拉伸的。橫向占滿屏幕也是就(left,right)為(-1f, 1f),反之一樣道理上下占滿。
橫向占滿采用:Matrix.orthoM(matrix, 0, -1f, 1f, -videoRatio / screenRatio, videoRatio / screenRatio, -1f, 1f);
這行代碼是設(shè)置居于頂點坐標(biāo)[-1,1]
之間一個正交投影矩陣,-1f, 1f, -videoRatio / screenRatio, videoRatio / screenRatio
分別為float left, float right, float bottom, float top
左右為滿left=-1,right=1,上下縮放top=videoRatio / screenRatio,bottom= -videoRatio / screenRatio,重點理解 :居于頂點坐標(biāo)[-1,1]
之間一個正交投影矩陣
//頂點坐標(biāo)(原點為顯示區(qū)域中心店)
private final float[] vertexData = {
-1.0f, -1.0f, //左下角
1.0f, -1.0f, //右下角
-1.0f, 1.0f, //左上角
1.0f, 1.0f, //右上角
};
String vertexSource ="attribute vec4 av_Position;\n" +
"attribute vec4 af_Position;//S T 紋理坐標(biāo)\n" +
"varying vec2 v_texPosition;\n" +
"uniform mat4 u_Matrix;\n" +
"void main() {\n" +
" v_texPosition= af_Position.xy;\n" +
" gl_Position = u_Matrix*av_Position ;\n" +
"}\n";
public void updateProjection(int videoWidth, int videoHeight) {
float screenRatio = (float) screenWidth / screenHeight;
this.videoWidth = videoWidth;
this.videoHeight = videoHeight;
float videoRatio = (float) videoWidth / videoHeight;
if (videoRatio > screenRatio) {
Matrix.orthoM(matrix, 0, -1f, 1f, -videoRatio / screenRatio, videoRatio / screenRatio, -1f, 1f);
} else {
Matrix.orthoM(matrix, 0, -screenRatio / videoRatio, screenRatio / videoRatio, -1f, 1f, -1f, 1f);
}
}
案例2:
坐標(biāo)原點對應(yīng)屏幕左上角的貼紙怎么實現(xiàn)貼在屏幕上
頂點著色器文章來源:http://www.zghlxwxcb.cn/news/detail-837433.html
uniform mat4 uMVPMatrix; // 變換矩陣
attribute vec4 aPosition; // 圖像頂點坐標(biāo)
attribute vec2 aTextureCoord; // 圖像紋理坐標(biāo)
varying vec2 textureCoordinate; // 圖像紋理坐標(biāo)
void main() {
gl_Position = uMVPMatrix * aPosition;
textureCoordinate = aTextureCoord.xy;
}
vertexData
是按貼紙的實際像素 float[] value={0f,0f,500,500};//x,y,w,h
為準(zhǔn), Matrix.orthoM(matrix, 0, 0, mFrameWidth, mFrameHeight,0 , -1, 1);
和案例1原理一樣,按寬高mFrameWidth,mFrameHeight的模版,0, mFrameWidth, mFrameHeight,0
分別為 float left, float right, float bottom, float top
,left=0,right=mFrameWidth,bottom=mFrameHeight,top=0,對應(yīng)vertexData
的點位和正交投影矩陣相乘實現(xiàn),歸一化標(biāo)準(zhǔn)的OpenGL的[-1,1]的標(biāo)準(zhǔn)點位文章來源地址http://www.zghlxwxcb.cn/news/detail-837433.html
//坐標(biāo)原點對應(yīng)屏幕左上角
private final float[] vertexData = {
0f, 0f,//左上角坐標(biāo)
500f, 0f,//右上角坐標(biāo)
0f, 500f, //左下角坐標(biāo)
500f, 500f,//右下角坐標(biāo)
};
Matrix.orthoM(matrix, 0, 0, mFrameWidth, mFrameHeight,0 , -1, 1);
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, matrix,0);
//傳遞頂點坐標(biāo)到shader
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glVertexAttribPointer(mPositionHandle, 2, GLES20.GL_FLOAT, false,
8, vertexBuffer);
到了這里,關(guān)于第五章 OpenGL ES 基礎(chǔ)-透視投影矩陣與正交投影矩陣的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!