如何在WebGL开发中保护美术资源(以及为什么你不需要保护它们)

今天早上我收到了一封来自于一个WebGL开发者的E-mail,其中描述的一个窘境我想大多数其他的开发者不久也会遇到:

“对于在WebGL中保护美术资源你有没有什么经验啊?我写了一个快餐方案来保护纹理资源,就是在传输中用base64编码,在服务器端合并,然后在客户端分割再建立图片。但是,使用WebGL Inspector仍然可以获取到所有传输到显卡端的纹理。我想请问你有没有什么好的方法可以阻止用户窃取美术资源呢?就我本人来讲,我不介意对我的应用进行开源,但关键是我的客户很介意。”

额,最后一句话的确是一个致命的问题。我认识的大多数开发者都不介意与他人共享他们的作品和成果,因为我们越来越觉得代码里很少有什么非得隐藏不可的惊天秘密;但是以此说服你的老板,那就是另外一回事了。所以,当公司老板要求“保护我们的资源”的时候,开发者该怎么办呢?

我觉得有两个办法。

  1. 尽量说服你的老板其实不需要保护那些资源;
  2. 参考第一条。

如果这两个办法都失败了,那么只能尽可能的混淆模糊那些资源了。

为什么美术资源不需要保护?不保护美术资源能给你带来哪些好处?

首先,你也许并不需要保护你的美术资源。我知道一些人肯定会说:“你不知道我们的具体情况!我们的美术资源很特殊!”但是,请听我说完。

大多数情况下我们听到内容保护(DRM,即数字版权加密保护技术)都是和电影或者音乐相关的。这是有他们的道理的。因为在这里电影和音乐本身就是一种产品。但是这个理由是否可以同样套用在WebGL应用中的美术资源身上呢?那么,让我们来精确地分析下你想要提供的产品到底是什么?

比如说E-mail中提到的纹理贴图,让我们拿下面这张纹理来举个例子。

这是一张在游戏《军团要塞2》中使用的木质纹理贴图。你也许会不以为然,但是我觉得这张纹理图片的的确确是一个艺术品。即使作为一张独立的图片,它也有它的内在价值。你可以想象一下,多少个天才的美术设计师为了制作这张纹理贴图而付出了他们血汗甚至泪水。当我们评估一个美术资源的价值的时候不能只看资源本身,还要考虑这个美术资源所在的游戏场景和关卡。如果没有了这张纹理贴图,那么那个关卡和场景肯定会大不同。所以对于消费者来说,纹理图片的价值仅仅体现在它是作为场景的一部分的。

那么就是说整个场景都应该被保护了,对吧?

也许吧,但是实际上消费者觉得这些静态场景有多少价值呢?有多少玩家会闲着没事在整个场景里瞎转悠并以此作为主要的快乐源泉?当然,也许在某些行业这的确是产品的主体功能,比如在AutoDesk Cloud中就全都是静态场景,这些场景对于类似建筑设计师的人来说是非常有价值的。但是在游戏中呢?

事实就是,尽管这些场景做得再漂亮,也没人去为了能够在场景里瞎转悠而掏钱买游戏的!相反,让人们掏出钱包的主要动力在于可以拿枪朝着其他人脑袋上啪啪啪地射击!这才是真正的“产品”的意义所在。玩家才不管什么美术资源呢,他们在乎的是整个游戏体验。美术资源只是整个体验中的一个部分,尽管是占了大部分,但也还是一个更大的黑洞中的一部分。这个黑洞中还包括多人在线的服务器、成就系统、付费装备商店、好友列表等等。这些才是真正需要保护的!而且,它们很好保护。

玩家们付费,作为回报可以在他们自己的电脑上安装游戏。所以这就很容易混淆所谓产品的具体所指,很多人认为游戏内容本身就是产品,但实际上它们不是,“能够玩到游戏的能力和权利”才是产品!换一句话说,游戏发行商出售的是服务而不是内容!所以,你也一样。

以上就是在HTML5圈子里不断被重复并加以传播的主旨思想,在大部分情况下我都完全同意。但它也不是永远正确的,不过适用的场合绝对要比你想的要多的多。它完全归结于你能够为你的消费者提供哪些独一无二不可复制的东西?

在现实中,不管你如何拼命去阻止,终端用户总是有办法提取出那些你认为是心肝宝贝的内置资源的,并且根据他们自己的目的加以复用。我们不能单纯依靠用户拖动鼠标点击接受用户协议来过活,我们需要看看我们还能提供哪些东西去填充整个游戏体验,使得用户不是仅仅把游戏载入到硬盘上就算完事。多人在线显然是一个很好的选择,但是在单机游戏甚至一些非游戏类的应用里都还有很多值得借鉴的地方。比如交流社区、整合社会化网络、成就系统、实时数据反馈……这个依赖于服务器端行为来填充整体体验的清单还可以一直写下去。进一步讲,作为Web App的开发者,我们更应该从第一天起就努力整合这些在线特性。否则,你干嘛还要把web作为你的产品平台呢?

也许你还是有把你的美术资源看作是“圣洁女神,神圣不可侵犯”的想法,是吗?E-mail的作者还写道:

“有了Firebug,一个三岁的小屁孩都能下载到纹理贴图,然后在我的游戏人物脸上画个鸡巴,然后上传到什么地方。”

对于有这个问题和为此担忧的人,我要说,你的问题实在是太美妙啦!如果有用户因为觉得你的产品有趣而去修改、制作Mod,那你应该爽死才对!诚然,没人希望在优酷上看到自己游戏中的角色被玩家在脸上画上生殖器官,但是这实际上又能对你造成多大损失呢?做这种恶意修改的人是不会把修改的内容传播给其他人的,即使传播了也没人会下载用到自己的游戏体验中,除非他们真的是喜欢作为一个“大象”进入到游戏世界中。玩家们就是笑笑而已,笑过之后就会忘记,而且——最重要的是——他们使用了你的产品!这不正是你一开始开发产品的初心吗?

从开始到现在,几乎所有成功的游戏大作都可以很轻松地被修改。比如Doom、Quake、上古卷轴、虚幻系列、半条命等等,这些游戏如此长久的生命力都紧密依赖于Mod制作社区。比如当下流行的Minecraft,如果制作公司对纹理贴图严加看管,那还会像现在这么受人欢迎吗?人们总是喜欢去创造属于他们自己的独一无二的东西,喜欢以此推动前沿技术去实现这些可能性,这是人类的天性。这同样也应该是所有开发人员的梦想。

所以,归结成一句话:要向用户提供服务来实现自身价值,而不是提供内容本身;另外别害怕那些想要修改你产品的用户,往往他们才是你最忠诚的粉丝!

说的好!但这毫无意义!我的老板还是不同意,怎么办?

那么现在来解答一下你的疑问,并且假设你的产品内容真的真的非常特殊,非常需要保护起来。其实有很多方法可以增加第三方获取你的产品内容的难度。请注意我说的是“增加难度”,而不是完全杜绝,这是因为现实很简单也很残酷,如果你的产品内容可以显示在用户显示器上,那么它一定可以被复制。这只是个时间问题。你所能做的就是尽量在其中为那些偷窃者制造麻烦,打击他们的信心。

第一种保护你的产品内容的方法就是——我很讨厌说这个——就是不要使用WebGL。伙计们,让我们睁开眼面对现实吧,作为一向开放的web标准,它本身就是被设计成用来传播内容的,而不是私藏内容。你想要使用开放标准,同时又完美的嵌入内容保护机制,这根本就和当失足妇女还要立牌坊是一样的行为。

内容保护本质上是属于私有制的一种东西,所以你最好的方案是使用那些带插件的开发框架,比如Flash、Silverlight或者Unity。它们是一开始就被设计成要保障产品内容安全性的,如果内容保护是你的头等大事,那么使用这些工具开发会很简单。

如果“插件”这个词在你或者你的老板眼中和脏话没什么区别的话,那么你也许可以试试Google的Native Client。科普一下,Native Client(或者叫做NaCl,Google喜欢这么称呼它)允许开发者在沙盘环境中向用户提供编译过的二进制内容。因为你所要面对的是本地的、编译过的代码,所以有很多低俗的小技巧可以让你确保内容安全。但是目前来说,这样做的缺点是NaCl程序只能运行在Google的浏览器中。不过,如果你这么关心保护你的内容不被剽窃,那么在你眼中要求用户必须使用指定的浏览器也不会是什么大事。

但是,也许你就是那种吃着碗里还瞧着锅里的人?你想要拥抱开放的web标准,同时又要阻止任何人提取你的宝贝资源……

首先,你要尽量的让所有的代码变的非常难读。当然不是开发时候用的原始代码文件,而是那些要放在公共服务器上的代码。Closure编译器在这方面做的很好,有时候碰巧了它还能提高你的代码运行速度!

另外一种可能性,你可以发明一种独有的格式来储存所有资源。但是,私有的网格或纹理格式永远都比标准的Collada文件或PNG格式更加难以读取。

让我们再来详细说下纹理的问题。你可以随心所欲的加密你的纹理,但是终有一刻你要解密纹理并传递给什么东西,这样才能显示在屏幕上,对吧?我的主意就是尽量可能地拖延最终解密的这一步骤,以此避免类似于Firebug或WebGL Inspector的实时工具可以正常显示这些纹理。为了达到这一目的,你或许可以在着色器解压方面做出很多创新。Ben Adams写了一篇很棒的文章,讲解了如何在着色器中手动解压纹理,从中你会得到很多启发。基本的原理就是,让片元着色器(也就是我们需要混淆模糊的地方)在即将写屏幕时去用数学方法解密或解压纹理。显然,这样做会付出性能损耗的代价,但是我再次不惮以最坏的恶意揣测你,如果你真的这么在乎内容保护,那么性能损耗你同样也应该看不上眼。

类似地,你可以同样在顶点着色器中解密顶点数组,如果你同样担心模型也被窃取的话,不过它们本身就很难读懂。

另一个可以帮助你阻止纹理图片被这么容易地获取的方法是,不要使用HTML图片对象作为纹理源。你可以直接向GPU传递类型化数组(Typed Array)作为纹理源,只要你可以提供纹理数据的尺寸和格式就行。我经常使用这种方法来创建单一颜色的纹理。

1
2
3
4
5
6
7
8
9
function createSolidTexture(gl, r, g, b) {
var data = new Uint8Array([r, g, b]);
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, data);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
return texture;
}

这就是说,你可以把纹理当成是不透明的二进制数据来请求,通过AJAX调用而不是Image对象。这种方法可以有效地拦截Chrome的开发者工具,但是还是会被WebGL Inspector提取出来,所以还是需要尽量加密纹理数据来实现最大程度的混淆和模糊。

好了,说完了。我觉得你还是很不幸,尽管我确实说了这么多,但是还是没有任何一种方法可以完全阻止别人跟踪你的代码、研究出算法、截取数据流、运行同样的解密处理,以及重新构建源文件。但是正如一开始我说过的,这些方法只能制造麻烦,而不能绝对的杜绝。

如果你真的碰上了这么个顽固的用户,费尽心思地逆向工程你的应用只是为了获得下面的纹理贴图或其他什么内容,那么我想请你认真的想一下为什么一开始要阻止他呢?


原文作者:Brandon Jones
原文地址:http://blog.tojicode.com/2011/12/protecting-webgl-content-and-why-you.html
HiWebGL翻译整理,转载请注明出处

分享到: 更多
Posted in 博文精选. Bookmark the permalink.

3 comments

  1. 站长写得真是非常好,我要让我的冤家也看一看这篇乏味的文章内容.

    回复

  2. 嗨,这篇文章内容本人以为很是有意思,叨教博主可以让我转走吗?我会保留原文来由的链接和你的姓名.

    回复

  3. 写得十分独到,把核心的思想都表白了进去。

    回复

Post a Comment

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>