效果预览

基本原理

和视差映射类似,但是是把一个正方体内的东西映射到一个平面上。

知识准备

CubeMap采样

cubeMap的学习

如何获取室内点

这里和求AABB的方法相同。
image-1651584330472
首先假设相机cam,视角方向d,近平面交点为p1远平面焦点为p2设时间t。
那么可以得到这个公式。

①p1=cam+t1*d
②p2=cam+t2*d

由于贴图是模拟窗户,所以p1就是每个frag片元的位置,然后现在需要知道p2的位置。
我们把②反过来看t2就可以得到t2=(p2-cam)/d
看上去这是一个鸡生蛋蛋生鸡的问题,但是这个包围盒的大小我们是可以设置的。例如在模型空间内这个包围盒是(0,0,0)~(1,1,1),但是我们选择在(-1,-1,-1)~(1,1,1)内计算(这里后面会解释)。
那么t2要如何解出来呢,可以这么想:一根射线穿过盒子,可以当做是和盒子的6个平面(这6个平面是无限大的)做相交。
我们先看二维情况,三维是完全相同的。
image-1651584539317
我们把d沿着x和y做分解,那么就会得到这个射线与x=1这根直线以及与y=1这根直线相交的时间tx,ty那么直线和包围盒相交的点p2的时间t2应该是min(tx,ty),这是因为首先它和x=1相交了。
放到三维则就是与平面x=1,平面y=1,平面z=1相交,同理t2应该是min(tx,ty,tz)。

这时还有一个小问题就是这里是和y=1做相交,但是室内还有y=-1的时候呢,这又怎么解决呢?
这里就是用(-1,-1,-1)~(1,1,1)的好处了,我们只需要对方向取上绝对值即t2=abs(1/d)-cam/d就可以把=-1的问题和=1问题统一了。

此时就可以用得到的t2反解出p2了,p2=p1+t2*d由于我们是在frag里使用,因此p2
就可以直接使用p1来计算,原理和cam一样。

如何把获取到的p2用于cubemap采样

我们先前使用(-1,-1,-1)~(1,1,1)定义p2还有一个好处就是在这,cubemap的范围就是从(-0.5,-0.5,-0.5)~(0.5,0.5,0.5),我们直接使用p2的点坐标就是对应的cubemap的uvw。

代码

Shader "Unlit/fakeInterior"
{
    Properties
    {
        _RoomTex ("Texture", CUBE) = "white" {}
        _Rotate("Rotate around XYZ",vector)=(0,0,0)

    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            //旋转函数
            float3 rotateVectorAboutX(float angle, float3 vec)
            { 
            angle = radians(angle);
            float3x3 rotationMatrix ={float3(1.0,0.0,0.0),
                                        float3(0.0,cos(angle),-sin(angle)),
                                        float3(0.0,sin(angle),cos(angle))};
            return mul(vec, rotationMatrix);
            }
            
            float3 rotateVectorAboutY(float angle, float3 vec)
            { 
            angle = radians(angle);
            float3x3 rotationMatrix ={float3(cos(angle),0.0,sin(angle)),
                                        float3(0.0,1.0,0.0),
                                        float3(-sin(angle),0.0,cos(angle))};
            return mul(vec, rotationMatrix);
            }
            
            float3 rotateVectorAboutZ(float angle, float3 vec)
            {
            angle = radians(angle);
            float3x3 rotationMatrix ={float3(cos(angle),-sin(angle),0.0),
                                        float3(sin(angle),cos(angle),0.0),
                                        float3(0.0,0.0,1.0)};
            return mul(vec, rotationMatrix);
            }

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float3 vertex_OS:TEXCOORD1;
                float3 viewDir_OS:TEXCOORD2;
                float4 vertex : SV_POSITION;
            };

            samplerCUBE _RoomTex;
            float4 _RoomTex_ST;
            float4 _Rotate;

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                // slight scaling adjustment to work around "noisy wall" 
                // when frac() returns a 0 on surface
                o.vertex_OS = v.vertex * _RoomTex_ST.xyx * 0.999 + _RoomTex_ST.zwz;

                // get object space camera vector
                float4 objCam = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0));
                o.viewDir_OS = v.vertex.xyz - objCam.xyz;

                // adjust for tiling
                o.viewDir_OS *= _RoomTex_ST.xyx;
                return o;
            }

            fixed4 frag(v2f i):SV_TARGET{
                float3 d=normalize(i.viewDir_OS);
                
                float3 pos=frac(i.vertex_OS);
                pos=pos*2-1;

                float tx=abs(1/d.x)-pos.x/d.x;
                float ty=abs(1/d.y)-pos.y/d.y;
                float tz=abs(1/d.z)-pos.z/d.z;
                float t=min(tx,min(ty,tz));

                float3 p2=pos+t*d;
                //rotate
                p2=rotateVectorAboutX(_Rotate.x,p2);
                p2=rotateVectorAboutY(_Rotate.y,p2);
                p2=rotateVectorAboutZ(_Rotate.z,p2);

                fixed3 col;
                col=texCUBE(_RoomTex,p2);
                return fixed4(col,1.0);

            }
           
            ENDCG
        }
    }
}

参考

fakeinterior实现:

https://zhuanlan.zhihu.com/p/376762518
https://zhuanlan.zhihu.com/p/159439811

制作cubemap

https://blog.csdn.net/weixin_43839583/article/details/104213119

旋转cubemap

https://polycount.com/discussion/97779/shader-rotate-cubemap-any-tips

Q.E.D.


寄蜉蝣于天地,渺沧海之一粟