正常的平面贴图是根据mesh顶点找到对应的uv坐标,然后根据重心坐标插值计算三角面内每个点的uv坐标值。最后根据uv坐标值查找平面贴图上的颜色值。
立方体贴图的6个面分别为6张图片,我们怎么根据mesh面上的坐标值来计算对应平面上的uv坐标值。
基本算法是根据原点与mesh面上点坐标(x,y,z)的射线和立方体贴图的交点作为uv坐标。
这里介绍一下uv坐标计算的数学推导过程
直线的表示方程为
p
⃗
=
o
⃗
+
t
d
⃗
\vec{p}=\vec{o}+t\vec{d}
p=o+td,这里
o
⃗
\vec{o}
o和
d
⃗
\vec{d}
d为向量,
o
⃗
\vec{o}
o表示起始点,这里为原点;
d
⃗
\vec{d}
d表示直线的方向,这里为mesh面上的3维点坐标与原点的方向
n
o
r
m
(
(
x
,
y
,
z
)
−
(
0
,
0
,
0
)
)
=
n
o
r
m
(
x
,
y
,
z
)
norm((x,y,z)-(0,0,0))=norm(x,y,z)
norm((x,y,z)−(0,0,0))=norm(x,y,z);t为需要计算的值。 代入后直线表示方程为
p
⃗
=
t
∗
n
o
r
m
(
x
,
y
,
z
)
\vec{p}=t*norm(x,y,z)
p=t∗norm(x,y,z)
平面的表示方程为
(
p
⃗
−
p
0
⃗
)
⋅
n
⃗
=
0
(\vec{p}-\vec{p0})\cdot \vec{n}=0
(p−p0)⋅n=0,其中
p
⃗
\vec{p}
p为需要求的点坐标,
p
0
⃗
\vec{p0}
p0为已知的平面上任一点;
n
⃗
\vec{n}
n为平面的法向量。 以立方体贴图的右平面举例,其法向量
n
⃗
\vec{n}
n为x轴的方向(1,0,0),
p
0
⃗
\vec{p0}
p0取(1,0,0),代入后平面表示方程为
(
p
⃗
−
(
1
,
0
,
0
)
)
⋅
(
1
,
0
,
0
)
=
0
(\vec{p}-(1,0,0))\cdot(1,0,0)=0
(p−(1,0,0))⋅(1,0,0)=0
直线与平面的交点表示该点同时满足直线和平面表示方程,将上述两个方程联立,得到
(
t
∗
n
o
r
m
(
x
,
y
,
z
)
−
(
1
,
0
,
0
)
)
⋅
(
1
,
0
,
0
)
=
0
(t*norm(x,y,z)-(1,0,0))\cdot(1,0,0)=0
(t∗norm(x,y,z)−(1,0,0))⋅(1,0,0)=0,简化后得
t
∗
x
n
o
r
m
−
1
=
0
t*xnorm-1=0
t∗xnorm−1=0,
t
=
1
/
x
n
o
r
m
t=1/xnorm
t=1/xnorm,其中xnorm表示(x,y,z)归一化后x分量的值。
将t代入直线方程可以得到y,z的坐标为 ynorm/xnorm、znorm/xnorm。因为在右平面上x值都为1,最后的uv坐标值为
(
y
n
o
r
m
/
x
n
o
r
m
,
z
n
o
r
m
/
x
n
o
r
m
)
(ynorm/xnorm, znorm/xnorm)
(ynorm/xnorm,znorm/xnorm)
然后再以左平面为例,左平面的法向量
n
⃗
\vec{n}
n为(-1,0,0),
p
0
⃗
\vec{p0}
p0取(-1,0,0),联立方程为
(
t
∗
n
o
r
m
(
x
,
y
,
z
)
−
(
−
1
,
0
,
0
)
)
⋅
(
−
1
,
0
,
0
)
=
0
(t*norm(x,y,z)-(-1,0,0))\cdot(-1,0,0)=0
(t∗norm(x,y,z)−(−1,0,0))⋅(−1,0,0)=0,可得
t
=
−
1
/
x
n
o
r
m
t=-1/xnorm
t=−1/xnorm,uv坐标值为
(
−
y
n
o
r
m
/
x
n
o
r
m
,
−
z
n
o
r
m
/
x
n
o
r
m
)
(-ynorm/xnorm, -znorm/xnorm)
(−ynorm/xnorm,−znorm/xnorm)
综上可得简化后的计算过程如下:
设mesh上坐标点为(x,y,z),取这3个分量中绝对值最大的分量表示位于哪个平面上,然后分别用其他两个分量除以该分量绝对值表示uv坐标。如点(3,-8,-5),表示位于立方体的下方平面上,uv坐标为(3/8,-5/8)
在opengl中我们要根据上述计算过程来实现uv坐标的计算。在unity中有samplerCUBE,可以调用texCUBE用mesh坐标直接从立方体贴图中获取颜色值。代码如下所示,注意mesh的坐标值都是用的物体局部坐标系,不是世界坐标系。
Shader "CubemapSampler"
{
Properties
{
_CubeMap("CubeMap", CUBE) = ""{}
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 vertex : SV_POSITION;
float4 vertexLocal : TEXCOORD1;
};
v2f vert (appdata v)
{
v2f o;
o.vertexLocal = v.vertex;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
samplerCUBE _CubeMap;
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = texCUBE(_CubeMap, normalize(i.vertexLocal.xyz));
return col;
}
ENDCG
}
}
}