游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

分享使用纹理集创造HTML5游戏的方法

发布时间:2012-09-25 14:45:11 Tags:,,

作者:Colt McAnlis

对于网站设计来说,HTML5是一种非常棒的技术。HTML5标记和JavaScript的灵活性都让网站开发者能够使用单个图像元素去创造他们想要的内容。

这一方法非常适合那些经费较少的小型网站建设,而对于游戏或其它高成本网站来说,使用单个图像元素将导致长时间的消耗以及性能的降低,而最终创造出糟糕的终端用户体验。

在这一生态系统中,短短的3秒就足以让你流失了大半用户,所以开发者们便需要使用适当的工具去应对纹理集问题。

什么是纹理集?

在实时图像制作中,纹理集是指一张包含了许多小幅子图像的较大的单一图像,并且每张图像都可以被独立引用。纹理集是随着3D游戏的出现而出现,即当我们最初创造出这类型游戏时它便已经诞生了(例如《Quake 2》中最初的光照映射图)。

纹理集最初是用于提升3D游戏的性能。因为在3D管线的光栅化阶段中更换或引用新纹理需要投入大量的成本,而如果开发者能够将所有较小的纹理整合进更大的纹理中,便能够有效地降低图像管线所需要的较高成本,从而创造出更棒的性能。

grits_effects(from gamaustra)

grits_effects(from gamaustra)

HTML5游戏《GRITS》的纹理集。这个单一的纹理包含了游戏中的所有小图像。在单一纹理中加载这些图像能够减少加载时间并完善运行性能。

边注术语:许多制作HTML5游戏的人员可能会说纹理集与精灵图表没两样,但是我却不这么认为。精灵图表是指包含精灵的一个图表集。

而与之相反的是,纹理集可以包含多张图表,并且每张图表都是一张拥有一种单一图像资源的大型图像—-例如精灵(动画),UI纹理(非动画),字型(字体加工)等等。纹理集是一种技术理念,而精灵图表则拥有一种特殊的功能概念。

在HTML5游戏中使用纹理集的好处

在HTML5游戏中使用纹理集能够帮助你的游戏提高加载速度,从而减少你的带宽成本。

更快速的HTTP加载时间

就像我在Google I/O中所谈到的,我们只需要花241毫秒便能够下载一张源自服务器的4k x 4k纹理,可想而知这是多块的下载速度。如果你将一次简单的下载划割成4096个不同的请求,每个请求的规格为64×64像素,那么对于相同的像素数量,整体的加载时间将会发生巨大的变化,即将从241毫秒提升至4.3秒,即增加了17倍。

下图的时间表便清楚地呈现出这种差别:

HTTP requests(from gamasutra)

HTTP requests(from gamasutra)

源自服务器的一张4096×4096规格的纹理需要一个单一的HTTP请求,且我们能够快速解析这一请求。

如果是分别基于不同HTTP请求去获得资产将需要花费更长时间。持续条左边的浅色区块代表的是浏览器阻塞连接的时间,因为这期间将会发布许多请求。

我们必须清楚请求(浏览器向单一服务器所发起的)的数量也具有上限。浏览器本身便会设置这种上限,而当到达了这一限制时,浏览器便会阻塞之后的请求,直到能够使用开放连接。

这也是为何个体资产与集体资产的性能会出现差别的主要原因。如果你拥有4千个待定的HTTP请求,但是这时候却只能进行6次连接,那么所有的请求便只能被暂时搁置着。在上图中,持续条中的浅色区块便是Chrome所阻塞,并等待着激活连接的内容。

除了能够减少加载时间,纹理集同样也能够减少你的应用所发出的HTTP请求数量。这是HTML5版本的《命令与征服》的开发者所遇到的一大苛刻的问题:在开发期间他们的应用将呈现出病毒式发展,而他们的主机服务将封锁其帐号,并直到请求负载下降时才解锁。

减少浏览器运行时的开销

使用个体纹理集资产也能够有效地影响你的游戏运行期性能。

浏览器具有两个主要内容:JavaScript VM以及DOM/布局引擎(通常便是WebKit)。为了优化再加载时间,WebKit具有一个源自网络的资源缓存,从而让他们在之后发出请求时无需再次加载资源(或从磁盘高速缓存中加载)。

为了有效地使用缓存,WebKit可以检测到什么时候系统是受到资源的驱使(游戏邦注:如当可用内存较低时),并将启动缓存收回过程而删除那些不需要的资源,从而更好地完善性能。

对于那些拥有少数DOM元素的应用来说,缓存收回过程并不是问题所在,但是当缓存对象数量提高时,它便是问题所在了,因为垃圾回收法则将花费更多时间去寻找无用对象进行回收。

对于基于图像的2D游戏,这便会成为一个相当严重的问题,例如当2千多个动画精灵和背景纹理分别被加载或引用于一个浏览器上时。

对于这些应用来说,图像通常都是最大的凶手,不过纹理集却能够有效地惩治这一凶手:通过将个体图像整合到更大的纹理集中,独特的可缓存资源的数量将会减少,从而让WebKit在资源有限的环境下可以花更少时间于缓存回收过程中。

(需要明确的是,这里所说的WebKit的缓存行为与JavaScript的垃圾回收法则和你在处理JavaScript对象时所遇到的问题并没有任何关系。)

减少GPU开销

在Chrome中,页面上的2D帆布元素将能够帮助硬件进行加速。这也就意味着GPU将处理你的所有图像和转化,而这将能够有效地完善性能。缓存是存在于你的API调用和硬件执行间的一些抽象层面。

它一直都是以减少图像应用领域的改变,并提高性能这一功效而出名。

这与使用硬件加速帆布没有多大区别。每个纹理都必须在绘制出几何体图像前与GPU连接在一起,并且特定的GPU都必须拥有一个特定数量的纹理槽,所以在抽样单位间进行纹理转化将需要花费大量的时间。

而纹理集则能够通过让GPU将一个单一纹理(或少量的纹理)绑定在图像驱动器上而减少转化所需要的额外开销。

减少内存占用空间

除了能够直接提供像素数据(如.RAW文件)以外,大多数格式文件可以与附加的标头数据(需要被转化成文件)共同用于游戏开发。这里所说的数据并不受到文件规格或内部数据类型的限制。

举个例子来说吧,DDS文件主要是用于压缩纹理的DirectX执行,其一般费用是根据标头文件中的每128个字节收取,也许这听起来不是什么大数目,但是将其与内部数据进行比较时,结果便不是这样了。如果是2千份结构分散的文件(对于AAA级游戏来说并不罕见),我们便需要多投入250k的开销去实现线路传输(在用户加载游戏的时候)。

而将这些纹理整合进一个单一的纹理集将能够删除标头字节所需要的开销,从而减少你的程序的整体规模。

将图表并进纹理集中

charts(from gamasutra)

charts(from gamasutra)

创建纹理集是一件非常棘手的工程任务。纹理包装其实类似于装箱问题,而这也被证明是NP难度的问题。这个问题非常具有挑战性,所以我总是会在招聘时将其作为考核应聘者技能的标准。

TexturePacker便是一种非常棒的现成工具,能够快速地适应于大多数内容管线中;它将生成纹理集数据,从而提供一整列的纹理,并伴随着能够将个体资源图像映射在纹理集的最终位置上的数据文件。

如果你想要使用你自己的纹理包装机,我建议你先进行一定的搜索。以下便是一些不错的起点:

“A Thousand Ways to Pack the Bin – A Practical Approach to Two-Dimensional Rectangle Bin Packing”(pdf文件);附有完整的源代码。

我记得Blackpawn.com有第一个开放式bin包装法则;并且现在已经有一个很棒的实时JavaScript运行例子,你便可以清楚地看到运转中的法则。

如果你想要采用更复杂的包装(如字体字型包装),那么大多数字体光栅化库将带有纹理集包装机。Freetype-gl便拥有一个最棒的包装机(我所认为的),它主要是使用Rectangle Bin Pack法则。

当你的数据被整合进图表中时,你将输出列有纹理集中每个叶子图像(个体图像)位置的映射文件。

在你的游戏中使用纹理集

HTML5游戏中的纹理集的使用也会因为不同游戏类型而有所不同:

单纯的DOM游戏可以使用CSS精灵图表,并在加载了纹理数据文件后将CSS属性设置在DOM图像上。

帆布游戏可以使用canvas.drawImage API,在帆布位置上画出一张图像的小部分。

WebGL游戏能够修改顶点的UV坐标,并使用标准化的浮点值去表明更大图像的一小部分内容。

关于HTML5游戏开发的一些观点

浏览器是一种非常惊人的程序:它们能够提取网页加载和渲染中最深层次的复杂性,并将引擎罩内装置隐藏在一般网页开发者所看不到的位置。而对于具有更高性能的网页游戏来说,这并不是一种理想的情况。

提取层的存在让开发者不清楚该如何整合数据才能有效地提高性能,从而导致开销的提高。与面对大多数开发平台一样,为了提高性能,开发者就需要真正理解事情的进展,并明确在开发实时应用时该使用哪些工具。

退一步来看的话,HTML5游戏还是具有很大的发展前景。对于开发者来说其较快的迭代速度以及在互联网上部署内容的能力都非常有帮助。但是除此之外开发者还必须基于一个适当的视角去看待HTML5游戏开发。

与主机,手机和桌面游戏开发一样,开发HTML5游戏也必须警惕各种陷阱。而游戏开发者就需要在开发过程中不断探索,努力找到各种复杂问题的解决方法。

本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

Building an HTML5 game? Don’t shrug off atlases

By Colt McAnlis

HTML5 is an amazing technology for designing web sites. The general flexibility of HTML5 markup and JavaScript often leads web developers to create their content using individual image elements.

This approach works well for small sites with low overhead, but for games or other high-load websites, using droves of single image elements leads to long load times and slow performance, resulting in a poor end-user experience.

In an ecosystem where three seconds may cause you to lose half your users, it’s important to use the proper tool to address this issue: texture atlasing.

What is texture atlasing?

In real-time graphics, a texture atlas is a single large image that contains many smaller sub-images, each of which is referenced independently. Texture atlases sprang up with the advent of 3D games, and have been around as long as we’ve had such games (for instance, here’s one of the original lightmaps used in Quake 2).

Atlasing was originally used to accelerate the performance of 3D games, where there is significant overhead when swapping out or referencing new textures during the rasterization stages of a 3D pipeline. By combining all the smaller textures into one larger one, the graphics pipeline incurs less overhead from swapping, resulting in better performance.

A texture atlas from the HTML5 game GRITS. This single texture holds all the smaller images from the game that contain alpha values. Loading these images in a single texture reduces load time and improves runtime performance.

A side note on terminology: Many of you fancy HTML5 folks out there might say that a texture atlas is the same thing as a sprite sheet, but I don’t believe that’s correct. A sprite sheet is an atlas that contains only sprites.

In contrast, an atlas can contain many charts; each chart is a large image that holds a single type of graphic resource – e.g., sprites (for animation), UI textures (which are not animated), glyphs (for font-processing), and so on. An atlas is a technical concept, while a sprite sheet has a specific functional notion.

The benefits of using atlases in HTML5 games

Using a texture atlas in your HTML5 game can help your game load faster and run faster, and also reduces your bandwidth cost.

Faster HTTP load times

As I explained in my Google I/O talk, a 4k x 4k texture fetched from a server can take around 241ms to download, which is pretty fast. If you were to chop up that single download into 4096 separate requests, at 64×64 pixels each, the total load time changes drastically for the same number of pixels: It would increase from 241 ms to  4.3 seconds, an increase by a factor of 17x.

The timing charts below illustrate this difference graphically:

Fetching a 4096×4096 texture from a server requires a single HTTP request, and the request resolves quickly.

Fetching assets individually with multiple HTTP requests takes a long time. The long, lighter-shaded left-hand portion of the duration bars represent time where the browser blocked the connection because too many requests were being issued.

It’s important to understand that there exists an upper limit on the number of requests that a browser can make to a single server. The browser itself sets this upper limit, and when the limit is reached, the browser blocks subsequent requests until an open connection becomes available.

This is the primary reason for the performance difference we see with individual assets versus atlased assets. If you have 4,000 pending HTTP requests, and only six connections are available, all the requests get stacked. In the figure above, the lighter-shaded portion of each duration bar is where Chrome blocked, waiting for an active connection to come along.

In addition to reducing overall load time, atlasing also helps reduce the number of HTTP requests from your app. This is a hyper-critical issue that the developers of HTML5 version of Command & Conquer found out the hard way: During development their app went viral, and their hosting service suspended their account until their request load dropped.

Reduced browser runtime overhead

Using individual texture assets can also have a very large impact on your game’s runtime performance.

Web browsers have two main components: a JavaScript VM and a DOM/Layout engine (usually WebKit).  To optimize reload times, WebKit keeps an in-memory cache of resources that are loaded from the network, so that the resources do not have to be re-downloaded (or reloaded from the disk cache) when they are requested in the future.

For example, when a DOM element such as an image is deleted, the garbage collection process in WebKit can optionally keep the element in memory even though the JavaScript engine no longer needs it.

To use the cache properly, WebKit can detect when the system is resource-constrained (i.e., when the amount of available RAM is low), and instantiate a cache-eviction process to remove unneeded resources in an effort to improve performance.

For applications with a small number of DOM elements, the cache-eviction process is generally not an issue, but it can become a problem as the number of cacheable objects increases and the garbage collection algorithm spends more time looking for dead objects to reclaim.

For 2D, image-based games this can be a considerable problem, e.g.,  when 2,000+ animated sprites and background textures are loaded and referenced individually in a browser.

For these apps, images are generally the biggest offenders, and atlasing can really help: By combining the individual images into larger atlases, the number of unique cacheable resources decreases, allowing WebKit to spend less time in its cache-eviction process in resource-constrained environments.

(To be clear, the WebKit in-memory caching behavior described here has nothing to do with the JavaScript garbage collection algorithm and the problems you can get from incorrectly managing JavaScript objects.)

Reduced GPU overhead

In Chrome, the 2D canvas element on a page has access to hardware acceleration if it’s available. That means all of your draws, images, and transforms are handled by the GPU, which significantly improves performance. The catch, however, is that there are a few abstraction layers between your API calls and what the hardware is doing.

It’s been known for quite some time that reducing state changes in your graphics application yields higher performance (see for example Chapter 3 in this old NVIDIA GPU Programming Guide (pdf)).

Working with a hardware-accelerated canvas is no different. Each texture must be bound to the GPU before the primitive quad can be drawn, and with a limited number of texture slots for a given GPU, there’s quite a bit of time spent swapping textures in and out of the proper sampling units.

Atlasing reduces this overhead by allowing the GPU to bind a single texture (or a smaller number of textures) to the graphics driver, eliminating the extra overhead of the swap.

Reduced Memory footprint

With the exception of dumping the pixel data directly (ie, a .RAW file) most file formats used for game development come with additional header data that needs to be transferred with the file. This data is there, regardless of the dimensions of the file, or type of data inside it.

For instance, a DDS file, typically used for DirectX implementations of compressed textures, has a general overhead of 128 bytes for the header, which may not seem huge when compared to the data within it; However consider that 2000 loose texture files (not uncommon for a AAA game) would incur an extra overhead of 250k that needs to be transferred across the wire for each user loading your game.

Packing these textures into a single atlas removes the redundant overhead of these header bytes, reducing the overall size of your program.

Packing charts into an atlas

Creating a texture atlas is a tricky engineering task. Texture packing is a type of bin packing problem, which has been proven to be NP-hard. The problem is so challenging that I frequently use a variant of this algorithm as an interview question to evaluate the skills of potential hires.

TexturePacker is a great off-the-shelf tool that fits into most content pipelines quickly; it will generate the atlas data given a list of textures, alongside a data file that maps the individual source images to their final locations in the atlas.

If you’re going to roll your own texture packer, I suggest you begin with some research. Here are some great places to start:

A Thousand Ways to Pack the Bin – A Practical Approach to Two-Dimensional Rectangle Bin Packing (pdf) (pdf); comes with full source code

Blackpawn.com posted one of the first open bin-packing algorithm I remember seeing; there’s now a great JavaScript example that runs in real time, so you can see the algorithm in motion

If you’re looking to do highly complex packing (e.g., font glyph packing), most font rasterization libraries come with very aggressive atlas packers. Freetype-gl has one of the best ones I’ve seen; it uses the Rectangle Bin Pack algorithm.

Once your data has been packed into charts, you’ll also need to output a mapping file that lists where each leaf-image (individual image) is in the atlas.

Using atlases in your game

Use of atlases in HTML5 games varies by game type:

A pure DOM game can use a CSS sprite sheet, and set CSS properties on a DOM image after loading the atlas data file.

A canvas game can use the canvas.drawImage API, specifying a subsection of an image to be drawn to the canvas location.

A WebGL game can modify the UV-Coordinates of a vertex to reference a subsection of a larger image using normalized floating point values.

Some perspective on game development in HTML5

Web browsers are amazing programs: They abstract out the underlying complexities of loading and rendering web pages, hiding the under-the-hood machinery from the average web developer. For high-performance web games, however, this is less than ideal.

The layers of abstraction make it generally unclear how to organize data for maximum performance, and can lead to increased overhead. As with most development platforms, the key to high performance is a thoughtful understanding of what’s really going on, and knowing what tools are available to aid in developing real-time applications.

Stepping back a bit, it’s clear that HTML5 games have great promise. The ability to iterate quickly and deploy content across the Internet is a powerful incentive for developers. But it’s important to approach HTML5 game development with the proper perspective.

Regardless of the unicorn dust of the web, working in HTML5 has many of the same pitfalls as console, mobile, and desktop development. Game developers need to continually explore and find great solutions for difficult problems.(source:GAMASUTRA)


上一篇:

下一篇: