學習之路系列
效果圖
今天主要粗略的講解以下幾個坐標系統(tǒng):
- 局部空間(Local Space左驾,或者稱為物體空間(Object Space))
- 世界空間(World Space)
- 觀察空間(View Space陵珍,或者稱為視覺空間(Eye Space))
- 裁剪空間(Clip Space)
- 屏幕空間(Screen Space)
概述
為了將坐標從一個坐標系變換到另一個坐標系,我們需要用到幾個變換矩陣界斜,最重要的幾個分別是模型(Model)慷彤、觀察(View)、投影(Projection)三個矩陣虑粥。我們的頂點坐標起始于局部空間(Local Space),在這里它稱為局部坐標(Local Coordinate)宪哩,它在之后會變?yōu)槭澜缱鴺?World Coordinate)娩贷,觀察坐標(View Coordinate),裁剪坐標(Clip Coordinate)锁孟,并最后以屏幕坐標(Screen Coordinate)的形式結束彬祖。下面的這張圖展示了整個流程以及各個變換過程做了什么:
- 局部坐標是對象相對于局部原點的坐標,也是物體起始的坐標品抽。
- 下一步是將局部坐標變換為世界空間坐標储笑,世界空間坐標是處于一個更大的空間范圍的。這些坐標相對于世界的全局原點圆恤,它們會和其它物體一起相對于世界的原點進行擺放突倍。
- 接下來我們將世界坐標變換為觀察空間坐標,使得每個坐標都是從攝像機或者說觀察者的角度進行觀察的盆昙。
- 坐標到達觀察空間之后羽历,我們需要將其投影到裁剪坐標。裁剪坐標會被處理至-1.0到1.0的范圍內淡喜,并判斷哪些頂點將會出現(xiàn)在屏幕上窄陡。
- 最后,我們將裁剪坐標變換為屏幕坐標拆火,我們將使用一個叫做視口變換(Viewport Transform)的過程跳夭。視口變換將位于-1.0到1.0范圍的坐標變換到由glViewport函數(shù)所定義的坐標范圍內。最后變換出來的坐標將會送到光柵器们镜,將其轉化為片段币叹。
原文鏈接LearnOpenGL
里面講解的非常通俗易懂, 可以到里面看看
局部空間(Local Space,或者稱為物體空間(Object Space))
局部空間其實相當于我們iOS
中UIKit
里面的@property(nonatomic) CGRect bounds;
屬性. 以自身為參照點模狭,默認是(0,0,0)
.
世界空間(World Space)
世界空間顧名思義, 指的是整個空間. 每個物體有一個坐標. 這個跟iOS
中的UIKit
中的@property(nonatomic) CGRect frame;
屬性類似類似
觀察空間(View Space颈抚,或者稱為視覺空間(Eye Space))
這個就更好理解了, 指的是觀察事物的位置及角度. 在OpenGL
里面把這個概念稱之為攝像機(Camera)
裁剪空間(Clip Space)
在裁剪空間OpenGL會將圖像處理至-1.0 ~ 1.0
的空間, 超出的部分都會被裁剪掉. 在前幾篇文章中也有講過, 頂點坐標必須在-1.0 ~ 1.0
之間,超出部分不會顯示.
屏幕空間(Screen Space)
指的就是呈現(xiàn)在屏幕上的大小, 由glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
該函數(shù)進行操作
我們先把上面這個效果實現(xiàn)一下,有不會的小伙伴可以參考這篇文章
組合
我們創(chuàng)建模型(Model)贩汉、觀察(View)驱富、投影(Projection)
三個矩陣, 然后把他們組合在一起
修改頂點著色器中變量
attribute vec4 Position;
attribute vec2 TexCoordIn;
varying vec2 TexCoordOut;
uniform mat4 Projection; //new
uniform mat4 View; //new
uniform mat4 Model; //new
void main(void){
// gl_Position = Position;
gl_Position = Projection * View * Model * Position;
TexCoordOut = vec2(TexCoordIn.x, 1.0 - TexCoordIn.y);
}
在render:
中新增模型矩陣、觀察矩陣和投影矩陣
, 并將其傳入到著色器中. 這里先把他們都創(chuàng)建成初等矩陣
GLKMatrix4 projection = GLKMatrix4Identity;
GLKMatrix4 model = GLKMatrix4Identity;
GLKMatrix4 view = GLKMatrix4Identity;
glUniformMatrix4fv(_model, 1, GL_FALSE, model.m);
glUniformMatrix4fv(_view, 1, GL_FALSE, view.m);
glUniformMatrix4fv(_projection, 1, GL_FALSE, projection.m);
投影( 正射投影匹舞、透視投影 )
參考文章:LearnOpenGL
-
透視投影
比較貼合實際生活中的現(xiàn)象, 物體離你越遠看起來越小 -
正射投影
投射到屏幕上看不出太大的效果, 不管物體離你多遠看起來都是一樣大的
下面我們來試一下這兩種效果
- 透視投影
我們將上面的那個投影的初等矩陣做下修改
//投影設置
/*
* fovyRadians: 設置觀察空間的大小
* aspect : 設置寬高比
* nearZ : 設置平截頭體的近平面
* farZ : 設置平截頭體的遠平面
* 在近平面和遠平面內且處于平截頭體內的頂點才會被渲染
*/
GLKMatrix4 projection = GLKMatrix4MakePerspective(45, 1, 0.1f, 100.0f);
glUniformMatrix4fv(_projection, 1, GL_FALSE, projection.m);
觀察矩陣
我們下篇文章再講褐鸥,這里先隨意設置一個在平截面內的值。由于OpenGL的投影矩陣交換了左右手赐稽,所以Z軸是反的
GLKMatrix4 view = GLKMatrix4MakeTranslation(0.0, 0.0, -3.0);
glUniformMatrix4fv(_view, 1, GL_FALSE, view.m);
上面的Gif圖中叫榕,可以看到當物體超出平截面的范圍時,超出部分不會顯示
透視投影姊舵,非常的符合真實情況晰绎,越遠物體越小
- 正射投影
將這個投影矩陣修改下
//設置正射投影
/*
* 由上、下括丁、左荞下、右、近平面史飞、遠平面控制平截面的區(qū)域
*/
GLKMatrix4 projection = GLKMatrix4MakeOrtho(1, -1, 1, -1, 0.1f, 100.0f);
glUniformMatrix4fv(_projection, 1, GL_FALSE, projection.m);
可以從上面的Gif中看出锄弱,正射投影不會隨著距離的遠近改變物體的大小
下面我們創(chuàng)建一個正方體,這里我直接給出頂點數(shù)據(jù)祸憋,如下:
//4個頂點(分別表示xyz軸)
static const float Vertices[] = {
//前面4個坐標
-1, -1, 1,
1, -1, 1,
-1, 1, 1,
1, 1, 1,
//后面4個坐標
-1, -1, -1,
1, -1, -1,
-1, 1, -1,
1, 1, -1,
//左邊4個坐標
-1, -1, -1,
-1, -1, 1,
-1, 1, -1,
-1, 1, 1,
//右邊4個坐標
1, -1, -1,
1, -1, 1,
1, 1, -1,
1, 1, 1,
//上邊4個坐標
1, 1, -1,
-1, 1, -1,
1, 1, 1,
-1, 1, 1,
//下邊4個坐標
1, -1, -1,
-1, -1, -1,
1, -1, 1,
-1, -1, 1,
};
static const float Texture[] = {
0, 0,
1, 0,
0, 1,
1, 1,
0, 0,
1, 0,
0, 1,
1, 1,
0, 0,
1, 0,
0, 1,
1, 1,
0, 0,
1, 0,
0, 1,
1, 1,
0, 0,
1, 0,
0, 1,
1, 1,
0, 0,
1, 0,
0, 1,
1, 1,
};
static const GLubyte Indices[] = {
//前面
0, 1, 2,
2, 3, 1,
//后面
4, 5, 6,
6, 7, 5,
//左面
8, 9, 10,
10, 11, 9,
//右面
12, 13, 14,
14, 15, 13,
//上面
16, 17, 18,
18, 19, 17,
//下面
20, 21, 22,
22, 23, 21
};
設置好以后還需要設置一下透視投影
, 將其投影成一個正方體. 將下面的投影矩陣
寬高比設置一下
GLKMatrix4 projection = GLKMatrix4MakePerspective(45.0f, _width / _height, 0.1f, 100.0f);
看下效果
發(fā)現(xiàn)確實是個立方體会宪,可是感覺怪怪的。原來是因為沒有添加
深度測試
, 我們上篇文章(三維變換)里面有說過
在調用渲染緩沖區(qū)前面添加下面代碼
/**
* 設置深度緩沖區(qū)
*/
- (void)setupDepthBuffer {
glGenRenderbuffers(1, &_depthRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _width, _height);
}
并且在render:
函數(shù)中的
glClear(GL_COLOR_BUFFER_BIT);
修改為
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
效果就是這樣子了蚯窥,灰常的棒
下面在給他加多一個笑臉的圖案掸鹅,可以參考這篇文章
下面我們創(chuàng)建10個立方體,立方體的坐標如下:
static const float cubePositions[10][3] = {
{ 0.0f, 0.0f, 0.0f},
{ 2.0f, 5.0f, -15.0f},
{-1.5f, -2.2f, -2.5f},
{-3.8f, -2.0f, -12.3f},
{ 2.4f, -0.4f, -3.5f},
{-1.7f, 3.0f, -7.5f},
{ 1.3f, -2.0f, -2.5f},
{ 1.5f, 2.0f, -2.5f},
{ 1.5f, 0.2f, -1.5f},
{-1.3f, 1.0f, -1.5f},
};
在render:
中循環(huán)10次創(chuàng)建10個立方體
//循環(huán)創(chuàng)建10個立方體
for(int i = 0; i < 10; i++) {
CGFloat angle = 20.0f * i;
GLKMatrix4 model = GLKMatrix4MakeTranslation(cubePositions[i][0] * 2.0f,
cubePositions[i][1] * 2.0f,
cubePositions[i][2] * 2.0f);
model = GLKMatrix4Rotate(model, angle, 1.0f, 0.3f, 0.5f);
glUniformMatrix4fv(_model, 1, GL_FALSE, model.m);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, Indices);
}