CubeMap生成原理
我们不管是在某个点用工或者用反射探针reflectionProbe生成CubeMap, 原理上都是以透视视角的方式,下面以RP生成CubeMap为例来说 从点的位置以点到物体的每个顶点的连线为方向投影映射到物体应该对应的长方体贴图的面上, 可能投影到三个面或者两个面或者四五个面,取决于物体到点的距离和物体在六个投影面中的位置和物体大小。
如下图中,RP周围的物体无论是在黄色长方体里面还是外面都会映射到对应的面上
这个长方体可以包括或者不包括需要映射的物体,取决于想要的效果。
CubeMap不适合做反射情况
与Cubemap生成点不同位置并且要反射的物体有一定体积的反射
我们实际上可能会CubeMap做地面的反射
用CubeMap做地面反射时 如果反射面的边缘与实际的黄色矩形的面有一定的距离 单个CubeMap不能完整将远近所有的映射都完整的怼齐到真实的物体 根据原理 有一定体积的物体或者比较靠近反射探针的物体映射过去都是有面积的偏差的。 虽然靠近对应的投影面但有一定体积的物体或者比较靠近反射探针的物体映射过去会产生比较大的CubeMap占比面积。
这时如果我们观察反射的位置不和反射探针一致 通过地面来对这部分进行采样做反射就会产生不太正确的效果了
如下图所示靠近反射探针的物体被映射到了A点的面,然后地面进行反射时采样A点得到了错误的结果。 但是图中A的反射如果是在RP点去观察反射,结果将会是正确的。
CubeMap适合做反射情况
观察点与RP相同
根据原理,不论是物体多大在什么位置和旋转,在RP点去观察反射一定是对的
观察点任意,但所有反射五都面类型并且很贴齐到反射矩形的物体
如果环境中应该映射到某个面的物体都很贴齐到那个面 并且他们和反射探针之间没有其他 物体阻挡,并且要反射的地面恰好和反射探针的底面基本重合, 则映射的结果会比较正确。
如下图所示,这里的黄色框的四个面刚好保卫的地面的四个边缘 这里的窗户也是对的齐的 下图中的四面墙中墙壁上的窗子和墙壁,他们都很贴近CubeMap那个面,他们和反射探针之间也没有其他的遮挡物。
CubeMap Shader代码
Shader "JiZhan/GlassPureCubeMap"
{
Properties
{
_MatcapTex("高光贴图", Cube) = ""{}
_boxCenter ("天空盒中心", vector) = (0,0,0,0)
_boxSize ("天空盒长宽高", vector) = (0,0,0,0)
_alpha("透明度", float) = 0.5
}
SubShader
{
Tags {
"RenderType"="Opaque"
"Queue" = "Transparent"
}
LOD 100
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
samplerCUBE _MatcapTex;
float4 _MatcapTex_ST;
float _alpha;
float4 _boxCenter;
float4 _boxSize;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float3 reflectionDir : TEXCOORD3;
float3 pos_WS : TEXCOORD4;
float4 vertex : SV_POSITION;
};
fixed4 GetCubeMapColor(v2f input)
{
float4 boxMin = _boxCenter - _boxSize * 0.5;
float4 boxMax = _boxCenter + _boxSize * 0.5;
_boxCenter.w = 1;
float3 reflectDir =
BoxProjectedCubemapDirection(normalize(input.reflectionDir), input.pos_WS,
_boxCenter, boxMin,
boxMax);
fixed4 cubeMapColor = texCUBE(_MatcapTex, reflectDir);
return cubeMapColor;
}
float3 GetRelfectionDir(appdata v)
{
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
float3 worldViewDir = WorldSpaceViewDir(v.vertex);
float3 reflectionDir = reflect(-worldViewDir, worldNormal);
return reflectionDir;
}
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.reflectionDir = GetRelfectionDir(v);
o.pos_WS = mul(unity_ObjectToWorld, v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 cubeMapColor = GetCubeMapColor(i);
cubeMapColor.a = _alpha * cubeMapColor.r;
return cubeMapColor;
}
ENDCG
}
}
}
使用注意事项
-
可以使用反射探针Bake生成的图作为Shader中的Cubemap 反射探针生成的图的后缀是.exr 一些工具调用UnityAPI生成的图的后缀可能不同 但实际上好像都是Cubemap -
最好用ReflectionProbe渲染的CubeMap,这样用上述代码读取CubeMap纹理时, ReflectionProbe也对应开启BoxProjection选项,读取出来的CubeMap在映射时才容易与场景对称。 -
根据原理 如果要做反射 注意ReflectionProbe的BoxSize调成四个面与反射面的边缘衔接 -
只有标记为”ReflectionProbe Static”的对象才会被反射探头取样 从属性接口上静态对象(Static)的下拉选单打勾即可 -
调节ReflectionProbe的范围注意这两个地方要打开并且同时要选中反射探针
- 临时为了判断调节RP的尺寸和位置是否正确将使用CubeMap的反射,可以用Standard材质作为平面的材质然后勾选Reflections同时将Smoothness调节到最大。
- 在有两个RP的情况下,RP之间重合的区域,内置管线中是通过这两个RP的importance进行调节过渡的,类似于A的颜色乘以A的IMPORTANCE/总IMPORTANCE+B的颜色乘以B的IMPORTANCE/总IMPORTANCE。但是在URP中,重合区域直接显示IMPORTANCE大的RP的颜色,并且URP没有BoxProjectionCubeMapDirection这个函数了,Cubemap好像被Unity抛弃了。
- 在Unity 2019.3.8f1中, 使用BoxProjectedCubemapDirection时,如果是这样写
BoxProjectedCubemapDirection(normalize(input.reflectionDir), input.pos_WS,
unity_SpecCube0_ProbePosition,
unity_SpecCube0_BoxMin,
unity_SpecCube0_BoxMax);
那么BoxProjectedCubemapDirection函数里面的最外围的判断
if (cubemapCenter.w > 0.0)
会根据实际的情况去走正确或者错误的判断
但是如果后面传的三个参数是自定义的参数,如下面这样写
float4 boxMin = _BoxCenter - _BoxSize * 0.5;
float4 boxMax = _BoxCenter + _BoxSize * 0.5;
float3 reflectDir =
BoxProjectionCubeMap(normalize(input.reflectionDir), input.pos_WS,
_BoxCenter, boxMin,
boxMax);
fixed4 cubeMapColor = texCUBE(_CubeTex, reflectDir);
那么要自己注意传入的第三个参数的w分量是0还是1,
|