UnityShader-环境反射

 

环境的反射算在间接光的镜面反射中

UnityIndirect CreateIndirectLight(v2f i,float3 viewDir){
    ...
    #if defined(FORWARD_BASE_PASS)
    ...
    //得到反射方向
    float3 reflectDir=reflect(-viewDir,i.normal);
    //得到环境采样
    float4 envSample = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, reflectDir);
    //天空盒可能是HDR的,要转回普通的颜色
    indirectLight.specular=DecodeHDR(envSample, unity_SpecCube0_HDR);;
    #endif
    ...
}

unity_SpecCube0保存了环境的立体贴图(天空盒和后面的反射探针形成的立体贴图,都算在这里)

UNITY_SAMPLE_TEXCUBE 用于采样立体贴图

DecodeHDR将高动态贴图转为正常RGB,如果导入的不是HDR贴图,就没有变化,我这里默认的天空盒不是HDR的,所以加不加都一样 unity_SpecCube0_HDR.xy和颜色的alpha通道(M)存储了转化的信息

finalcolor=xM^y*hdrcolor

反射探针 

右键创建资源->Light->Reflection Probe

UnityShader-环境反射_第1张图片

黄框就是渲染的立方体贴图的范围,一共三个模式,一般用Bake模式,它只会烘焙静态物体,就是下面这个选项是静态的

UnityShader-环境反射_第2张图片

 物体进入黄框范围,他的unity_SpecCube0就有这个反射探针的立体贴图了,

但此时环境反射还不收Smoothness的影响,所以要换一下采样贴图的方法,让他根据粗糙度读取不同层级的mipmap

float roughness = 1 - _Smoothness;
roughness *= 1.7 - 0.7 * roughness;
float4 envSample = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflectDir,roughness*UNITY_SPECCUBE_LOD_STEPS);

Unity会为环境贴图生成不同层级的mipmap,0-5级从模糊到清晰,所以UNITY_SPECCUBE_LOD_STEPS定义为6。

粗糙度是光滑度的相反,

粗糙度和mipmap不是线性的关系,所以要转化一下

m=1.7r-0.7r^2

上面的采样操作可以由Unity的Unity_GlossyEnvironment()方法统一处理

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

Box Projection

反射探针可以开启BoxProjection,这样就可以根据物体的位置来采样,如果没开启,就像前面那样,那么无论物体在方框内的哪个位置,同一个角度采样得到的数据都是一样的。

UnityShader-环境反射_第3张图片

所以重新计算采样方向,把这个过程独立写在一个函数里

float3 BoxProjection (float3 direction, float3 position,float4 cubemapPosition, float3 boxMin, float3 boxMax) 
    {   
        //boxMin加BoxMax可以用于表示一个立方体的范围
        
        //1、将立方体坐标相对物体位置。note:物体坐标变为(0,0,0),但不一定是立方体中心,仍是之前的相对关系,不要搞混
        boxMin -= position;
        boxMax -= position;
        //2、我们要找到一个倍数k,可以让单位反射向量*k=刚好到边界的向量(SD)
        //我们要找最近的面,一共有6个面,direction.x正负判断可以减去3面,也就是在xyz三个方向的面里找最近的面
        
        float x = (direction.x > 0 ? boxMax.x : boxMin.x) / direction.x;
        float y = (direction.y > 0 ? boxMax.y : boxMin.y) / direction.y;
        float z = (direction.z > 0 ? boxMax.z : boxMin.z) / direction.z;
        float scalar = min(min(x, y), z);
        
        //上述的合并操作
        //float3 factors = ((direction > 0 ? boxMax : boxMin) - position) / direction;
        //float scalar = min(min(factors.x, factors.y), factors.z);
        
        //CD=SD+CS
        return direction * scalar + (position - cubemapPosition);
    }
envData.reflUVW = BoxProjection(reflectDir, i.worldPos,unity_SpecCube0_ProbePosition,unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);

下图是2D的情况,我们可以通过相似三角形得到Scale,x是scale较小的一方,用它的缩放值可以得到想要的OB,而y的scale较大,如果用它缩放,得到的就是错误的OC ,三维同理。

UnityShader-环境反射_第4张图片

用cubemapPosition.w 判断是否要使用box projection,>0就是开启

#if UNITY_SPECCUBE_BOX_PROJECTION //判断平台能不能用box projection
if(cubemapPosition.w>0){判断是否开启box projection
    float3 factors = ((direction > 0 ? boxMax : boxMin) - position) / direction;
    float scalar = min(min(factors.x, factors.y), factors.z);
    direction=direction * scalar + (position - cubemapPosition);
}
#endif
return direction;

 BoxProjection启用后,移动物体可以发现反射变化(不仅仅是视角变化带来的变化的变化)

左关闭右开启

UnityShader-环境反射_第5张图片UnityShader-环境反射_第6张图片

混合反射探针 

混合开关不在反射探针组件上,而是在每个物体的MeshRenderer上

off 关闭反射探针

simple 不混合

blend probes 混合

blend probes and skybox 能和天空盒混合

UnityShader-环境反射_第7张图片

混合就需要采样多次,shader中unity_SpecCube1_xxx表示第二个立方体贴图的相关信息,但是采样器只有一个,所以要把unity_SpecCube1绑定到unity_SpecCube0的采样器上。

float3 reflectDir=reflect(-viewDir,i.normal);
//第一份贴图采样
envData.reflUVW = BoxProjection(reflectDir, i.worldPos,unity_SpecCube0_ProbePosition,unity_SpecCube0_BoxMin, unity_SpecCube0_BoxMax);
float3 probe0=Unity_GlossyEnvironment(UNITY_PASS_TEXCUBE(unity_SpecCube0), unity_SpecCube0_HDR, envData);
	     
#if UNITY_SPECCUBE_BLENDING //判断是否开启混合
if (interpolator < 0.99999){//虽然开启了混合,但如果目前只受一个探针影响,就不需要计算了

    //第二份贴图采样 用UNITY_PASS_TEXCUBE_SAMPLER来绑定unity_SpecCube0的采样器采样    unity_SpecCube1
    envData.reflUVW = BoxProjection(reflectDir,     i.worldPos,unity_SpecCube1_ProbePosition,unity_SpecCube1_BoxMin, unity_SpecCube1_BoxMax);
    float3 probe1=Unity_GlossyEnvironment(UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1,unity_SpecCube0), unity_SpecCube1_HDR, envData);
	     
    //混合 unity_SpecCube0_BoxMin.w保存了两个贴图的混合权重
    indirectLight.specular=lerp(probe0,probe1,unity_SpecCube0_BoxMin.w);
}
#else
indirectLight.specular=probe0;
#endif

多次反射

反射探针可以烘焙多次,出现镜中镜的类似效果,最多反弹5次

UnityShader-环境反射_第8张图片


https://catlikecoding.com/unity/tutorials/rendering/part-8/

http://gad.qq.com/program/translateview/7173985

你可能感兴趣的:(Unity)