2013年5月22日 星期三

C語言巨集定義技巧

Debug別人寫的Code常會遇到奇奇怪怪的Define形式

轉貼一下網頁資料,以方便後續查用,


轉載自:
http://miaozl.spaces.live.com/?_c11_blogpart_blogpart=blogview&_c=blogpart&_c02_owner=1&partqs=amonth%3d2%26ayear%3d2007

及 http://stenlyho.blogspot.tw/2007/04/c_04.html

原始GCC官方Macro位址
http://gcc.gnu.org/onlinedocs/cpp/Macros.html#Macros


C語言巨集定義技巧


1,防止一個頭檔被重複包含 


#ifndef COMDEF_H

#define COMDEF_H

//頭檔內容

#endif  



2,重新定義一些類型,防止由於各種平臺和編譯器的不同,而產生的類型位元組數差異,方便移植。 


typedef unsigned char boolean; /* Boolean value type. */

typedef unsigned long int uint32; /* Unsigned 32 bit value */

typedef unsigned short uint16; /* Unsigned 16 bit value */

typedef unsigned char uint8; /* Unsigned 8 bit value */


typedef signed long int int32; /* Signed 32 bit value */

typedef signed short int16; /* Signed 16 bit value */

typedef signed char int8; /* Signed 8 bit value */
//下面的不建議使用

typedef unsigned char byte; /* Unsigned 8 bit value type. */

typedef unsigned short word; /* Unsinged 16 bit value type. */

typedef unsigned long dword; /* Unsigned 32 bit value type. */

typedef unsigned char uint1; /* Unsigned 8 bit value type. */

typedef unsigned short uint2; /* Unsigned 16 bit value type. */

typedef unsigned long uint4; /* Unsigned 32 bit value type. */

typedef signed char int1; /* Signed 8 bit value type. */

typedef signed short int2; /* Signed 16 bit value type. */

typedef long int int4; /* Signed 32 bit value type. */

typedef signed long sint31; /* Signed 32 bit value */

typedef signed short sint15; /* Signed 16 bit value */

typedef signed char sint7; /* Signed 8 bit value */  



3,得到指定位址上的一個位元組或字 



#define MEM_B( x ) ( *( (byte *) (x) ) )

#define MEM_W( x ) ( *( (word *) (x) ) )  



4,求最大值和最小值 


#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )

#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) ) 



5,得到一個field在結構體(struct)中的偏移量


#define FPOS( type, field ) \

/*lint -e545 */ ( (dword) &(( type *) 0)-> field ) /*lint +e545 */



6,得到一個結構體中field所佔用的位元組數



#define FSIZ( type, field ) sizeof( ((type *) 0)->field )  


7,按照LSB格式把兩個位元組轉化為一個Word 

#define FLIPW( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] ) 


8,按照LSB格式把一個Word轉化為兩個位元組 


#define FLOPW( ray, val ) \

(ray)[0] = ((val) / 256); \

(ray)[1] = ((val) & 0xFF) 


  

9,得到一個變數的位址(word寬度) 


#define B_PTR( var ) ( (byte *) (void *) &(var) )

#define W_PTR( var ) ( (word *) (void *) &(var) ) 



10,得到一個字的高位和低位元位元組 


#define WORD_LO(xxx) ((byte) ((word)(xxx) & 255))

#define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8)) 



11. 返回一個比X大的最接近的8的倍數 


#define RND8( x ) ((((x) + 7) / 8 ) * 8 ) 


12. 將一個字母轉換為大寫 


#define UPCASE( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) ) 


13. 判斷字元是不是10進值的數字 


#define DECCHK( c ) ((c) >= '0' && (c) <= '9') 


14. 判斷字元是不是16進值的數字 


#define HEXCHK( c ) ( ((c) >= '0' && (c) <= '9') ||\

((c) >= 'A' && (c) <= 'F') ||\

((c) >= 'a' && (c) <= 'f') ) 



15. 防止溢出的一個方法 


#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val)) 


16. 返回陣列元素的個數 


#define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) ) 


17. 返回一個無符號數n尾的值MOD_BY_POWER_OF_TWO(X,n)=X%(2^n) 


#define MOD_BY_POWER_OF_TWO( val, mod_by ) \

( (dword)(val) & (dword)((mod_by)-1) ) 



18. 對於IO空間映射在存儲空間的結構,輸入輸出處理 

#define inp(port) (*((volatile byte *) (port)))

#define inpw(port) (*((volatile word *) (port)))

#define inpdw(port) (*((volatile dword *)(port)))

#define outp(port, val) (*((volatile byte *) (port)) = ((byte) (val)))

#define outpw(port, val) (*((volatile word *) (port)) = ((word) (val)))

#define outpdw(port, val) (*((volatile dword *) (port)) = ((dword) (val)))

19. 使用一些宏跟蹤調試 


A N S I標準說明了五個預定義的宏名。它們是: 
_ L I N E _

_ F I L E _

_ D A T E _

_ T I M E _

_ S T D C _



如果編譯不是標準的,則可能僅支援以上宏名中的幾個,或根本不支持。記住編譯程序

也許還提供其他預定義的宏名。

_ L I N E _及_ F I L E _巨集指令在有關# l i n e的部分中已討論,這裏討論其餘的宏名。

_ D AT E _巨集指令含有形式為月/日/年的串,表示原始檔案被翻譯到代碼時的日期。

源代碼翻譯到目標代碼的時間作為串包含在_ T I M E _中。串形式為時:分:秒。

如果實現是標準的,則宏_ S T D C _含有十進位常量1。如果它含有任何其他數,則實現是

非標準的。

可以定義宏,例如:

當定義了_DEBUG,輸出資料資訊和所在檔所在行
 

#ifdef _DEBUG

#define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)

#else

#define DEBUGMSG(msg,date)

#endif 



20. 巨集定義防止使用是錯誤


用小括弧包含。

例如:#define ADD(a,b) (a+b)

用do{}while(0)語句包含多語句防止錯誤

例如:#difne DO(a,b) a+b;\

a++;

應用時:if(….)

DO(a,b); //產生錯誤

else



解決方法: #difne DO(a,b) do{a+b;\

a++;}while(0)


宏中"#"和"##"的用法
一、一般用法
我們使用#把巨集引數變為一個字串,用##把兩個巨集引數貼合在一起.
用法: 


#i nclude
#i nclude
using namespace std;

#define STR(s) #s
#define CONS(a,b) int(a##e##b)

int main()
{
printf(STR(vck)); // 輸出字串"vck"
printf("%d\n", CONS(2,3)); // 2e3 輸出:2000
return 0;




二、當巨集引數是另一個宏的時候

需要注意的是凡巨集定義裏有用'#'或'##'的地方巨集引數是不會再展開.

1, 非'#'和'##'的情況
#define TOW (2)
#define MUL(a,b) (a*b)

printf("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW));
這行的宏會被展開為:
printf("%d*%d=%d\n", (2), (2), ((2)*(2)));
MUL裏的參數TOW會被展開為(2).

2, 當有'#'或'##'的時候 // 常遇到
#define A (2)
#define STR(s) #s
#define CONS(a,b) int(a##e##b)

printf("int max: %s\n", STR(INT_MAX)); // INT_MAX #i nclude
這行會被展開為:
printf("int max: %s\n", "INT_MAX");

printf("%s\n", CONS(A, A)); // compile error
這一行則是:
printf("%s\n", int(AeA));


INT_MAX和A都不會再被展開, 然而解決這個問題的方法很簡單. 加多一層中間轉換宏.
加這層巨集的用意是把所有宏的參數在這層裏全部展開, 那麼在轉換巨集裏的那一個巨集(_STR)就能得到正確的巨集引數.

#define A (2)
#define _STR(s) #s
#define STR(s) _STR(s) // 轉換宏
#define _CONS(a,b) int(a##e##b)
#define CONS(a,b) _CONS(a,b) // 轉換宏

printf("int max: %s\n", STR(INT_MAX)); // INT_MAX,int型的最大值,為一個變數 #i nclude
輸出為: int max: 0x7fffffff
STR(INT_MAX) --> _STR(0x7fffffff) 然後再轉換成字串;

printf("%d\n", CONS(A, A));
輸出為:200
CONS(A, A) --> _CONS((2), (2)) --> int((2)e(2))

三、'#'和'##'的一些應用特例
1、合併匿名變數名
#define ___ANONYMOUS1(type, var, line) type var##line
#define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line)
#define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示該行行號;
第一層:ANONYMOUS(static int); --> __ANONYMOUS0(static int, __LINE__);
第二層: --> ___ANONYMOUS1(static int, _anonymous, 70);
第三層: --> static int _anonymous70;
即每次只能解開當前層的宏,所以__LINE__在第二層才能被解開;

2、填充結構
#define FILL(a) {a, #a}

enum IDD{OPEN, CLOSE};
typedef struct MSG{
IDD id;
const char * msg;
}MSG;

MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};
相當於:
MSG _msg[] = {{OPEN, "OPEN"},
{CLOSE, "CLOSE"}};

3、記錄檔案名
#define _GET_FILE_NAME(f) #f
#define GET_FILE_NAME(f) _GET_FILE_NAME(f)
static char FILE_NAME[] = GET_FILE_NAME(__FILE__);

4、得到一個數值類型所對應的字串緩衝大小
#define _TYPE_BUF_SIZE(type) sizeof #type
#define TYPE_BUF_SIZE(type) _TYPE_BUF_SIZE(type)
char buf[TYPE_BUF_SIZE(INT_MAX)];
--> char buf[_TYPE_BUF_SIZE(0x7fffffff)];
--> char buf[sizeof "0x7fffffff"];
這裏相當於:
char buf[11];

Variadic Macros (可变参数 Macro)

GCC 官方網址 http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

 #define eprintf(...) fprintf (stderr, __VA_ARGS__) 
 
  eprintf ("%s:%d: ", input_file, lineno) 
 
 ==>  fprintf (stderr, "%s:%d: ", input_file, lineno) 
 
 
#define eprintf(args...) fprintf (stderr, args)
#define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__) 
 
eprintf("success!\n", );
          ==> fprintf(stderr, "success!\n", );
 
細節直接看官方說明 
 

2013年5月20日 星期一

OpenGL基本瞭解(十二) (GLSL 深入瞭解)


在OpenGL中向shader传递信息 

主要參考網頁 http://blog.csdn.net/racehorse/article/details/6634830 

 

可先行參考OpenGL基本瞭解(四) (GLSL基本資料)


一个OpenGL程序可以用多种方式和shader通信。注意这种通信是单向的,因为shader的输出只能是渲染到某些目标,比如颜色和深度缓存。

OpenGL的部分状态可以被shader访问,因此程序改变OpenGL某些状态就可以与shader进行通信了。例如一个程序想把光的颜色传给shader,可以直接调用OpenGL接口,就像使用固定功能流水线时做的那样。

不过,使用OpenGL状态并不是设置shader中使用数据的直观方式。比如一个shader需要一个表示时间变化的变量来计算动画,在OpenGL状态中就没有现成的变量可用。

幸运的是,GLSL允许用户自定义变量,实现OpenGL应用程序与shader通信。


GLSL提供的两种类型修饰符(更多的类型将在后面提到):

·一致变量(Uniform)
·属性(Attribute)

在GLSL檔案中的例子VertexShader.glsl

uniform mat4 projection;
uniform mat4 modelView;
attribute vec4 vKPosition;


void  main(void)
{
    gl_Position = projection * modelView * vKPosition;
}



在shader中定义的变量如果用这两种类型修饰符,表示对shader来说,它们是只读的。下面将详细讲述怎样使用这些类型的变量。

还有一种将变量送给shader的方法:使用纹理。一个纹理不止可以表示一张图片,它还可以表示一个数组。事实上,你完全可以决定如何在shader中解释纹理数据,即使它真是一幅图片。

模型矩阵与视图矩阵的合体叫着模型-视图矩阵。在OpenGLES 1.1中,所有的顶点都先经过模型-视图矩阵作用,然后再由投影矩阵作用。而OpenGL ES 2.0中, 你可以任意变换, 但是常常也是按照模形-视图/投影的过程变换,至少得差不多。

在ES1.1中,当前变换可以用矩阵来表达,如下:
     float projection[16] = { ... };
     float modelview[16] = { ... };
    
     glMatrixMode(GL_PROJECTION);  //指定哪一个矩阵是当前矩阵, GL_MODELVIEW/GL_PROJECTION/GL_TEXTURE
     glLoadMatrixf(projection); 
    
     glMatrixMode(GL_MODELVIEW);
      glLoadMatrixf(modelview);



***********************************
glMatrixMode(mode)说明
glMatrixMode设置当前矩阵模式:
GL_MODELVIEW,对模型视景矩阵堆栈应用随后的矩阵操作.
GL_PROJECTION,对投影矩阵应用随后的矩阵操作.
GL_TEXTURE,对纹理矩阵堆栈应用随后的矩阵操作.


在ES2.0中,并没有模形-视图矩阵,也没有glMatrixMode与glLoadMatrixf这样的方法。取而代之的是shaders中的 uniform变量。在后面我们会学到,uniforms是一种shader中用到的类型,我们可以简单的把它理解为shader不能改变的常數


加载方式如下:
    float projection[16] = { ... };
     float modelview[16] = { ... };
    
     GLint projectionUniform = glGetUniformLocation(program, "Projection");
     glUniformMatrix4fv(projectionUniform, 1, 0, projection);
    
     GLint modelviewUniform = glGetUniformLocation(program, "Modelview");
      glUniformMatrix4fv(modelviewUniform, 1, 0, modelview);



其中glUniformMatrix4fv函数作為传递数据之用。


glUniformMatrix4fv(gWorldLocation, 1, GL_TRUE, &World.m[0][0]);

 
第一个参数是第一个参数是shader uniform变量的位置索引
第二个参数是被赋值的矩阵的数目(因为 uniform变量可以是一个数组)。 通常為1
第三个参数表明在向uniform变量赋值时该矩阵是否需要转置。GL_TRUE => 需要轉置
最后一个参数就是传递给 uniform变量的pointer了。

原文說明
The commands glUniformMatrix{2|3|4|2x3|3x2|2x4|4x2|3x4|4x3}fv are used to modify a matrix or an array
       of matrices. The numbers in the command name are interpreted as the dimensionality of the matrix. The
       number 2 indicates a 2 x 2 matrix (i.e., 4 values), the number 3 indicates a 3 x 3 matrix (i.e., 9
       values), and the number 4 indicates a 4 x 4 matrix (i.e., 16 values). Non-square matrix
       dimensionality is explicit, with the first number representing the number of columns and the second
       number representing the number of rows. For example, 2x4 indicates a 2 x 4 matrix with 2 columns and
       4 rows (i.e., 8 values). If transpose is GL_FALSE, each matrix is assumed to be supplied in column
       major order. If transpose is GL_TRUE, each matrix is assumed to be supplied in row major order. The
       count argument indicates the number of matrices to be passed. A count of 1 should be used if
       modifying the value of a single matrix, and a count greater than 1 can be used to modify an array of
       matrices.

为何OpenGL中的好多方法都由f或fv结尾。许多方法(如glUniform*)可以是浮点-指针参数的方法,可以是整形参数 的方法,可是以其它类型参数的方法。OpenGL是C型的API,而C又不支持方法重载,所以每个方法得用不同的名字加以区分。表2.4 “OpenGL ES 方法后缀”,是方法的后缀的解释。随便说一下,v表示是一个指针型参数(pointer *)。
表2.4 OpenGL ES方法后缀
后缀
类型
i
32位整形
x
16位定点
f
32位浮点
ub
8位无符号byte
ui
32位无符号整形




数据类型和变量
下面是GLSL中的基本数据类型:·float
·bool
·int
浮点类型与C中类似,布尔类型可以为true或false。这些基本类型可以组成2、3或4维向量,如下所示:
·vec{2,3,4} a vector of 2,3,or 4 floats
·bvec{2,3,4} bool vector
·ivec{2,3,4} vector of integers
GLSL还包括2×2、3×3或4×4型矩阵,因为这些矩阵类型在图形处理中很常用:
·mat2
·mat3
·mat4
此外,还有一组用来实现纹理访问的特殊类型,它们被称为采样器(sampler),在读取纹理值(也称为纹素texel)时用到。下面就是纹理采样用到的数据类型:
·sampler1D – for 1D textures
·sampler2D – for 2D textures
·sampler3D – for 3D textures
·samplerCube – for cube map textures
·sampler1DShadow – for shadow maps
·sampler2DShadow – for shadow maps


參考使用例

struct dirlight // type definition
{
vec3 direction;
vec3 color;
};
dirlight d1;
dirlight d2 = dirlight(vec3(1.0,1.0,0.0),vec3(0.8,0.8,0.4));
vec4 a = vec4(1.0,2.0,3.0,4.0);
float posX = a.x;
float posY = a[1];
vec2 posXY = a.xy;
float depth = a.w


關於 shader的数据类型和变量就必須參考
http://blog.csdn.net/racehorse/article/details/6634830
 
变量修饰符

修饰符给出了变量的特殊含义,GLSL中有如下修饰符:

·const – 声明一个编译期常量。

·attribute– 随不同顶点变化的全局变量,由OpenGL应用程序传给顶点shader。这个修饰符只能用在顶点shader中,在shader中它是一个只读变量。

·uniform– 随不同图元变化的全局变量(即不能在glBegin/glEnd中设置),由OpenGL应用程序传给shader。这个修饰符能用在顶点和片断shader中,在shader中它是一个只读变量。

·varying –用于顶点shader和片断shader间传递的插值数据,在顶点shader中可写,在片断shader中只读。



一致变量(Uniform Variables)
不同于顶点属性在每个顶点有其自己的值,一个一致变量在一个图元的绘制过程中是不会改变的,所以其值不能在glBegin/glEnd中设置。一致变量适合描述在一个图元中、一帧中甚至一个场景中都不变的值。一致变量在顶点shader和片断shader中都是只读的。
首先你需要获得变量在内存中的位置,这个信息只有在连接程序之后才可获得。注意,对某些驱动程序,在获得存储位置前还必须使用程序(调用glUseProgram)。
获取一个一致变量的存储位置只需要给出其在shader中定义的变量名即可:

GLint loc1,loc2,loc3,loc4;
float specIntensity = 0.98;
float sc[4] = {0.8,0.8,0.8,1.0};
float threshold[2] = {0.5,0.25};
float colors[12] = {0.4,0.4,0.8,1.0,
0.2,0.2,0.4,1.0,
0.1,0.1,0.1,1.0};

loc1 = glGetUniformLocation(p,"specIntensity");
glUniform1f(loc1,specIntensity);

loc2 = glGetUniformLocation(p,"specColor");
glUniform4fv(loc2,1,sc);

loc3 = glGetUniformLocation(p,"t");
glUniform1fv(loc3,2,threshold);

loc4 = glGetUniformLocation(p,"colors");
glUniform4fv(loc4,3,colors);

属性变量(Attribute Variables)
在前一节提到,一致变量只能针对一个图元全体,就是说不能在glBegin和glEnd之间改变。
如果要针对每个顶点设置变量,那就需要属性变量了。事实上属性变量可以在任何时刻更新。在顶点shader中属性变量是只读的。因为它包含的是顶点数据,所以在片断shader中不能直接应用。
与一致变量相似,首先你需要获得变量在内存中的位置,这个信息只有在连接程序之后才可获得。注意,对某些驱动程序,在获得存储位置前还必须使用程序。


GLint glGetAttribLocation(GLuint program,char *name);
参数:
program – the handle to the program.
name – the name of the variable

void glVertexAttrib1f(GLint location, GLfloat v0);
void glVertexAttrib2f(GLint location, GLfloat v0, GLfloat v1);
void glVertexAttrib3f(GLint location, GLfloat v0, GLfloat v1,GLfloat v2);
void glVertexAttrib4f(GLint location, GLfloat v0, GLfloat v1,,GLfloat v2, GLfloat v3);
或者
GLint glVertexAttrib{1,2,3,4}fv(GLint location, GLfloat *v);
参数:
location – the previously queried location.
v0,v1,v2,v3 – float values.
v – an array of floats.


loc = glGetAttribLocation(p,"height");

 

易变变量(Varying Variables)
前 面说过,shader包括两种类型:顶点shader和片断shader。为了计算片断的值,往往需要访问顶点的插值数据。例如,当使用逐片断光照时,我 们需要知道当前片断的法线,但是在OpenGL中只为每个顶点指定了法线。顶点shader可以访问这些法线,而片断shader不能,因为法线是 OpenGL程序作为属性变量指定的。
顶 点变换后的数据移动到流水线的下一个阶段,在这个阶段通过使用连接信息,生成了所有图元并完成片断化。对每个片断,有一组变量会被自动进行插值并提供给片 断shader,这些都是固定功能。片断的颜色就是这么处理的,到达片断shader的颜色就是组成图元的顶点颜色插值的结果。
像片断shader接收到的这种插值产生的变量,就是“易变变量”类型。GLSL包含一些预先定义的易变变量,例如前面提到的颜色。用户也可以自己定义易变变量,它们必须同时在顶点shader和片断shader中声明:

varying float intensity;
一个易变变量必须先在顶点shader中声明,然后计算每个顶点的变量值。在片断shader中,接收这个变量通过插值得到的结果,注意此时这个变量是只读的。


语句和函数
控制流语句
与C语言类似,GLSL中有类似if-else的条件语句,for,while,do-while等循环语句。
 

vec4 toonify(in float intensity)
{
vec4 color;
if (intensity > 0.98)
color = vec4(0.8,0.8,0.8,1.0);
else if (intensity > 0.5)
color = vec4(0.4,0.4,0.8,1.0);
else if (intensity > 0.25)
color = vec4(0.2,0.2,0.4,1.0);
else
color = vec4(0.1,0.1,0.1,1.0);

return(color);
}





 
 
Shader-Centric View of OpenGL (VS = Vertex Shader, FS = Fragment
      Shader)”.


Shader-Centric View of OpenGL (VS = Vertex Shader, FS = Fragment Shader)



Floating-point Precision in 3rd Generation Devices

OpenGLES on IOS (二)

整理一下整個OPENGLES在IOS上實做過程的資料

參考網頁
http://blog.csdn.net/kesalin/article/details/8223649
http://blog.csdn.net/kesalin/article/details/7168967


實作紀錄網頁
http://kirenenko-tw.blogspot.tw/2013/04/opengl_11.html


1. 首先使用 initWithCoder 初始化 Storyboard 上的元件(OpenGLView.m)

initWithCoder 是物件既有的一個初始化方法函式,正常來說,你不會在使用程式碼動態產生物件時去呼叫它產生新的物件,而是在使用 Storyboard 設計介面時,直接將 Storyboard 上的元件與類別中的 initWithCoder 方法做連結,使用類似靜態的方式,讓應用程式在一開始執行時就將介面上的元件直接定義成該類別。

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self setupLayer];     // 初始設定 一個 CAEAGLLayer


        [self setupContext];    // 初始設定一個 rendering context

        [self setupProgram];  // 載入shader的執行程式碼 ,最重要的主程式

        [self setupProjection];  //設定投射矩陣
     
        [self resetTransform];  // 回歸初始值
       
    }
   
    return self;
}





OpenGL ES是Core Animation的客户,要使用OpenGL ES需要创建一个UIView,这个UIView由一个特殊的core animation layer支持,这个特殊的layer是一个CAEAGLLayer对象。CAEAGLLayer是OpenGLES和core animation联系的桥梁。当应用程序渲染完一帧后,CAEAGLLayer的内容被呈现并且和其他view的数据组合。


參考網頁 http://hi.baidu.com/wwssttt/item/e161f725e2ba9941469962d3

- (void)setupLayer
{
    _eaglLayer = (CAEAGLLayer*) self.layer;
   
    // CALayer 默认是透明的,必须将它设为不透明才能让其可见
    _eaglLayer.opaque = YES;
   
    // 设置描绘属性,在这里设置不维持渲染内容以及颜色格式为 RGBA8
    _eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
}




EAGLContext : 定义了rendering context。应用程序创建和初始化一个EAGLContext,设定他为当前的OpenGL命令的目标。OpenGL命令通常存放在一个 context所维护的队列里面,并在之后被执行。EAGLContext也提供了方法将图像呈现给Core Animation。

- (void)setupContext {
    // 指定 OpenGL 渲染 API 的版本,在这里我们使用 OpenGL ES 2.0
    EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
    _context = [[EAGLContext alloc] initWithAPI:api];
    if (!_context) {
        NSLog(@" >> Error: Failed to initialize OpenGLES 2.0 context");
        exit(1);
    }
   
    // 设置为当前上下文
    if (![EAGLContext setCurrentContext:_context]) {
        _context = nil;
        NSLog(@" >> Error: Failed to set current OpenGL context");
        exit(1);
    }
}




在前面提到可编程管线通过用 shader 语言编写脚本文件实现的,这些脚本文件相当于 C 源码,有源码就需要编译链接,因此需要对应的编译器与链接器,shader 对象与 program 对象就相当于编译器与链接器。shader 对象载入源码,然后编译成 object 形式(就像C源码编译成 .obj文件)。经过编译的 shader 就可以装配到 program 对象中,每个 program对象必须装配两个 shader 对象:一个顶点 shader,一个片元 shader,然后 program 对象被连接成“可执行文件”,这样就可以在 render 中是由该“可执行文件”了。 

參考網頁 http://content.gpwiki.org/index.php/OpenGL:Tutorials:Loading_and_using_GLSL_shaders#Creating_the_Program



- (void)setupProgram
{
    // Load shaders
    //
   
    NSString * vertexShaderPath;
    NSString * fragmentShaderPath;
   
    vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"VertexShader"
                                                       ofType:@"glsl"];
    fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"FragmentShader"
                                                         ofType:@"glsl"];
 
   
   
    //從 加入 shader,此處的
loadProgramg需自己處理   
_programHandle = [GLESUtils loadProgram:vertexShaderPath
                 withFragmentShaderFilepath:fragmentShaderPath];
    if (_programHandle == 0) {
        NSLog(@" >> Error: Failed to setup program.");
        return;
    }
   
    glUseProgram(_programHandle);
   
    // Get the attribute position slot from program
    // 通过调用 glGetAttribLocation 我们获取到 shader 中定义的变量 vPosition 在 program 的槽位,通过该槽位我们就可以对 vPosition 进行操作。
    // Returns the location of an attribute variable
    _positionSlot = glGetAttribLocation(_programHandle, "vPosition");
   
    // Get the uniform model-view matrix slot from program
    //  Returns the location of a uniform variable
    _modelViewSlot = glGetUniformLocation(_programHandle, "modelView");

   
    // Get the uniform projection matrix slot from program
    //
    _projectionSlot = glGetUniformLocation(_programHandle, "projection");
   
}




 



其中會用到兩隻檔案
VertexShader.glsl  及  FragmentShader.glsl





VertexShader.glsl
uniform mat4 projection;
uniform mat4 modelView;
attribute vec4 vPosition;


void  main(void)
{
    gl_Position = projection * modelView * vPosition;
}


FragmentShader.glsl

precision mediump float;

void main()
{
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}


此處要說明一下


Uniforms and Attributes

Uniforms 是一个program 中统一分配的,vertext 和fragment中同名的Uniform必须同类型。对应于不经常变化的变量。

Attributes 变化率高的变量。主要用来定义输入的每次点属性。

Uniforms and Attributes 在shader中通过location 和 name 来对应的。

通过GLint glGetUniformLocation(GLuint program,const char* name).根据一个Uniform的名称获取其location. 通过 glUniform***系列函数可以给一个location 设置一个Uniform的值。

參考網頁  http://blog.csdn.net/beelike/article/details/5774288


對於 loadProgram,可將其放在外部檔案,此例放在GLESUtils.m
+(GLuint)loadProgram:(NSString *)vertexShaderFilepath withFragmentShaderFilepath:(NSString *)fragmentShaderFilepath
{
    // Load the vertex/fragment shaders, and compile,此處的
loadShader也需要自己處理  
// shader type => GL_VERTEX_SHADER
    GLuint vertexShader = [self loadShader:GL_VERTEX_SHADER
                              withFilepath:vertexShaderFilepath];
    if (vertexShader == 0)
        return 0;
   
    // shader type => GL_FRAGMENT_SHADER
    GLuint fragmentShader = [self loadShader:GL_FRAGMENT_SHADER
                                withFilepath:fragmentShaderFilepath];
    if (fragmentShader == 0) {
        glDeleteShader(vertexShader);
        return 0;
    }
   
    // Create the program object
    GLuint programHandle = glCreateProgram();
    if (programHandle == 0)
        return 0;
   
    glAttachShader(programHandle, vertexShader);
    glAttachShader(programHandle, fragmentShader);
   
    // Link the 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 );
        return 0;
    }
   
    // Free up no longer needed shader resources
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
   
    return programHandle;
}
 


+(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;
}
 


基本shader創建的步驟,但是省略了檢查錯誤的步驟,實作上卻是必要的。
1. glCreateShader创建着色器对象

2. glShaderSource为着色器加载源代码,讀入檔案

3. glCompileShader编译每个着色器

4. glCreateProgram创建程序对象



5. glAttachShader 把着色器对象连接到程序对象

6. glLinkProgram 链接程序对象,生成可执行程序

7. glUseProgram安装可执行程序替换OpengGL固定功能流水线处理模块








設定投影矩陣
此處可參考網頁 http://kirenenko-tw.blogspot.tw/2013/05/opengl_16.html,瞭解整個投影的需求。 
-(void)setupProjection
{
    // Generate a perspective matrix with a 60 degree FOV
    //
    float aspect = self.frame.size.width / self.frame.size.height;  // 目前的畫面是投射近距離的畫面,要觀察遠距離的畫面。
    ksMatrixLoadIdentity(&_projectionMatrix);
   
    // 使用一個單元矩陣來產生一個投射的矩陣
    ksPerspective(&_projectionMatrix, 60.0, aspect, 1.0f, 20.0f);

   
    // Load projection matrix
    glUniformMatrix4fv(_projectionSlot, 1, GL_FALSE, (GLfloat*)&_projectionMatrix.m[0][0]);
    ///glUniformMatrix4fv函数用我们得到的变换矩阵来更新着色器程序里的uniform变量矩阵modelview。第一个参数是要修改的uniform变量的索引位置,第二个参数是要修改的矩阵数据,这里是1个。第三个参数是第四个参数是按行主序还是列主序指定的,这里我们用GL_FALSE(0),表明是列主序指定的,矩陣不需要轉置。最后一个参数是我们前面设定的变换矩阵。  
}



- (void)resetTransform
{
    if (_displayLink != nil) {
        [_displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        _displayLink = nil;
    }

    self.posX = 0.0;
    self.posY = 0.0;
    self.posZ = -5.5;
   
    self.rotateX = 0.0;
    self.rotateY = 0.0;
    self.scaleZ = 1.0;
   
    [self updateTransform];
}
 


// 重新計算所有modelView的對應矩陣
- (void)updateTransform
{
    // Generate a model view matrix to rotate/translate/scale
    //
    ksMatrixLoadIdentity(&_modelViewMatrix);
   
    // Translate away from the viewer
    //
    ksMatrixTranslate(&_modelViewMatrix, self.posX, self.posY, self.posZ);
   
    // Rotate the triangle
    //
    ksMatrixRotate(&_modelViewMatrix, self.rotateX, 1.0, 0.0, 0.0);  // rotate X
   
    ksMatrixRotate(&_modelViewMatrix, self.rotateY, 0.0, 1.0, 0.0);  // rotate Y
   
   
    // Scale the triangle
    ksMatrixScale(&_modelViewMatrix, 1.0, 1.0, self.scaleZ);
   
    // Load the model-view matrix,將
modelViewMatrix載入到_modelViewSlot中
     glUniformMatrix4fv(_modelViewSlot, 1, GL_FALSE, (GLfloat*)&_modelViewMatrix.m[0][0]);
}
 


2. 一旦有所需要,就開始繪圖,此處包含在render內


- (void)render
{
    if (_context == nil)
        return;
   
    glClearColor(0, 1.0, 0, 1.0);  // specify clear values for the color buffers
    glClear(GL_COLOR_BUFFER_BIT);  //  clear buffers to preset values, The three masks are GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, and GL_STENCIL_BUFFER_BIT.
   
    // Setup viewport
    //
    glViewport(0, 0, self.frame.size.width, self.frame.size.height); // glViewport 表示渲染 surface 将在屏幕上的哪个区域呈现出来
   
    [self drawCube];  // 劃出方形磚塊
   
    [_context presentRenderbuffer:GL_RENDERBUFFER];  // Displays a renderbuffer’s contents on screen.
}

3. 使用layoutSubviews來繪製畫面,這是一個類似重畫的機制,

可參考 http://kirenenko-tw.blogspot.tw/2013/04/layoutsubviews.html


- (void)layoutSubviews
{
    [EAGLContext setCurrentContext:_context];  // The graphics-context object to set as the current one. This must be an instance of a concrete subclass of NSGraphicsContext.
   
    glUseProgram(_programHandle);  // Installs a program object as part of current rendering state
   
    [self destoryBuffers];
   
    [self setupBuffers];
        


    // 任何目標的轉移與縮放,基本上都只用到下面兩個函數呼叫  
    [self updateTransform];
    [self render];

}



- (void)destoryBuffers
{
    glDeleteRenderbuffers(1, &_colorRenderBuffer);
    _colorRenderBuffer = 0;
   
    glDeleteFramebuffers(1, &_frameBuffer);
    _frameBuffer = 0;
}



- (void)setupBuffers {
    glGenRenderbuffers(1, &_colorRenderBuffer); // Specifies an array in which the generated renderbuffer object names are stored.
   
   
    // 设置为当前 renderbuffer
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
    // bind a renderbuffer to a renderbuffer target
   
   
    // 为 color renderbuffer 分配存储空间
    [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
    // Binds a drawable object’s storage to an OpenGL ES renderbuffer object.
   
   
    glGenFramebuffers(1, &_frameBuffer); //
    //  Specifies an array in which the generated framebuffer object names are stored.
   
    // 设置为当前 framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
    //bind a framebuffer to a framebuffer target
   
   
    // 将 _colorRenderBuffer 装配到 GL_COLOR_ATTACHMENT0 这个装配点上
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                              GL_RENDERBUFFER, _colorRenderBuffer);
    //attach a renderbuffer as a logical buffer to the currently bound framebuffer object
   
   
}




整個程序到此,可以繪出一個3D的Model。如果有任何Model上的變化,只需要用到下面兩行就可以了。

// 任何目標的轉移與縮放,基本上都只用到下面兩個函數呼叫  
    [self updateTransform];
    [self render];