# Unity中的反射

###### CubeMap采样

Unity提供了Unity_GlossyEnvironment函数来对cubemap进行采样。该函数的实现如下：

``````half3 Unity_GlossyEnvironment (UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_GlossyEnvironmentData glossIn)
{
half perceptualRoughness = glossIn.roughness /* perceptualRoughness */ ;

// TODO: CAUTION: remap from Morten may work only with offline convolution, see impact with runtime convolution!
// For now disabled
#if 0
float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameter
const float fEps = 1.192092896e-07F;        // smallest such that 1.0+FLT_EPSILON != 1.0  (+1e-4h is NOT good here. is visibly very wrong)
float n =  (2.0/max(fEps, m*m))-2.0;        // remap to spec power. See eq. 21 in --> https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdf

n /= 4;                                     // remap from n_dot_h formulatino to n_dot_r. See section "Pre-convolved Cube Maps vs Path Tracers" --> https://s3.amazonaws.com/docs.knaldtech.com/knald/1.0.0/lys_power_drops.html

perceptualRoughness = pow( 2/(n+2), 0.25);      // remap back to square root of real roughness (0.25 include both the sqrt root of the conversion and sqrt for going from roughness to perceptualRoughness)
#else
// MM: came up with a surprisingly close approximation to what the #if 0‘ed out code above does.
perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness);
#endif

half mip = perceptualRoughnessToMipmapLevel(perceptualRoughness);
half3 R = glossIn.reflUVW;
half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, R, mip);

return DecodeHDR(rgbm, hdr);
}
``````

UNITY_ARGS_TEXCUBE宏是一个用来定义cubemap作为函数参数的宏，用于函数的声明，我们在调用函数时需要相应地使用UNITY_PASS_TEXCUBE宏进行cubmap参数传递。hdr参数用于当cubemap中包含hdr颜色时，需要将hdr转换到rgb颜色，一般直接传unity_SpecCube0_HDR即可。Unity_GlossyEnvironmentData是unity定义的一个数据结构，我们需要设置它的roughness和reflUVW属性，roughness就是材质的粗糙程度，越粗糙物体的反射越模糊；reflUVW就是反射向量，用于采样cubemap。我们可以这样调用该函数：

``````float3 reflectionDir = reflect(-viewDir, i.normal);
Unity_GlossyEnvironmentData envData;
envData.roughness = 1 - _Smoothness;
envData.reflUVW = reflectionDir;
float3 specular = Unity_GlossyEnvironment(UNITY_PASS_TEXCUBE(unity_SpecCube0), unity_SpecCube0_HDR, envData);
``````

\[r = 1.7r - 0.7r^2 \]

###### box projection

``````inline float3 BoxProjectedCubemapDirection (float3 worldRefl, float3 worldPos, float4 cubemapCenter, float4 boxMin, float4 boxMax)
{
// Do we have a valid reflection probe?
UNITY_BRANCH
if (cubemapCenter.w > 0.0)
{
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;
}
``````

worldRefl即为世界空间的反射向量，worldPos即为要计算的点的世界坐标，cubemapCenter是反射探针的坐标，boxMin和boxMax是反射探针包围盒的最小最大坐标。我们可以这样调用BoxProjectedCubemapDirection：

``````float3 reflectionDir = reflect(-viewDir, i.normal);
float3 reflUVW = BoxProjectedCubemapDirection(reflectionDir, i.worldPos, unity_SpecCube0_ProbePosition, unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);
``````

``````            float3 rbmax = (boxMax.xyz - worldPos);
float3 rbmin = (boxMin.xyz - worldPos);
``````

``````			float3 nrdir = normalize(worldRefl);
float3 rbmax = (boxMax.xyz - worldPos) / nrdir;
float3 rbmin = (boxMin.xyz - worldPos) / nrdir;
``````

``````			float3 rbminmax = (nrdir > 0.0f) ? rbmax : rbmin;
float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
``````

``````			float3 u = nrdir * fa;
``````

``````			float3 v = cubemapCenter.xyz - worldPos;
``````

``````			float3 w = nrdir * fa + worldPos - cubemapCenter.xyz;
``````
###### 反射探针插值

Unity允许让我们对两个反射探针采样的值进行插值融合，得到一个过渡的效果。Unity提供了UNITY_SPECCUBE_BLENDING宏来判断当前平台是否支持反射探针融合。另外，unity_SpecCube0_BoxMin的w分量存储了第一个反射探针所占的权重，如果权重比较大，我们的实现代码就可以忽略第二个反射探针的存在，避免不必要的性能开销：

``````		#if UNITY_SPECCUBE_BLENDING
float interpolator = unity_SpecCube0_BoxMin.w;
UNITY_BRANCH
if (interpolator < 0.99999) {
float3 probe1 = Unity_GlossyEnvironment(
UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1, unity_SpecCube0),
unity_SpecCube0_HDR, envData
);
float3 specular = lerp(probe1, probe0, interpolator);
}
else {
float3 specular = probe0;
}
#else
float3 specular = probe0;
#endif
``````

[1] Reflections

Unity中的反射

(0)
(0)