一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Android - OpenGL ES透視投影實現方法(四)

OpenGL ES透視投影實現方法(四)

2022-02-24 15:25weiers Android

這篇文章主要為大家詳細介紹了OpenGL ES透視投影的實現方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下

在之前的學習中,我們知道了一個頂點要想顯示到屏幕上,它的x、y、z分量都要在[-1,1]之間,我們回顧一下渲染管線的圖元裝配階段,它實際上做了以下幾件事:剪裁坐標、透視分割、視口變換。圖元裝配的輸入是頂點著色器的輸出,抓喲是物體坐標gl_Position,之后到光柵化階段。

圖元裝配

剪裁坐標

當頂點著色器寫入一個值到gl_Position時,這個點要求必須在剪裁空間中,即它的x、y、z坐標必須在[-w,w]之間,任何這個范圍之外的點都是不可見的。

這里需要注意以下,對于attribute類型的屬性量。OpenGL會用默認的值替換屬性中未指定的分量,前三個分量會被設定為0,最后一個分量w會被設定為1.

站在gl_position的角度來說,[-w,w]之間的坐標點才是可見的,否則都是不可見會被剪裁掉。往前看,在做投影變換的時候我們說,在視景體內的物體有效,視景體外的會被剪裁,實際上是對應的,剪裁就是發生在圖元裝配階段判斷所有的坐標是否在[-w,w]之間。

剪裁實際上就是判斷每一個最小三角形、直線、點單元的坐標是否規范。

透視除法

對上面的剪裁坐標的點的x、y、z坐標除以它的w分量,除以w的坐標叫做歸一化設備坐標。如果w分量大,除以w后的點就接近(0,0,0),在三維空間中,距離我們較遠的坐標如果它的w分量較大,進行透視除法后,就距離原點越近,原點作為遠處物體的消失點,就有三維場景的效果。

視口變換

前面已經使用過視口變換的函數glViewport了,視口是一個而為矩形窗口區域。是OpenGL渲染操作最終顯示的地方。

?
1
2
3
4
5
6
public static native void glViewport(
  int x,
  int y,
  int w,
  int h
 );

從歸一化設備坐標(x,y,z)到窗口坐標(X,Y,Z)的轉換公式

OpenGL ES透視投影實現方法(四)

上面公式中的f和n是如下API設置的

?
1
2
3
4
public static native void glDepthRangef(
  float n,
  float f
 );

n,f指定所需的深度范圍,n,f的取值限于(0.0,1.0)之間,n,f的默認值為0.0和1.0

glDepthRangef函數和glViewport函數指定的值用于將頂點位置從歸一化設備坐標轉換為窗口坐標。

利用w分量產生三維效果

在前面的代碼中,修改傳入的頂點坐標,增加w分量

?
1
2
3
4
5
6
float[] vertexArray = new float[] {
   (float) -0.5, (float) -0.5, 0, 1,
   (float) 0.5, (float) -0.5, 0, 1,
   (float) -0.5, (float) 0.5, 0, 3,
   (float) 0.5, (float) 0.5, 0, 3
  };

同時修改頂點著色器:

?
1
2
3
4
5
private String vertexShaderCode = "uniform mat4 uMVPMatrix;"
   + "attribute vec4 aPosition;"
   + "void main(){"
   + "gl_Position = uMVPMatrix * aPosition;"
   + "}";

以及獲取uProjectionMatrix以及傳入頂點數據對應的代碼,就可以看到如下所示效果

OpenGL ES透視投影實現方法(四)

透視投影

然而這樣讓物體產生三維效果的做法太死板了,如果我們還要讓物體平移縮放旋轉,這樣固定的指定w的值就不太好了。

透視投影這個時候就能派上用場了,利用透視投影矩陣自動生成w的值。投影矩陣主要是為w產生正確的值,這樣在渲染管線的后續操作中做透視除法,遠處的物體就看起來比進出物體小,很容易想到,可以利用頂點位置的z分量,將這個距離映射到w分量上,z越大,w也越大。

有兩個函數可以生成透視投影矩陣frustumM和perspectiveM。參數具體含義可以參考下面的圖

?
1
2
3
4
5
6
public static void perspectiveM(float[] m, // 生成的投影矩陣
        int offset,
        float fovy, // 視角角度
        float aspect, // 近平面的寬高比
        float zNear, // 近平面
        float zFar) // 遠平面

frustumM函數原型

?
1
2
3
4
public static void frustumM(float[] m, int offset,
 float left, float right, float bottom, float top, // 近平面左右下上部與中心點的距離
 float near, float far //近平面和元平面與攝像機觀察點的距離
 )

OpenGL ES透視投影實現方法(四)

透視投影背后的數學原理

創建下面的矩陣

OpenGL ES透視投影實現方法(四)

a表示視角焦距,焦距等于1/tan(視野/2)
取aspect=1.8,視野45度即a = 1,f = 10,n = 5,得到的透視投影矩陣為

OpenGL ES透視投影實現方法(四)

計算下面幾個點

OpenGL ES透視投影實現方法(四)

上面這三個點越來越遠,通過透視投影后,z和w都變大了,可以想到,在后面的透視除法時,x和y分量都會變小,于是就會出現距離越遠,匯聚到一個點,也就是三維效果。

同時也可以看到,上面的幾個點他們的z坐標都是負值,這也從側面表達了,事實上所有的有效的點z坐標必須是負值,也就是從攝像機的坐標來看是在z軸負方向,也就是必須在視景體里面,這一點通過攝像機矩陣來保證。

前面使用正交投影,它的矩陣不會使得w粉量增加,于是通過透視除法也不會使w分量增加,所以正交投影不會出現近大遠小的效果,透視投影會出現近大遠小的效果

透視投影例子

在上面矩形Demo的基礎上修改上面的正方形的頂點數據

?
1
2
3
4
5
6
float vertices[] = new float[] {
  (float) -0.5, (float) -0.5 + + (float)(-0.1*i), (float) (1*i),
  (float) 0.5, (float) -0.5 + + (float)(-0.1*i), (float) (1*i),
  (float) -0.5, (float) 0.5 + + (float)(-0.1*i), (float) (1*i),
  (float) 0.5, (float) 0.5 + + (float)(-0.1*i), (float) (1*i)
};

在繪圖時,定義一個數組,傳遞不同的i值,比如繪制四個正方形,這四個正方形的距離越來越遠。

?
1
2
3
4
mRectangles = new Rectangle[5];
   for (int i = 0; i < mRectangles.length; i++) {
    mRectangles[i] = new Rectangle(i);
   }

在onSurfaceChanged函數里面設置攝像機位置和透視投影矩陣

?
1
2
Matrix.perspectiveM(mProjectionMatrix, 0, 45, (float)width/height, 2, 15);
   Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 12, 0, 0, 0, 0, 1, 0);

然后在onDrawFram函數里面繪制這5個矩形

?
1
2
3
4
5
6
7
8
for (Rectangle rectangle : mRectangles) {
  Matrix.setIdentityM(mModuleMatrix, 0);
  Matrix.rotateM(mModuleMatrix, 0, xAngle, 1, 0, 0);
  Matrix.rotateM(mModuleMatrix, 0, yAngle, 0, 1, 0);
  Matrix.multiplyMM(mViewProjectionMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
  Matrix.multiplyMM(mMVPMatrix, 0, mViewProjectionMatrix, 0, mModuleMatrix, 0);
  rectangle.draw(mMVPMatrix);
}

為了呈現出3d效果,增加觸摸旋轉事件,這樣滑動屏幕就可以看到三維物體的全貌

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public boolean onTouchEvent(MotionEvent e) {
  float y = e.getY();
  float x = e.getX();
  switch (e.getAction()) {
  case MotionEvent.ACTION_MOVE:
   float dy = y - mPreviousY;
   float dx = x - mPreviousX;
   mMyRender.yAngle += dx;
   mMyRender.xAngle+= dy;
   requestRender();
  }
  mPreviousY = y;
  mPreviousX = x;
  return true;
 }

然后就可以看到三維效果。

OpenGL ES透視投影實現方法(四)

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/cauchyweierstrass/article/details/52904550

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 特黄特a级特别特级特毛片 特黄a级三级三级野战 | 精品无人区一区二区三区 | 99国产精品久久久久久久... | 四虎2023| 精品亚洲欧美中文字幕在线看 | 19+韩国女主播激情vip视频在线 | 91碰碰 | 99久久国产综合精麻豆 | 成人影院在线观看 | 免费的强动漫人物的 | 亚洲国产精品久久久久久 | 午夜A级理论片左线播放 | 91精品国产美女福到在线不卡 | 亚洲AV无码国产精品色午夜情 | 日韩欧美国产在线 | a级毛片毛片免费很很综合 a级黄色视屏 | 女娃开嫩苞经历小说 | 成人免费观看在线视频 | 欧美性理论片在线观看片免费 | 91制片厂制作传媒免费版樱花 | 日韩v| 91porny丨首页 | 校园纯肉H教室第一次 | 九九九九九九伊人 | 99av涩导航 | 欧美春宫| 西西人体大胆啪啪私拍色约约 | 91在线 在线播放 | 亚洲午夜性春猛交xxxx | luanlun绝对真实乱 | 双性少爷受糙汉攻h | 亚洲国产精品无码中文字幕 | 日本在线观看a | xxxxxx日本处大片免费看 | 韩国最新三级网站在线播放 | 青草午夜精品视频在线观看 | 我的家教老师 | 99热精品成人免费观看 | 俺去啦最新地址 | 人与善交大片免费看 | 色婷婷激婷婷深爱五月老司机 |