學(xué)習(xí)之路系列
今天我們實(shí)現(xiàn)一個(gè)三維空間中物體的變換操作
本篇主要內(nèi)容
三維坐標(biāo)變換
效果圖
實(shí)現(xiàn)過程
首先我們跟之前一樣給出以下頂點(diǎn)數(shù)據(jù)
//4個(gè)頂點(diǎn)(分別表示xyz軸)
static const float Vertices[] = {
-0.5, -0.5, 0, //左下
0.5, -0.5, 0, //右下
-0.5, 0.5, 0, //左上
0.5, 0.5, 0, //右上
};
//4個(gè)點(diǎn)的顏色(分別表示RGBA值)
static const float Colors[] = {
1, 0, 0, 1,
0, 1, 0, 1,
0, 0, 1, 1,
0, 0, 0, 1,
};
然后進(jìn)行渲染, 中間的渲染緩沖區(qū)拓巧、幀緩沖區(qū)、編譯著色器
我們暫時(shí)先省略骗污,有不明白的小伙伴可以參考這篇文章
- (void)render:(CADisplayLink *)displayLink {
//用指定的顏色清除,清除顏色被設(shè)置為(0.5f, 0.5f, 0.5f, 1.0f), 所以為黑色
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//設(shè)定窗口的范圍(如果不是很明白, 可以自己動(dòng)手修改下試試)
//他這個(gè)是左下角為(0,0) 右上角為(width,height)
glViewport(0, 0, _width, _height);
//指定了渲染時(shí)索引值為 index 的頂點(diǎn)屬性數(shù)組的數(shù)據(jù)格式和位置智听。
/*
* indx:指定要修改的頂點(diǎn)屬性的索引值
* size:指定每個(gè)頂點(diǎn)屬性的組件數(shù)量。(必須坐標(biāo)xyz軸就是3, 顏色rgba就是4)
* type:指定數(shù)組中每個(gè)組件的數(shù)據(jù)類型。(一般為GL_FLOAT)
* normalized:一般為GL_FALSE
* stride:指定連續(xù)頂點(diǎn)屬性之間的偏移量。如果為0羡儿,那么頂點(diǎn)屬性會(huì)被理解為:它們是緊密排列在一起的。初始值為0是钥。
* ptr:指向數(shù)據(jù)的指針
*/
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, Vertices);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, 0, Colors);
glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(Vertices) / (sizeof(int) * 3));
//把緩沖區(qū)的數(shù)據(jù)呈現(xiàn)到UIView上
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
渲染結(jié)果
我們接下來需要把他弄成正方形的失受,由于還沒有學(xué)習(xí)投影,我們先改動(dòng)渲染的窗口來達(dá)成這一目的
將render:
方法中的
glViewport(0, 0, _width, _height);
改成
glViewport(0, 0, _width, _width);
看下效果
這樣子就實(shí)現(xiàn)了正方形了
三維變換
給大家介紹一個(gè)網(wǎng)址LearnOpenGL, 上面對(duì)三維變換的講解很詳細(xì), 下面就來實(shí)現(xiàn)下這三種操作(縮放咏瑟、位移、旋轉(zhuǎn))
系統(tǒng)已經(jīng)提供了矩陣操作的框架痪署, 網(wǎng)上也有一些大神提供的矩陣操作的第三方庫码泞,我們可以直接拿到使用,不會(huì)矩陣的小伙伴也可以玩轉(zhuǎn)三維操作O(∩_∩)O狼犯。這里我們直接使用系統(tǒng)的
我們先修改下頂點(diǎn)著色器
attribute vec4 Position;
attribute vec4 InColor;
varying vec4 OutColor;
uniform mat4 Model; //new
void main(void){
OutColor = InColor;
// gl_Position = Position;
gl_Position = Model * Position;
}
并在編譯著色器(compileShaders:)
的地方獲取著色器中的ModelView
變量
_modelView = glGetUniformLocation(_program, "ModelView");
下面的的代碼直接在
-
縮放
GLKMatrix4 modelView = GLKMatrix4MakeScale(_transformScale[0],
_transformScale[1],
_transformScale[2]);
//給著色器變量賦值
/*
location: 變量的位置
count : 要更改變量的個(gè)數(shù)
transpose: 是否需要轉(zhuǎn)置
value : 給出對(duì)應(yīng)count個(gè)元素的指針
*/
glUniformMatrix4fv(_modelView, 1, GL_FALSE, modelView.m);
GLKMatrix4MakeScale
是系統(tǒng)初始化縮放矩陣的函數(shù)
看下效果:
-
位移
GLKMatrix4 modelView = GLKMatrix4MakeTranslation(_transformTranslation[0],
_transformTranslation[1],
_transformTranslation[2]);
//給著色器變量賦值
/*
location: 變量的位置
count : 要更改變量的個(gè)數(shù)
transpose: 是否需要轉(zhuǎn)置
value : 給出對(duì)應(yīng)count個(gè)元素的指針
*/
glUniformMatrix4fv(_modelView, 1, GL_FALSE, modelView.m);
GLKMatrix4MakeTranslation
是系統(tǒng)初始化平移矩陣的函數(shù)
看下效果:
-
旋轉(zhuǎn)
GLKMatrix4 modelView = GLKMatrix4MakeRotation(_transformRotation[0],1,0,0);
modelView = GLKMatrix4Rotate(modelView, _transformRotation[1], 0, 1, 0);
modelView = GLKMatrix4Rotate(modelView, _transformRotation[2], 0, 0, 1);
//給著色器變量賦值
/*
location: 變量的位置
count : 要更改變量的個(gè)數(shù)
transpose: 是否需要轉(zhuǎn)置
value : 給出對(duì)應(yīng)count個(gè)元素的指針
*/
glUniformMatrix4fv(_modelView, 1, GL_FALSE, modelView.m);
旋轉(zhuǎn)操作需要將3個(gè)軸(x余寥、y、z)
分開單獨(dú)操作
上面的旋轉(zhuǎn)函數(shù)需要用弧度悯森。
角度 = 弧度 * (180.0f / PI)
弧度 = 角度 * (PI / 180.0f)
看下效果:
-
矩陣組合
學(xué)過矩陣的童鞋都知道宋舷,矩陣相乘不遵守交換律
縮放、位移瓢姻、旋轉(zhuǎn)
三種矩陣按照不同的順序相乘會(huì)產(chǎn)生不一樣的效果
舉個(gè)例子: 先縮放
在位移
那么當(dāng)你縮放完以后祝蝠,你的位移向量一樣會(huì)被縮放,比如你要向右移動(dòng)兩個(gè)單位幻碱,這時(shí)候縮小兩倍绎狭,那么的平移的向量一樣會(huì)縮小兩倍,就變成了一個(gè)單位
順序的話一般都沒有硬性規(guī)定, 看需求, 本文順序位移->旋轉(zhuǎn)->縮放
下面我們把三種效果組合試一下
GLKMatrix4 modelView = GLKMatrix4MakeTranslation(_transformTranslation[0],
_transformTranslation[1],
_transformTranslation[2]);
modelView = GLKMatrix4Rotate(modelView, _transformRotation[0], 1, 0, 0);
modelView = GLKMatrix4Rotate(modelView, _transformRotation[1], 0, 1, 0);
modelView = GLKMatrix4Rotate(modelView, _transformRotation[2], 0, 0, 1);
modelView = GLKMatrix4Scale(modelView,
_transformScale[0],
_transformScale[1],
_transformScale[2]);
//給著色器變量賦值
/*
location: 變量的位置
count : 要更改變量的個(gè)數(shù)
transpose: 是否需要轉(zhuǎn)置
value : 給出對(duì)應(yīng)count個(gè)元素的指針
*/
glUniformMatrix4fv(_modelView, 1, GL_FALSE, modelView.m);
看下效果:
本來想著這篇文章到這樣就結(jié)束了褥傍,但是回頭一看發(fā)現(xiàn)內(nèi)容有點(diǎn)少儡嘶,就順便在實(shí)現(xiàn)多一個(gè)效果吧
先看下下面這段頂點(diǎn)數(shù)據(jù)
//4個(gè)頂點(diǎn)(分別表示xyz軸)
static const float Vertices[] = {
-0.5, -0.5, 0, //左下
0.5, -0.5, 0, //右下
-0.5, 0.5, 0, //左上
0.5, 0.5, 0, //右上
};
這里有個(gè)Z軸還沒用到的,上面是其中一個(gè)面
我們還需要把后面的4個(gè)坐標(biāo)點(diǎn)加上恍风,才能湊夠一個(gè)立方體(8個(gè)點(diǎn))
//4個(gè)頂點(diǎn)(分別表示xyz軸)
static const float Vertices[] = {
//前面4個(gè)坐標(biāo)
-0.5, -0.5, 0.5,
0.5, -0.5, 0.5,
-0.5, 0.5, 0.5,
0.5, 0.5, 0.5,
//后面4個(gè)坐標(biāo)
-0.5, -0.5, -0.5,
0.5, -0.5, -0.5,
-0.5, 0.5, -0.5,
0.5, 0.5, -0.5,
};
//4個(gè)點(diǎn)的顏色(分別表示RGBA值)
static const float Colors[] = {
1, 0, 0, 1,
0, 1, 0, 1,
0, 0, 1, 1,
0, 0, 0, 1,
1, 0, 0, 1,
0, 1, 0, 1,
0, 0, 1, 1,
0, 0, 0, 1,
};
由于我們只給了8個(gè)點(diǎn)的坐標(biāo), 所以我們用原來的這個(gè)方法有點(diǎn)不合適
glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(Vertices) / (sizeof(float) * 3));
我們來介紹另一個(gè)方法glDrawElements
, 這個(gè)方法跟glDrawArrays
一樣都是渲染
這兩種方法的介紹蹦狂,可以看下這篇文章
創(chuàng)建索引數(shù)組
static const GLubyte Indices[] = {
0, 1, 2,
2, 3, 1,
4, 5, 6,
6, 7, 5,
2, 3, 6,
6, 7, 3,
0, 1, 4,
4, 5, 1,
0, 4, 2,
2, 6, 4,
1, 5, 3,
3, 7, 5
};
渲染
/*
* mode : 渲染的方式
* count : 索引數(shù)組元素的數(shù)量
* type : 索引值的類型
* indices: 索引數(shù)組的指針
*/
glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, Indices);
很明顯的看到誓篱,層次有點(diǎn)不對(duì)
我們需要新增一個(gè)深度緩沖區(qū)
深度緩沖區(qū)簡(jiǎn)單來說就是讓物體的渲染有層次感, 詳解看這篇文章
下面這段代碼需要在渲染緩沖區(qū)前調(diào)用
/**
* 設(shè)置深度緩沖區(qū)
*/
- (void)setupDepthBuffer {
glGenRenderbuffers(1, &_depthRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _width, _height);
}
在幀緩沖區(qū)中新增一段代碼
/**
* 設(shè)置幀緩沖區(qū)
*/
- (void)setupFrameBuffer {
glGenFramebuffers(1, &_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer); //new
}
將render:
中的glClearColor
修改下
glClearColor(0.8f, 0.8f, 0.8f, 1.0f);
// glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_DEPTH_TEST); //new
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //new
效果:
結(jié)語
自從把這個(gè)三維的立方體實(shí)現(xiàn)以后,突然感覺OpenGLES很好玩凯楔,雖然在這過程中不斷的踩坑調(diào)試窜骄,但是實(shí)現(xiàn)以后又有了滿滿的信心.(??????)??加油
后面的例子會(huì)越來越好玩的,一起一步一步的學(xué)下去吧
Demo
1.可以嘗試著把
縮放啼辣、位移啊研、旋轉(zhuǎn)
三種方式的順序換一下,看會(huì)有怎么樣子的效果
2.感興趣的小伙伴可以把紋理加上
3.下篇文章我們來實(shí)現(xiàn)一個(gè)更好玩的坐標(biāo)系統(tǒng)