0.简介
上次解决了光源问题,下面我们解决透明物体透光问题。
注:贴出源码与最终源码可能有所不同,以最终源码为准。
1.透明物体
透明物体受到光照,比较直直观的就是透镜效果,放大镜聚焦这类的,我是这么考虑的,透明物体也相当于一种光源,只是这种光源和真正的光源不同,真正的光源发光点就是某个中心点,例如球形光源就是球的中心,但是透镜不是,透镜是透镜上所有点都有发光的可能,因为透镜"发"出的光是别的光源或者物体发射过来的光,这些光来的方向不一定,所以对于透镜所产生的阴影下的某一点来说,可能会受到透镜多个角度透过来的光的影响,而真正的光源是从一点发出光,那么某一点受光照影响的话,也只是受到某一条光源方向来的光(这里假设场景中只有一个光源),所以在采用与光源类似的方法同时,也要有所变动,就是当计算物体是否与光源直接能联通的同时,如果中间有物体格挡,这个物体若是光源,则直接生成与光源连接的光线,若不是光源的情况下,如果如果是不透明物体,就不去计算,然后就会留下阴影,如果是透明物体,那么在透明物体上采样几十个点,然后对这几十个点光追,最后求一个平均值,这就是透明物体阴影下光线计算。
2.实现
for (auto obj : s)
{
vec3 color = vec3(0, 0, 0);
float light = 0;
int count = 0;
//能发光的话,单个物体
if (!obj->isSet && (obj->m->light > 0.0f))
{
//for (int i = 0; i < 5; i++)
{
if (((Polygon*)(r.polygon))->m != nullptr && ((Polygon*)(r.polygon))->m->light > 0&&(r.polygon!=obj))
break;
//获取光源上随机一点
vec3 point = obj->getLightCenter();
//构造光线,光源与当前物体的直接连线
lightRay = Ray(normalize(point - r.end.position), r.end.position, r.intensity, r.color, nullptr);
//计算光碰撞的颜色,
minDistance = FLT_MAX;
for (int i = 0; i < s.size(); i++)
{
Ray t = s[i]->intersect(lightRay);
if (t.polygon != nullptr && t.distance < minDistance)
{
minDistance = t.distance;
lightRay = t;
}
}
//如果物体是直接对着光源,获取光源的信息
if (lightRay.polygon == obj)
{
light += lightRay.intensity;
color += lightRay.color;
count++;
}
else
{
//这里是处理阴影部分,如果是透明材质的阴影,则继续计算
if (lightRay.polygon!=nullptr && ((Polygon*)(lightRay.polygon))->m != nullptr && ((Polygon*)(lightRay.polygon))->m->transparent > 0)
{
Ray tranRay;
for (int j = 0; j < 100; j++)
{
//如果自身是透镜就不用计算了
if (lightRay.polygon == r.polygon)
break;
//获取光源上随机一点
vec3 point = ((Polygon*)(lightRay.polygon))->getRandomPoint();
//vec3 point = obj->getRandomPoint();
//构造光线,光源与当前物体的直接连线
tranRay = Ray(normalize(point - r.end.position), r.end.position, r.intensity, r.color, nullptr);
//计算光碰撞的颜色,
minDistance = FLT_MAX;
for (int k = 0; k < s.size(); k++)
{
Ray t = s[k]->intersect(tranRay);
if (t.polygon != nullptr && t.distance < minDistance)
{
minDistance = t.distance;
tranRay = t;
}
}
//如果物体是直接对着透明物体
if (lightRay.polygon == tranRay.polygon)
{
Ray rt = rayTrac(tranRay, s, times - 1);
light += rt.intensity;
color += rt.color;
count++;
}
}
}
}
}
if (count == 0)
{
color = vec3(0, 0, 0);
light = 0;
//continue;
}
else
{
light /= count;
color /= (count * 1.0);
}
lightRay.intensity = light;
lightRay.color = color;
lightsRay.push_back(lightRay);
}
}
这个里就是之前处理光源光线的,在else情况里,就是物体与光源中间有其他物体的情况下,如果中间物体是透明物体,则进行光追。这里要注意,如果计算中透镜遇到了自身,则不进行计算,跳过进行别的物体计算,不然会非常慢,就是当前物体是透镜,那么中间物体又遇到了透镜自身,这种情况不用计算,上一篇中光源也是,不和自身进行重复计算。
在球和平面等类中加入了随机获取点的函数,目的就是为了采样计算透镜透光产生阴影的效果。
vec3 Sphere::getRandomPoint()
{
//生成一个随机方向
vec3 direct = normalize(vec3(sgn(rand() % 100,50)*rand() % 1000, sgn(rand() % 100, 50) * rand() % 1000, sgn(rand() % 100, 50) * rand() % 1000));
//球心加上半径*direct
return center + direct * radius;
}
这里就贴出球类的代码。
3.效果
![](https://img-blog.csdnimg.cn/20200127013338255.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0FSVEVMRQ==,size_16,color_FFFFFF,t_70)
可以看到透明球和透明方块的阴影部分效果还不错。
4.源码
源码还在整理,有些地方改动比较多,需要后面发布。