在Stage3D中实现一个天空盒

天空盒这货,其实核心技术就是CubeMap. 给一个Cube的6个面,贴上不同的纹理。

最笨的方法是创建6个平面,每个面贴一个单张纹理罢了。
实际上CubeMap 的采样方式也差不多, 只不过是用面法线来代替UV 曲纹理罢了。

最开始查资料的时候,资料说用面法线来取6个面,我以为是是用6个法线来代表6个面, 然后就纠结在如果对应上片段着色器。后来经过试验,才发现网上说的并不细致,作为一个规则形状(立方体,球形),直接归一化的它们的顶点,比如立方体8个顶点生成8个法线,传入片段着色器即可,完全不要被6个面对应的6个法线这些网络上的讲解误导了。 GPU会根据顶点间的法线差值正确的取出对应的 纹理。

在 Flare3D 的Demo中,有6个面在一张纹理上的贴图, 我不知道是否真有如此的API,讲6张图自动的拆开上传。
我的做法是自己创建6个裁剪矩形进行6张图分拆,然后上传

[cc lang=”actionscript”]
private function initClipList() : void
{
m_clipList = new Vector.();
m_clipList.push(new Rectangle(2*m_size,m_size,m_size,m_size));
m_clipList.push(new Rectangle(0,m_size,m_size,m_size));

m_clipList.push(new Rectangle(m_size,0,m_size,m_size));
m_clipList.push(new Rectangle(m_size,2*m_size,m_size,m_size));

m_clipList.push(new Rectangle(m_size,m_size,m_size,m_size));
m_clipList.push(new Rectangle(3*m_size,m_size,m_size,m_size));
}
[/cc]

m_clipList 的分拆顺序,就是CubeMap上传纹理的顺序
如下
[cc lang=”actionscript”]
public static const POSITIVE_X:uint = 0;
public static const NEGATIVE_X:uint = 1;
public static const POSITIVE_Y:uint = 2;
public static const NEGATIVE_Y:uint = 3;
public static const POSITIVE_Z:uint = 4;
public static const NEGATIVE_Z:uint = 5;
[/cc]

对于CubeMap 我们需要人为的上传所有miplevel
层级数量依照纹理大小而定, 如果你的纹理是 515 * 512
那么依次上传的level 是 512*512 -> 256 * 256 -> 128 * 128 -> 64 * 64 – > 32* 32 -> 16 * 16 -> 4 * 4 -> 2 * 2 -> 1* 1
6个面中,每个面都要这么上传。

到此为止,CubeMap 实际上已经是完成了。
如果要做成天空盒,我们还要修改一下渲染的Shader

在游戏中的天空盒一般是不随着玩家移动而缩放的, 给人一种无法到达的感觉。 因此我们可以以 viewPorj 的矩阵来渲染它, 由于没有model矩阵,本质就是天空盒总是以相机的位置为中心,同时接收相机的旋转,但不缩放。

如果要做不接受旋转的天空盒,甚至直接用 proj 矩阵渲染就够了。

是用的Shader如下

顶点着色器
[cc lang=”actionscript3″]
public override function getVertexProgram():ByteArray
{
return new AGALMiniAssembler().assemble(Context3DProgramType.VERTEX,
“m44 op va”+vaPos+” vc”+vcProjection+”n”+
“nrm vt0.xyz va”+vaPos+”.xyzn”+
“mov vt0.w vc”+vertexConstIndex+”.xn”+
“mov v0 vt0n”);
}
[/cc]

第一行是 proj 矩阵 * Cube的顶点
第二行是 归一化个顶点,计算出法线
第三行是 将该法线的w 分量设置为1(不缩放就是靠它了)
第四行是 将法线传给像素着色器去取 CubeMap

像素着色器
[cc lang=”actionscript3″]
public override function getFragmentProgram():ByteArray
{
return new AGALMiniAssembler().assemble(Context3DProgramType.FRAGMENT,
“tex oc v0 fs”+fcTexture+”n”);
}
[/cc]

发表评论

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