【简单解析】WAD文件

您所在的位置:网站首页 cs16地图制作工具 【简单解析】WAD文件

【简单解析】WAD文件

2024-03-27 18:28| 来源: 网络整理| 查看: 265

0 序言

        大家好,我是m明天灬过后。《简单解析》系列专栏是一个科普专栏,科普的内容围绕金源引擎,也就是半条命、CS1.6使用的引擎。我会科普金源引擎使用的各种文件(比如说纹理,模型、地图、插件等),从它们的历史、制作使用方法和原理等方面分析这些文件。从这些文章中,你可以了解到金源引擎的发展历史和技术细节。

        为什么要制作这个系列呢?我是一名CS1.6地图制作者,回想起地图制作的这段经历,我发现,其中有许多的细节被我忽略了,相信同时期大多数的地图制作者和我一样,对金源引擎的工作方式并不了解,但依旧能够制作地图,只是遇到一些棘手的问题时,只能凭感觉测试,或者问知道的人解决方法,如果找不到知道的人,那么最终也无法解决,这是一种遗憾。因此,这个系列便会解决这个问题,让你对金源引擎整体有更深的理解,让地图、模型、插件、mod的制作更加顺畅。

        话不多说,本期主题是WAD纹理文件,我会告诉你什么是纹理、WAD文件的历史、一些查看、编辑WAD文件的软件、纹理的分类、WAD文件的存储和使用,以及最后 WAD文件的编码格式。

1 什么是纹理

        纹理的本质其实就是图片。

半条命中的几张纹理

        这是一个没有纹理的3D模型,你只能看出物体的形状是一个正方体。

正方体白模

        要真实地还原物体的样子,还 需要光照,以及各类贴图(比如漫反射贴图、法线贴图、高光贴图等)

各类贴图

        而这里的纹理就是指漫反射贴图。金源引擎使用wad文件来存储纹理,一个wad文件可以存放很多张纹理,而每张纹理有对应的名称和大小。

2 WAD历史2.1 起源

        wad文件起源于Id Software的游戏《毁灭战士》(Doom),并作为《毁灭战士》的资源文件,包含了如地图(map)、图标(sprite)、纹理(texture)和声音(sound)等。wad单词的本意是一团、一叠、一卷(纸张、衣物等),此外还有一个常见的说法:wad是Where is All the Data的简称,这暗示了wad文件的功能。玩家还可以通过制作wad文件来实现《毁灭战士》的模组,也就是我们常说的mod。

《Doom》游戏封面2.2 WAD2

        后来在还是Id Software的游戏《雷神之锤》(Quake)中,wad文件的功能 被pak文件替代了,但wad文件没有被移除,它被打包在pak中,并且只用于存储纹理了。此外,wad的结构也被更改,不再和最初的wad兼容。这个版本的wad被称为第二版,wad2。

《Quake》游戏封面2.3 WAD3

        金源引擎(GoldSrc)是维尔福软件公司,也就是Valve Software,V社,开发的游戏引擎,该引擎基于Quake引擎改造,Quake也就是《雷神之锤》使用的同名引擎。《半条命》(Half-Life)便是金源引擎的第一作,而半条命的模组《反恐精英》(Counter-Strike)自然也使用金源引擎。金源引擎保留了wad存储纹理的功能,但在wad2的基础上又做了一些修改,修改后的wad为第三版本,即wad3。本视频的内容也集中在wad3,并以半条命、CS1.6为例进行讲解。

《Half-Life》游戏封面《Counter-Strike》游戏封面3 WAD软件

        下面是一些用于查看、编辑wad文件的软件。

3.1 Wally

        Wally是最常用的wad编辑软件,它支持多种图片格式的导入导出,但对于wad3来说,它只支持解析miptex纹理(关于miptex纹理以及之后提到的color palatte, qpic和font,我会在第四章纹理的分类中详细讲解)。Wally内置有简单的编辑功能,可以很方便的对纹理进行调整(比如简单的绘制,编辑调色板等),但制作复杂的纹理贴图一般还是使用其他图像处理软件,比如PS。

wally软件截图

3.2 GCFScape

        GCFScape可用于打开gcf文件,新版的GCFScape也可以用于打开vpk文件,gcf和vpk是V社使用的技术,它们都类似《毁灭战士》的wad文件,《雷神之锤》的pak文件,用于打包存放各种游戏资源。GCFScape也能够打开wad文件,并且支持解析qpic和miptex纹理 。但它只支持查看和导出,不支持编辑和保存。

GCFScape软件截图3.3 VTFEdit

        VTFEdit主要用于vtf和vmt文件的查看和制作,vmt(Valve Material Type)用于描述物体的材质,而vtf(Valve Texture Format)是一张纹理,由vmt文件控制使用,这两者在起源引擎(Source)中引入。

        VTFEdit也用来可以打开wad,并且支持解析qpic和miptex纹理。

VTFEdit软件截图

        补充: 实际上GCFScape和VTFEdit都是由作者Nem制作的,Nem还制作了bspviewer地图查看工具,terrain generator地形生成工具等国内也很常见的软件。

3.4 WAD3 Viewer

        上面提到了有四种纹理类型,分别是color palatte,qpic,miptex和font纹理。但各个软件都不能完美解析这些纹理,很不方便我测试,我自己用WPF写了个小软件,用来解析wad3文件里的这四种纹理。这个软件支持四类纹理的查看和导出,但暂时不支持编辑保存,有需要的可以在github搜索wad3 viewer找到。(你看我都这么努力了,三连来一个好吗!秋梨膏!)

WAD3 Viewer截图4 纹理的分类

        纹理分类有两个层面,一个是数据结构的层面,另一个是纹理功能的层面。

4.1 数据结构层面的分类

        金源引擎中存在四种纹理类型:color palette(调色板纹理)、qpic(quake纹理)、miptex(mipmap纹理)和font(字体纹理)。

4.1.1 color palette(0x40) 调色板纹理

        首先是color palette调色板纹理,编号0x40,这种纹理只出现在tempdecal.wad中,tempdecal.wad也就是半条命/CS1.6的喷图文件。这个wad中只有一张纹理,对应了喷图,比如这就是一个tempdecal.wad中的喷图。

lambda喷图

         既然提到了喷图,那我们可以再深入一点。在《半条命》的游戏文件夹中,我找到了三处和喷图相关的文件。

        首先,valve文件夹下有一个spraypaint.wad的纹理文件,里面存储了默认喷图的图片。他们的纹理类型都是稍后会讲的miptex纹理,但是这个文件似乎对游戏没有影响,删除也不会导致报错。

spraypaint.wad文件

        然后,valve文件夹下还有个logos文件夹,下面存储有bmp格式的默认喷图,并且存有一张remapped.bmp

logos文件夹

        我仿照logo文件夹中bmp的格式制作了一张图片,把它放入logo文件夹后,在游戏的喷图选项中可以选择,而且可以使用。

albk.bmp

选择喷图

使用自定义喷图

        修改或删除remapped.bmp,不会影响喷图实际的样子,所以我猜测,这是用于测试的遗留物。

        最后,就是tempdecal.wad了,打开发现里面存储有一张color palette类型,名称为LOGO的纹理,这才是游戏实际使用的喷图文件,修改这个文件会直接改变游戏内的喷图(需要重新进入服务器)。半条命也支持miptex格式的喷图,所以喷图文件可以直接使用Wally软件制作。比如这里我就使用Wally制作了一张彩色喷图。

使用wally绘制彩色喷图

使用彩色喷图

        需要注意,纹理的像素数量是有限制的(最多12288个像素),且长宽必须是16的倍数,超过后喷图无法上传到服务器,会变成一个小的lambda图标,此外,如果服务器禁止了上传喷图,或者你删除了tempdecal.wad,喷图也会变成这个图标:

小lambda喷图

        然而这个图标既不在logo文件夹中,也不在spraypaint.wad里,那么它存储在哪儿的呢?其实它存储在valve文件夹下的印花纹理decals.wad里。打开decals.wad,其中叫{lambda06的纹理就对应着这个图标。

简 单 编 辑 后保存,重启游戏,就可以看到它的确改变了!

lambda ×, hl3 √4.1.2 qpic (0x42)

        下一个是qpic quake纹理,编号0x42,它是quake引擎遗留的格式。在cached.wad和gfx.wad中还存在。cached.wad中有两张qpic,LOADING和CONBACK(console background),分别存放了载入界面和控制台背景。

cached.wad LOADING

cached.wad CONBACK

        然而进入游戏,载入界面和控制台并没有类似的图片诶╰(‵□′)╯。实际上,半条命经历了多次版本升级,界面变化极大。如果你玩过CS1.5或更早的版本应该会明白。

cs1.5 主界面

cs1.5 创建服务器界面

        而LOADING和CONBACK正是老版半条命的加载页面和控制台背景图片。老版的半条命称为WON版,而新版的半条命称为steam版。这是因为最初半条命和反恐精英的网络服务由WON提供,WON是Sierra Games雪乐山公司开发的服务,你可能对这个开头很有印象:

Sierra Games LOGO

        这个就是雪乐山公司的logo。在2004年7月,Valve关停了最后一台won服务器,取而代之的是自己开发的steam服务,也就是如今steam的雏形。在WON版半条命中,我找到了这个载入界面和控制台背景。

LOADINGCONBACK4.1.3 miptex (0x43)

        接下来是Miptex(Mipmap texture),mipmap纹理,编号0x43,是最常用的纹理类型,我们制作地图使用的纹理都是miptex,并且VHE也不支持其他3种格式。那么名称中的Mipmap是什么意思呢?

       简单来说,Mipmap是图形学中的一种贴图渲染技术,可以用来加快渲染速度,并且可以减轻图像混叠问题。比如这里有一张棋盘格纹理:

常用的棋盘格图片

        把它平铺到平面上,然后从侧面看过去,此时远处的纹理会出现因为降采样导致的摩尔纹(moiré pattern),特别是在运动的时候,摩尔纹会更加明显(注意远处形成的规则的曲线,随着运动曲线也在扭曲),这便是图像混叠的问题之一。

摩尔纹

        而mipmap的做法是,对一张贴图,事先压缩成一组不同大小的贴图,在距离近时选择大的贴图渲染,在距离远时选择小的贴图渲染,这样既能够省去远处采样大贴图时的额外损耗,也因为不会大幅度降采样,摩尔纹也得到缓解。

mipmap使用

有无mipmap对比

         一般mipmap有8层,即一组共八张贴图。如一张长宽256像素的贴图,则有长宽128像素,长宽64像素, 长宽32像素,依次往下,一共八张贴图。金源引擎使用的mipmap层级为4,即一组mipmap有四张贴图。且金源引擎额外要求mipmap纹理的长宽必须是16的整数倍。

4.1.4 font (0x46)

        最后是font,字体纹理,编号0x46,这种纹理在fonts.wad和gfx.wad里存有。字体纹理用一张图片存储扩展ASCII码中的256个字符,这种格式继承自quake引擎,quake引擎字体纹理宽度一般为128像素,而金源引擎中一般为256像素。这些字体纹理似乎也只在WON版半条命中使用,所以这里就不做深入研究了。

font纹理Bonus 调色板

        以上四种纹理都使用了调色板。当然这不是美术上的调色板,在计算机层面,调色板是指一组颜色列表。调色板一般对应索引颜色方法,相比于传统的依次存储每个像素的颜色,索引颜色方法存储了一组索引值和一个调色板。首先准备一个调色板,这个调色板存储了图像里出现的所有颜色,而原本存储像素颜色的位置,变成存储调色板内的索引,也就是颜色的编号。

调色板

        wad使用的颜色为24位RGB,以及8位调色板。8位调色板意味着图像最多包含256种颜色,因此如果颜色多于256种,多的颜色只能在调色板内找一个颜色相近的替代。这就导致图像信息损失,质量下降,但是使用索引颜色方法可以对图像进行压缩,减少图像文件大小。

        假如一张长宽256像素,24位RGB存储的图像,以传统方式存储,每个颜色需要3字节,总共256x256个颜色,因此需要192KB来存储,而如果使用8位调色板的索引颜色方法,调色板需要存储256个8位RGB值,总计6KB,此外每个索引占一个字节,每个像素对应了一个索引,共有256*256个像素,总计64KB,图像索引和调色板一共只占70KB,压缩了空间。

4.2 纹理功能的分类

        金源引擎为了实现一些特殊的功能,使用了一些特殊的纹理来实现特别的效果。(这里的分类主要参考了X-man天书的分类,命名也使用X-man天书的对应中文名)

4.2.1 固体纹理

        固体纹理是最普通,最常见的纹理,它没有任何特殊效果。我们所见的大部分纹理都是固体纹理。

4.2.2 液体纹理

        液体纹理名称以!开头。贴有!的纹理会成为和func_water效果相同的水实体。此外如果前缀是!lava,则水实体的“内容”是岩浆,前缀是!slime,则水实体的“内容”是毒液。岩浆和毒液会造成对应类型的伤害,并且伤害很高。

液体纹理水实体和内容物选项4.2.3 透明纹理

        透明纹理以{开头,其调色板的最后一个颜色代表透明色,一般设置为纯蓝色(#0000FF)。透明纹理能够实现部分区域完全透明的效果(索引为255,即最后一个颜色的部分是透明的),用来实现梯子、通风管窗等。注意默认情况下透明纹理的透明色部分会呈现为黑色,需要将贴有透明纹理的物体转换为实体,并且渲染模式(render mode)选择固体(solid)或纹理(texture),这样才能使透明部分变为透明。

透明纹理4.2.4 随机纹理

        随机纹理是一组以"-N"开头的纹理,,其中N为一个0到9的数字,"-N"后的纹理名称要相同,比如halflife.wad中一组名为OUT_RK3的纹理就是:-0OUT_RK3、-1OUT_RK3、-2OUT_RK3、-3OUT_RK3和-4OUT_RK3

随机纹理OUT_RK3

        使用随机纹理时,选择这一组内的任意一张纹理都能达到效果。

        X-man天书提到说CS里随机纹理不可用,但经过测试后发现,至少在目前最新的CS版本中,随机纹理是可用的。随机纹理大小似乎必须是224*224,否则纹理会强制平铺到224*224,并且以这个大小为一个单元进行随机(但缩放纹理则块的大小也跟着缩放,所以可以自由缩放纹理)。

随机纹理的大小和平铺问题

        此外还要注意纹理的偏移量,以u方向为右,v方向为上的话,则块以面的左下角顶点为基准点划分,即-u,-v方向。关于u,v方向,这一块比较复杂,在第五章 纹理的使用中还会提到。(注意,有时即使你在编辑器里设置正确了,在游戏中偏移量还是出现问题,可能是因为编译时的自动优化,不可见的部分被切割掉了导致的)

一组随机纹理随机纹理使用效果4.2.5 动画纹理

        动画纹理是一组以"+N"开头的纹理,其中N为一个0到9的数字,"+N"后的纹理名称要相同。比如halflife.wad中一组名为GENERIC_113的纹理就是,视网膜扫描仪的闪烁效果就是它制作的:

动画纹理GENERIC_113

        动画纹理的作用就是产生动画,动画纹理每0.1秒会自动切换到下一帧,切换的顺序为0到9然后返回0,循环往复。

- 切换纹理

        动画纹理可以附加一张以"+A"开头的纹理,"+A"后的纹理名称要和其他动画纹理相同,这张纹理会作为一个静止帧。这个纹理需要配合固体实体使用,每当固体实体被触发(trigger)一次,纹理会在动画和静止帧之间切换,当然如果只想要这种切换效果,那么一张+0和一张+A就可以了。切换纹理可以用来可以制作灯泡的亮灭,开关的开启和关闭等效果。

nyancat动画和开关都是动画、切换纹理制作的4.2.6 滚动纹理

        滚动纹理是一组以"scroll"开头的纹理。它可以用来用于实现传送带、瀑布之类的效果。使用这个纹理需要配合func_conveyo传送带实体,纹理会在U方向(黄线方向)上“滚动”(平移)。

传送带纹理(SCROLL_CON3)和U方向

        纹理的滚动速度也与传送带实体设置的相同(速度单位是“单位”unit,而缩放纹理会让这个速度也缩放,因此如果一个传送带实体上多个面缩放不同,则速度也不同)

滚动纹理的缩放

        最后,传送带实体可以带动踩在上面的玩家的,但带动的方向由实体方向决定。传送带上小跳有惊喜

4.2.7 印花纹理

        印花纹理存在于decals.wad之中,地图中也只能使用decals.wad中的印花纹理,使用其他wad内的纹理在游戏中是看不见的。印花纹理的作用和喷图类似,它可以在墙面、地面等已经有纹理的地方像贴纸一样“贴”一张额外的图片,用来增加地图细节。如墙面的血迹,炸弹安放点的图标。

de_cbble 优化纹理

de_aztec 印花纹理

        印花纹理需要通过infodecal点实体放置。此外,印花纹理也属于miptex纹理,但其索引的0-254代表灰度,而调色板的最后一位代表印花的颜色。

自定义印花纹理自定义印花纹理的使用4.2.8 发光纹理

        发光纹理实际上不是指某个纹理,所有的纹理使用发光效果。在编译的rad过程中,编译程序会读取其目录下的light.rad文件,这个文件包含了发光纹理的信息。每一行代表一个发光纹理,各个字段分别是纹理名称,R, G, B的值,最后一位是发光的亮度。

light.rad文件

        新版的FGD中提供了一个名为info_texlights的点实体,用以实现和light.rad一样的功能。创建这个点实体,添加一组键值,键名为纹理名称,键值填RGB和亮度,则这张地图所有对应的纹理都会有发光效果,这个点实体可以放置在地图任何位置。

info_texlights点实体

发光纹理效果4.2.9 标记纹理

        标记纹理是指AAATRIGGER纹理,位于halflife.wad中,属于工具纹理(tool texture)。

AAATRIGGER

        标记纹理用于标注那些不可见的固体实体,一般不可见的固体实体也都使用它作为纹理,这样是便于和环境区分开。此外标记纹理就没有其他特殊的作用了。新版编译程序里标记纹理是不可见的,但是在旧版的编译程序中,标记纹理会被渲染,这意味着如果你给可见的固体或固体实体贴上该纹理,会像固体纹理一样正常显示出来。

4.2.10 天空纹理

       天空纹理是指sky纹理,位于halflife.wad中,属于工具纹理(tool texture)。

sky

        贴有天空纹理的面会被渲染为天空。3D渲染里的天空是指一个场景的背景环境,它可以让场景显得更加宽阔。金源引擎的天空使用了天空盒技术(skybox),即用上、下、左、右、前、后六个方向,总共六张图片来表现天空。除了天空盒,还有如天空穹(skydome)技术,使用球或半球作为天空等。

        对金源引擎的天空盒来说,玩家在场景中移动时,天空盒将相对玩家保持静止,这其实是模拟了现实中极远的物体,例如云、太阳等,即使你在移动,却感觉远处物体几乎静止的特性。金源引擎的天空盒是完全静止的,这相当于物体处于无限远的位置,玩家的移动不会产生任何视差,因此你不会感觉到这些天空是立体的,显得比较“假”,但这样的天空盒实现很简单(只需要获取摄像机的朝向,而忽略摄像机的位置,以天空盒中心为起点,获取对应方向上的纹理采样即可)

4.2.11 轴心纹理

       轴心纹理是指origin纹理,位于halflife.wad中,属于工具纹理(tool texture)。

origin

        轴心纹理用于标记轴心位置。轴心指的是物体的变换的中心。将物体和贴有轴心纹理的固体一起转换成固体实体,那么这个固体实体的轴心就确定了:位于贴有轴心纹理的固体的中心。

        如何定义一个轴心固体的中心呢?这里要提到一个包围盒(bounding box)的概念,如果贴有轴心纹理的固体以包围盒的中心为其中心,特别是但它不是规范的长方体时,就更需要考虑包围盒的问题了。简单来说,包围盒就是给定长宽高不定的盒子(盒子的所有边要和和xyz轴的一个平行,即盒子不是斜的),这个盒子要直接包裹该物体,并且其长宽高要是最小的。而包围盒的中心就好计算了,就是长方体的几何中心。

包围盒

        一个固体实体最多拥有一个轴心,没有轴心的固体实体默认轴心在世界坐标原点(func_train路径移动的中心在固体中心,与轴心无关,但它的旋转是以轴心为中心)

4.2.12 贴附纹理

       贴附纹理是指clip纹理,位于halflife.wad中,属于工具纹理(tool texture)。

clip

        贴附纹理它会阻挡玩家(包括人质和func_pushable可推动实体)通过,但子弹、枪械和手雷可以穿过,因此用来制作空气墙、子弹可穿过的铁丝网等效果。

        新的编译程序中,你可以在固体的一个面贴上clip,这样整个固体都具有clip的性质,但是其他面会正常显示。传统的子弹可穿过的铁丝网制作方法是,使用func_illusionary可穿过实体显示铁丝网,加上另一个重叠且只贴clip的空气墙,而现在只需要制作铁丝网(可以是固体,如果使用了透明纹理,可以转成实体),并在一个看不见的面上贴clip即可。

        clip相关还有几张特殊纹理,会在之后提到。

4.2.13 空纹理

       空纹理是指null纹理,是zhlt编译程序在zhlt.wad中提供的纹理。

null

        空纹理用于减少渲染面数,优化地图大小和FPS,我主要用于解决Allocblock:full的问题。贴有空纹理的面在编译时会被移除,它一般贴在玩家看不到的面,如地图外侧的面,固体接缝处的面等等,是很常用的优化工具。注意,虽然贴有null的面被移除,但其固体依旧可以阻挡玩家和子弹。

4.2.14 提示纹理

       提示纹理是指hint纹理,是zhlt编译程序在zhlt.wad中提供的纹理。

hint

        提示纹理用于辅助编译VIS过程的区域划分,可以优化FPS,需要配合Skip跳过纹理使用。关于提示纹理和跳过纹理的具体作用和用法,我会在之后的编译程序话题中讨论。

skip

        以上是比较常用的特殊纹理。注意,以上提到的大部分特殊纹理不能混合使用,即一个固体不能包含多种特殊纹理。但也有例外,如null可以贴在任何固体或实体上。

4.2.15 其他纹理

        下面有一些不常用的纹理,这里只简单介绍一下,其中一些的原理我并不是很清楚,如果有了解的请在评论区告诉我!谢谢!

BEVEL:和NULL功能相同,但是贴这个纹理的面不向外扩展(这里向外扩展意味着什么我并不是很清楚,另外好像有些人相比null更喜欢用BEVEL)

black_HIDDEN:用于自定义模型光照。将black_HIDDEN作为发光纹理,并放置一个贴有black_HIDDEN纹理的固体,则所有在这个固体上方(且在长宽范围内)的模型光照都受其影响而改变(包括玩家模型)

CLIPBEVEL: Clip和Bevel功能兼具

CLIPBEVELBRUSH:固体所有的面都具有CLIPBEVEL的效果(因此只需要贴一个面就可以了)

CLIPHULL1, CLIPHULL2, CLIPHULL3:分别对应站立、大尺寸(big size)和蹲伏的空气墙,分别会阻挡站立的玩家、设置为大尺寸的可推动物体(但测试中发现好像还和pushable的尺寸有关..)和蹲下的玩家,使用这个纹理只需要贴在一个面上,且可以叠加使用。clip相当于这三个纹理的叠加。

CONTENTEMPTY:内容为空,类似func_illusionary,也和fgd中设置玩家可穿过类似。

CONTENTWATER:和水的效果类似。

NOCLIP:删除clip,玩家和物体都可以穿过,但是子弹、手雷还是会被阻挡。

SOLIDHINT:贴在两个完全重合面的面上,可以用于避免固体面相互切割。

Bonus 无缝纹理

        无缝纹理(seamless texture),有人也会称为四方连续图与二方连续图,指的是在某个方向上平铺排列,图像的拼合位置看不出接缝的纹理。半条命中大量使用了这类纹理,使用它能用一张小的纹理铺满一个很大的面。

        对于一些不规则的、自然的照片,将其制作成无缝纹理很简单,在一个方向上截取部分,然后移动到另一边的边缘,最后添加渐变蒙版即可得到一张无缝纹理。

无缝纹理制作5 纹理的存储和使用5.1 存储

       半条命会从根目录的valve文件夹下读取wad文件,CS1.6除了valve文件夹,还在cstrike文件夹下读取。此外,不同语言版本对应了额外的文件夹,如简体中文的半条命还有valve_schinese,简体中文的CS1.6还有cstrike_schinese文件夹等。

        除了单独存放在各个文件夹里的wad文件,wad也可以被打包在bsp地图文件内。在CSG编译时使用"-wadinclude wadPath"参数就可以打包指定纹理。此外,如果出现纹理重名的问题,会优先读取bsp内打包的纹理。

        地图包含所需wad的信息,载入地图时会在上面提到的文件夹里查找需要的wad,如果未找到对应的wad,游戏程序会崩溃并弹出错误。

wad丢失的错误

        如果所有使用的wad中里都没有找到地图使用的某张纹理,那么这个纹理会变成默认的紫黑格,代表纹理丢失。

紫黑格5.2 贴附

       假设你要给一面墙贴上墙纸,你不仅需要确定墙纸的花纹,还需要确定从哪儿开始贴,甚至可能你觉得旋转一下更好看。你看到了,要把墙纸贴到墙上需要考虑这么多东西。而把纹理贴到面上也是同理,我们需要确定从哪儿开始贴,还要考虑有没有缩放和旋转。

贴墙纸

        在纹理上我们建立一个二维坐标系,横轴为,范围为,纵轴为,范围,这就是常用的UV坐标(据说是因为XYZ被3D使用,所以使用UV表示纹理二维坐标系的坐标轴)。

UV坐标

        一般来说,每一个面上的每个顶点都对应了UV坐标系中的一个位置(UV坐标),确定了各个顶点对应的UV坐标后,通过插值计算出每个像素的颜色。但金源引擎地图不支持复杂的UV编辑,并且使用了更加简单的办法,我会从直观的角度讲述它是怎么做的。

        假设有一个面需要贴上纹理,这个面记为F。

面F

        从世界坐标原点作面的垂线,垂足O作为面纹理坐标的原点。然后我们需要“指定”一个投影平面,注意,投影平面不一定和面F平行!要确定投影平面,我们只需要确定U,V两个方向。确认投影平面后,先把纹理垂直平铺到平面上,然后再映射到面F上,而投影平面就像是一个中介。这样的处理方法和bsp地图结构有关,一个投影平面可以被多次利用,节省地图空间。

纹理映射(五毛特效)

        那么如何确定U(黄色轴)和V(绿色轴)的方向呢?VHE提供了两种模式:固体(solid)和面(face),用于确定UV方向。首先要知道,金源引擎使用左手坐标系,且X轴代表前后,Y轴代表左右,Z轴代表上下。

        固体模式下,UV方向会在世界坐标的后方(即-x轴),右方(即+y轴)和下方(即-z轴)三个方向中选择最接近的两个分别作为UV方向。而到底选择哪两个,和面的朝向有关:方向和面的夹角越小则越倾向于选择这个方向。如果夹角相同的话,则根据右>下>后的优先级选择。最后,两个方向选择了,但谁是U方向,谁是V方向呢?这个还是按照右>下>后的优先级,优先级越高越倾向于作为U方向。比如两个方向里有右方,则右方作为u方向,另一个作为v方向,而如果没有选中右方(就是选了后方和下方呗),则后方为u方向,下方为v方向。

        而面模式下,方向通过面的朝向(法线方向)和上方(+z轴)确认。想一想生活中是怎么判断左和右的?想一想,如果你倒立着看东西,左和右就和之前恰恰相反了?生活中之所以对左右形成共识,是因为我们的上方是固定的,我们的身体大部分情况下是站立的,也就是说我们的上方是确定的。这样有了前方和上方,通过叉乘可以得到左右两个方向:

如何得到“左”方

        那如果法线方向和上方平行怎么办?我们知道,如果两个向量平行,则其叉乘等于零向量,零向量不能用来确认方向。如果法向量和上方同向,则说明面是竖直朝上的!hammer直接规定,这种情况下,UV方向为右方(+y轴),方向为后方(-x轴);同理,如果法向量和上方反向,则说明面竖直朝下,规定UV方向为左方(-y轴),方向为后方(-x轴)。

        有时固体模式和面模式计算的UV方向相同,则编辑器中会显示为都被选中。

“固体”和“面”都选中

        有时固体模式和面模式和当前UV的方向都不吻合,编辑器中会显示为都不选中。这一般是直接编辑顶点造成的。

“固体”和“面”都未选中

        得到投影平面后,就可以计算纹理映射了。直观的讲,我们先将平面P铺满纹理,进行缩放和平移,然后把这个平面投影到面F,注意投影方向是投影平面法线方向,而不是面F的法线方向。

还是纹理映射(注意旋转操作实际是对UV进行的)

        用数学来描述的话,对某个顶点P, u代表u方向,v代表v方向,su代表u方向缩放,sv代表v方向缩放,ou代表u方向偏移量,ov代表v方向偏移量,w是纹理宽度(像素),h是纹理高度(像素),则P点对应的UV坐标为:

UV坐标公式6 WAD文件结构

       终于到最后一节了!这一节的内容是wad文件的数据存储结构。首先要说的是,wad是二进制文件,而不是文本文件。当然,计算机文件都是以二进制方式存储的,文本文件也不例外,所以广义的二进制文件就是指所有文件。而这里和之后提到的二进制文件,是指狭义的二进制文件,即所有非文本文件。

        所谓文本文件指的是以字符编码存储,可直接阅读的文件,比如VHE地图编辑器的地图源文件map文件。

test.map 字符编码的文本文件

        而二进制文件的数据就不一定是字符编码了,而是把文件分成很多个部分,每个部分有多大,存储什么东西,用什么样的方法存储都是人为规定的,且一般不能直接阅读。读取二进制文件一般以字节(bytes,简写成大写B)为单位。一个字节等于8个位,或者叫做比特(bits,简写成小写b)。

        如果你不知道什么是位,这里简单说一下。一个位相当于一个容器,它可以存放一个0或一个1,而计算机正是用这些连起来的0或1来记录数据的。因为8个位太长了,不方便查看,我们一般用十六进制表示,而8位的二进制数范围是00000000-11111111,即十进制的0-255,正好对应2位的十六进制数,0x00-0xff,0x前缀表示这是一个十六进制数。

        一个或多个字节构成一个有意义的数据,称为一个字段。假设我设计一个二进制文件用于存储图片,我们需要知道图片的宽度,高度和每个像素的颜色,那么我设计一个8字节的字段用于存储宽度,另外8字节用于存储高度,然后之后的数据都是像素的颜色数据。

图片文件的结构

        在这里也可以看出,要读取二进制文件,我们需要提前知道这个文件有哪些字段,每个字段在哪个位置。

       wad是二进制文件,所以也存在特定的数据结构。它主要存在三个部分,标头header,块信息lumpinfo和块数据lumpData。

(注:wad所有字段使用小端编码,一下提到的无符号整型如未标注则代表32位int)

6.1 标头 Header

       wad的起始是一个标头结构,这个结构如下图(官方没有提供字段的名字,这里使用网络搜集到的命名)。

wad header数据结构

magicNumber 魔数:必定是字符串WAD3对应的ASCII码,即0x57 0x41 0x44 0x33,这里的魔数用于判断文件类型,识别是否是wad3类型的文件。(ASCII码,全称美国信息交换标准代码,是一种文本字符编码方法,它用7个位编码了大写和小写字母,数字0到9,标点符号和控制字符等符号)

numLumps 块数量:一个块存储了一张纹理,所以块数量就是纹理数量。

lumpInfoOffset 块信息表偏移量:指的是第一个块信息距离文件开头多少个字节。块信息一般放在在wad文件的末端,标头放在开始,中间放的是块数据。

6.2 块信息 LumpInfo

       块信息存储了一张纹理的名称,类型,偏移量等信息,它的结构如下图:

wad lumpInfo 数据结构

lumpDataOffset 块数据偏移量:指块数据距离文件开头多少个字节。

compressedSize 压缩后大小:大部分文章说这是块数据压缩后的大小,但我没有找到关于wad压缩的资料…但它一般都是0,即无压缩,所以不影响解析。

size 原大小:块数据的原始大小。

type 纹理类型:即第四章中提到的四种纹理类型,分别是0x40(color palatte), 0x42(qpic), 0x43(miptex)和0x46(font)

cType 压缩类型:和compressedSize 压缩后大小对应,未找到相关资料。大部分软件的处理方法是无压缩,compressType为0,compressedSize等于size。

padding 占位:只用于占位,用于字节对齐。

textureName纹理名称:ASCII编码字符串。纹理名称必须以\0符号结束,这意味着纹理名称最长只有15个字符。

6.3 块数据 LumpData

       四种类型的纹理,块数据的结构各不相同。

6.3.1 0x40 color palette 纹理0x40 color palette

texName 纹理名称:和块信息里的纹理名称一致。

texWidth 纹理宽度:宽度以像素为单位。

texHeight 纹理宽度:宽度以像素为单位。

offset x4 偏移量:四个偏移量数据,每个占用四字节,无符号整型。分别对应四层mipmap的纹理数据距离文件开头多少个字节。

data x4 纹理数据:四部分纹理数据,对应四层mipmap的纹理。如果你还记得我们提到的索引颜色方法的话,应该还记得一个颜色只用一个字节表示。因此第一层data占width*height字节,第二层data占width*height/4,第三层data占width*height/16,第四层data占width*height/64。

usedColorNum 调色板颜色数量:字面意思,但不确定具体有什么作用,调色板的大小是固定256个颜色,因此无论颜色多与少占用的空间不变,因此这个字段的存在好像并没有意义。

palatte 调色板:wad以8位RGB存储颜色,因此一个颜色需要三个字节。调色板大小为256,因此调色板占用256*3字节。

padding 占位:占用两字节。只用于占位,用于字节对齐。 

        tempdecal.wad喷图文件中,调色板前255位都是空,最后一位代表喷图颜色。在使用tempdecal.wad时,会对调色板从纯黑(0x000000)到最后一位颜色进行插值。

         (老版的pldecal.wad里,调色板前255位不是空,而是从纯黑(0x000000)到纯白(0xffffff)的插值

6.3.2 0x42 qpic 纹理

        qpic最大特点就是长宽没有16的倍数的限制,且没有mipmap。其块数据的结构如下图:

0x42 qpic

texWidth 纹理宽度:宽度以像素为单位。

texHeight 纹理宽度:宽度以像素为单位。

offset 偏移量:偏移量数据,占用四字节,无符号整型。指纹理数据距离文件开头多少个字节。

data 纹理数据:索引颜色方法,一个颜色用一个字节表示。因此data占width*height字节。

usedColorNum 调色板颜色数量:同0x40

palatte 调色板:同样是256*3字节。

padding 占位:占用两字节。只用于占位,用于字节对齐。

6.3.3 0x43 miptex纹理

       miptex的格式和0x40完全一致的,只是解析上有一定的区别(color palette需要自己进行一次颜色混合,miptex不用)。

0x43 miptex

        每个字段啥意思翻0x40就行了。

6.3.4 0x46 font纹理

        字体纹理的块数据结构如下:

0x46 font

texWidth 纹理宽度:宽度以像素为单位。注意金源里Font纹理的宽度都是256。

texHeight 纹理宽度:宽度以像素为单位。

rowCount 每行个数:指每行有多少个字符,每行的字符数量是固定的。

rowHeight 行高:单位是像素,所有字符的行高是固定的。

charInfo 字符信息:每个字符对应一个字符信息,一个字符信息包含两个字段:offset偏移量和charWidth字符宽度(意味着支持非定宽字符),两个字段都是占用四字节,无符号整型。字符数量为256(对应扩展ASCII的256个字符),总共占用256*8个字节。

data 纹理数据: 索引颜色方法,一个颜色用一个字节表示。data占width*height字节。

usedColorNum 调色板颜色数量:和其他块相同。

palatte 调色板:和其他块相同。

padding 占位:占用两字节。只用于占位,用于字节对齐。

7 END

        真不敢相信你能看到这里!这篇文章本计划做成一个视频发布的,其中的一些素材也是半成品视频中拿出来的,还意外地挺合适。之所以放弃掉做视频还是发现效率太低了,查资料、码字可能要一两个星期,但把文章变成视频的工作量就远不止于此了,忙活半天也只能做出一个小特效,实在是太低效了。因此我决定还是给文章加配图发布。

        最后,谢谢你能看到这里!如果你有什么疑问,或者发现了什么错误,请在评论区告诉我,我们共同进步!

        再次谢谢!( •̀ ω •́ )✧

参考文献

X-man天书 https://lotc.cc/xman/

doom wad 维基:https://en.wikipedia.org/wiki/Doom_WAD, https://doomwiki.org/wiki/WAD, https://doom.fandom.com/wiki/WAD

quake wad 维基:https://quakewiki.org/wiki/Texture_Wad

v社wad维基:https://developer.valvesoftware.com/wiki/WAD

v社gcf维基:https://developer.valvesoftware.com/wiki/GCF

The Doom Bible: https://5years.doomworld.com/doombible/

Unofficial BSP v30 File Spec:http://hlbsp.sourceforge.net/index.php?content=waddef

Quake Documentation Version 3.4, The WAD2 files:http://www.gamers.org/dEngine/quake/spec/quake-spec34/qkspec_7.htm

zhlt编译程序官网:http://zhlt.info

一篇vis原理的文章 Hint Brush Tutorial:http://www.countermap2.com/Tutorials/tutorial0b30.html?id=2

The Whole Half-Life, r_speed 维基:https://twhl.info/wiki/page/Tutorial%3A_R_Speeds

CS 有害命令屏蔽的更新记录:https://steamcommunity.com/games/10/announcements/detail/1009075542294868964

V大编译程序更新历史 vhlt  http://ys-f.ys168.com/275247040/l4G4554387JML4kSmQPs/%E6%9B%B4%E6%96%B0%E5%8E%86%E5%8F%B2.txt

V大编译程序帖子 https://forums.svencoop.com/showthread.php/38059-ARCHIVE-Custom-ZHLT-by-vluzacn

一篇关于进阶编译的文章 Advanced Compilation:https://sites.google.com/site/svenmanor/tutorials/advancedlight

- 更新:修改de_dust印花纹理图片,因其不是印花纹理,改成de_cbble的截图



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3