本教程涵盖了利用立方体贴图作为背景的环境映射的渲染。
它基于章节“反射表面”。如果你没有阅读过这章,这会是一个很好的机会来学习它。
在背景渲染天空盒
从东方明珠上看。只要背景是静态的并且足够远,这对于天空盒是一个很好的备选。
正如在章节“反射表面”中说明的,一个天空盒可以被认为是一个无限大的环绕着场景的纹理盒子。有时,天空盒(或者天空穹顶)由足够大的纹理模型来实现,它近似于一个无限大的盒子(或穹顶)。但是,章节“反射表面”介绍了立方体贴图的概念,它实际上表示一个无限大的盒子;于是,我们就不需要一个盒子的近似(什么意思?)或一个有限尺寸的穹顶。相反,我们可以渲染任何屏幕填充的模型(它是否是一个盒子、一个穹顶或一棵苹果树并没什么关系,只要它覆盖了整个背景),在顶点着色器中计算从摄像机到光栅化表面点的观察向量(就像我们在章节“反射表面”中做的一样),以及在片元着色器中用这个观察向量(在章节“反射表面”中会用反射观察向量代替)在立方体纹理中进行查找:
float4 frag(vertexOutput input) : COLOR
{
return texCUBE(_Cube, input.viewDir);
}
为了更好的性能,我们当然应该用一个少数面的模型来渲染,并且每个像素应该只被光栅化一次。因此在一个包含着摄像机(或者整个场景)的立方体内部渲染是很好的。
完整的着色器代码
这个着色器应该被附属到一个材质上,它应该被附属到一个围绕着摄像机的立方体上。在这个着色器代码中,我们用ZWrite Off停止写入深度缓冲,这样就没有物体会被天空盒遮挡。(查阅章节“逐片元操作”中关于深度测试的描述。)使用Cull Front激活前面剔除,这样只有立方体的“内部”才会被光栅化。(查阅章节“裁剪”。){ “Queue” = “Background” }这行标记会通知Unity在渲染其它对象之前渲染该通道(pass)。
Shader "Cg shader for skybox" {
Properties {
_Cube ("Environment Map", Cube) = "" {}
}
SubShader {
Tags { "Queue" = "Background" }
Pass {
ZWrite Off
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// User-specified uniforms
uniform samplerCUBE _Cube;
struct vertexInput {
float4 vertex : POSITION;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float3 viewDir : TEXCOORD1;
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
float4x4 modelMatrix = _Object2World;
output.viewDir = mul(modelMatrix, input.vertex).xyz
- _WorldSpaceCameraPos;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
return output;
}
float4 frag(vertexOutput input) : COLOR
{
return texCUBE(_Cube, input.viewDir);
}
ENDCG
}
}
}
Unity天空盒系统的着色器代码
以上的着色器解释了如何通过使用指定着色器渲染围绕摄像机的立方体来渲染天空盒。这是一个非常通用的办法。但是,Unity有它自己的天空盒系统,它并不需要任何游戏对象:你只需要用在菜单栏Window > Lighting > Scene > Skybox的天空盒着色器指定这个材质,然后Unity就会完成剩下的工作。不幸的是,我们不能使用这个系统的着色器,因为我们必须在由顶点纹理坐标指定的位置处进行立方体纹理的查询。这个实际上比计算观察方向简单。以下是代码:
Shader "Cg shader for Unity-specific skybox" {
Properties {
_Cube ("Environment Map", Cube) = "white" {}
}
SubShader {
Tags { "Queue"="Background" }
Pass {
ZWrite Off
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// User-specified uniforms
samplerCUBE _Cube;
struct vertexInput {
float4 vertex : POSITION;
float3 texcoord : TEXCOORD0;
};
struct vertexOutput {
float4 vertex : SV_POSITION;
float3 texcoord : TEXCOORD0;
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
output.vertex = mul(UNITY_MATRIX_MVP, input.vertex);
output.texcoord = input.texcoord;
return output;
}
fixed4 frag (vertexOutput input) : COLOR
{
return texCUBE (_Cube, input.texcoord);
}
ENDCG
}
}
}
正如上述提到的,我们应该用这个着色器创建一个材质并且把它拖到Window > Lighting > Scene > Skybox上去。这里没有任何必要把这个材质附着到任何游戏对象上。
总结
恭喜,你又完成了另外一章的学习!我们学到了:
- 如何用通过方法渲染天空盒。
- 如何在Unity中不用游戏对象来渲染天空盒。
扩展阅读
如果你想要了解得更多: