在繪制之前,我們需要了解下面的知識(shí):
一众弓、渲染管線
下圖中展示整個(gè)OpenGL ES 2.0可編程渲染管線
圖中Vertex Shader和Fragment Shader 是可編程管線恩溅;
1).Vertex Array/Buffer objects
頂點(diǎn)數(shù)據(jù)來源,這時(shí)渲染管線的頂點(diǎn)輸入田轧,通常使用 Buffer objects效率更好暴匠。
2).Vertex Shader
頂點(diǎn)著色器通過矩陣變換位置、計(jì)算照明公式來生成逐頂點(diǎn)顏色已經(jīng)生成或變換紋理坐標(biāo)等基于頂點(diǎn)的操作傻粘。
3).Primitive Assembly
圖元裝配經(jīng)過著色器處理之后的頂點(diǎn)在圖片裝配階段被裝配為基本圖元每窖。OpenGL ES 支持三種基本圖元:點(diǎn),線和三角形弦悉,它們是可被 OpenGL ES 渲染的窒典。接著對(duì)裝配好的圖元進(jìn)行裁剪(clip):保留完全在視錐體中的圖元,丟棄完全不在視錐體中的圖元稽莉,對(duì)一半在一半不在的圖元進(jìn)行裁剪瀑志;接著再對(duì)在視錐體中的圖元進(jìn)行剔除處理(cull):這個(gè)過程可編碼來決定是剔除正面,背面還是全部剔除污秆。
4).Rasterization
光柵化劈猪。在光柵化階段,基本圖元被轉(zhuǎn)換為二維的片元(fragment)良拼,fragment 表示可以被渲染到屏幕上的像素战得,它包含位置,顏色庸推,紋理坐標(biāo)等信息常侦,這些值是由圖元的頂點(diǎn)信息進(jìn)行插值計(jì)算得到的浇冰。這些片元接著被送到片元著色器中處理。這是從頂點(diǎn)數(shù)據(jù)到可渲染在顯示設(shè)備上的像素的質(zhì)變過程聋亡。
5).Fragment Shader
片元著色器通過可編程的方式實(shí)現(xiàn)對(duì)每個(gè)片元的操作肘习。在這一階段它接受光柵化處理之后的fragment,color坡倔,深度值漂佩,模版值作為輸入,片元著色器可以拋棄片元致讥,也可以生成一個(gè)或多個(gè)顏色值作為輸出仅仆。
6).逐片段操作
1.像素歸屬測(cè)試(Pixel Ownership Test):這一步驟由OpenGL ES內(nèi)部進(jìn)行,不由開發(fā)人員控制垢袱;測(cè)試確定幀緩沖區(qū)的位置的像素是否歸屬當(dāng)前OpenGL ES所有墓拜,如不屬于或被另一個(gè)窗口遮擋,從而完全不顯示這些像素请契。
2.裁剪測(cè)試(Scissor Test):判斷像素是否在由 glScissor 定義的剪裁矩形內(nèi)咳榜,不在該剪裁區(qū)域內(nèi)的像素就會(huì)被剪裁掉;
3.模板和深度測(cè)試(Stencil And Depth Test):測(cè)試輸入片段的模板和深度值上進(jìn)行爽锥,以確定片段是否應(yīng)該被拒絕涌韩;深度測(cè)試比較下一個(gè)片段與幀緩沖區(qū)中的片段的深度,從而決定哪一個(gè)像素在前面氯夷,哪一個(gè)像素被遮擋臣樱;
4.混合(Blending):是將片段的顏色和幀緩沖區(qū)中已有的顏色值進(jìn)行混合,并將混合所得的新值寫入幀緩沖腮考;
5.抖動(dòng)(Dithering):可用于最小化因?yàn)槭褂糜邢蘧仍趲彌_區(qū)中保存顏色值而產(chǎn)生的偽像雇毫。
6.To Framebuffer:這是流水線的最后一個(gè)階段,F(xiàn)ramebuffer 中存儲(chǔ)這可以用于渲染到屏幕或紋理中的像素值踩蔚,也可以從Framebuffer 中讀回像素值棚放,但不能讀取其他值(如深度值,模版值等)馅闽。
注:以上渲染管線資料來自http://www.cnblogs.com/edisongz/p/6918428.html
二飘蚯、頂點(diǎn)著色器 Vertex Shader
下面來仔細(xì)看看頂點(diǎn)著色器:
頂點(diǎn)著色器接收的輸入:
Attributes:由 vertext array 提供的頂點(diǎn)數(shù)據(jù),如空間位置福也,法向量局骤,紋理坐標(biāo)以及頂點(diǎn)顏色,它是針對(duì)每一個(gè)頂點(diǎn)的數(shù)據(jù)暴凑。屬性只在頂點(diǎn)著色器中才有峦甩,片元著色器中沒有屬性。屬性可以理解為針對(duì)每一個(gè)頂點(diǎn)的輸入數(shù)據(jù)搬设。OpenGL ES 2.0 規(guī)定了所有實(shí)現(xiàn)應(yīng)該支持的最大屬性個(gè)數(shù)不能少于 8 個(gè)穴店。
Uniforms:uniforms保存由應(yīng)用程序傳遞給著色器的只讀常量數(shù)據(jù)。在頂點(diǎn)著色器中拿穴,這些數(shù)據(jù)通常是變換矩陣泣洞,光照參數(shù),顏色等默色。由 uniform 修飾符修飾的變量屬于全局變量球凰,該全局性對(duì)頂點(diǎn)著色器與片元著色器均可見,也就是說腿宰,這兩個(gè)著色器如果被連接到同一個(gè)應(yīng)用程序中呕诉,它們共享同一份 uniform 全局變量集。因此如果在這兩個(gè)著色器中都聲明了同名的 uniform 變量吃度,要保證這對(duì)同名變量完全相同:同名+同類型甩挫,因?yàn)樗鼈儗?shí)際是同一個(gè)變量。此外椿每,uniform 變量存儲(chǔ)在常量存儲(chǔ)區(qū)伊者,因此限制了 uniform 變量的個(gè)數(shù),OpenGL ES 2.0 也規(guī)定了所有實(shí)現(xiàn)應(yīng)該支持的最大頂點(diǎn)著色器 uniform 變量個(gè)數(shù)不能少于 128 個(gè)间护,最大的片元著色器 uniform 變量個(gè)數(shù)不能少于 16 個(gè)亦渗。
Samplers:一種特殊的 uniform,用于呈現(xiàn)紋理汁尺。sampler 可用于頂點(diǎn)著色器和片元著色器法精。
Shader program:由 main 申明的一段程序源碼,描述在頂點(diǎn)上執(zhí)行的操作:如坐標(biāo)變換痴突,計(jì)算光照公式來產(chǎn)生 per-vertex 顏色或計(jì)算紋理坐標(biāo)搂蜓。
頂點(diǎn)著色器的輸出:
Varying:varying 變量用于存儲(chǔ)頂點(diǎn)著色器的輸出數(shù)據(jù),當(dāng)然也存儲(chǔ)片元著色器的輸入數(shù)據(jù)苞也,varying 變量最終會(huì)在光柵化處理階段被線性插值洛勉。頂點(diǎn)著色器如果聲明了 varying 變量,它必須被傳遞到片元著色器中才能進(jìn)一步傳遞到下一階段如迟,因此頂點(diǎn)著色器中聲明的 varying 變量都應(yīng)在片元著色器中重新聲明同名同類型的 varying 變量收毫。OpenGL ES 2.0 也規(guī)定了所有實(shí)現(xiàn)應(yīng)該支持的最大 varying 變量個(gè)數(shù)不能少于 8 個(gè)。
在頂點(diǎn)著色器階段至少應(yīng)輸出位置信息-即內(nèi)建變量:gl_Position殷勘,其它兩個(gè)可選的變量為:gl_FrontFacing 和 gl_PointSize此再。
三、片元著色器 Fragment Shader
接下來仔細(xì)看看片元著色器:
片元管理器接受如下輸入:
Varyings:這個(gè)在前面已經(jīng)講過了玲销,頂點(diǎn)著色器階段輸出的 varying 變量在光柵化階段被線性插值計(jì)算之后輸出到片元著色器中作為它的輸入输拇,即上圖中的 gl_FragCoord,gl_FrontFacing 和 gl_PointCoord贤斜。OpenGL ES 2.0 也規(guī)定了所有實(shí)現(xiàn)應(yīng)該支持的最大 varying 變量個(gè)數(shù)不能少于 8 個(gè)策吠。
Uniforms:前面也已經(jīng)講過逛裤,這里是用于片元著色器的常量,如霧化參數(shù)猴抹,紋理參數(shù)等带族;OpenGL ES 2.0 也規(guī)定了所有實(shí)現(xiàn)應(yīng)該支持的最大的片元著色器 uniform 變量個(gè)數(shù)不能少于 16 個(gè)。
Samples:一種特殊的 uniform蟀给,用于呈現(xiàn)紋理蝙砌。
Shader program:由 main 申明的一段程序源碼,描述在片元上執(zhí)行的操作跋理。
在頂點(diǎn)著色器階段只有唯一的 varying 輸出變量-即內(nèi)建變量:gl_FragColor择克。
四,頂點(diǎn)著色與片元著色在編程上的差異
1前普,精度上的差異
著色語言定了三種級(jí)別的精度:lowp, mediump, highp肚邢。我們可以在 glsl 腳本文件的開頭定義默認(rèn)的精度。如下代碼定義在 float 類型默認(rèn)使用 highp 級(jí)別的精度
precision highp float;
在頂點(diǎn)著色階段拭卿,如果沒有用戶自定義的默認(rèn)精度道偷,那么 int 和 float 都默認(rèn)為 highp 級(jí)別;而在片元著色階段记劈,如果沒有用戶自定義的默認(rèn)精度勺鸦,那么就真的沒有默認(rèn)精度了,我們必須在每個(gè)變量前放置精度描述符目木。此外换途,OpenGL ES 2.0 標(biāo)準(zhǔn)也沒有強(qiáng)制要求所有實(shí)現(xiàn)在片元階段都支持 highp 精度的。我們可以通過查看是否定義 GL_FRAGMENT_PRECISION_HIGH 來判斷具體實(shí)現(xiàn)是否在片元著色器階段支持 highp 精度刽射,從而編寫出可移植的代碼军拟。當(dāng)然,通常我們不需要在片元著色器階段使用 highp 級(jí)別的精度誓禁,推薦的做法是先使用 mediump 級(jí)別的精度懈息,只有在效果不夠好的情況下再考慮 highp 精度。
2摹恰,attribute 修飾符只可用于頂點(diǎn)著色辫继。這個(gè)前面已經(jīng)說過了。
3俗慈,或由于精度的不同姑宽,或因?yàn)榫幾g優(yōu)化的原因,在頂點(diǎn)著色和片元著色階段同樣的計(jì)算可能會(huì)得到不同的結(jié)果闺阱,這會(huì)導(dǎo)致一些問題(z-fighting)炮车。因此 glsl 引入了 invariant 修飾符來修飾在兩個(gè)著色階段的同一變量,確保同樣的計(jì)算會(huì)得到相同的值。
注:以上關(guān)于頂點(diǎn)著色器和片元著色器資料來自 http://www.cnblogs.com/kesalin/archive/2012/11/25/opengl_es_tutorial_02.html
五瘦穆,使用頂點(diǎn)著色器與片元著色器
好了纪隙,理論知識(shí)講得足夠多了,下面我們來看看如何在代碼中添加頂點(diǎn)著色器與片元著色器扛或。我們?cè)谇耙黄恼隆禣penGLES-01 渲染第一步》代碼的基礎(chǔ)上進(jìn)行編碼瘫拣。在前面提到可編程管線通過用 shader 語言編寫腳本文件實(shí)現(xiàn)的,這些腳本文件相當(dāng)于 C 源碼告喊,有源碼就需要編譯鏈接,因此需要對(duì)應(yīng)的編譯器與鏈接器派昧,shader 對(duì)象與 program 對(duì)象就相當(dāng)于編譯器與鏈接器黔姜。shader 對(duì)象載入源碼,然后編譯成 object 形式(就像C源碼編譯成 .obj文件)蒂萎。經(jīng)過編譯的 shader 就可以裝配到 program 對(duì)象中秆吵,每個(gè) program對(duì)象必須裝配兩個(gè) shader 對(duì)象:一個(gè)頂點(diǎn) shader,一個(gè)片元 shader五慈,然后 program 對(duì)象被連接成“可執(zhí)行文件”纳寂,這樣就可以在 render 中是由該“可執(zhí)行文件”了。
1.首先泻拦,我們創(chuàng)建頂點(diǎn)著色器腳本文件
然后命名為:VertexShader.glsl ,(glsl:gl shader language)話說這樣命名才能有代碼提示和校驗(yàn)毙芜,然而我沒體驗(yàn)到提示和校驗(yàn)。
編輯文件內(nèi)容如下:
attribute vec4 vPosition;
void main(void)
{
gl_Position = vPosition;
}
頂點(diǎn)著色腳本的源碼很簡(jiǎn)單争拐,如果你仔細(xì)閱讀了前面的介紹腋粥,就一目了然。 attribute 屬性 vPosition 表示從應(yīng)用程序輸入的類型為 vec4 的位置信息架曹,輸出內(nèi)建 vary 變量 vPosition隘冲。留意:這里使用了默認(rèn)的精度(highp)。
2.創(chuàng)建片元著色器腳本文件
創(chuàng)建方式如1绑雄,命名為FragmentShader.glsl,然后編輯其內(nèi)容如下:
precision mediump float;
void main()
{
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
片元著色腳本源碼也很簡(jiǎn)單,前面說過片元著色要么自己定義默認(rèn)精度,要么在每個(gè)變量前添加精度描述符崔涂,在這里自定義 float 的精度為 mediump邢享。然后為內(nèi)建輸出變量 gl_FragColor 指定為綠色。 (故意亮瞎你的眼脚粟,請(qǐng)自行修改靡砌,此外,提下人對(duì)顏色的敏感度珊楼,人對(duì)綠色的敏感度比紅和藍(lán)都要高通殃,所以16位的顏色數(shù)據(jù)里,紅綠藍(lán)占比為5:6:5)。
3.編寫工具類GLESUtils文件來使用shader腳本文件
首先創(chuàng)建一個(gè)GLESUtils類集成NSObject画舌,修改.h為:
#import <Foundation/Foundation.h>
#include <OpenGLES/ES3/gl.h>
@interface GLESUtils : NSObject
// Create a shader object, load the shader source string, and compile the shader.
//
+(GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString;
+(GLuint)loadShader:(GLenum)type withFilepath:(NSString *)shaderFilepath;
@end
然后在.m中添加如下函數(shù):
+(GLuint)loadShader:(GLenum)type withFilepath:(NSString *)shaderFilepath
{
NSError* error;
NSString* shaderString = [NSString stringWithContentsOfFile:shaderFilepath
encoding:NSUTF8StringEncoding
error:&error];
if (!shaderString) {
NSLog(@"Error: loading shader file: %@ %@", shaderFilepath, error.localizedDescription);
return 0;
}
return [self loadShader:type withString:shaderString];
}
+(GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString
{
// Create the shader object
GLuint shader = glCreateShader(type);
if (shader == 0) {
NSLog(@"Error: failed to create shader.");
return 0;
}
// Load the shader source
const char * shaderStringUTF8 = [shaderString UTF8String];
glShaderSource(shader, 1, &shaderStringUTF8, NULL);
// Compile the shader
glCompileShader(shader);
// Check the compile status
GLint compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
if (infoLen > 1) {
char * infoLog = malloc(sizeof(char) * infoLen);
glGetShaderInfoLog (shader, infoLen, NULL, infoLog);
NSLog(@"Error compiling shader:\n%s\n", infoLog );
free(infoLog);
}
glDeleteShader(shader);
return 0;
}
return shader;
}
工具類GLESUtils中用兩個(gè)類方法實(shí)現(xiàn)對(duì)shader腳本文件的創(chuàng)建堕担、裝載和編譯,接下來詳細(xì)介紹每個(gè)步驟:
1)曲聂,創(chuàng)建/刪除 shader
函數(shù) glCreateShader 用來創(chuàng)建 shader霹购,參數(shù) GLenum type 表示我們要處理的 shader 類型,它可以是 GL_VERTEX_SHADER 或 GL_FRAGMENT_SHADER朋腋,分別表示頂點(diǎn) shader 或 片元 shader齐疙。它返回一個(gè)句柄指向創(chuàng)建好的 shader 對(duì)象。
函數(shù) glDeleteShader 用來銷毀 shader旭咽,參數(shù)為 glCreateShader 返回的 shader 對(duì)象句柄贞奋。
2),裝載 shader
函數(shù) glShaderSource 用來給指定 shader 提供 shader 源碼穷绵。第一個(gè)參數(shù)是 shader 對(duì)象的句柄轿塔;第二個(gè)參數(shù)表示 shader 源碼字符串的個(gè)數(shù);第三個(gè)參數(shù)是 shader 源碼字符串?dāng)?shù)組仲墨;第四個(gè)參數(shù)一個(gè) int 數(shù)組勾缭,表示每個(gè)源碼字符串應(yīng)該取用的長(zhǎng)度,如果該參數(shù)為 NULL目养,表示假定源碼字符串是 \0 結(jié)尾的俩由,讀取該字符串的內(nèi)容指定 \0 為止作為源碼,如果該參數(shù)不是 NULL癌蚁,則讀取每個(gè)源碼字符串中前 length(與每個(gè)字符串對(duì)應(yīng)的 length)長(zhǎng)度個(gè)字符作為源碼采驻。
3),編譯 shader
函數(shù) glCompileShader 用來編譯指定的 shader 對(duì)象匈勋,這將編譯存儲(chǔ)在 shader 對(duì)象中的源碼礼旅。我們可以通過函數(shù) glGetShaderiv 來查詢 shader 對(duì)象的信息,如本例中查詢編譯情況洽洁,此外還可以查詢 GL_DELETE_STATUS痘系,GL_INFO_LOG_STATUS,GL_SHADER_SOURCE_LENGTH 和 GL_SHADER_TYPE饿自。在這里我們查詢編譯情況汰翠,如果返回 0,表示編譯出錯(cuò)了昭雌,錯(cuò)誤信息會(huì)寫入 info 日志中复唤,我們可以查詢?cè)?info 日志,從而獲得錯(cuò)誤信息烛卧。
六佛纫、準(zhǔn)備繪制
回到我們的MyGLView,添加下面兩個(gè)成員變量:
GLuint _programHandle;
GLuint _positionSlot;
再添加如下函數(shù)配置program
- (void)setupProgram
{
// Load shaders
//
NSString * vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"VertexShader"
ofType:@"glsl"];
NSString * fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"FragmentShader"
ofType:@"glsl"];
GLuint vertexShader = [GLESUtils loadShader:GL_VERTEX_SHADER
withFilepath:vertexShaderPath];
GLuint fragmentShader = [GLESUtils loadShader:GL_FRAGMENT_SHADER
withFilepath:fragmentShaderPath];
// Create program, attach shaders.
_programHandle = glCreateProgram();
if (!_programHandle) {
NSLog(@"Failed to create program.");
return;
}
glAttachShader(_programHandle, vertexShader);
glAttachShader(_programHandle, fragmentShader);
// Link program
//
glLinkProgram(_programHandle);
// Check the link status
GLint linked;
glGetProgramiv(_programHandle, GL_LINK_STATUS, &linked );
if (!linked)
{
GLint infoLen = 0;
glGetProgramiv (_programHandle, GL_INFO_LOG_LENGTH, &infoLen );
if (infoLen > 1)
{
char * infoLog = malloc(sizeof(char) * infoLen);
glGetProgramInfoLog (_programHandle, infoLen, NULL, infoLog );
NSLog(@"Error linking program:\n%s\n", infoLog );
free (infoLog );
}
glDeleteProgram(_programHandle);
_programHandle = 0;
return;
}
glUseProgram(_programHandle);
// Get attribute slot from program
//
_positionSlot = glGetAttribLocation(_programHandle, "vPosition");
}
有了前面的介紹,上面的代碼很容易理解。首先我們是由 GLESUtils 提供的輔助方法從前面創(chuàng)建的腳本中創(chuàng)建呈宇,裝載和編譯頂點(diǎn) shader 和片元 shader好爬;然后我們創(chuàng)建 program,將頂點(diǎn) shader 和片元 shader 裝配到 program 對(duì)象中甥啄,再使用 glLinkProgram 將裝配的 shader 鏈接起來存炮,這樣兩個(gè) shader 就可以合作干活了。注意:鏈接過程會(huì)對(duì) shader 進(jìn)行可鏈接性檢查蜈漓,也就是前面說到同名變量必須同名同型以及變量個(gè)數(shù)不能超出范圍等檢查穆桂。我們?nèi)绾螜z查 shader 編譯情況一樣融虽,對(duì) program 的鏈接情況進(jìn)行檢查享完。如果一切正確,那我們就可以調(diào)用 glUseProgram 激活 program 對(duì)象從而在 render 中使用它句狼。通過調(diào)用 glGetAttribLocation 我們獲取到 shader 中定義的變量 vPosition 在 program 的槽位,通過該槽位我們就可以對(duì) vPosition 進(jìn)行操作秘遏。
接下來我們?cè)趇nitWithFrame方法里調(diào)用此方法(在render方法前):
-(instancetype)initWithFrame:(CGRect)frame{
if (self==[super initWithFrame:frame]) {
[self setupLayer];
[self setupContext];
[self setupRenderBuffer];
[self setupFrameBuffer];
[self setupProgram]; //配置program
[self render];
}
return self;
}
以上都是繪制前的準(zhǔn)備工作丘薛,接下來開始繪制:
七、開始繪制
修改render方法里的代碼:
-(void)render
{
//設(shè)置清屏顏色,默認(rèn)是黑色邦危,如果你的運(yùn)行結(jié)果是黑色洋侨,問題就可能在這兒
glClearColor(0.3, 0.5, 0.8, 1.0);
/*
glClear指定清除的buffer
共可設(shè)置三個(gè)選項(xiàng)GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT
也可組合如:glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
這里我們只用了color buffer倦蚪,所以只需清除GL_COLOR_BUFFER_BIT
*/
glClear(GL_COLOR_BUFFER_BIT);
// Setup viewport
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
//Draw Point
GLfloat pointVertices[] = {
0.0f, 0.8f, 0.0f,
0.1f, 0.8f, 0.0f,
0.2f, 0.8f, 0.0f,
0.2f, 0.7f, 0.0f
};
//loadData
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, pointVertices );
glEnableVertexAttribArray(_positionSlot);
glDrawArrays(GL_POINTS, 0, 4); //draw
//Draw Line
GLfloat lineVertices[] = {
0.0f, 0.6f, 0.0f,
-0.1f, 0.6f, 0.0f,
0.2f, 0.6f, 0.0f,
0.2f, 0.5f, 0.0f
};
//loadData
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, lineVertices );
glEnableVertexAttribArray(_positionSlot);
glDrawArrays(GL_LINES, 0, 4); //draw
// glDrawArrays(GL_LINE_LOOP, 0, 4);
// glDrawArrays(GL_LINE_STRIP, 0, 4);
// Draw triangle
GLfloat triangleVertices[] = {
-0.5f, 0.4f, 0.0f,
0.5f, 0.4f, 0.0f,
-0.5f, -0.4f, 0.0f,
0.5f, -0.4f, 0.0f
};
//loadData
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, triangleVertices );
glEnableVertexAttribArray(_positionSlot);
// glDrawArrays(GL_TRIANGLES, 0, 4); //這里若想畫出2個(gè)三角形希坚,還得添加2個(gè)頂點(diǎn),請(qǐng)自行添加(012陵且,345)裁僧。
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
[_context presentRenderbuffer:_renderBuffer];
}
⊙⊙⊙先來介紹下相關(guān)函數(shù):
void glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr);
參數(shù) index :為頂點(diǎn)數(shù)據(jù)(如頂點(diǎn),顏色,法線锅知,紋理或點(diǎn)精靈大胁ゼ薄)在著色器程序中的槽位;
參數(shù) size :指定每一種數(shù)據(jù)的組成大小售睹,比如頂點(diǎn)由 x, y, z 3個(gè)組成部分桩警,紋理由 u, v 2個(gè)組成部分;
參數(shù) type :表示每一個(gè)組成部分的數(shù)據(jù)格式昌妹;
參數(shù) normalized : 表示當(dāng)數(shù)據(jù)為法線數(shù)據(jù)時(shí)捶枢,是否需要將法線規(guī)范化為單位長(zhǎng)度,對(duì)于其他頂點(diǎn)數(shù)據(jù)設(shè)置為 GL_FALSE 即可飞崖。如果法線向量已經(jīng)為單位長(zhǎng)度設(shè)置為 GL_FALSE 即可烂叔,這樣可免去不必要的計(jì)算,提升效率固歪;
stride : 表示上一個(gè)數(shù)據(jù)到下一個(gè)數(shù)據(jù)之間的間隔(同樣是以字節(jié)為單位)蒜鸡,OpenGL ES根據(jù)該間隔來從由多個(gè)頂點(diǎn)數(shù)據(jù)混合而成的數(shù)據(jù)塊中跳躍地讀取相應(yīng)的頂點(diǎn)數(shù)據(jù);
ptr :值得注意牢裳,這個(gè)參數(shù)是個(gè)多面手(后面再提)逢防。這里它指向 CPU 內(nèi)存中的頂點(diǎn)數(shù)據(jù)數(shù)組。
glEnableVertexAttribArray(); 允許使用頂點(diǎn)數(shù)據(jù)
1.glViewport 表示渲染 surface 將在屏幕上的哪個(gè)區(qū)域呈現(xiàn)出來,請(qǐng)自己修改其參數(shù)運(yùn)行以便理解蒲讯。
2.我們構(gòu)造了點(diǎn)忘朝、線、三角形的頂點(diǎn)數(shù)據(jù)(vertices)判帮,然后繪制出來局嘁。
3.關(guān)于繪制點(diǎn),若就以上圖代碼晦墙,繪制出來的點(diǎn)會(huì)很小悦昵,可能你會(huì)看不見,這時(shí)晌畅,我們?cè)陧旤c(diǎn)著色器中添加:
gl_PointSize = 10.0; //只能是float
就會(huì)讓點(diǎn)變大旱捧。
4.關(guān)于繪制線,繪制線有3種選項(xiàng)踩麦,分別為GL_LINES枚赡、GL_LINE_LOOP、GL_LINE_STRIP谓谦。
Line Strip , 指首尾相接的線段贫橙,第一條線和最后一條線沒有連接在一起;
Line Loops, 指首尾相接的線段反粥,第一條線和最后一條線連接在一起卢肃,即閉合的曲線疲迂;
5.關(guān)于繪制三角形,繪制三角形也有三種選項(xiàng)莫湘,分別為GL_TRIANGLES尤蒿、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN幅垮。
Triangle Strip, 指條帶腰池,相互連接的三角形
Triangle Fan, 指扇面,相互連接的三角形
最后運(yùn)行結(jié)果如下:
所有教程代碼在此 : https://github.com/qingmomo/iOS-OpenGLES-