games101:四,着色频率、纹理贴图(放大插值、缩小mipmap)+ 作业3
一,着色频率
1,Flat Shading----逐三角形
面块边缘明显但效率高;
但随着模型复杂度提高,效率降低、效果与其他两种差距减小,见上图。
2,Gouraud Shading----逐顶点
计算与顶点相邻所有面法向量的加权平均(权重与三角形面积有关,看具体实现)
3,Phong Shading----逐像素
计算出三角形3个顶点的法向量后,插值计算内部每个像素的法向量:
n
=
α
n
0
+
β
n
1
+
γ
n
2
n = αn_{0} + βn_{1} + γn_{2}
n=αn0+βn1+γn2
其中n0、n1、n2分别是三角形三个顶点的法线向量,α , β , γ为三角形面内任一点的重心坐标,↓。
3.1重心坐标
(重心坐标α , β , γ相等时,所在点为三角形重心)
面积不好算,公式可以简化为如下:
- 该方法除计算法向量外还可以推广到任意图像属性
- 重心坐标没有投影不变性!经过投影重心会产生偏移-----不能在投影后的三角形中插值,应该先插值后再投影(逆变换即可),或者使用透视矫正,如下:
3.2透视矫正插值(Perspective-Correct Interpolation)
以深度Z为例,如图,c->C的投影过程中明显位置有偏移。
由A、B、C与a、b、c坐标斜率相同得到3个式子,由u123坐标符合
u
s
=
u
1
+
s
(
u
2
−
u
1
)
u_{s} = u_{1} +s (u_{2}-u_{1})
us=u1+s(u2−u1),X123坐标、Z123坐标同理得到3个式子。解6个式子,最终得
t
=
s
Z
1
s
Z
1
+
(
1
−
s
)
Z
2
⟹
Z
t
=
1
s
Z
2
+
1
−
s
Z
1
t = \frac{sZ_{1}}{sZ_{1} + (1-s)Z_{2}} \implies Z_{t} = \frac{1}{ \frac{s}{Z_{2}} + \frac{1-s}{Z_{1}}}
t=sZ1+(1−s)Z2sZ1⟹Zt=Z2s+Z11−s1
同理,重心坐标插值矫正结果:
Z
t
=
1
α
Z
A
+
β
Z
B
+
γ
Z
C
Z_{t} = \frac{1}{ \frac{α}{Z_{A}} + \frac{β}{Z_{B}} + \frac{γ}{Z_{C}}}
Zt=ZAα+ZBβ+ZCγ1
由以上深度Z的插值结果拓展到任意属性V的插值结果:
V
t
=
(
V
1
Z
1
+
s
(
V
2
Z
2
+
V
1
Z
1
)
)
V_{t} = (\frac{V_{1}}{Z_{1}} + s( \frac{V_{2}}{Z_{2}} + \frac{V_{1}}{Z_{1}} ))
Vt=(Z1V1+s(Z2V2+Z1V1))
V
t
=
(
α
V
A
Z
A
+
β
V
B
Z
B
+
γ
V
C
Z
C
)
/
1
Z
t
V_{t} = (α\frac{V_{A}}{Z_{A}} + β\frac{V_{B}}{Z_{B}} + γ\frac{V_{C}}{Z_{C}} ) /\frac{1}{Z_{t}}
Vt=(αZAVA+βZBVB+γZCVC)/Zt1
3.3模型变换中的法线向量
模型变换矩阵为M,则法向量变换矩阵为 ( M − 1 ) T (M^{-1})^T (M−1)T
推导过程:
n
T
t
=
0
=
n
T
M
−
1
⋅
M
⋅
t
=
(
n
T
M
−
1
)
M
t
⟹
N
n
T
=
n
T
M
−
1
⟹
N
n
=
(
M
−
1
)
T
n
n^{T}t = 0 = n^{T}M^{-1} \cdot M \cdot t=(n^{T}M^{-1})M_{t} \implies N_{n}^T = n^{T}M^{-1} \implies N_{n} = (M^{-1})^Tn
nTt=0=nTM−1⋅M⋅t=(nTM−1)Mt⟹NnT=nTM−1⟹Nn=(M−1)Tn
二,纹理贴图
1,小纹理放大----双线性插值
小纹理直接放大会出现马赛克现象,一个纹理对应多个像素
- 双线性插值:利用周围4个顶点,先计算u1、u2在计算u(方向先后不重要),相当于x、y两个维度插值
- 双三次插值:利用周围16个顶点,做x、y方向插值,每次用4个点做3次的差值
2,大纹理缩小----mipmap
大纹理直接缩小会出现锯齿和摩尔纹现象,多个纹理对应一个像素,采样频率问题。
- 可以使用超采样方法,如512x超采样,效果不错但效率过低。
- 也可以从点查询Point Query迈向区域查询Range Query,即mipmap法
mipmap----快速的、近似的、方形的范围查询
将原纹理宽高逐层缩小2倍组成的查询图(纹理内存增加三分之一):
计算过程:
- 计算每个像素对应的纹理范围位置的最大长度的2底对数D----插值对应的mipmap层数
- 对D的向上和向下取整的两个层级插值(避免由于mipmap整数层级产生过渡断层现象)
三线性插值
三线性插值:即双线性插值+mipmap层级之间插值=3个维度
ripmap----各向异性
但mipmap会产生过渡模糊的情况,因为在不规则映射下“方形”限制不太友好。ripmap可以缓解mipmap的“方形”限制,可以查询“长条形”,内存占用增加3倍纹理:
作业3
blinn-phong光照模型:注意a与d、s的强度不一样
Eigen::Vector3f phong_fragment_shader(const fragment_shader_payload& payload)
{
Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
Eigen::Vector3f kd = payload.color;
Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);
auto l1 = light{{20, 20, 20}, {500, 500, 500}};
auto l2 = light{{-20, 20, 0}, {500, 500, 500}};
std::vector<light> lights = {l1, l2};
Eigen::Vector3f amb_light_intensity{10, 10, 10};
Eigen::Vector3f eye_pos{0, 0, 10};
float p = 150;
Eigen::Vector3f color = payload.color;
Eigen::Vector3f point = payload.view_pos;
Eigen::Vector3f normal = payload.normal;
Eigen::Vector3f result_color = {0, 0, 0};
Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);
result_color += La;
for (auto& light : lights)
{
float r = (light.position - point).norm();
Eigen::Vector3f l = (light.position - point).normalized();
Eigen::Vector3f h = ((eye_pos - point).normalized() + l).normalized();
Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity) / pow(r,2) * std::max(normal.dot(l), 0.0f);
Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity) / pow(r,2) * pow(std::max(normal.dot(h), 0.0f),p);
result_color += (Ld + Ls);
}
return result_color * 255.f;
}
凹凸贴图模型:凹凸纹理color值代表法向量增量值,本来应该用一维图表示,这里rgb图用向量长度表示->.norm(),其他注意见作业3助教说明:
Eigen::Vector3f bump_fragment_shader(const fragment_shader_payload& payload)
{
Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
Eigen::Vector3f kd = payload.color;
Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);
auto l1 = light{{20, 20, 20}, {500, 500, 500}};
auto l2 = light{{-20, 20, 0}, {500, 500, 500}};
std::vector<light> lights = {l1, l2};
Eigen::Vector3f amb_light_intensity{10, 10, 10};
Eigen::Vector3f eye_pos{0, 0, 10};
float p = 150;
Eigen::Vector3f color = payload.color;
Eigen::Vector3f point = payload.view_pos;
Eigen::Vector3f normal = payload.normal;
float kh = 0.2, kn = 0.1;
// TODO: Implement bump mapping here
// Let n = normal = (x, y, z)
// Vector t = (x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z))
// Vector b = n cross product t
// Matrix TBN = [t b n]
// dU = kh * kn * (h(u+1/w,v)-h(u,v))
// dV = kh * kn * (h(u,v+1/h)-h(u,v))
// Vector ln = (-dU, -dV, 1)
// Normal n = normalize(TBN * ln)
float x = normal[0];
float y = normal[1];
float z = normal[2];
Eigen::Vector3f t{x*y/std::sqrt(x*x+z*z),std::sqrt(x*x+z*z),z*y/std::sqrt(x*x+z*z)};
Eigen::Vector3f b = normal.cross(t);
Eigen::Matrix3f TBN;
TBN << t.x(), b.x(), normal.x(),
t.y(), b.y(), normal.y(),
t.z(), b.z(), normal.z();
float w = payload.texture->width, h = payload.texture->height;
float bumpcolor1 = payload.texture->getColor(payload.tex_coords[0],payload.tex_coords[1]).norm();
float bumpcolor2 = payload.texture->getColor(payload.tex_coords[0]+1.0f/w,payload.tex_coords[1]).norm();
float bumpcolor3 = payload.texture->getColor(payload.tex_coords[0],payload.tex_coords[1]+1.0f/h).norm();
float dU = kh * kn * (bumpcolor2-bumpcolor1);
float dV = kh * kn * (bumpcolor3-bumpcolor1);
Eigen::Vector3f ln{-dU, -dV, 1.0f};
normal = (TBN * ln).normalized();
Eigen::Vector3f result_color = {0, 0, 0};
result_color = normal;
return result_color * 255.f;
}