不会飞的鸟

2010年12月10日 ... 不鸟他们!!! 我要用自己开发的分布式文件系统、分布式调度系统、分布式检索系统, 做自己的搜索引擎!!!大鱼有大志!!! ---杨书童

#

[linux]ld.so.conf 和 ldconfig

今天重新编译以前的一个程序,里面用到iconv库:gcc test.cc -liconv
运行时:a.out:error while loading shared libraries: libiconv.so.2: cannot open shared object file: No such file or directory
以前编译运行是可以的,可能是不久前升级了iconv库影响。在/usr/local/lib下可以找到libiconv.so.2,把/usr/local/lib加到路径中也不行。
google了一下,解决了:在/etc/ld.so.conf中加一行/usr/local/lib,运行ldconfig。再运行a.out,行了。
ld.so.conf和ldconfig是维护系统动态链接库的。真不明白为什么iconv库安装时不把这一步也做了。



//注意
如果你不是root,ldconfig也运行不了的,解决的方法就是,设置环境变量 LDFLAGS=-L/usr/local/lib

posted @ 2009-09-24 12:46 不会飞的鸟 阅读(464) | 评论 (0)编辑 收藏

例解 autoconf 和 automake 生成 Makefile 文件

本文介绍了在 linux 系统中,通过 Gnu autoconf 和 automake 生成 Makefile 的方法。主要探讨了生成 Makefile 的来龙去脉及其机理,接着详细介绍了配置 Configure.in 的方法及其规则。

引子

无论是在Linux还是在Unix环境中,make都是一个非常重要的编译命令。不管是自己进行项目开发还是安装应用软件,我们都经常要用到make或 make install。利用make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和 makefile工具就可以轻而易举的理顺各个源文件之间纷繁复杂的相互关系。

但是如果通过查阅make的帮助文档来手工编写Makefile,对任何程序员都是一场挑战。幸而有GNU 提供的Autoconf及Automake这两套工具使得编写makefile不再是一个难题。

本文将介绍如何利用 GNU Autoconf 及 Automake 这两套工具来协助我们自动产生 Makefile文件,并且让开发出来的软件可以像大多数源码包那样,只需"./configure", "make","make install" 就可以把程序安装到系统中。





回页首


模拟需求

假设源文件按如下目录存放,如图1所示,运用autoconf和automake生成makefile文件。


图 1文件目录结构
图 1文件目录结构

假设src是我们源文件目录,include目录存放其他库的头文件,lib目录存放用到的库文件,然后开始按模块存放,每个模块都有一个对应的目录,模块下再分子模块,如apple、orange。每个子目录下又分core,include,shell三个目录,其中core和shell目录存放.c文件,include的存放.h文件,其他类似。

样例程序功能:基于多线程的数据读写保护(联系作者获取整个autoconf和automake生成的Makefile工程和源码,E-mail:normalnotebook@126.com)。





回页首


工具简介

所必须的软件:autoconf/automake/m4/perl/libtool(其中libtool非必须)。

autoconf是一个用于生成可以自动地配置软件源码包,用以适应多种UNIX类系统的shell脚本工具,其中autoconf需要用到 m4,便于生成脚本。automake是一个从Makefile.am文件自动生成Makefile.in的工具。为了生成Makefile.in,automake还需用到perl,由于automake创建的发布完全遵循GNU标准,所以在创建中不需要perl。libtool是一款方便生成各种程序库的工具。

目前automake支持三种目录层次:flat、shallow和deep。

1) flat指的是所有文件都位于同一个目录中。

就是所有源文件、头文件以及其他库文件都位于当前目录中,且没有子目录。Termutils就是这一类。

2) shallow指的是主要的源代码都储存在顶层目录,其他各个部分则储存在子目录中。

就是主要源文件在当前目录中,而其它一些实现各部分功能的源文件位于各自不同的目录。automake本身就是这一类。

3) deep指的是所有源代码都被储存在子目录中;顶层目录主要包含配置信息。

就是所有源文件及自己写的头文件位于当前目录的一个子目录中,而当前目录里没有任何源文件。 GNU cpio和GNU tar就是这一类。

flat类型是最简单的,deep类型是最复杂的。不难看出,我们的模拟需求正是基于第三类deep型,也就是说我们要做挑战性的事情:)。注:我们的测试程序是基于多线程的简单程序。





回页首


生成 Makefile 的来龙去脉

首先进入 project 目录,在该目录下运行一系列命令,创建和修改几个文件,就可以生成符合该平台的Makefile文件,操作过程如下:

1) 运行autoscan命令

2) 将configure.scan 文件重命名为configure.in,并修改configure.in文件

3) 在project目录下新建Makefile.am文件,并在core和shell目录下也新建makefile.am文件

4) 在project目录下新建NEWS、 README、 ChangeLog 、AUTHORS文件

5) 将/usr/share/automake-1.X/目录下的depcomp和complie文件拷贝到本目录下

6) 运行aclocal命令

7) 运行autoconf命令

8) 运行automake -a命令

9) 运行./confiugre脚本

可以通过图2看出产生Makefile的流程,如图所示:


图 2生成Makefile流程图
图 2生成Makefile流程图




回页首


Configure.in的八股文

当我们利用autoscan工具生成confiugre.scan文件时,我们需要将confiugre.scan重命名为confiugre.in文件。confiugre.in调用一系列autoconf宏来测试程序需要的或用到的特性是否存在,以及这些特性的功能。

下面我们就来目睹一下confiugre.scan的庐山真面目:


# Process this file with autoconf to produce a configure script.
            AC_PREREQ(2.59)
            AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
            AC_CONFIG_SRCDIR([config.h.in])
            AC_CONFIG_HEADER([config.h])
            # Checks for programs.
            AC_PROG_CC
            # Checks for libraries.
            # FIXME: Replace `main' with a function in `-lpthread':
            AC_CHECK_LIB([pthread], [main])
            # Checks for header files.
            # Checks for typedefs, structures, and compiler characteristics.
            # Checks for library functions.
            AC_OUTPUT
            

每个configure.scan文件都是以AC_INIT开头,以AC_OUTPUT结束。我们不难从文件中看出confiugre.in文件的一般布局:


AC_INIT
            测试程序
            测试函数库
            测试头文件
            测试类型定义
            测试结构
            测试编译器特性
            测试库函数
            测试系统调用
            AC_OUTPUT
            

上面的调用次序只是建议性质的,但我们还是强烈建议不要随意改变对宏调用的次序。

现在就开始修改该文件:


$mv configure.scan configure.in
            $vim configure.in
            

修改后的结果如下:


            #                                -*- Autoconf -*-
            # Process this file with autoconf to produce a configure script.
            AC_PREREQ(2.59)
            AC_INIT(test, 1.0, normalnotebook@126.com)
            AC_CONFIG_SRCDIR([src/ModuleA/apple/core/test.c])
            AM_CONFIG_HEADER(config.h)
            AM_INIT_AUTOMAKE(test,1.0)
            # Checks for programs.
            AC_PROG_CC
            # Checks for libraries.
            # FIXME: Replace `main' with a function in `-lpthread':
            AC_CHECK_LIB([pthread], [pthread_rwlock_init])
            AC_PROG_RANLIB
            # Checks for header files.
            # Checks for typedefs, structures, and compiler characteristics.
            # Checks for library functions.
            AC_OUTPUT([Makefile
            src/lib/Makefile
            src/ModuleA/apple/core/Makefile
            src/ModuleA/apple/shell/Makefile
            ])
            

其中要将AC_CONFIG_HEADER([config.h])修改为:AM_CONFIG_HEADER(config.h), 并加入AM_INIT_AUTOMAKE(test,1.0)。由于我们的测试程序是基于多线程的程序,所以要加入AC_PROG_RANLIB,不然运行automake命令时会出错。在AC_OUTPUT输入要创建的Makefile文件名。

由于我们在程序中使用了读写锁,所以需要对库文件进行检查,即AC_CHECK_LIB([pthread], [main]),该宏的含义如下:



其中,LIBS是link的一个选项,详细请参看后续的Makefile文件。由于我们在程序中使用了读写锁,所以我们测试pthread库中是否存在pthread_rwlock_init函数。

由于我们是基于deep类型来创建makefile文件,所以我们需要在四处创建Makefile文件。即:project目录下,lib目录下,core和shell目录下。

Autoconf提供了很多内置宏来做相关的检测,限于篇幅关系,我们在这里对其他宏不做详细的解释,具体请参看参考文献1和参考文献2,也可参看autoconf信息页。





回页首


实战Makefile.am

Makefile.am是一种比Makefile更高层次的规则。只需指定要生成什么目标,它由什么源文件生成,要安装到什么目录等构成。

表一列出了可执行文件、静态库、头文件和数据文件,四种书写Makefile.am文件个一般格式。


表 1Makefile.am一般格式
表 1Makefile.am一般格式

对于可执行文件和静态库类型,如果只想编译,不想安装到系统中,可以用noinst_PROGRAMS代替bin_PROGRAMS,noinst_LIBRARIES代替lib_LIBRARIES。

Makefile.am还提供了一些全局变量供所有的目标体使用:


表 2 Makefile.am中可用的全局变量
表 2 Makefile.am中可用的全局变量

在Makefile.am中尽量使用相对路径,系统预定义了两个基本路径:


表 3Makefile.am中可用的路径变量
表 3Makefile.am中可用的路径变量

在上文中我们提到过安装路径,automake设置了默认的安装路径:

1) 标准安装路径

默认安装路径为:$(prefix) = /usr/local,可以通过./configure --prefix=<new_path>的方法来覆盖。

其它的预定义目录还包括:bindir = $(prefix)/bin, libdir = $(prefix)/lib, datadir = $(prefix)/share, sysconfdir = $(prefix)/etc等等。

2) 定义一个新的安装路径

比如test, 可定义testdir = $(prefix)/test, 然后test_DATA =test1 test2,则test1,test2会作为数据文件安装到$(prefix)/ /test目录下。

我们首先需要在工程顶层目录下(即project/)创建一个Makefile.am来指明包含的子目录:


SUBDIRS=src/lib src/ModuleA/apple/shell src/ModuleA/apple/core
            CURRENTPATH=$(shell /bin/pwd)
            INCLUDES=-I$(CURRENTPATH)/src/include -I$(CURRENTPATH)/src/ModuleA/apple/include
            export INCLUDES
            

由于每个源文件都会用到相同的头文件,所以我们在最顶层的Makefile.am中包含了编译源文件时所用到的头文件,并导出,见蓝色部分代码。

我们将lib目录下的swap.c文件编译成libswap.a文件,被apple/shell/apple.c文件调用,那么lib目录下的Makefile.am如下所示:


noinst_LIBRARIES=libswap.a
            libswap_a_SOURCES=swap.c
            INCLUDES=-I$(top_srcdir)/src/includ
            

细心的读者可能就会问:怎么表1中给出的是bin_LIBRARIES,而这里是noinst_LIBRARIES?这是因为如果只想编译,而不想安装到系统中,就用noinst_LIBRARIES代替bin_LIBRARIES,对于可执行文件就用noinst_PROGRAMS代替bin_PROGRAMS。对于安装的情况,库将会安装到$(prefix)/lib目录下,可执行文件将会安装到${prefix}/bin。如果想安装该库,则Makefile.am示例如下:


bin_LIBRARIES=libswap.a
            libswap_a_SOURCES=swap.c
            INCLUDES=-I$(top_srcdir)/src/include
            swapincludedir=$(includedir)/swap
            swapinclude_HEADERS=$(top_srcdir)/src/include/swap.h
            

最后两行的意思是将swap.h安装到${prefix}/include /swap目录下。

接下来,对于可执行文件类型的情况,我们将讨论如何写Makefile.am?对于编译apple/core目录下的文件,我们写成的Makefile.am如下所示:


noinst_PROGRAMS=test
            test_SOURCES=test.c
            test_LDADD=$(top_srcdir)/src/ModuleA/apple/shell/apple.o $(top_srcdir)/src/lib/libswap.a
            test_LDFLAGS=-D_GNU_SOURCE
            DEFS+=-D_GNU_SOURCE
            #LIBS=-lpthread
            

由于我们的test.c文件在链接时,需要apple.o和libswap.a文件,所以我们需要在test_LDADD中包含这两个文件。对于Linux下的信号量/读写锁文件进行编译,需要在编译选项中指明-D_GNU_SOURCE。所以在test_LDFLAGS中指明。而test_LDFLAGS只是链接时的选项,编译时同样需要指明该选项,所以需要DEFS来指明编译选项,由于DEFS已经有初始值,所以这里用+=的形式指明。从这里可以看出,Makefile.am中的语法与Makefile的语法一致,也可以采用条件表达式。如果你的程序还包含其他的库,除了用AC_CHECK_LIB宏来指明外,还可以用LIBS来指明。

如果你只想编译某一个文件,那么Makefile.am如何写呢?这个文件也很简单,写法跟可执行文件的差不多,如下例所示:


noinst_PROGRAMS=apple
            apple_SOURCES=apple.c
            DEFS+=-D_GNU_SOURCE
            

我们这里只是欺骗automake,假装要生成apple文件,让它为我们生成依赖关系和执行命令。所以当你运行完automake命令后,然后修改apple/shell/下的Makefile.in文件,直接将LINK语句删除,即:


…….
            clean-noinstPROGRAMS:
            -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
            apple$(EXEEXT): $(apple_OBJECTS) $(apple_DEPENDENCIES)
            @rm -f apple$(EXEEXT)
            #$(LINK) $(apple_LDFLAGS) $(apple_OBJECTS) $(apple_LDADD) $(LIBS)
            …….
            

通过上述处理,就可以达到我们的目的。从图1中不难看出为什么要修改Makefile.in的原因,而不是修改其他的文件。

posted @ 2009-06-21 16:25 不会飞的鸟 阅读(221) | 评论 (0)编辑 收藏

实战Makefile.am

实战Makefile.am

Makefile.am是一种比Makefile更高层次的规则。只需指定要生成什么目标,它由什么源文件生成,要安装到什么目录等构成。

表一列出了可执行文件、静态库、头文件和数据文件,四种书写Makefile.am文件个一般格式。


表 1Makefile.am一般格式


 

对于可执行文件和静态库类型,如果只想编译,不想安装到系统中,可以用noinst_PROGRAMS代替bin_PROGRAMS,noinst_LIBRARIES代替lib_LIBRARIES。

Makefile.am还提供了一些全局变量供所有的目标体使用:

表 2 Makefile.am中可用的全局变量

在Makefile.am中尽量使用相对路径,系统预定义了两个基本路径:

表 3Makefile.am中可用的路径变量

在上文中我们提到过安装路径,automake设置了默认的安装路径:

1)标准安装路径

默认安装路径为:$(prefix) = /usr/local,可以通过./configure --prefix=<new_path>的方法来覆盖。

其它的预定义目录还包括:bindir = $(prefix)/bin, libdir = $(prefix)/lib, datadir = $(prefix)/share, sysconfdir = $(prefix)/etc等等。

2) 定义一个新的安装路径

比如test, 可定义testdir = $(prefix)/test, 然后test_DATA =test1 test2,则test1,test2会作为数据文件安装到$(prefix)/ /test目录下。

我们首先需要在工程顶层目录下(即project/)创建一个Makefile.am来指明包含的子目录:

SUBDIRS=src/lib src/ModuleA/apple/shell src/ModuleA/apple/core

CURRENTPATH=$(shell /bin/pwd)

INCLUDES=-I$(CURRENTPATH)/src/include -I$(CURRENTPATH)/src/ModuleA/apple/include

export INCLUDES

由于每个源文件都会用到相同的头文件,所以我们在最顶层的Makefile.am中包含了编译源文件时所用到的头文件,并导出,见蓝色部分代码。

我们将lib目录下的swap.c文件编译成libswap.a文件,被apple/shell/apple.c文件调用,那么lib目录下的Makefile.am如下所示:

noinst_LIBRARIES=libswap.a

libswap_a_SOURCES=swap.c

INCLUDES=-I$(top_srcdir)/src/includ

细心的读者可能就会问:怎么表1中给出的是bin_LIBRARIES,而这里是noinst_LIBRARIES?这是因为如果只想编译,而不想安装到系统中,就用noinst_LIBRARIES代替bin_LIBRARIES,对于可执行文件就用noinst_PROGRAMS代替bin_PROGRAMS。对于安装的情况,库将会安装到$(prefix)/lib目录下,可执行文件将会安装到${prefix}/bin。如果想安装该库,则Makefile.am示例如下:

bin_LIBRARIES=libswap.a

libswap_a_SOURCES=swap.c

INCLUDES=-I$(top_srcdir)/src/include

swapincludedir=$(includedir)/swap

swapinclude_HEADERS=$(top_srcdir)/src/include/swap.h

最后两行的意思是将swap.h安装到${prefix}/include /swap目录下。

接下来,对于可执行文件类型的情况,我们将讨论如何写Makefile.am?对于编译apple/core目录下的文件,我们写成的Makefile.am如下所示:

noinst_PROGRAMS=test

test_SOURCES=test.c

test_LDADD=$(top_srcdir)/src/ModuleA/apple/shell/apple.o $(top_srcdir)/src/lib/libswap.a

test_LDFLAGS=-D_GNU_SOURCE

DEFS+=-D_GNU_SOURCE

#LIBS=-lpthread

由于我们的test.c文件在链接时,需要apple.o和libswap.a文件,所以我们需要在test_LDADD中包含这两个文件。对于Linux下的信号量/读写锁文件进行编译,需要在编译选项中指明-D_GNU_SOURCE。所以在test_LDFLAGS中指明。而test_LDFLAGS只是链接时的选项,编译时同样需要指明该选项,所以需要DEFS来指明编译选项,由于DEFS已经有初始值,所以这里用+=的形式指明。从这里可以看出,Makefile.am中的语法与Makefile的语法一致,也可以采用条件表达式。如果你的程序还包含其他的库,除了用AC_CHECK_LIB宏来指明外,还可以用LIBS来指明。

如果你只想编译某一个文件,那么Makefile.am如何写呢?这个文件也很简单,写法跟可执行文件的差不多,如下例所示:

noinst_PROGRAMS=apple

apple_SOURCES=apple.c

DEFS+=-D_GNU_SOURCE

我们这里只是欺骗automake,假装要生成apple文件,让它为我们生成依赖关系和执行命令。所以当你运行完automake命令后,然后修改apple/shell/下的Makefile.in文件,直接将LINK语句删除,即:

…….

clean-noinstPROGRAMS:

    -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)

apple$(EXEEXT): $(apple_OBJECTS) $(apple_DEPENDENCIES)

    @rm -f apple$(EXEEXT)

#$(LINK) $(apple_LDFLAGS) $(apple_OBJECTS) $(apple_LDADD) $(LIBS)

…….

posted @ 2009-06-21 16:19 不会飞的鸟 阅读(849) | 评论 (0)编辑 收藏

带你轻松接触PowerDesigner中的反向工程

Power Designer是Sybase公司的CASE工具集,使用它可以方便地对管理信息系统进行分析设计,它几乎包括了数据库模型设计的全过程。利用Power Designer可以制作数据流程图、概念数据模型、物理数据模型,可以生成多种客户端开发工具的应用程序,还可为数据仓库制作结构模型,也能对团队设计模型进行控制。

Power Designer的4种模型:概念数据模型 (CDM)物理数据模型 (PDM) 面向对象模型 (OOM) 业务程序模型 (BPM) 我主要介绍一下PDM。

PDM 叙述数据库的物理实现,帮助你考虑真实的物理实现的细节。你能通过修正PDM来适合你的表现或物理约束。主要目的是把CDM中建立的现实世界模型生成特定的DBMS脚本,产生数据库中保存信息的储存结构,保证数据在数据库中的完整性和一致性。

PDM是适合于系统设计阶段的工具。简单说:就是PDM可以自动生成诸如''create table''之类的sql脚本.在数据建模过程中,我们建立概念数据模型,通过正向工程生成物理数据模型,生成数据库建库脚本,最后将物理数据模型生成关系数据库。

系统数据库设计人员希望能够将数据库设计和关系数据库生成无缝地集成起来,如何保证物理数据模型与其对应数据库之间的双向同步成为数据建模非常关键的一点。

Powerdesigner作为强大的Case工具,为我们提供了方便的逆向工程特性。可以将目前所有流行的后端数据库(包括Sybase、DB2、Oracle等)的结构信息通过逆向工程加入到PowerDesigner的物理数据模型和概念数据模型中,包括表、索引、触发器、视图等。

用PowerDesigner进行逆向工程

◆1.我用的数据库是oracle9i,我为了访问oracle数据库,在我的机器上安装了oracle客户端(提供了oracle客户端的驱动程序,而精简客户端则不可以),配置一个名称为mylcl的服务:MYLCL = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.3.106)(PORT = 1521)) ) (CONNECT_DATA = (SID = pwsc) ) )用户名为:testuser,密码为test。

◆2.在pd中,新建一个pdm,选择数据库为oracle9i。

◆3.选择Database->configure connections,转到system dsn标签,点击"添加",选择驱动程序,由于我的数据库是oracle,所以我选择"oracle in oraclient10g_home1"(安装了oracle客户端才有这个驱动,而精简客户端没有此驱动)。

◆4.在data source name 中,可以随便命名一个"ora-test",在tns-server name中选择第一步中的服务名称:mylcl.点击"test connection",输入用户名密码,connection ok!

◆5.点击database->reverse engineer database ,选择odbc datasource:ora-test.然后点击确定。(责任编辑:卢兆林)

posted @ 2009-05-28 06:44 不会飞的鸟 阅读(904) | 评论 (0)编辑 收藏

linux c 一个autotools的最简单例子

     摘要:   1、准备:        需要工具autoscan aclocal autoheader automake autoconf make 等工具.  2、测试程序编写:         &...  阅读全文

posted @ 2009-05-14 17:31 不会飞的鸟 阅读(2014) | 评论 (1)编辑 收藏

游戏引擎基础(一)(渲染和构造3D世界)

     摘要:  本系列转自:http://www.cppblog.com/orlando/archive/2007/12/03/37734.html 谢谢作者有这么好的文章与大家共享!代表所有有幸阅读到次书的读者谢谢先!第1部分: 游戏引擎介绍, 渲染和构造3D世界介绍  自Doom游戏时代以来我们已经走了很远。 DOOM不只是一款伟大的游戏,它同时也开创了一种新的游戏编程模式: 游戏 "引...  阅读全文

梦在天涯 2007-12-04 13:14 发表评论

posted @ 2009-04-10 10:44 不会飞的鸟 阅读(118) | 评论 (0)编辑 收藏

游戏引擎基础(二)(3D环境的光照和纹理)

2部份: 3D环境的光照和纹理


世界的灯光
  在变换过程中, 通常是在称为观察空间的坐标空间中, 我们遇到了最重要的运算之一: 光照计算。 它是一种这样的事情, 当它工作时,你不关注它,但当它不工作时, 你就非常关注它了。有很多不同的光照方法,从简单的计算多边形对于灯光的朝向,并根据灯光到多边形的方向和距离加上灯光颜色的百分比值,一直到产生边缘平滑的灯光贴图叠加基本纹理。而且一些 API 实际上提供预先建造的光照方法。举例来说,OpenGL 提供了每多边形,每顶点,和每像素的光照计算。

  在顶点光照中,你要决定一个顶点被多少个多边形共享,并计算出共享该顶点的所有多边形法向量的均值(称为法向量),并将该法向量赋顶点。一个给定多边形的每个顶点会有不同的法向量,所以你需要渐变或插值多边形顶点的光照颜色以便得到平滑的光照效果。 你没有必要用这种光照方式查看每个单独的多边形。 这种方式的优点是时常可以使用硬件转换与光照(T & L)来帮助快速完成。 不足之处是它不能产生阴影。 举例来说,即使灯光是在模型的右侧,左手臂应该在被身体投影的阴影中,而实际上模型的双臂却以同样的方式被照明了。

  这些简单的方法使用着色来达到它们的目标。 当用平面光照绘制一个多边形时, 你让渲染(绘制)引擎把整个多边形都着上一种指定的颜色。这叫做平面着色光照。 (该方法中,多边形均对应一个光强度,表面上所有点都用相同的强度值显示,渲染绘制时得到一种平面效果,多边形的边缘不能精确的显示出来)

  对于顶点着色 ( Gouraud 着色) ,你让渲染引擎给每个顶点赋予特定的颜色。 在绘制多边形上各点投影所对应的像素时,根据它们与各顶点的距离,对这些顶点的颜色进行插值计算。 (实际上Quake III 模型使用的就是这种方法, 效果好的令人惊奇)

  还有就是 Phong 着色。如同 Gouraud 着色,通过纹理工作,但不对每个顶点颜色进行插值决定像素颜色值, 它对每个顶点的法向量进行插值,会为每个顶点投影的像素做相同的工作。对于 Gouraud 着色,你需要知道哪些光投射在每个顶点上。对于 Phong 着色,你对每个像素也要知道这么多。

  一点也不令人惊讶, Phong 着色可以得到更加平滑的效果,因为每个像素都需要进行光照计算,其绘制非常耗费时间。平面光照处理方法很快速, 但比较粗糙。Phong 着色比 Gouraud 着色计算更昂贵,但效果最好,可以达到镜面高光效果("高亮") 这些都需要你在游戏开发中折衷权衡。


不同的灯光
  接着是生成照明映射,你用第二个纹理映射(照明映射)与已有的纹理混合来产生照明效果。这样工作得很好, 但这本质上是在渲染之前预先生成的一种罐装效果。如果你使用动态照明 (即,灯光移动, 或者没有程序的干预而打开和关闭),你得必须在每一幀重新生成照明映射,按照动态灯光的运动方式修改这些照明映射。灯光映射能够快速的渲染,但对存储这些灯光纹理所需的内存消耗非常昂贵。你可以使用一些压缩技巧使它们占用较少的的内存空间,或减少其尺寸大小, 甚至使它们是单色的 (这样做就不会有彩色灯光了),等等。 如果你确实在场景中有多个动态灯光, 重新生成照明映射将以昂贵的CPU周期而告终。

  许多游戏通常使用某种混合照明方式。 Quake III为例,场景使用照明映射, 动画模型使用顶点照明。 预先处理的灯光不会对动画模型产生正确的效果 -- 整个多边形模型得到灯光的全部光照值 -- 而动态照明将被用来产生正确的效果。 使用混合照明方式是多数的人们没有注意到的一个折衷,它通常让效果看起来"正确" 这就是游戏的全部做一切必要的工作让效果看起来"正确",但不必真的是正确的。

  当然,所有这些在新的Doom引擎里面都不复存在了,但要看到所有的效果,至少需要 1GHZ CPU GeForce 2 显卡。是进步了,但一切都是有代价的。

  一旦场景经过转换和照明, 我们就进行裁剪运算。 不进入血淋淋的细节而,剪断运算决定哪些三角形完全在场景 (被称为观察平截头体) 之内或部份地在场景之内。完全在场景之内的三角形被称为细节接受,它们被处理。对于只是部分在场景之内的三角形, 位于平截头体外面的部分将被裁剪掉,余下位于平截头体内部的多边形部分将需要重新闭合,以便其完全位于可见场景之内。 (更多的细节请参考我们的 3D 流水线指导一文)

  场景经过裁剪以后,流水线中的下一个阶段就是三角形生成阶段(也叫做扫描 线转换),场景被映射到2D 屏幕坐标。到这里,就是渲染(绘制)运算了。


纹理与MIP映射
  纹理在使3D场景看起来真实方面异常重要,它们是你应用到场景区域或对象的一些分解成多边形的小图片。多重纹理耗费大量的内存,有不同的技术来帮助管理它们的尺寸大小。纹理压缩是在保持图片信息的情况下,让纹理数据更小的一种方法。纹理压缩占用较少的游戏CD空间,更重要的是,占用较少内存和3D 显卡存储空间。另外,在你第一次要求显卡显示纹理的时候,压缩的(较小的) 版本经过 AGP 接口从 PC 主存送到3D 显卡, 会更快一些。纹理压缩是件好事情。 在下面我们将会更多的讨论纹理压缩。


MIP
映射(多纹理映射)
  游戏引擎用来减少纹理内存和带宽需求的另外一个技术就是 MIP 映射。 MIP 映射技术通过预先处理纹理,产生它的多个拷贝纹理,每个相继的拷贝是上一个拷贝的一半大小。为什么要这样做?要回答这个问题,你需要了解 3D 显卡是如何显示纹理的。最坏情况,你选择一个纹理,贴到一个多边形上,然后输出到屏幕。我们说这是一对一的关系,最初纹理映射图的一个纹素 (纹理元素) 对应到纹理映射对象多边形的一个像素。如果你显示的多边形被缩小一半,纹理的纹素就每间隔一个被显示。这样通常没有什么问题 -- 但在某些情况下会导致一些视觉上的怪异现象。让我们看看砖块墙壁。 假设最初的纹理是一面砖墙,有许多砖块,砖块之间的泥浆宽度只有一个像素。如果你把多边形缩小一半, 纹素只是每间隔一个被应用,这时候,所有的泥浆会突然消失,因为它们被缩掉了。你只会看到一些奇怪的图像。

  使用 MIP 映射,你可以在显示卡应用纹理之前,自己缩放图像,因为可以预先处理纹理,你做得更好一些,让泥浆不被缩掉。当 3D 显卡用纹理绘制多边形时,它检测到缩放因子,说,"你知道,我要使用小一些的纹理,而不是缩小最大的纹理,这样看起来会更好一些。" 在这里, MIP 映射为了一切,一切也为了 MIP 映射。


多重纹理与凹凸映射
  单一纹理映射给整个3D 真实感图形带来很大的不同, 但使用多重纹理甚至可以达到一些更加令人难忘的效果。过去这一直需要多遍渲染(绘制),严重影响了像素填充率。 但许多具有多流水线的3D 加速卡,如ATI's Radeon nVidia's GeForce 2及更高级的显卡,多重纹理可以在一遍渲染(绘制)过程中完成。 产生多重纹理效果时, 你先用一个纹理绘制多边形,然后再用另外一个纹理透明地绘制在多边形上面。这让你可以使纹理看上去在移动,或脉动, 甚至产生阴影效果 (我们在照明一节中描述过)。绘制第一个纹理映射,然后在上面绘制带透明的全黑纹理,引起一种是所有的织法黑色的但是有一个透明分层堆积过它的顶端 这就是 -- 即时阴影。 该技术被称为照明映射 ( 有时也称为 暗映射),直至新的Doom ,一直是Id引擎里关卡照明的传统方法。

  凹凸贴图是最近涌现出来的一种古老技术。几年以前 Matrox 第一个在流行的 3D 游戏中发起使用各种不同形式的凹凸贴图。就是生成纹理来表现灯光在表面的投射,表现表面的凹凸或表面的裂缝。 凹凸贴图并不随着灯光一起移动 -- 它被设计用来表现一个表面上的细小瑕疵,而不是大的凹凸。 比如说,在飞行模拟器中,你可以使用凹凸贴图来产生像是随机的地表细节,而不是重复地使用相同的纹理,看上去一点趣味也没有。

  凹凸贴图产生相当明显的表面细节,尽管是很高明的戏法,但严格意义上讲,凹凸贴图并不随着你的观察角度而变化。比较新的 ATI nVidia 显卡片能执行每像素运算,这种缺省观察角度的不足就真的不再是有力而快速的法则了。 无论是哪一种方法, 到目前为止,没有游戏开发者太多的使用; 更多的游戏能够且应该使用凹凸贴图。


高速缓存抖动 = 糟糕的事物
  纹理高速缓存的管理游戏引擎的速度至关重要。 和任何高速缓存一样,缓存命中很好,而不命中将很糟糕。如果遇到纹理在图形显示卡内存被频繁地换入换出的情况,这就是纹理高速缓存抖动。发生这种情况时,通常API将会废弃每个纹理,结果是所有的纹理在下一幀将被重新加载,这非常耗时和浪费。对游戏玩家来说,当API重新加载纹理高速缓存时,会导致幀速率迟钝。

  在纹理高速缓存管理中,有各种不同的技术将纹理高速缓存抖动减到最少这是确保任何 3D 游戏引擎速度的一个决定性因素。 纹理管理是件好事情这意味着只要求显卡使用纹理一次,而不是重复使用。这听起来有点自相矛盾,但效果是它意谓着对显卡说,"看, 所有这些多边形全部使用这一个纹理,我们能够仅仅加载这个纹理一次而不是许多次吗?" 这阻止API ( 或图形驱动软件) 上传多次向显卡加载纹理。象OpenGL这样的API实际上通常处理纹理高速缓存管理,意谓着,根据一些规则,比如纹理存取的频率,API决定哪些纹理储存在显卡上,哪些纹理存储在主存。 真正的问题来了:a) 你时常无法知道API正在使用的准确规则。 b)你时常要求在一幀中绘制更多的纹理,以致超出了显卡内存空间所能容纳的纹理。

  另外一种纹理高速缓存管理技术是我们早先讨论的纹理压缩。很象声音波形文件被压缩成 MP3 文件,尽管无法达到那样的压缩比率,但纹理可以被压缩。 从声音波形文件到MP3的压缩可以达到 11:1的压缩比率,而绝大多数硬件支持的纹理压缩运算法则只有 4:1 的压缩比率,尽管如此,这样能产生很大的差别。 除此之外,在渲染(绘制)过程中,只有在需要时,硬件才动态地对纹理进行解压缩。这一点非常棒,我们仅仅擦除即将可能用到的表面。

  如上所述,另外一种技术确保渲染器要求显卡对每个纹理只绘制一次。确定你想要渲染(绘制)的使用相同纹理的所有多边形同时送到显卡,而不是一个模型在这里,另一个模型在那里,然后又回到最初的纹理论。仅仅绘制一次,你也就通过AGP接口传送一次。Quake III 在其阴影系统就是这么做的。处理多边形时,把它们加入到一个内部的阴影列表,一旦所有的多边形处理完毕,渲染器遍历纹理列表,就将纹理及所有使用这些纹理的多边形同时传送出去。

  上述过程在使用显卡的硬件 T & L(如果支持的话)时,并不怎么有效。你面临的结局是,满屏幕都是使用相同纹理的大量的多边形小群组,所有多边形都使用不同的变换矩阵。这意谓着更多的时间花在建立显卡的硬件 T & L 引擎 ,更多的时间被浪费了。 无论如何,因为他们有助于对整个模型使用统一的纹理,所以它对实际屏幕上的模型可以有效地工作。但是因为许多多边形倾向使用相同的墙壁纹理,所以对于世界场景的渲染,它常常就是地狱。通常它没有这么严重,因为大体而言,世界的纹理不会有那么大,这样一来API的纹理缓存系统将会替你处理这些,并把纹理保留在显卡以备再次使用。

  在游戏机上,通常没有纹理高速缓存系统(除非你写一个)。在 PS2 上面,你最好是远离"一次纹理" 的方法。在 Xbox 上面, 这是不重要的,因为它本身没有图形内存(它是 UMA 体系结构),且所有的纹理无论如何始终保留在主存之中。

  事实上,在今天的现代PC FPS 游戏中,试图通过AGP接口传送大量纹理是第二个最通常的瓶颈。最大的瓶颈是实际几何处理,它要使东西出现在它应该出现的地方。在如今的3D FPS 游戏中,最耗费时间的工作,显然是那些计算模型中每个顶点正确的世界位置的数学运算。如果你不把场景的纹理保持在预算之内,仅居其次的就是通过AGP接口传送大量的纹理了。然而,你确实有能力影响这些。 通过降低顶层的 MIP 级别(还记得系统在哪里不断地为你细分纹理吗?) 你就能够把系统正在尝试送到显卡的纹理大小减少一半。你的视觉质量会有所下降-- 尤其是在引人注目的电影片断中--但是你的幀速率上升了。这种方式对网络游戏尤其有帮助。实际上,Soldier of Fortune IIJedi Knight II: Outcast这两款游戏在设计时针对的显卡还不是市场上的大众主流显卡。为了以最大大小观看他们的纹理,你的3D 显卡至少需要有128MB的内存。这两种产品在思想上都是给未来设计的。

  上面就是第 2 部份。在下面章节中,我们将介绍许多主题,包括内存管理,雾效果,深度测试, 抗锯齿,顶点着色,API等。



梦在天涯 2007-12-04 13:16 发表评论

posted @ 2009-04-10 10:44 不会飞的鸟 阅读(206) | 评论 (0)编辑 收藏

游戏引擎基础(三)(内存使用,特效和API)

3部份: 内存使用,特效和API


关于内存使用的思考
  让我们想一想,在今天实际上是如何使用3D 显卡内存的以及在将来又会如何使用。 如今绝大多数3D显卡处理32位像素颜色,8位红色, 8位蓝色,8 位绿色,和 8 位透明度。这些组合的红,蓝和绿256个色度,可以组成 167 百万种颜色-- 那是你我可以在一个监视器上看见的所有颜色。

  那么,游戏设计大师John Carmack 为什么要求 64 位颜色分辨率呢? 如果我们看不出区别,又有什么意义呢? 意义是: 比如说, 有十几个灯光照射模型上的点,颜色颜色各不相同。 我们取模型的最初颜色,然后计算一个灯光的照射,模型颜色值将改变。 然后我们计算另外的一个灯光, 模型颜色值进一步改变。 这里的问题是,因为颜色值只有8位,在计算了4个灯光之后,8位的颜色值将不足以给我们最后的颜色较好的分辨率和表现。分辨率的不足是由量化误差导致的,本质原因是由于位数不足引起的舍入误差。

  你能很快地用尽位数,而且同样地,所有的颜色被清掉。每颜色16 32 位,你有一个更高分辨率,因此你能够反复着色以适当地表现最后的颜色。这样的颜色深度很快就能消耗大量的存储空间。我们也应提到整个显卡内存与纹理内存。这里所要说的是,每个3D 显卡实际只有有限的内存,而这些内存要存储前端和后端缓冲区,Z 缓冲区,还有所有的令人惊奇的纹理。最初的 Voodoo1 显卡只有2MB显存,后来 Riva TNT提高到16MB显存。然后 GeForce ATI Rage32MB显存, 现在一些 GeForce 2 4的显卡和 Radeons 带有 64MB 128MB 的显存。 这为什么重要? 好吧,让我们看一些数字

  比如你想让你的游戏看起来最好,所以你想要让它以32位屏幕, 1280x1024分辨率和32 Z- 缓冲跑起来。 好,屏幕上每个像素4个字节,外加每个像素4字节的Z-缓冲,因为都是每像素32位。我们有1280x1024 个像素也就是 1310720个像素。基于前端缓冲区和Z-缓冲区的字节数,这个数字乘以8,是 10485760字节。包括一个后端缓冲区,这样是 1280x1024x12 也就是 15728640 字节, 15MB 在一个 16MB 显存的显卡上,就只给我们剩下1MB 来存储所有的纹理。 现在如果最初的纹理是真32 位或 4字节宽,那么我们每幀能在显卡上存储 1MB/4字节每像素 = 262144个像素。这大约是4 256x256 的纹理页面。

  很清楚,上述例子表明,旧的16MB 显卡没有现代游戏表现其绚丽画面所需要的足够内存。很明显,在它绘制画面的时候,我们每幀都必须重新把纹理装载到显卡。实际上,设计AGP总线的目的就是完成这个任务,不过, AGP 还是要比 3D 掀卡的幀缓冲区慢,所以你会受到性能上的一些损失。很明显,如果纹理由32位降低到16位,你就能够通过AGP以较低的分辨率传送两倍数量的纹理。如果你的游戏以每个像素比较低的色彩分辨率跑, 那么就可以有更多的显示内存用来保存常用的纹理 (称为高速缓存纹理) 但实际上你永远不可能预知使用者将如何设置他们的系统。如果他们有一个在高分辨率和颜色深度跑的显卡,那么他们将会更可能那样设定他们的显卡。



  我们现在开始讲雾,它是某种视觉上的效果。如今绝大多数的引擎都能处理雾, 因为雾非常方便地让远处的世界淡出视野,所以当模型和场景地理越过观察体后平面进入视觉范围内时,你就不会看见它们突然从远处跳出来了。 也有一种称为体雾的技术。这种雾不是随物体离照相机的距离而定,它实际上是一个你能看见的真实对象,并且可以穿越它,从另外一侧出去 -- 当你在穿越对象的时候,视觉上雾的可见程度随着变化。想象一下穿过云团 -- 这是体雾的一个完美例子。体雾的一些好的实现例子是Quake III一些关卡中的红色雾,或新的Rogue Squadron II Lucas Arts GameCube 版本。其中有一些是我曾经见过的最好的云--大约与你能看见的一样真实。

  在我们讨论雾化的时候,可能是简短介绍一下 Alpha 测试和纹理Alpha混合的好时机。当渲染器往屏幕上画一个特定像素时,假定它已经通过 Z- 缓冲测试 (在下面定义),我们可能最后做一些Alpha测试。我们可能发现为了显示像素后面的某些东西,像素需要透明绘制。这意味着我们必须取得像素的已有值,和我们新的像素值进行混和,并把混合结果的像素值放回原处。这称为读-修改-写操作,远比正常的像素写操作费时。

  你可以用不同类型的混合,这些不同的效果被称为混合模式。直接Alpha混合只是把背景像素的一些百分比值加到新像素的相反百分比值上面。还有加法混合,将旧像素的一些百分比,和特定数量(而不是百分比)的新像素相加。 这样效果会更加鲜明。 (Kyle's Lightsaber Jedi Knight II 中的效果)

  每当厂商提供新的显卡时,我们可以得到硬件支持的更新更复杂的混合模式,从而制作出更多更眩目的效果。GF3+4和最近的Radeon显卡提供的像素操作,已经到了极限。


模板阴影与深度测试
  用模板产生阴影效果,事情就变得复杂而昂贵了。这里不讨论太多细节(可以写成一篇单独的文章了),其思想是,从光源视角绘制模型视图,然后用这个把多边形纹理形状产生或投射到受影响的物体表面。

  实际上你是在视野中投射将会在其他多边形上面的光体。最后你得到看似真实的光照,甚至带有视角在里面。因为要动态创建纹理,并对同一场景进行多遍绘制,所以这很昂贵。

  你能用众多不同方法产生阴影,情形时常是这样一来,渲染质量与产生效果所需要的渲染工作成比例。有所谓的硬阴影或软阴影之分,而后者较好,因为它们更加准确地模仿阴影通常在真实世界的行为。 通常有一些被游戏开发者偏爱的足够好的方法。如要更多的了解阴影,请参考 Dave Salvator 3D 流水线一文。


深度测试
  现在我们开始讨论深度测试, 深度测试丢弃隐藏的像素,过度绘制开始起作用。过度绘制非常简单在一幀中,你数次绘制一个像素位置。它以3D场景中Z(深度)方向上存在的元素数量为基础,也被称为深度复杂度。如果你常常太多的过度绘制, -- 举例来说, 符咒的眩目视觉特效,就象Heretic II,能让你的幀速率变得很糟糕。当屏幕上的一些人们彼此施放符咒时,Heretic II设计的一些最初效果造成的情形是,他们在一幀中对屏幕上每个相同的像素画了40! 不用说,这必须调整,尤其是软件渲染器,除了将游戏降低到象是滑雪表演外,它根本不能处理这样的负荷。深度测试是一种用来决定在相同的像素位置上哪些对象在其它对象前面的技术,这样我们就能够避免绘制那些隐藏的对象。

  看着场景并想想你所看不见的。 换句话说,是什么在其他场景对象前面,或者隐藏了其他场景对象? 是深度测试作出的这个决定。

  我将进一步解释深度深度如何帮助提高幀速率。想像一个很琐细的场景,大量的多边形 (或像素)位于彼此的后面,在渲染器获得他们之间没有一个快速的方法丢弃他们。对非Alpha混合的多边形分类排序( Z- 方向上),首先渲染离你最近的那些多边形,优先使用距离最近的像素填充屏幕。所以当你要渲染它们后面的像素(由Z或者深度测试决定)时,这些像素很快被丢弃,从而避免了混合步骤并节省了时间。如果你从后到前绘制,所有隐藏的对象将被完全绘制,然后又被其他对象完全重写覆盖。场景越复杂,这种情况就越糟糕,所以深度测试是个好东西。


抗锯齿
  让我们快速的看一下抗锯齿。当渲染单个多边形时,3D 显卡仔细检查已经渲染的,并对新的多边形的边缘进行柔化,这样你就不会得到明显可见的锯齿形的像素边缘。两种技术方法之一通常被用来处理。 第一种方法是单个多边形层次,需要你从视野后面到前面渲染多边形,这样每个多边形都能和它后面的进行适当的混合。如果不按序进行渲染,最后你会看见各种奇怪的效果。在第二种方法中,使用比实际显示更大的分辩率来渲染整幅幀画面,然后在你缩小图像时,尖锐的锯齿形边缘就混合消失了。这第二种方法的结果不错,但因为显卡需要渲染比实际结果幀更多的像素,所以需要大量的内存资源和很高的内存带宽。

  多数新的显卡能很好地处理这些,但仍然有多种抗锯齿模式可以供你选择,因此你可以在性能和质量之间作出折衷。对於当今流行的各种不同抗锯齿技术的更详细讨论请参见Dave Salvator 3D 流水线一文。


顶点与像素着色
  在结束讨论渲染技术之前,我们快速的说一下顶点和像素着色,最近它们正引起很多关注。顶点着色是一种直接使用显卡硬件特征的方式,不使用API。举例来说,如果显卡支持硬件 T & L ,你可以用DirectXOpenGL编程,并希望你的顶点通过 T & L 单元 (因为这完全由驱动程序处理,所以没有办法确信),或者你直接利用显卡硬件使用顶点着色。它们允许你根据显卡自身特征进行特别编码,你自己特殊的编码使用T & L 引擎,以及为了发挥你的最大优势,显卡必须提供的其他别的特征。 事实上,现在nVidia ATI 在他们大量的显卡上都提供了这个特征。

  不幸的是,显卡之间表示顶点着色的方法并不一致。你不能象使用DirectX或者OpenGL 那样,为顶点着色编写一次代码就可以在任何显卡上运行,这可是个坏消息。然而,因为你直接和显卡硬件交流,它为快速渲染顶点着色可能生成的效果提供最大的承诺。( 如同创造很不错的特效 -- 你能够使用顶点着色以API没有提供的方式影响事物)。事实上,顶点着色正在真的将3D 图形显示卡带回到游戏机的编码方式,直接存取硬件,最大限度利用系统的必须知识,而不是依靠API来为你做一切。对一些程序员来说,会对这种编码方式感到吃惊,但这是进步代价。

  进一步阐述,顶点着色是一些在顶点被送到显卡渲染之前计算和运行顶点效果程序或者例程。你可以在主CPU上面用软件来做这些事情,或者使用显卡上的顶点着色。 为动画模型变换网格是顶点程序的主选。

  像素着色是那些你写的例程,当绘制纹理时,这些例程就逐个像素被执行。你有效地用这些新的例程推翻了显卡硬件正常情况做的混合模式运算。这允许你做一些很不错的像素效果, 比如,使远处的纹理模糊,添加炮火烟雾, 产生水中的反射效果等。一旦 ATI nVidia 能实际上就像素着色版本达成一致( DX9's 新的高级阴影语言将会帮助促进这一目标), 我一点不惊讶DirectX OpenGL采用Glide的方式-- 有帮助开始, 但最终不是把任何显卡发挥到极限的最好方法。我认为我会有兴趣观望将来。


最后(In Closing...
  最终,渲染器是游戏程序员最受评判的地方。在这个行业,视觉上的华丽非常重要,因此它为知道你正在做的买单。对于渲染器程序员,最坏的因素之一就是3D 显卡工业界变化的速度。一天,你正在尝试使透明图像正确地工作;第二天 nVidia 正在做顶点着色编程的展示。而且发展非常快,大致上,四年以前为那个时代的 3D 显卡写的代码现在已经过时了,需要全部重写。 甚至John Carmack 这样描述过,他知道四年以前为充分发挥那个时期显卡的性能所写的不错的代码,如今很平凡 -- 因此他产生了为每个新的id项目完全重写渲染器的欲望。Epic Tim Sweeney赞同 -- 这里是去年他给我的评论:

  我们已经足足花费了9个月时间来更换所有的渲染代码。最初的 Unreal 被设计为软件渲染和后来扩展为硬件渲染。下一代引擎被设计为 GeForce 及更好的图形显示卡,且多边形吞吐量是Unreal Tournament100倍。

  这需要全部替换渲染器。很幸运,该引擎模块化程度足够好,我们可以保持引擎的其余部分编辑器,物理学,人工智能,网络--不改动,尽管我们一直在以许多方式改进这些部分。

  搭配长篇文章的短篇报导(Sidebar):API -- 祝福和诅咒
  那么什么是API? 它是应用程序编程接口,将不一致的后端用一致的前端呈现出来。举例来说,很大程度上每种3D显示卡的3D实现方式都有所差别。然而,他们全部都呈现一个一致的前端给最终使用者或者程序员,所以他们知道他们为X 3D显示卡写的代码将会在Y 3D显示卡上面有相同的结果。好吧,不管怎样理论上是那样。 大约在三年以前这可能是相当真实的陈述,但自那以后,在nVidia 公司的引领下,3D显卡行业的事情发生了变化。

  如今在PC领域,除非你正计划建造自己的软件光栅引擎,使用CPU来绘制你所有的精灵,多边形和粒子 -- 而且人们仍然在这样做。跟Unreal一样,Age of Empires II: Age of Kings有一个优秀的软件渲染器否则你将使用两种可能的图形APIOpenGL或者 DirectX 之一。OpenGL是一种真正的跨平台API (使用这种API写的软件可以在LinuxWindowsMacOS上运行。) 而且有多年的历史了,为人所熟知,但也开始慢慢地显示出它的古老。 大约在四年以前,定义OpenGL驱动特征集一直是所有显示卡厂商工作的方向。

  然而,一旦在目标达成以后,没有预先制定特征工作方向的路线图,这时候,所有的显卡开发商开始在特征集上分道扬镳,使用OpenGL扩展。

  3dfx 创造了T- 缓冲。 nVidia 努力寻求硬件变换和光照计算。Matrox努力获取凹凸贴图。等等。 我以前说过的一句话,"过去几年以来,3D显示卡领域的事情发生了变化。"委婉地说明了这一切。

  无论如何,另一个可以选择的API DirectX。这受Microsoft公司控制,且在PC Xbox 上被完美地支持。由于明显的原因,DirectX 没有Apple或者 Linux 版本。因为Microsoft控制着 DirectX,大体上它容易更好地集成在Windows里面。

  OpenGLDirectX之间的基本差别是前者由社区拥有,而后者由Microsoft拥有。如果你想要 DirectX 为你的 3D 显示卡支持一个新的特征,那么你需要游说微软,希望采纳你的愿望,并等待新的 DirectX发行版本。对于OpenGL,由于显示卡制造商为3D显示卡提供驱动程序,你能够通过OpenGL扩展立即获得显示卡的新特征。这是好,但作为游戏开发者,当你为游戏编码的时候,你不能指望它们很普遍。它们可能让你的游戏速度提升50%,但你不能要求别人有一块GeForce 3 来跑你的游戏。好吧,你可以这么做,但如果你想来年还在这个行业的话,这是个相当愚蠢的主意。

  这是对这个问题极大的简单化,对我所有描述的也有各种例外情况,但这里一般的思想是很确实的。对于DirectX ,在任何既定时间你容易确切地知道你能从显示卡获得的特征,如果一个特征不能获得,DirectX 将会用软件模拟它(也不总是一件好事情,因为这样有时侯非常的慢,但那是另外一回事)。对于OpenGL,你可以更加贴近显示卡的特征,但代价是不能确定将会获得的准确特征。



梦在天涯 2007-12-04 13:17 发表评论

posted @ 2009-04-10 10:44 不会飞的鸟 阅读(84) | 评论 (0)编辑 收藏

游戏引擎基础(四)(模型与动画,细节级别)

4部份: 模型与动画,细节级别


角色建模与动画
  你的角色模型在屏幕上看起来怎么样,怎样容易创建它们,纹理,以及动画对于现代游戏试图完成的`消除不可信`因素来说至关重要。角色模型系统逐渐变得复杂起来, 包括较高的多边形数量模型, 和让模型在屏幕上移动的更好方式。

  如今你需要一个骨骼模型系统,有骨架和网格细节层次,单个顶点骨架的评估,骨架动画忽略,以及比赛中停留的角度忽略。而这些甚至还没有开始涉及一些你能做的很好的事情,像动画混合,骨架反向运动学(IK),和单个骨架限制,以及相片真实感的纹理。这个清单还能够继续列下去。但是真的,在用专业行话说了所有这些以后,我们在这里真正谈论的是什么呢?让我们看看。

  让我们定义一个基于网格的系统和一个骨骼动画系统作为开始。在基于网格的系统,对于每一个动画幀,你要定义模型网格的每个点在世界中的位置。举例来说,你有一个包含200 个多边形的手的模型,有 300 个顶点(注意,在顶点和多边形之间通常并不是3个对1个的关系,因为大量多边形时常共享顶点使用条形和扇形,你能大幅减少顶点数量)。如果动画有 10 幀,那么你就需要在内存中有300个顶点位置的数据。 总共有300 x 10 = 3000 顶点,每个顶点由xyz和颜色/alpha信息组成。你能看见这个增长起来是多么的快。Quake III III 都使用了这种系统,这种系统确实有动态变形网格的能力,比如使裙子摆动,或者让头发飘动。

  相比之下,在骨骼动画系统,网格是由骨架组成的骨骼( 骨架是你运动的对象) 网格顶点和骨架本身相关,所以它们在模型中的位置都是相对于骨架,而不是网格代表每个顶点在世界中的位置。因此,如果你移动骨架,组成多边形的顶点的位置也相应改变。这意谓着你只必须使骨骼运动,典型情况大约有 50 个左右的骨架很明显极大地节省了内存。


骨骼动画附加的好处
  骨骼动画的另一个优点是能够根据影响顶点的一些骨架来分别估价每个顶点。例如,双臂的骨架运动,肩,脖子而且甚至躯干都能在肩中影响网格。当你移动躯干的时候,网格就活像一个角色一样移动。总的效果是3D角色能够实现的动画更加流畅和可信,且需要更少的内存。每个人都赢了。

  当然这里的缺点是,如果你想要使有机的东西运动且很好,比如说头发,或者披肩,为了让它看起来自然,你最后不得不在里面放置数量惊人的骨架,这会抬高一些处理时间。

  基于骨骼的系统能带给你的一些其他事情是忽略特定层次骨架的能力 -- ,"我不关心动画想要对这块骨架所做的事情,我想要让它指向世界中的一个特定点"。这很棒。你能让模型着眼于世界中的事件,或者使他们的脚在他们站着的地面保持水平。这一切非常微妙,但它可以帮助带给场景附加的真实感。

  在骨骼系统,你甚至可以指定"我需要把这个特别的动画用於模型的腿,而一个不同的携枪或射击动画在模型躯干上播放,且那家伙(角色)叫喊的不同动画效果在模型的头部播放"。非常妙。Ghoul2 ( Soldier of Fortune II: Double Helix and Jedi Knight I: Outcast中使用了Raven的动画系统 ) 拥有所有这些好东西,且特别被设计为允许程序员使用所有这些忽略能力。这对动画的节省像你一样难以相信。像你一样的动画上的这次救援不相信. Raven有一个角色行走的动画和一个站立开火的动画,并在它同时行走和开火形下把这两个动画合并,而不是需要一个动画表示角色行走并开火。


More Skeletons in the Closet
  先前描述的效果可以通过具有层次的骨骼系统来完成。这是什么意思呢?意思是每块骨架实际上的位置相对于它的父亲,而不是每个骨架直接位于空间中的地方。这意谓着如果你移动父亲骨架,那么它所有的子孙骨架也跟着移动,在代码上不需要任何额外的努力。这是让你能够在任何骨架层次改变动画,而且通过骨骼其余部分向下传递的东西。

  创建一个没有层次的骨骼系统是可能的 -- 但那时你不能忽略一个骨架并且预期它工作。你所看到的只是身体上的一个骨架开始了新动画,除非你实现了某种向下传递信息的系统,否则在该骨架下面的其它骨架保持原来的动画。首先由一个层次系统开始,你就自动地获得这些效果。

  许多今天的动画系统中正开始出现一些比较新的特征,如动画混合,从一个正在播放的动画转变到另外一个动画需要经过一小段时间,而不是立即从一个动画突然转变到另外一个。举例来说,你有个角色在行走,然后他停了下来。你不是仅仅突然地转变动画,让他的腿和脚停在无效位置,而是一秒钟混合一半,这样脚似乎自然地移到了新的动画。不能够过高的评价这种效果 -- 混合是一个微妙的事情,但如果正确的运用,它真的有些差别。


反向运动学
  反向运动学 (IK) 是被许多人们丢弃的一个专业术语,对它的真实含义没有多少概念。IK 是如今游戏里面一个相对比较新的系统。使用 IK ,程序员能够移动一只手,或一条腿, 模型的其余关节自动重新定位,因此模型被正确定向。而且有模型的关节新位置的其馀者他们自己,因此模型正确的被定向。比如,你将会说,"好,手 , 去拾起桌子上的那个杯子"并指出杯子在世界中的位置。手就会移动到那里,且它后面的身体会调节其自身以便双臂移动,身体适当弯曲,等等。

  也有和IK相反的事情,叫做前向运动学,本质上与 IK 工作的次序相反。想像一只手,手附着在手臂上,手臂附着在身体上。现在想像你重重地击中了身体。通常手臂像连迦般抽动,且手臂末梢的手随之振动。 IK 能够移动身体,并让其余的四肢自己以真实的方式移动。基本上它需要动画师设定每种工作的大量信息 -- 像关节所能通过的运动范围,如果一块骨架前面的骨架移动,那么这块骨架将移动多少百分比,等等。

  和它现在一样,尽管很好,它是一个很大的处理问题,不用它你可以有不同的动画组合而脱身。值得注意的是,真正的 IK 解决办法需要一个层次骨骼系统而不是一个模型空间系统 -- 否则它们都耗时太多以致无法恰当地计算每个骨架。


LOD
几何系统
  最后,我们应当快速讨论一下与缩放模型几何复杂度相关的细节级别(LOD)系统(与讨论MIP映射时使用的LOD相对照)。假定如今绝大多数PC游戏支持的处理器速度的巨大范围,以及你可能渲染的任何给定可视场景的动态性质(在屏幕上有一个角色还是12个?) 你通常需要一些系统来处理这样的情况,比如,当系统接近极限试图同时在屏幕上绘制出12个角色,每个角色有3000个多边形,并维持现实的幀速率。 LOD 被设计来协助这样的情景中。最基本的情况,它是在任何给定时间动态地改变你在屏幕上绘制的角色的多边形数量的能力。面对现实吧,当一个角色走远,也许只有十个屏幕像素高度,你真的不需要3000个多边形来渲染这个角色 -- 或许300个就够了,而且你很难分辨出差别。

  一些 LOD 系统将会需要你建立模型的多个版本,而且他们将会依靠模型离观察者的接近程度来改变屏幕上的LOD级别, 以及多少个多边形正被同时显示。更加复杂的系统实际上将会动态地减少屏幕上的多边形数量,在任何给定时间,任何给定的角色,动态地 -- MessiahSacrifice包括了这种风格的技术,尽管在CPU方面并不便宜。你必须确信,与首先简单地渲染整个事物相比,你的 LOD 系统没有花较多的时间计算出要渲染那些多边形(或不渲染)。 任一方式都将会工作,由于如今我们试图要在屏幕上绘制的多边形数量,这是件非常必要的事情。注意, DX9 将会支持硬件执行的自适应几何缩放(tessellation)

  归结起来是,得到一个运动流畅,其表现和移动在视觉上可信,屏幕上看起来逼真的模型。流畅的动画时常是通过手工建造动画和运动捕捉动画的组合得到。有时你仅仅手工建立了一个给定的动画 -- 当你在为一个模型做一些你在现实生活中不能做到的事情的动画时, 你倾向于这样做 -- 举例来说,你确实不能向后弯腰,或像Mortal Kombat 4中的Lui Kang那样在行进的脚踏车上踢腿,通常运动捕捉这时候就出局了! 通常运动捕捉动画 -- 实际上视频捕捉活生生的演员贯穿于你想在屏幕上所看到的动画 -- 是得到逼真的东西的方式。真实感的东西能使一款普通游戏看起来很棒,而且能掩饰许多事情。比如 NFL Blitz,屏幕上的模型大约有 200 个多边形。它们在静止站立时看起来可怕的斑驳,一旦这些模型跑动起来它们就有快速流畅的动画,模型自身的许多丑陋消失了。眼睛容易看见的是 '逼真的' 动画而不是模型自身的结构。 一个不错的模型设计师能够掩饰大多数模型缺陷。

  我希望这些带给你对模型和动画问题的洞察力。在第五部份中,我们将会更加深入3D世界的建造,讨论一些物理,运动和效果系统的东西。



梦在天涯 2007-12-04 13:18 发表评论

posted @ 2009-04-10 10:44 不会飞的鸟 阅读(113) | 评论 (0)编辑 收藏

游戏引擎基础(六)(声音系统,音频APIs)

6部分: 声音系统,音频APIs


声音系统
  由于人们玩的游戏在种类和技术上的进步,声音和音乐近几年来在游戏中正逐渐变得重要起来(声音是一个实际游戏的可玩特点,比如在Thief和其它同类游戏中的听觉提示)。现在四声道环绕系统在游戏玩家的宝库中是负担得起的和平常的事。给定空间的声音,噪音的障碍和闭塞,和动态的音乐,如今许多游戏使用这些提高玩家情绪上的反应,更多的关注投入到这个领域就不足为奇了。

  现在在PC竞技场中,游戏玩家实际上只有一种声音卡可以选择 -- PC声卡制造商创新公司(Creative Labs)的Sound Blaster Live 从旧的时间个人计算机声音卡片制造业者有创造力的中心. 多年来创新公司已经为DirectX提供了他们的EAX声音扩展,并且他们是发起新的OpenAL(开放音频库Open Audio Library)的创立者。就如同OpenGL是一个图形API一样,OpenAL,像它起来听一样,是一个声音系统的APIOpenAL 被设计为支持大多数通常声卡的许多特征,而且在一个特定的硬件特征不可得时提供一个软件替代。

  为了更好的定义 OpenAL,我向创新公司的Garin Hiebert询问了其定义:

  "这里借用我们的 " OpenAL 规格和叁考" 的一个定义:

  OpenAL 是对音频硬件的一个软件接口,给程序员提供一个产生高质量多通道输出的能力。OpenAL 是在模拟的三维环境里产生声音的一种重要方法。它想要跨平台并容易使用,在风格和规范上与OpenGL相似。任何已经熟悉OpenGL的程序员将发现OpenAL非常熟悉。

  OpenAL API能容易地被扩展适应插件技术.创新公司已经把EAX支持加入到这套API了,程序员可以用来给他们的声音环境增加复杂的反响,比赛和障碍效果。

  如同Jedi Knight II: Outcast 一样,连同Eagle 世界/声音特征编辑器,Soldier of Fortune II 以这个新系统为特征。什么是Eagle 在介绍这个以前,让我们讨论一些其他的系统,并定义一些声音术语。


  另外的一个系统是Miles声音系统。Miles是一家公司,它为你的代码生产插件,在充分利用每块声卡时处理所有必须的到特定声音卡的说话(比如Sound Blaster Live!系列,或者老的A3D声卡)。它非常像一个API前端,捆绑了一些额外的特征在里面。 在其他事物当中Miles让你存取一些事物像MP3解压缩。 它是很好的解决方案,但像任何事一样,它花费金钱并是你的代码和硬件之间的额外一层。虽然对於快速的声音系统制造,它非常有用,而且他们有段时间了,因此他们的确精通自己的业务。


声音术语
  让我们开始障碍和闭塞。它们听起来一样,但不是这样。闭塞基本上意谓着一个声音在播放时听者在他们之间有一些闭合的障碍物。

  比如说,在NOLF2的一个屏幕镜头上你听到房子里面坏蛋的声音。你能听到他们,但是他们的声音相当低沉而沙哑。障碍是相似的,但是你和声音之间的障碍物并不是闭合的。一个好的例子就是在你和声源之间有一根柱子。由于房间中的回声你仍然听得到这个声音,但是它和声音直接传递到你的耳朵里是不同的。当然这确实依赖于知道在你的耳朵和声源之间的直线上是什么。而且根据房间的大小,声源到你的距离等等,需要的处理能变得相当耗时。后面我们将会谈到跟踪--足可以说它时常是比较慢的幀速率的原因。Quake III 里面的A3D 代码做了这些事情,关闭这些选项通常能够提高幀速率。Tribe 2 是这种弊病的另外一个受害者。关闭3D声音选项则你的幀速率立即好转,这在你考虑Tribes世界有多大和你能看见多远时有意义。

  接着是声音物质的特征。大部分声卡可以让你能够用可定义的过滤器作用于声音从而修正播放的声音。例如,在水下,或者在一个布料遮盖的房间中,或者在一个长的走廊中,或者在歌剧院,听到的声音有着很大的不同。能够根据你所处的环境改变你听到声音的方式是相当不错的。

  我们回到Eagle… 这是一个编辑器,允许多数第一人称射击游戏地图设计者将他们的地图导入到这个工具,然后构造简化的几何形体来为实际游戏引擎中的EAX代码产生一个声音地图。其思想是你不需要一个真实的图形地图的复杂几何形体来模拟声音环境。你也能够给产生的简化地图分配声音物质,这样声音环境就能够动态地改变。我亲眼目睹了这在Soldier of FortuneUnreal Tournament上的示范,确实相当引人注目。 我这在财富和 Unreal 巡回赛和它的军人上真的对示范是证人相当醒目. 当你跳入水中时,听到所有的声音改变,这是一个非常令人沉浸的经历。

  好,让我们继续吧。

  对于游戏机,由于静态的硬件,你的各种可能性会更受限制尽管在PlayStation 2Xbox上,硬件相当不错。我说的限制,仅仅是指扩展,而不是它所能够做的。我一点也不会感到惊讶看到这些游戏机上的游戏很快支持杜比数字5.1Dolby Digital 5.1)输出。Xbox ,由于它的 MCP 音频处理器,能够将任何游戏音频编码为5.1,并且游戏不需要特别编码就能利用这个特征。杜比(Dolby)把ProLogic II 带到了 PS2 上,并与Factor 5合作为GameCube游戏实现了ProLogic II。在 Xbox 之上,Halo, Madden 2002 Project Gotham Racing等游戏都有5.1杜比数字音频内容。DTS最近也为 PS2 游戏开发者发布了SDK,为这个平台上的游戏带来了降低了比特率的DTS音频版本。


位置的声音--一个复杂的世界
  现在有一些很少有处理的声音空间化问题。我说的是把声音放在一个真实的3D世界中。有四个扬声器在你周围是一个很棒的开始,但这仍然只是在二维方向。在你的上方和下方没有扬声器,你没有真正获得3D声音。有一些声音调制过滤器试图解决这个问题,但实际上没有真实东西的代替物。当然真实地大多数游戏多半只是在二维方向上,因此这仍然不是太大的问题。

  实际上任何声音系统最重要的特征之一是把声音混合在一起。根据你所处的位置,空间中声音的位置,每个声音的音量大小,一旦你决定了实际上你能够听到的声音,然后你必须混合这些声音。通常声音卡自己处理这些,这首先是声音卡存在的主要原因。然而,外面有一些引擎决定首先用软件做一次预混合。直到你着眼于一点点历史以前,这并没有真正地带来多大的意义。

  当声音卡最初问世的时候,有许多不同的混合方法。一些声卡可以混合8种声音,一些单位16种,一些32种,等等。 如果你总想听到16种可能的声音,但你不知道声音卡是否能够处理,那么你回到了尝试和试验的道路上 就是你自己用软件混合。这实际上是Quake III声音系统的工作方式,但提一个问题:"Quake III是为A3DSound Blaster Live!声卡世界发布的,这比以前更加标准化,为什么还这样做?" 这是个好问题。实际上Quake III的声音系统几乎每行代码都和Quake II中的声音系统一样。而且Quake I,甚至Doom也是这样。你想一想,向上直到 A3D 声卡和 SB Live! 声卡,许多年来声音系统的需求没有真正地改变过。两个扬声器,二维方向,音量简单地随着距离减小。从Doom一直到Quake III没有发生太大变化。而且在游戏行业中,如果不是迫不得已,别理会它。

  通常你会仅仅使用DirectSound为你做声音混合,因为它会可以使用的声音硬件,或者转而依靠软件,很多地方就像DirectX3D显示卡所做的一样。在 90% 的声音情形中,依靠软件混合对你的幀速率没有真正发生太多不同。当DirectSound在一些狂热的编码者眼中甚至还不是一丝光线时,Doom引擎就已经产生了。它从来没有得到更新过,因为它从来就没有真的需要更新。

  当然,你可以使用 SoundBlaster Live!声卡的一些聪明特征,例如房间的回声特性: 一块石窟,或一个礼堂,一个巨穴, 一个足球体育馆等。而且你真的应该使用由硬件提供的混合器,毕竟,那是它存在的目的。这种方法的一个不足之处是程序本身时常无法获得混合结果,因为混合是在声卡内部完成而不是在主存。如果由于某种原因你需要看到产生的音量,你是运气不好。


Music Tracks in Games
(游戏中的音轨)
  我们没有过多的谈到游戏中的音乐生成。传统的有两种方法,一种是简单的音乐 .wav 文件(或同等物)。它被预先制作做好,准备运行,和最小忙乱。然而,这些在内存和回放时间方面很昂贵。第二种方式用预设的样本编码MIDI音轨。这时常比较节省内存,但缺点是必须同时把一些声音混合在一起,因而会把声音通道用光。

  动态音乐就是根据在游戏中目睹的行动改变你的音乐的能力,比如探险用慢节奏的音乐,战斗用快节奏的音乐。预先制作的音乐的一个困难之处是要合拍,因此你可以从一段音乐渐弱到另一段音乐,这对于MIDI音轨比较容易。尽管时常你足够快速地淡出,或者一段音乐在播放另一段音乐之前已经消失了,你能侥幸不被察觉。

  在我们离开这个主题之前,顺便说一下,值得一提的是存在一些公司专门为你的游戏创作特定意义的音乐。FatMan(www.fatman.com) 就是一家这样的公司。音乐可能比其他别的东西更加容易外包,这是他们存在的方式。

  最后,游戏现在的事情自然是MP3格式,允许巨大的11 1的声音样本压缩,然而在送到声音卡之前只花费CPU很少的时间解压缩。当我在Rave Software工作时,在Star Trek Voyager: Elite Force 中,我们设法用MP3在一张CD上面完全支持三种语言,仍然为较多的图形留有空间。主要地,我们 MP3 只用于非玩家角色(NPC)的语音,由于游戏的全部音频效果MP3流和动态解压缩超出了硬件的处理能力,虽然在将来这是肯定可能的。比较新的格式,如来自 Dolby AAC 和来自微软的WMA,以将近两倍MP3的压缩率提供了相等或者更高的音频质量(实际上一半的比特率),可能应用到将来的游戏中。

  以上是这一章节的内容,下面将是网络和连线游戏环境的开发。



梦在天涯 2007-12-04 13:20 发表评论

posted @ 2009-04-10 10:44 不会飞的鸟 阅读(285) | 评论 (0)编辑 收藏

仅列出标题
共9页: 1 2 3 4 5 6 7 8 9