BoxProjectedCubemapDirection--镜面反射中的盒型投影函数

作用:

返回一个用于提取反射探头环境贴图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;
}

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值