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
|
Vector3f castRay(
const Vector3f &orig, const Vector3f &dir, const Scene& scene,
int depth)
{
// 递归出口
if (depth > scene.maxDepth) {
return Vector3f(0.0,0.0,0.0);
}
Vector3f hitColor = scene.backgroundColor;
if (auto payload = trace(orig, dir, scene.get_objects()); payload)
{
Vector3f hitPoint = orig + dir * payload->tNear;
Vector3f N; // normal
Vector2f st; // st coordinates
// 获取该点位的法线和纹理坐标
payload->hit_obj->getSurfaceProperties(hitPoint, dir, payload->index, payload->uv, N, st);
// 根据材质不同进行不同的处理
switch (payload->hit_obj->materialType) {
// 反射+折射
case REFLECTION_AND_REFRACTION:
{
// 获得反射方向
Vector3f reflectionDirection = normalize(reflect(dir, N));
// 获得折射方向
Vector3f refractionDirection = normalize(refract(dir, N, payload->hit_obj->ior));
// 当光线击中物体表面后,反射光线的起点如果直接使用 hitPoint(命中点),由于浮点数精度限制,新光线可能会误判为与同一物体再次相交(即“自交”),导致渲染错误(如表面黑斑或无限递归)。 根据反射方向与表面法线 N 的关系,将起点沿法线方向 轻微偏移,使其略微离开原始表面,避免自交。
Vector3f reflectionRayOrig = (dotProduct(reflectionDirection, N) < 0) ?
hitPoint - N * scene.epsilon :
hitPoint + N * scene.epsilon;
Vector3f refractionRayOrig = (dotProduct(refractionDirection, N) < 0) ?
hitPoint - N * scene.epsilon :
hitPoint + N * scene.epsilon;
// 递归计算这次反射和折射后映射到该位置的颜色
Vector3f reflectionColor = castRay(reflectionRayOrig, reflectionDirection, scene, depth + 1);
Vector3f refractionColor = castRay(refractionRayOrig, refractionDirection, scene, depth + 1);
// 菲涅尔效应(Fresnel Effect)动态混合反射和折射颜色。菲涅尔效应表明,光线与表面交互时,反射和折射的比例取决于入射角:当光线 垂直入射(入射角接近 0°)时,反射比例 kr 较小,折射占主导。
float kr = fresnel(dir, N, payload->hit_obj->ior);
hitColor = reflectionColor * kr + refractionColor * (1 - kr);
break;
}
// 只有反射
case REFLECTION:
{
float kr = fresnel(dir, N, payload->hit_obj->ior);
Vector3f reflectionDirection = reflect(dir, N);
Vector3f reflectionRayOrig = (dotProduct(reflectionDirection, N) < 0) ?
hitPoint + N * scene.epsilon :
hitPoint - N * scene.epsilon;
hitColor = castRay(reflectionRayOrig, reflectionDirection, scene, depth + 1) * kr;
break;
}
// BPhong光照模型了进行默认,这里重点要看一下阴影是如何产生的
default:
{
// [comment]
// We use the Phong illumation model int the default case. The phong model
// is composed of a diffuse and a specular reflection component.
// [/comment]
Vector3f lightAmt = 0, specularColor = 0;
Vector3f shadowPointOrig = (dotProduct(dir, N) < 0) ?
hitPoint + N * scene.epsilon :
hitPoint - N * scene.epsilon;
// [comment]
// Loop over all lights in the scene and sum their contribution up
// We also apply the lambert cosine law
// [/comment]
for (auto& light : scene.get_lights()) {
Vector3f lightDir = light->position - hitPoint;
// square of the distance between hitPoint and the light
float lightDistance2 = dotProduct(lightDir, lightDir);
lightDir = normalize(lightDir);
float LdotN = std::max(0.f, dotProduct(lightDir, N));
// is the point in shadow, and is the nearest occluding object closer to the object than the light itself?
// 在一个只考虑光照模型的hitpoint上,以这个点为起始点,向光源方向发出光线,查看是否被遮挡,如果遮挡物距离比光影距离近,则生成阴影
auto shadow_res = trace(shadowPointOrig, lightDir, scene.get_objects());
bool inShadow = shadow_res && (shadow_res->tNear * shadow_res->tNear < lightDistance2);
// 如果被遮挡,则漫反射贡献将变为0
lightAmt += inShadow ? 0 : light->intensity * LdotN;
Vector3f reflectionDirection = reflect(-lightDir, N);
specularColor += powf(std::max(0.f, -dotProduct(reflectionDirection, dir)),
payload->hit_obj->specularExponent) * light->intensity;
}
hitColor = lightAmt * payload->hit_obj->evalDiffuseColor(st) * payload->hit_obj->Kd + specularColor * payload->hit_obj->Ks;
break;
}
}
}
return hitColor;
}
|