学习Objective-C:入门手册

Leave a comment

Objective-C是一种简单的计算机语言,设计为可以支持真正的面向对象编程。Objective-C通过提供类定义,方法以及属性的语法, 还有其他可以提高类的动态扩展能力的结构等,扩展了标准的ANSI C语言。类的语法和设计主要是基于Smalltalk,最早的面向对象编程语言之一。

如 果你以前使用过其他面向对象编程语言,那么下面的信息可以帮助你学习Objective-C的基本语法。许多传统的面向对象概念,例如封装,继承以及多 态,在Objective-C中都有所体现。这里有一些重要的不同,但是这些不同在这文章会表现出来,而且如果你需要还有更多详细的信息存在。

如 果你从来没有使用任何编程语言编过程序,那么你至少需要在开始之前,对相关概念进行一些基础的了解。对象的使用和对象对象架构是iPhone程序设计的基 础,理解他们如何交互对创建你的程序非常重要。想了解面向对象概念的,请参看使用Objective-C进行面向对象编程。此外,参看Cocoa基础指南 可以获得Cocoa中的面向对象设计模式的信息。

想了解更多Objective-C语言和语法的细节介绍,请参看Objective-C 2.0编程语言。

Objective-C: C的超集

Objective-C是ANSI版本C编程语言的超集,支持C的基本语法。在C代码中,你定义头文件和源代码文件,从代码实现细节分离公共声明。Objective-C头文件使用的文件名列在表1中。

表1 Objective-C代码的文件扩展名

扩展名 内容类型
.h 头文件。头文件包含类,类型,函数和常数的声明。
.m 源代码文件。这是典型的源代码文件扩展名,可以包含Objective-C和C代码。
.mm 源代码文件。带有这种扩展名的源代码文件,除了可以包含Objective-C和C代码以外还可以包含C++代码。仅在你的Objective-C代码中确实需要使用C++类或者特性的时候才用这种扩展名。

当你需要在源代码中包含头文件的时候,你可以使用标准的#include编译选项,但是Objective-C提供了更好的方法。#import选项 和#include选项完全相同,只是它可以确保相同的文件只会被包含一次。Objective-C的例子和文档都倾向于使用#import,你的代码也 应该是这样的。

字符串

作 为C语言的超集,Objective-C支持C语言字符串方面的约定。也就是说,单个字符被单引号包括,字符串被双引号包括。然而,大多数 Objective-C通常不使用C语言风格的字符串。反之,大多数框架把字符串传递给NSString对象。NSString类提供了字符串的类包装, 包含了所有你期望的优点,包括对保存任意长度字符串的内建内存管理机制,支持Unicode,printf风格的格式化工具,等等。因为这种字符串使用的 非常频繁,Objective-C提供了一个助记符可以方便地从常量值创建NSString对象。要使用这个助记符,你需要做的全部事情,是在普通的双引 号字符串前放置一个@符号,如下面的例子所示:

   NSString*  myString = @"My String\n";
   NSString*  anotherString = [NSString stringWithFormat:@"%d %s", 1, @"String"];

   // 从一个C语言字符串创建Objective-C字符串
   NSString*  fromCString = [NSString stringWithCString:"A C string" encoding:NSASCIIStringEncoding];

如 同所有其他的面向对象语言,类是Objective-C用来封装数据,以及操作数据的行为的基础结构。对象就是类的运行期间实例,它包含了类声明的实例变 量自己的内存拷贝,以及类成员的指针。Objective-C的类规格说明包含了两个部分:接口和实现。接口部分包含了类声明和实例变量的定义,以及类相 关的方法。实现部分包含了类方法的实际代码。图1展现了声明一个叫做MyClass的类的语法,这个类继承自NSObject基础类。类声明总是由 @interface编译选项开始,由@end编译选项结束。类名之后的(用冒号分隔的)是父类的名字。类的实例(或者成员)变量声明在被大括号包含的代 码块中。实例变量块后面就是类声明的方法的列表。每个实例变量和方法声明都以分号结尾。

图1 类声明

列 表1展现了前面例子中MyClass类的实现。类同与类声明,类实现的位置也由两个编译选项确定,@implementation和@end。这些选项给 编译器提供了要将方法和对应类联系起来,所需的范围信息。因此方法的定义和接口中对应的声明是匹配的,只是多了个代码块而已。

列表1 类实现

   @implementation MyClass

   - (id)initWithString:(NSString *) aName
   {
       if (self = [super init]) {
           count count = 0;
           data = nil;
           name = [aName copy];
           return self;
       }
   }

   + (MyClass *)createMyClassWithString: (NSString *) aName
   {
       return [[[self alloc] initWithString:aName] autorelease];
   }
   @end

注意: 虽然前面的类只声明了方法,但是类可以声明属性。想了解更多关于属性的信息,参看“属性”。

当 用变量保存对象的时候,始终应该使用指针类型。Objective-C对变量包含的对象支持强弱两种类型。强类型指针的变量类型声明包含了类名。弱类型指 针使用id作为对象的类型。弱类型指针常用于类的集合,在集合中对象精确的类型可以是未知的。如果你用过强类型语言,你也许觉得使用弱类型变量可能会带来 问题,但是他们实际上给了Objective-C程序巨大的灵活性,而且使它更强大。

下面的例子里,展示了MyClass类的强类型和弱类型声明变量:

   MyClass*  myObject1;    // Strong typing
   id        myObject2;    // Weak typing

方法

Objective-C中的类可以声明两种类型的方法:实例方法和类方法。实例方法就是一个方法,它在类的一个具体实例的范围内执行。也就是说,在你调用一个实例方法前,你必须首先创建类的一个实例。而类方法,比较起来,也就是说,不需要你创建一个实例。

方 法声明包括方法类型标识符,返回值类型,一个或多个方法标识关键字,参数类型和名信息。图2展示insertObject:atIndex:实例方法的声 明。声明由一个减号(-)开始,这表明这是一个实例方法。方法实际的名字(insertObject:atIndex:)是所有方法标识关键的级联,包含 了冒号。冒号表明了参数的出现。如果方法没有参数,你可以省略第一个(也是唯一的)方法标识关键字后面的冒号。本例中,这个方法有两个参数。

图2 方法声明语法

当 你想调用一个方法,你传递消息到对应的对象。这里消息就是方法标识符,以及传递给方法的参数信息。发送给对象的所有消息都会动态分发,这样有利于实现 Objective-C类的多态行为。也就是说,如果子类定义了跟父类的具有相同标识符的方法,那么子类首先收到消息,然后可以有选择的把消息转发(也可 以不转发)给他的父类。

消息被中括号( [ 和 ] )包括。中括号中间,接收消息的对象在左边,消息(包括消息需要的任何参数)在右边。例如,给myArray变量传递消息insertObject:atIndex:消息,你需要使用如下的语法:

   [myArray insertObject:anObj atIndex:0];

为了避免声明过多的本地变量保存临时结果,Objective-C允许你使用嵌套消息。每个嵌套消息的返回值可以作为其他消息的参数或者目标。例如,你可 以用任何获取这种值的消息来代替前面例子里面的任何变量。所以,如果你有另外一个对象叫做myAppObject拥有方法,可以访问数组对象,以及插入对 象到一个数组,你可以把前面的例子写成如下的样子:

    [[myAppObject getArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];

虽然前面的例子都是传递消息给某个类的实例,但是你也可以传递消息给类本身。当给类发消息,你指定的方法必须被定义为类方法,而不是实例方法。你可以认为类方法跟C++类里面的静态成员有点像(但是不是完全相同的)。

类方法的典型用途是用做创建新的类实例的工厂方法,或者是访问类相关的共享信息的途径。类方法声明的语法跟实例方法的几乎完全一样,只有一点小差别。与实例方法使用减号作为方法类型标识符不同,类方法使用加号( + )。

下面的例子演示了一个类方法如何作为类的工厂方法。在这里,arrayWithCapacity是NSMutableArray类的类方法,为类的新实例分配内容并初始化,然后返回给你。

   NSMutableArray*   myArray = nil;    // nil is essentially the same as NULL

   // Create a new array and assign it to the myArray variable.
   myArray = [NSMutableArray arrayWithCapacity:0];

属性

属性是用来代替声明存取方法的便捷方式。属性不会在你的类声明中创建一个新的实例变量。他们仅仅是定义方法访问已有的实例变量的速记方式而已。暴露 实例变量的类,可以使用属性记号代替getter和setter语法。类还可以使用属性暴露一些“虚拟”的实例变量,他们是部分数据动态计算的结果,而不 是确实保存在实例变量内的。

实际上可以说,属性节约了你必须要些的大量多余的代码。因为大多数存取方法都是用类似的方式实现的,属性 避免了为类暴露的每个实例变量提供不同的getter和setter的需求。取而代之的是,你用属性声明指定你希望的行为,然后在编译期间合成基于声明的 实际的getter和setter方法。

属性声明应该放在类接口的方法声明那里。基本的定义使用@property编译选项,紧跟着类型信息和属性的名字。你还可以用定制选项对属性进行配置,这决定了存取方法的行为。下面的例子展示了一些简单的属性声明:

   @property BOOL flag;
   @property (copy) NSString* nameObject;  // Copy the object during assignment.
   @property (readonly) UIView* rootView;  // Create only a getter method.

使用属性另外的好处就是你可以在代码中访问他们的时候,使用点语法,如下面的例子所示:

   myObject.flag = YES;
   CGRect   viewFrame = myObject.rootView.frame;

虽然前面例子里面的对象和属性名是故意这么取的,他们还是展现了属性的灵活性。点语法实际上隐藏了对应的方法调用。每个可读的属性由一个与属性同名的方法 支持。每个可写属性由一个叫做“set属性名”的额外方法来支持,属性名的第一个字母要大写。(这些方法是属性的实际实现方式,也是你可以声明一个没有任 何实例变量支持的属性声明的原因。)如果用方法来代替前面代码中的属性,你就要下下面的代码:

   [myObject setFlag:YES];
   CGRect   viewFrame = [[myObject rootView] frame];

要了解更多关于如何声明属性的信息,参看Objective-C 2.0编程语言的属性一章。

协议和代理

协议声明了可以被任何类实现的方法。协议不是那些类本身。他们仅是定义一个接口,其他的对象去负责实现。你实现了协议里面的方法,就叫做符合协议。

在iPhone OS中协议常用来实现委托对象。委托对象就是一个对象以其他的对象的模式行事。了解协议,委托和对象最好的办法就是看一个例子。

UIApplication 类实现了一个程序需要的行为。如果想接收程序当前状态的简单消息,并不需要强制你创建UIApplication的一个子类,反之 UIApplication类通过调用委托对象的指定方法来分发这些通知消息。实现UIApplicationDelegate方法的对象都可以接受这样 的通知,并进行响应的反应。

协议的声明跟类接口的声明很像,只是协议没有父类,而且他们不会定义任何实例变量。下面的例子展示了一个有一个方法的协议声明:

   @protocol MyProtocol
	- (void)myProtocolMethod;
   @end

在大多数委托协议情况下,使用某种协议仅仅是简单的实现协议定义的方法而已。 有些协议要求你明确的表明你支持这种协议,协议可以指定必须或者可选的方法。在你深入开发的过程中,你应该花点时间学习协议以及他们的用途,请阅读 Objective-C 2.0编程语言的协议章节。

Advertisements

ObjectiveC学习笔记

Leave a comment

ObjectiveC基本语法

消息
在objectiveC中,向一个对象发送一个消息的语法为

[ obj method:parameter];

类似的功能在C++中写作

obj->method(parameter);

在java中写作
obj.method(parameter);

在smalltalk中写作
obj method:parameter

显而易见objectiveC和smalltalk的语法基本是相同的。

当有两个或者两个以上的参数时,通常试用以的语法
[ obj method:parameter1 WithSecondParameter:parameter2];

定义一个类的代码放在一个.h文件中,下面是一个例子。
//macdevexample1.h
@interface ClassName:SuperClassName{
        //begin of instance variables
        @private
                int a;
        @protected
                float b;
        @public
                double c;
        //end of instance variables
}
//begin of method declaration
– instanceExampleMethod1;//instance method with no parameter
+ classExampleMethod1;//class method

– exampleMethod2:(int)parameter1; //instance method with 1 parameter
– (int)exampleMethod3:(int)parameter1 WithSecondParameter:(double)parameter2
;//instance method with 2 parameters

– (float)b;//method can have the same name of variable
– (void)setb:(float)_b;
//end of method declaration
@end
//end of macdevexample1.h

ObjectiveC类的定义以@interface开始,@end结束。@end后面不需要加分号,但是加了
也不会有什么大问题。数据部分包含在大括号中,缺省为protected。
ObjectiveC中的方法都为public。在声明方法时在方法命前加-表示是类的实例可以调
用的方法, +表示是类对象可以调用的方法(类似于C++、java里面的static方法)。
ObjectiveC定义类方法的语法同smalltalk类似,而不是像C++/Java一样同algo/C保持
一致。对于从C++、java转过来的开发人员来说可能有些不习惯。

ObjectiveC方法若没有声明返回值则返回id类型数据,id实际上是一个指向所有类的指
针,具体以后讨论。

实现一个类的代码会放在一个.m文件里
#import macdevexample1.h
@implementation ClassName
– instanceExampleMethod1{
//code goes here
}
+ classExampleMethod1{
//code goes here
}
..
@end
在objectiveC中可以试用#import或者#include来包含一个头文件。在使用#import时每
个头文件只会被包含一次,避免了类和变量重复定义的问题。

创建一个类的实例通常使用如下代码
ClassA *objA = [[ClassA alloc] init];
或者
ClassA *objA = [ClassA new];
或者
id objA = [[ClassA alloc] init];

alloc为类方法,负责分配内存(在heap上),然后返回一个id。init是实例方法,负责
初始化,然后返回一个id。同alloc类似的还有+ allocFromZone:(NXZone *)zone方法
,zone会在以后讨论。init也会有带参数的initWithXXX之类的方法。new方法把分配内
存和初始化在一个方法内完成。但土人俺觉得[[ClassA alloc] init]更清晰一点。

C++、java里面的this在objectiveC里面叫self(Smalltalk,Python, Objective
Pascal,Ruby也叫self, VB里面叫Me,Perl里面用$_[0] (俺Perl没学明白,不太敢确
认),PHP用$this表示实例用self表示类)。Java中的super在objectiveC中也叫super。

ObjectiveC中的BOOL类型的值为Yes和No而不是true和false。

Category是ObjectiveC提出的一个对类进行扩展的办法。C#3.0中extension methods的
概念和ObjectiveC的Category类似。

一下是一个例子,我们首先定义一个mitbbsExampleLocation类,文件名为
mitbbsExampleLocation.h。

#import <Cocoa/Cocoa.h>
@interface mitbbsExampleLocation : NSObject {
        int x,y;
}
-(void)moveToX:(int)x andY:(int)y;
@end

下面是implementation。
#import "mitbbsExampleLocation.h"
@implementation mitbbsExampleLocation
-(void)moveToX:(int)x andY:(int)y{
        self->x = x;
        self->y = y;
}
@end

下面定义一个扩展mitbbsExampleLocation类的category,定义的方法就是在类的名字
后面加一个小括号,然后把category的名字写在小括号里面,文件名不太重要,但是最
好为DisplayData.h。
#import "mitbbsExampleLocation.h"

@interface mitbbsExampleLocation (DisplayData)
        -(void) displayLocation;
        -(void) displayX;
        -(void) displayY;
@end

下面是implementation

#import "DisplayData.h"
@implementation  mitbbsExampleLocation (DisplayData)
        -(void) displayLocation{
                printf("x=%d\ty=%d\n", self->x, self->y);
        }
        -(void) displayX{
                NSAutoreleasePool * Pool = [[NSAutoreleasePool alloc] init];
                NSLog([[NSString stringWithFormat:@"%d\n", self->x]
autorelease]);
                [Pool release];
        }
        -(void) displayY{
                //…………….
        }
@end

Protocol是ObjectiveC中实现多重继承的机制。ObjectiveC中有两种protocol,一种是
非正式的,一种是正式的。非正式的protocol只是在文档中规定应该实现的方法。正式
的Protocol和java中的interface类似。在protocol中开发人员可以定义方法但是不能
定义实例变量。
@protocol mitbbsExampleClear
 -(void) Clear;
@end

@interface betterLocation: mitbbsExampleLocation <mitbbsExampleClear>
         -(void) Clear{
                self->x = 0;
                self->y = 0;
        }
@end

ObjectiveC中的消息机制是其面向对象的核心。在objectiveC中,向一个对象发送一条
消息的语法为

[receiver messageName:parameter];

这条命令会被编译器翻译为C代码

objc_msgSend(receiver, @selector(messageName), parameter);

在这里我们第一次遇到selector的概念。selector在objectiveC中的关键字为SEL。

在GNUStep中, SEL的定义为如下C代码。
typedef const struct objc_selector
{
  void *sel_id;
  const char *sel_types;
} *SEL;

由此可见,selector实际上是一个由一个方法名的字符串和一个unique id组成的结构
。所有同名的方法共有一个selector。当向某个对象发送消息时,objectiveC实际上告
诉对象需要调用某个selector对应的方法,该对象会从自己的Class所保存的selector-
method对应列表中查找是否可以相应该selector对应的方法,如果有就该干啥干啥,如
果不存在该selector对应的方法,则在superClass里面查询,如果还是没有则调用
message forward机制。message forward机制以后会讨论。

ObjectiveC会向实例方法传递两个隐含的参数,第一个为self第二个为该方法所对应的
消息的selector。C++仅传递一个隐含参数this。同C相比增加的参数数量会影响性能,
在IA32平台,参数通过栈传递,在PowerPC和x86-64平台参数通过寄存器传递。因此在
PowerPC和x86-64平台增加的参数对性能的影响相对较小。

ObjectiveC的消息机制同C++相比更加灵活,但是相对效率较低。ObjectiveC本质上是
动态的语言,而C++本质上是静态的语言,动态语言的本质导致ObjectiveC的效率必然
比C++低。因此在使用ObjectiveC进行开发时,对性能要求比较高的部分最好不用使用
消息机制(不要试用[receiver message]),而尽可能使用单纯的C语言。

在使用C代码时,可以试用@defs来获取ObjectiveC的Class的结构从而访问ObjectiveC
中的private和protected变量。以上面的mitbbsExampleLocation为例。

首先我们定义一个struct

typedef struct {
 @defs(mitbbsExampleLocation);
} *PLocation;

然后把一个指向mitbbsExampleLocation的指针cast成PLocation类型的变量。
PLocation p = (PLocation)[[mitbbsExampleLocation alloc]init];
p->x = 0;
p->y = 0;

获取一个ObjectiveC类的方法可以使用以下方法。
以mitbbsExampleLocation类的moveToX:andY:为例,首先定义一个指向该函数的指针

void (*moveToXAndY)(id, SEL, int, int);

然后获取方法的地址

moveToXAndY = (void (*)(id, SEL, int, int))[aLocation methodForSelector:@
selector(moveToX:andY:)];

然后moveToXAndY可以和调用普通方法一样调用但需要有连个隐含参数
moveToXAndY(anId, @selector(moveToX:andY:), 1, 2);

methodForSelector方法是在NSObject类中定义的。 其返回值为IMP。IMP的定义为

typedef id (*IMP)(id, SEL, …);

所以IMP就是一个指向返回值为id的ObjectiveC方法。如果返回值为其他类型我们需要
像上面一样自己定义一个方法的指针。

NSObject是目前所有ObjectiveC类的根类。NSObject中的NS表示NeXTSTEP。NSObject是
在NSObject.h文件里定义的。其核心内容如下
@interface NSObject <NSObject> {
    Class       isa;
}

+ (void)load;

+ (void)initialize;
– (id)init;

+ (id)new;
+ (id)allocWithZone:(NSZone *)zone;
+ (id)alloc;
– (void)dealloc;

#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
– (void)finalize;
#endif

– (id)copy;
– (id)mutableCopy;

+ (id)copyWithZone:(NSZone *)zone;
+ (id)mutableCopyWithZone:(NSZone *)zone;

+ (Class)superclass;
+ (Class)class;
+ (void)poseAsClass:(Class)aClass;
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
+ (BOOL)conformsToProtocol:(Protocol *)protocol;
– (IMP)methodForSelector:(SEL)aSelector;
+ (IMP)instanceMethodForSelector:(SEL)aSelector;
+ (int)version;
+ (void)setVersion:(int)aVersion;
– (void)doesNotRecognizeSelector:(SEL)aSelector;
– (void)forwardInvocation:(NSInvocation *)anInvocation;
– (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector;

#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED
+ (BOOL)isSubclassOfClass:(Class)aClass;
#endif

+ (NSString *)description;

– (Class)classForCoder;
– (id)replacementObjectForCoder:(NSCoder *)aCoder;
– (id)awakeAfterUsingCoder:(NSCoder *)aDecoder;

@end

先把所有的方法放在一边看数据。整个NSObject里面只有一个数据 Class        isa
。Class类型是在objc.h里面通过typedef定义的,原文为

typedef struct objc_class *Class;

所以Class就是一个指向objc_class结构的指针。而objc_class是在 objc-class.h里面
定义的,内容为

struct objc_class {                    
        struct objc_class *isa;
        struct objc_class *super_class;
        const char *name;              
        long version;
        long info;
        long instance_size;
        struct objc_ivar_list *ivars;

        struct objc_method_list **methodLists;

        struct objc_cache *cache;
        struct objc_protocol_list *protocols;
};

这里面最值的注意的是

const char *name;

这一行。objectiveC和C++不同,每个类的名字会被编译器存在编译后的代码中,这样
可以在运行时确定类的名字,这是C++中没有的功能。

struct objc_ivar_list *ivars; 包含了instance variable的信息,例如名字,类型
,位置,这样可以在运行时获得变量的相关信息。objc_ivar_list的定义为

struct objc_ivar_list {
        int ivar_count;
#ifdef __alpha__
        int space;
#endif
        struct objc_ivar ivar_list[1];          /* variable length structure
 */
};

在objc_ivar_list的定义中,定义objc_ivar的代码为
struct objc_ivar {
        char *ivar_name;
        char *ivar_type;
        int ivar_offset;
#ifdef __alpha__
        int space;
#endif
};

struct objc_method_list **methodLists;这一行储存所有的方法的信息。当一个类或
者一个实例收到一个消息的时候会在这个表里面查找有没有相对应的方法。同变量类似
,ObjectiveC可以在运行时获取方法的名字等信息。

struct objc_method_list {
        struct objc_method_list *obsolete;

        int method_count;
#ifdef __alpha__
        int space;
#endif
        struct objc_method method_list[1];      /* variable length structure
 */
};

typedef struct objc_method *Method;

struct objc_method {
        SEL method_name;
        char *method_types;
        IMP method_imp;
};

通过上面的分析,我们可以说isa指针(以及Class变量)和C++的vtable类似但是增加
了很多其他信息,这些信息在二进制代码中也是可读的,使用class-dump(http://www.codethecode.com/projects/class-dump/)直接从objectiveC编译完成的二进制代码生成interface信息。

id也是在objc.h中定义的,代码为

typedef struct objc_object {
        Class isa;
} *id;

所以本质上,id就是一个指向NSObject的实例的指针,由于在objectiveC中所有的类都
是NSObject的子类,所以id可以用来指向任何类的实例。

在很久以前,ObjectiveC中的基类叫做Object,后来NeXTSTEP和Sun一起合作开发了一
个叫做OpenStep的项目,所有的类前面都加上了NS。土人俺不知道这个NS到底是代表(N
)eXT(S)TEP还是(N)eXTSTEP-(S)UN。OpenStep是后来的GNUStep和Cocoa的基础,因此现
在的Cocoa中的类都带着NS。

现在我们已经提到了id,Class,SEL,IMP,BOOL, #import,@interface,@
implementation,@protocol,@end,@private,@protected,@public,@selector(
method_name), @defs(class_name)。

[转]介绍顶点着色器和像素着色器

Leave a comment

目录:
第一部分 顶点着色器
第二部分 像素着色器
第三部分 寄存器
第四部分
教程(分别使用了D3D和OpenGL)

第五部分 结论文档内容:

第一部分 顶点着色器

什么是顶点着色器?
1 顶点着色器是一组指令代码,这组指令代码在顶点被渲染时执行。
2 同一时间内,只能激活一个顶点着色器。
3 每个源顶点着色器最多拥有128条指令(DirextX8.1),而在DirectX9,则可以达到256条。

为什么大家要使用顶点着色器?
1 顶点着色器可以提高渲染场景速度。
2 用顶点着色器你可以做布类仿真,高级别动画,实时修改透视效果(比如水底效果),高级光亮(需要像素着色器支持)

顶点着色器如何运作?
简单说来,运作方式如下:当渲染一个顶点时,API会执行你在顶点着色器中所写的指令。依靠这种方法,你可以自己控制每个顶点,包括渲染,确定位置,是否显示在屏幕上。

如何创建一个顶点着色器?
用一个文本编辑器就可以了!我建议你们使用notepad或者vs开发环境来创建和修改着色器。另外,必须拥有一个支持可编程着色器的显卡。写完着色器
后,保存他。API就可以调用他了(Direct3D或OpenGL)。API通过一些函数来调用这些代码指令到硬件中。

第二部分 像素着色器



1 像素着色器也是一组指令,这组指令在顶点中像素被渲染时执行。在每个执行时间,都会有很多像素被渲染。(像素的数目依靠屏幕的分辨率决定)
2像素着色器的指令和顶点着色器的指令非常接近。像素着色器不能像顶点着色器那样,单独存在。他们在运行的时候,必须有一个顶点着色器被激活。

为什么大家要使用像素着色器?
1 像素着色器过去是一种高级图形技术,专门用来提高渲染速度。
2 和顶点着色器一样,使用像素着色器,程序员能自定义渲染每个像素。

像素着色器如何运作?
一个像素着色器操作顶点上单独的像素。和顶点着色器一样,像素着色器源代码也是通过一些API加载到硬件的。

如何创建一个像素着色器?
也和顶点着色器一样,你只需要一个文本编辑器和支持着色器编程的显卡即可。同样,API(Direct3D
OpenGL)加载像素着色器代码指令到硬件中。

第三部分 寄存器

我们需要什么样的硬件支持?
1 需要DirectX SDK 9.0(DX用户)。注意:我们的顶点着色器的版本是vs2.0。(Dx9支持)
2 需要OpenGL 1。4(OpenGL用户)。
3 可编程着色器显卡。

什么是寄存器?
寄存器是显卡GPU的内存位置,直观的说,可以把寄存器想象成变量存放的地方。现在,有相当数量的寄存器你可以使用!顶点着色器拥有比像素着色器更多的寄存器。

<顶点着色器寄存器>
我们先看看可供使用的顶点着色器有哪些?
1 输入寄存器16个
2 输出寄存器9个(GeForce显卡)或11个(Radeon显卡)(译者注:请参看最新的显卡介绍,硬件更新太快,没办法。)
3 常数寄存器96个(GeForce显卡)或192个(Radeon显卡)
4 临时寄存器12个
5 地址寄存器1个(顶点着色器vc2.0)

这些寄存器都是干什么的?
1 输入寄存器保留顶点的数值,当顶点要建立渲染时。比如(D3D),输入寄存器v0将保存顶点位置,v1保存颜色。(例子代码在下一章)
2
输出寄存器保存将要被渲染的顶点,最终的值。在下面的例子中,输出寄存器oPos保存顶点的输出位置,oD0保存顶点颜色。(这些值可能会被更新)
3
常量寄存器保存你自己写入的数据。例子中,你可以写进光线颜色,那么,就可以设定寄存器c[15]。你可以混合顶点颜色数据:基于当前光照oD0,使v1附上新的颜色。
4 临时寄存器被用来保存临时变量。
5 地址寄存器保存地址偏移量(整数)。

不要对上面介绍的东西害怕。我也是第一次学习这些东西(译者:我也是-_-!)。

<像素着色器寄存器>
然后我们看看像素着色器寄存器都有些什么?
1 常量寄存器8个
2 纹理寄存器4个(ps1.4里有6个)
3 临时寄存器2个(ps1.4里有6个)
4 颜色寄存器2个

这些寄存器都是干什么的?
1 常量寄存器保存用户输入的数据
2 纹理寄存器保存对应顶点的纹理坐标
3 颜色寄存器保存像素的输出颜色
4 临时寄存器保存临时值

后面还有教程看,耐心点。

第四部分 教程(分别使用了D3D和OpenGL)

顶点着色器例子(D3D版):

// Header section…

vs.2.0 // Vertex Shader version

// Define the input information…
dcl_position v0 // Declare that v0 will be the original position
dcl_color v1 // Declare that v1 will hold the original color

// Set the output information…
mov oPos, v0 // Move the original position to the final position
mov oD0, v1 // Move the original color to the final color

每个顶点着色器都是由3部分组成:头,输入定义段,数据段。

顶点着色器例子(OpenGL using vertex shader extension GL_ARB_vertex_program)

//Header section

!!ARBvp1.0 // Vertex Shader version

// Declare that VPos will be the original position and VCol is the
original color
ATTRIB VPos = vertex.position;
ATTRIB VCol = vertex.color;

// Declare that OutPos is the output position and OutColor is the output color
OUTPUT OutPos = result.position;
OUTPUT OutColor = result.color;

// Set the output information…
mov OutPos, VPos // Move the original position to the final position
mov OutColor, VCol // Move the original color to the final color

顶点着色器包括4部分:头,输入定义段,输出定义段,数据段。

像素着色器(Direct3D using pixel shader ps.1.1)

ps.1.1//version number.

mov r0, v0 // Move the original pixel color to the output color so we can use that.

此着色器使用原始颜色数据输出。

像素着色器(OpenGL using pixel shader extension GL_ARB_fragment_program)
!!ARBfp1.0

ATTRIB inColor = fragment.color.primary; # Original pixel color.

OUTPUT outColor = result.color; # Define output color.

MOV outColor, inColor; # Move original color to the output so we can
use that.
END # Define end of the pixel shader.

此着色器使用原始颜色数据输出。

第五部分 结论

使用顶点和像素着色器会极大的改善图形质量。