fake Interior笔记

效果预览

基本原理

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

知识准备

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。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2022-2023 Junto
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信