本教程涵盖了半球光照。
它是基于章节“漫反射”中描述的漫射逐顶点光照。如果你还没有阅读过这章,建议你先看一下。
半球照明基本上是用一个覆盖整个半球包围着场景的巨大光源来计算漫射照明的,比如像天空。它通常包括从另一个半球使用不同颜色的照明,因为计算几乎是不用什么代价的。在下面的照片中,这座球形建筑物被阴沉沉的天空照亮了。但是,周围绿色的水池也照亮着它,最终导致了建筑下半部的明显的绿色照明。
半球光照
If the surface normal N points in the direction of U, we have full illumination with a color specified by the user. If there is an angle γ between them (i.e. cos(γ) = U·N), only a spherical wedge (see the Wikipedia article) of the hemisphere illuminates the surface point. The fraction w of this illumination in comparison to the full illumination is:
如果我们假设一个半球在平面某点周围的每一个点(在方向L上)作为一个光源,我们应该把半球上所有点处的漫射光照(由在章节“漫反射”中讨论的max(0, L·N)指定)整合到一起。让我们把半球旋转轴的归一化方向称为U(即”up”)。如果表面法向量N指向U方向,我们就会有用户指定颜色的完全照明。如果它们之间有个角度γ(即cos(γ) = U·N),只有半球的球面楔(参考维基百科的文章)可以解释这个表面点。与完全照明相比,这种照明的积分w是:
于是,我们可以计算入射光为w乘上用户指定的半球完全照明的颜色。在相反方向的半球将会用1-w乘以另外的颜色(如果我们不需要的话,它可能是黑色的)来照亮表面上的点。下面将会解释如何推导出w这个等式。
等式的推导
For anyone interested (and because I didn’t find it on the web) here is a derivation of the equation for w. We integrate the illumination over the hemisphere at distance 1 in a spherical coordinate system attached to the surface point with the direction of N in the direction of the y axis. If N and U point in the same direction, the integral is (apart from a constant color specified by the user):
任何一个人感兴趣的话(因为我在网上还没找到),以下是w这个方程的推导。我们将距离为1的半球上的照明与位于y轴方向上N方向表面点所在的球面坐标系相结合。如果N和U是在相同的方向,积分就是(除了由用户指定的常量颜色外):
sin(θ)项是我们的积分在半径为1的球体表面的雅可比行列式, (x, y, z) 是(cos(φ)sin(θ), sin(φ)sin(θ), cos(θ)),以及N = (0,1,0)。于是,这个积分就变成了:
The constant π will be included in the user-defined color of the maximum illumination. If there is an angle γ with cos(γ) = U·N between N and U, then the integration is only over a spherical wedge (from γ to π):
常量π将包含在最大照明的用户自定义的颜色中。如果N和U之间有一个角度γ ,那么这个积分就只是在球形楔上((从γ到π)):
着色器代码
代码实现是基于章节“漫反射”中的代码。在一个更详细的实现方案中,其它光源的贡献也会被包含进来,比如会使用章节“镜面高光”中提到的Phone反射模型。在那种情况下,半球光明可能会像环境光照一样以相同的方式被包含进来。
但是,这里半球光照就是唯一的照明。w的等式如下:
我们在世界空间中实现这个等式,也就是说我们必须把表面法向量N变换到世界空间中去(参考章节“世界空间中的着色器编程”),而U是由用户在世界空间中指定的。在使用w和1-w基于用户指定的颜色计算照明之前我们会归一化向量和计算w。实际上,这很简单。
Shader "Cg per-vertex hemisphere lighting" {
Properties {
_Color ("Diffuse Material Color", Color) = (1,1,1,1)
_UpperHemisphereColor ("Upper Hemisphere Color", Color)
= (1,1,1,1)
_LowerHemisphereColor ("Lower Hemisphere Color", Color)
= (1,1,1,1)
_UpVector ("Up Vector", Vector) = (0,1,0,0)
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// shader properties specified by users
uniform float4 _Color;
uniform float4 _UpperHemisphereColor;
uniform float4 _LowerHemisphereColor;
uniform float4 _UpVector;
struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 col : COLOR;
//在顶点着色器中计算半球光照
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
float4x4 modelMatrix = _Object2World;
float4x4 modelMatrixInverse = _World2Object;
float3 normalDirection = normalize(
mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
float3 upDirection = normalize(_UpVector);
float w = 0.5 * (1.0 + dot(upDirection, normalDirection));
output.col = (w * _UpperHemisphereColor
+ (1.0 - w) * _LowerHemisphereColor) * _Color;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
return output;
}
float4 frag(vertexOutput input) : COLOR
{
return input.col;
}
ENDCG
}
}
}
总结
恭喜,你又完成了一章!我们看到了:
- 什么是半球光照。
- 半球光照的等式是什么。
- 如何实现一个半球光照。