Away3D 的官网有丰富的教程,然而美中不足的是没有中文版,我的英语水平不高,因此在看教程的时候相当艰难,这促使了我发布这篇译文,希望能帮助到同样在学习 Away3D 的朋友们。
因为我对 3D 知识的了解还很粗浅,所以文中有可能存在术语及概念的错误,朋友们可以提出来,我会及时改正。
文中“(*)”表示我不确定的语句或段落,如有好的翻译建议,还望朋友们不吝赐教。
以下是译文:
本教程将引导你一步步创建一个逼真的地球场景,侧重于介绍 Away3D 4.x 版本的材质和着色器。
内容:
- 介绍
- 设置场景
- 丰富场景
- 照明
- 额外的贴图
- 菲涅尔镜面法
- 添加云层
- 添加大气
- 更高级的大气
- 后续处理
- 结论
介绍
Away3D 是一个功能强大的 3D 引擎,它能够将我们从强大但低级的 Stage3D API 中抽离出来。只要几行代码,我们就可以实现高级的场景,而让引擎处理其余的事情。因此这是一篇高级材质的使用案例。在本教程中,我们将快速的导览 Away3D 的材质系统,并尝试模拟一个场景描绘我们荡漾在太空中的美丽地球。我们将从简单的几何图形和材质开始,并逐渐涉及到引擎强大的材质系统。现在,让我们开始吧,旅程愉快!
设置场景
首先,设置场景的几何形状。我们将用球来创建地球和月亮,为了能够模拟轴和轨道的旋转需要对它们进行适当的组合,并为它们贴上基本的材质。清单1给出了我们的基本设置:
清单1。基本的地球场景加上组合变换和非渐变(*non-lit)纹理材质。
我们已经对每一个天体重复了相同的步骤。现在,我们已经创建了包含一个纹理和一个几何图形的 mesh 对象,把它加入到容器中,再把容器加入到场景中。让我们快速的回顾这些步骤。
材质:
嵌入 jpg 图片并使用 Away3D 的 Cast 工具类创建 BitmapTexture
对象。请注意,这些都不是常规的 Flash BitmapData
对象,而是 Away3D 最终会通过 Stage3D API 上传到 GPU 的特殊 GPU 图形对象。接下来,这些对象将在 TextureMaterial 内部使用,TextureMaterial 是引擎的主角,可以用于任何材质纹理的着色(* which is the engine’s main player in any material that makes use of textures for shading PS:长句就是我的噩梦啊,翻译不能~)。这个例子中,我们仅仅用到强大材质系统中的一小部分,很快我们就会用到更多。
1 var moonSurfaceTexture:BitmapTexture = Cast.bitmapTexture( MoonSurfaceDiffuse ); 2 var moonSurfaceMaterial:TextureMaterial = new TextureMaterial( moonSurfaceTexture );
几何图形:
由 SphereGeometry
对象表示天体。几何图形主要是一个缓冲持有人,跟踪潜在的大量三角形。例如,它可以代表一个平面,在这个例子中代表球体,圆环面或者甚至更真实复杂的对象,比如拥有丰富细节的汽车或头部形状(* avatar shape)。Away3D 提供了一个 primitive
几何图形的基本设置,在本例中,我们使用具有相当细节程度的球体,拥有很高的多边形数量和分段以使我们可以看到完美丰满的天体。
1 var moonSurfaceGeometry:SphereGeometry = new SphereGeometry( 50, 100, 50 );
网格:
几何图形与材质相结合覆盖于网格对象,网格对象集合了实际渲染所必需的功能。几何图形决定了对象的形状,材质决定了对象的着色,或者说决定了对象如何绘制到屏幕上。网格也包装其他对象,比如 transforms。这是另一个 3D 渲染管线的重要组成部分,它允许我们定位,缩放和旋转场景中的对象。请注意我们是如何改变月球的 x 坐标的。
1 var moonSurfaceMesh:Mesh = new Mesh( moonSurfaceGeometry, moonSurfaceMaterial );
容器:
另一个拥有变换的是
ObjectContainer3D 对象。它也能包含子元素。在我们的例子中,我们将对象放置到容器内,这样就很容易对它们执行组合和变换。举个栗子,通过置换容器内月球的位置,我们只需通过旋转它的父容器就可以模拟它的轨道运动。以下是最终要加入到场景中的对象。
1 _moon = new ObjectContainer3D(); 2 _moon.rotationY = rand( 0, 360 ); 3 _view.scene.addChild( _moon ); 4 moonSurfaceMesh.x = 1000; 5 _moon.addChild( moonSurfaceMesh );
丰富场景:
我们的场景看起来还是很空,还有很多方面需要改进,我们可以将重点放在使它看起来更好些。我们可以改进对象的材质,并且我们会这么做,但我们会在场景中加入更多元素来充实它,让它看起来养眼些。我们会加入太阳,一些星星,和宇宙的图形到我们的场景中。让我们先看看结果,然后分析我们做了什么。
清单2。基本的场景,Sprite3D 表示的太阳和星星,SkyBox 表示的宇宙。
我们已经较上一个清单压缩了代码。随着教程的推进,我们会涉及越来越多的代码,所以我们会将之前学过的元素移出我们当前的主要焦点。我们的清单有3个函数,一个是创建太阳,另一个是创建星空,再一个是用天空盒创建宇宙的图形。
Sprite3D:
太阳和星星使用 Sprite3D
代替 Mesh。这些对象不需要真正的几何形状,但仍然拥有材质和变换等等。它们的几何图形实际上是一个总是面向摄像机的平面。在 3D 世界中,它们通常被称为 billboards,这是一种理想的方式来表现遥远的物体或粒子。它们能够用一个附着图像的平面“伪装”,而这正是我们在这里所做的。Sprite3D 与网格相比,仅使用较少的三角形,因此很划算。
注意新出现的函数 blackToTransparent()。这是一个简单的工具函,它数能够改变我们用在 billboards 上的图像的黑色背景为透明。我们这样做的方法是创建一个新的
BitmapData 对象并且拷贝图像上红色通道里的所有像素(任何不是黑色的像素)到透明通道。其实我们可以使用任何其它通道,因为这些图像基本上不是黑的就是白的。一旦我们有这样的透明图像,设置材质的
alphaBlending
属性为 true,将会让渲染管道知道这个对象的纹理要与其之后的任何对象做 alpha-blended。
1 var bitmapData:BitmapData = blackToTransparent( Cast.bitmapData( StarTexture ) ); 2 var starMaterial:TextureMaterial = new TextureMaterial( new BitmapTexture( bitmapData ) ); 3 starMaterial.alphaBlending = true;
星空使用循环创建 billboard 布置,生成随机球面坐标,计算出相应的直角坐标系,并分配给每个星星的位置。我们在这里使用球面坐标是因为它们往往更容易在球状分布里布局东西。你可以在这里阅读更多关于此系统及其用途。
这份清单中我们没有多大改变,只是增加了一个点光源,调整了一些设置和材质,以及关联材质的 lightPicker 属性。我们很快就会详细介绍这个属性。Away3D 中有不同类型的光线,PointLight 是其中之一。这代表一个光源从空间中的一个点向各个方向发出光线,还有其他类型的光线,以 DirectionalLight 为例,它的光线由非特定点的单方向发出。不同类型的光线产生不同的效果。在我们的例子中,我们使用点光源,并把它放在和我们的太阳精灵相同的位置。我们也把这个光线放进一个数组并分配给我们清单中的 StaticLightPicker。lightPicker 只是一个用于组合和收集场景中的光线的对象。光线拾取器被连续地分配给每一个我们想要照亮的对象。(* Consecutively, this light picker is assigned to each of the materials that we want illuminated.)在本例中,我们已经把它应用到纹理材质上,但它同样可以应用与其他类型的材质,比如颜色材质。在引擎内部,Away3D 将产生一个 phong 着色模型照亮受影响的对象。
Phong 着色
Stage3D 使用一个称为 AGAL 的相当复杂的着色语言。它看上去很像汇编,并且很难编写。它使我们能够编写可以被上传至GPU的着色器程序和几何形状,纹理,变换等,以及定义如何绘制内容到屏幕。幸运的是,Away3D 封装了所有这些复杂的东西到一个直观的材质系统,我们可以通过简单地操作参数和属性来控制着色模型。现在,我们已经接触了光源的环境,漫反射、反射值和材质的光泽度值。环境属性的光从非特定起源或方向照亮对象,模拟场景没有专门针对阴影的随机反射。在本例中,我们将它设置为 1 以便我们的天体的暗面并不完全在黑暗中。漫反射属性影响几何图形被照亮时,法线上的入射光的数量。我们将它设置为2夸大明亮的地方和黑暗的地方之间的区别。光的镜面属性会影响有多少反射亮点(根据光的入射方向),表面法线和视角照亮表面。最后,材质的光泽度属性决定反射高光如何扩散。为每一个属性尝试不同的值,你会更好地理解每一个属性的作用。或者如果你想要深入着色处理梦幻般的世界,那么 GLSL 而非 AGAL ,会是一个很好的开始,概念是什么并不重要。( * but the concepts are what matter. 这句话,我完全没搞明白啊)
要注意一件事,有时候材质,作为光,会共用一些环境,漫反射和镜面属性。所以你会得到在着色器中结合后的这些值(* These values get combined in the shader so you can have),不同的环境照亮一组受同一光源影响的对象。
额外的贴图
照明改善了我们的天体的外观了不是?然而我们仍然还有很多可以做的来加强它们。TextureMaterial 接受多个纹理或贴图。事实上,我们将应用比现在多 3 个以上的贴图;法线贴图,高光贴图和环境贴图。以下包括我们已经使用的贴图:
图1。多个贴图着色于球体。按顺序,漫反射贴图,法线贴图,高光贴图和环境贴图。
TextureMaterial
能为这些贴图使用更为高级的照明。让我们从高光贴图开始,它是最容易理解的。这个贴图只是作为材质反射高光的一种遮罩。贴图白色部分,允许反射高光存在,反之黑色部分不允许。值由亮到暗衰减。
环境贴图是用来作为一种漫反射贴图表示光线不能到达几何形状的地方(* The ambient map is used as a sort of diffuse map wherever no light reaches the geometry 不知道怎么翻译...)。在本例中,它会“绘制”地球的暗面,因为它不会收到任何来自太阳的光。
法线贴图会复杂一点,通过使用法线映射的技术,产生我们的模型具有较高几何图形数量的错觉。SphereGeometry
的每一个顶点都有法线,着色器用这些法线确定球体的漫反射和高光组件的照明。事实上,着色器内插法线为每个“像素”或每个三角形内的片段产生大量的法线,全指向远离球体的中心。贴图编码法线偏离原来方向的多少依据表示xyz偏移的红绿蓝颜色组件。(* This map encodes how much these normals deviate from their original directions by representing the deviations in x, y and z in the red, green and blue color components. 怎么翻译都很拗口,朋友们有更好的翻译建议吗?)当被照亮时,这给了球体更多照明信息,而实际上没有增加任何三角形。
让我们看看这些贴图一起使用的结果。注意反射高光只发生在海洋地区,星球的暗面如何显示城市的夜景灯光,法线映射如何显现地形的阴影,而实际上却没有增加球体边缘的地形海拔。
以下清单实现所有这些贴图。
清单4。用于地球着色的额外贴图的用法。
如你所见,这些相当高级的着色主题(*shading topics ??)在Away3D中实现起来简单的不可思议。在清单中最显著的变化就是增加了以下几行,只需指定提到的贴图到地球的 TextureMaterial:
1 earthMaterial.normalMap = Cast.bitmapTexture( EarthSurfaceNormals ); 2 earthMaterial.specularMap = Cast.bitmapTexture( EarthSurfaceSpecular ); 3 earthMaterial.ambientTexture = Cast.bitmapTexture( EarthSurfaceNight );
另外,请注意我们已经单独的调整了每个天体的照度值,以实现所需的各个方面。
菲涅尔镜面法
老实说,我们天体的反射高光看起来仍然不是很真实。它们使东西看起来更有趣了,但它们更像台球而不是天体。行星不是那样闪闪发亮的。此外注意当从后面看天体时,能观察到一个我们不想看到的反射高光缩小。让我们改变我们的 TextureMaterial
处理反射高光的方式。事实上,让我们告诉它使用一个完全不同的 specularMethod。
Away3D材质允许你使用函数合成材质。 例如,你可以更改 TextureMaterial 的环境,漫反射和高光函数。你可以认为函数是可互换的着色器代码块,由引擎合成产生完整的着色器程序。这赋予你的着色器惊人的可能性并且简单易用。
为了改进我们的高光,我们将使用 FresnelSpecularMethod。这是一个听起来很复杂但其实很简单照明技术。它的概念是:如果观众俯视表面产生的反射高光就很弱。如果他从与表面一致的位置看向表面的边缘,高光就会很强烈。想象一下,你在晴天看着泳池中的水。如果你站在泳池边直直的往下看,你会看到水面上的一点反光,还能看到池底。如果换成你在泳池里看着池水,背景是太阳,你只能看到一点池底和更多水面上直接从太阳反射的光线。我们的例子中实现了这个,并且产生了微妙的结果,除了消除了我们之前讨论过的非预期的效果以外,还产生了更逼真的模拟。(* Implementing this on our example produces subtle results, but eliminates the desired effects we discussed before, producing a more realistic simulation. 这句话不知道有没翻译对,原句怎么都感觉不对,就按自己的理解翻了...)
清单5。地球及月球表面实现了菲涅尔镜面法。
同样,代码中的实现非常简单。我们只需初始化 FresnelSpecularMethod
对象并将它们分配给地球和月亮的材质。很神奇吧!
1 var earthFresnelSpecularMethod:FresnelSpecularMethod = new FresnelSpecularMethod( true ); 2 earthFresnelSpecularMethod.fresnelPower = 1; 3 earthFresnelSpecularMethod.normalReflectance = 0.1; 4 earthFresnelSpecularMethod.shadingModel = SpecularShadingModel.PHONG; 5 //... 6 earthMaterial.specularMethod = earthFresnelSpecularMethod;
添加云层
地球包含丰富的大气,只要愿意,我们可以模拟更加丰富的效果。我们将首先向地球添加一个稍大一点的,包含云层纹理的透明球体。实现的方式有点儿业余(*naive 天真,幼稚?不知道怎么翻译~),不过却非常有效:
清单6。用一个稍大点的透明球体表示云层。
原文链接:http://away3d.com/tutorials/Globe_Materials_Tutorial