作用:
返回一个用于提取反射探头环境贴图CubeMap的向量。
源码:
inline float3 BoxProjectedCubemapDirection (float3 worldRefl, float3 worldPos, float4 cubemapCenter, float4 boxMin, float4 boxMax)
{
// unity内置宏,解析:https://blog.csdn.net/zengjunjie59/article/details/111866881
UNITY_BRANCH
if (cubemapCenter.w > 0.0) // 判断反射探头(Reflection Probe组件)是否勾选了BoxProjection
{
float3 nrdir = normalize(worldRefl);
#if 1
float3 rbmax = (boxMax.xyz - worldPos) / nrdir;
float3 rbmin = (boxMin.xyz - worldPos) / nrdir;
float3 rbminmax = (nrdir > 0.0f) ? rbmax : rbmin;
#else // Optimized version
float3 rbmax = (boxMax.xyz - worldPos);
float3 rbmin = (boxMin.xyz - worldPos);
float3 select = step (float3(0,0,0), nrdir);
float3 rbminmax = lerp (rbmax, rbmin, select);
rbminmax /= nrdir;
#endif
float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
worldPos -= cubemapCenter.xyz;
worldRefl = worldPos + nrdir * fa;
}
return worldRefl;
}
首先看下各个传入参数:
worldRef1:从摄像机出发的视线的反射向量
worldPos:片元世界坐标
cubemapCenter:反射探头坐标
boxMax:反射探头包围盒的最大顶点
boxMin:反射探头包围盒的最小顶点
目标是求得图中的蓝色向量
看一下这一段:
float3 rbmax = (boxMax.xyz - worldPos) / nrdir;
float3 rbmin = (boxMin.xyz - worldPos) / nrdir;
float3 rbminmax = (nrdir > 0.0f) ? rbmax : rbmin;
这三行都是swizzle操作,xyz其实是分开算的。当只看x的时候:
rbmax.x三个分量其实就是nrdir.x到(boxMax-worldPos).x的缩放倍数,当nrdir.x乘以这个倍数的时候,则缩放后的向量的末端则恰好到达包围盒边缘。
rbmin的xyz三个分量其实就是nrdir到(boxMin-worldPos)的缩放倍数
最后一行的float3 rbminmax = (nrdir > 0.0f) ? rbmax : rbmin;则是从两个方向中选出一个正确的方向,明显当nrdir.x为正的时候,缩放后的末端会落到与boxMax邻接的面(包围盒边缘)上,nrdir.x为负时,则会落到与boxMin邻接的面上。
所以这三行代码得到的rbminmax的三个分量与nrdir相乘之后都能让向量末端与包围盒边缘在同一个平面上(不一定在面内),但是其中只有一个分量可以让向量末端在包围盒边缘的面内。下面这行代码就是选出这个值。
float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
最后两行代码:
worldPos -= cubemapCenter.xyz;
worldRefl = worldPos + nrdir * fa;
如下图:第一行得出向量a,第二行的nrdir * fa则是得出向量b,最后的两者想加得出本文章主题函数BoxProjectedCubemapDirection的结果向量c
使用例子:
// 创建间接光
UnityIndirect CreateIndirectLight(Interpolators i, float3 viewDir){
// 间接光数据结构体
UnityIndirect indirectLight;
indirectLight.diffuse = 0;
indirectLight.specular = 0;
// 判断是否启用了顶点光
#if defined(VERTEXLIGHT_ON)
indirectLight.diffuse = i.vertexLightColor;
#endif
// base pass才进行环境光的计算
#if defined(FORWARD_BASE_PASS)
// 球谐光照
indirectLight.diffuse += max(0, ShadeSH9(float4(i.normal, 1)));
// 环境反射
float3 reflectionDir = reflect(-viewDir, i.normal);
Unity_GlossyEnvironmentData envData;
envData.roughness = 1 - _Smoothness;
// unity_SpecCube0_ProbePosition是unity_SpecCube0对应的反射探头坐标, unity_SpecCube0_BoxMin则是探头包围盒的最小端点,unity_SpecCube0_BoxMax是最大端点
envData.reflUVW = BoxProjectedCubemapDirection(reflectionDir, i.worldPos, unity_SpecCube0_ProbePosition, unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);
//Unity_GlossyEnvironment解析:https://blog.csdn.net/zengjunjie59/article/details/111631952
float3 probe0 = Unity_GlossyEnvironment(UNITY_PASS_TEXCUBE(unity_SpecCube0), unity_SpecCube0_HDR, envData);
// 判断目标平台是否支持混合
#if UNITY_SPECCUBE_BLENDING
float interpolator = unity_SpecCube0_BoxMin.w;// unity_SpecCube0_BoxMin.w存着反射探头的权重
UNITY_BRANCH
if(interpolator < 0.99999)
{
envData.reflUVW = BoxProjectedCubemapDirection(reflectionDir, i.worldPos, unity_SpecCube1_ProbePosition, unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax);
float3 probe1 = Unity_GlossyEnvironment(UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1, unity_SpecCube0), unity_SpecCube0_HDR, envData); // 环境贴图用unity_SpecCube1,但采样器用unity_SpecCube0,因为unity_SpecCube1没有采样器
indirectLight.specular = lerp(probe1, probe0, interpolator);
}
else
{
indirectLight.specular = probe0;
}
#else
indirectLight.specular = probe0;
#endif
#endif
return indirectLight;
}