【Ray Tracing from Ground Up】阴影 (Shadow)

1. 本影(umbra)和半影(penumbra)

如下图,在绿色横条下方的区域即为本影区,另外由于长条光源产生的微弱光照区域即为半影区。


产生软阴影的方法:

1.使用多个电光源模拟 区域光源,然后对于每个电光源的本影区域加入 累积buffer,求平均即可得软阴影

2. 简单使用一个pass,对本影缘边进行模糊,比如soler,filtering

2.平面阴影(planar shadow)

如下点光源,在平面 y = 0, 上投影,得p点x坐标为


假设平面方程为 n*x + d = 0, 可得到p为


生成半透明的阴影,即阴影下的纹理颜色也可以看见。此时可以

1.简单效果, 半透明投影的图形

2.使用stencil buffer,获取到阴影区域,这样阴影区域不会重复绘制,产生太重的阴影.

3.Shadow Volume

如下光线投射到两橙色杆子,我们可以采用如下办法判断远处橙色折杆阴影区域。

在折杆上取点投射到人眼,红色点表示进入某阴影区,粉红色点表示出了某个阴影区,则根据

一条视线上红色点和粉红色点的个数,可以判断物体是否在阴影区。


有另外一种方法可以更简便,参考《real time rendering》P341。 使用stencil buffer


4.Shadow Map

shadow map 采用存储深度值方法,且渲染两次判断是否在阴影区。如下,va处的深度值不大于存储在a处

的深度值,所以出于可视线区;相反vb的深度则大于存储的深度值,故在阴影区。


使用该方法,容易有个问题,存储的深度值和实际的深度判断在可视表面容易有误差区域,


实际在光线区域中的点,因为深度值判断小于存储的深度值,会有阴影。 

coding

1.plane

此处光线定义如下

        

                     p = 0 + t d;

p 与 a 共面条件 为 (p-a)n = 0; 即 (o + t d -a)n = 0;  t  = -b /a;  这里我们不必要计算出具体的 t 点,因为只有在渲染改点的时候才需要知道,另外,还不确定该点是否为需要渲染的点(只渲染距离view plane 最近的点),故t取一个范围值。

bool
Plane::shadow_hit(const Ray& ray, float& tmin) const {

	float t = (a - ray.o) * n / (ray.d * n);

	if (t > kEpsilon) {
		tmin = t;
		return (true);
	}
	else
		return (false);
}

2.sphere

p 为ray , c 为球心,则相交有(p - c) (p - c) - r*r = 0,  带入  p = o + t*d ,可得到一元二次方程。

 

Sphere::hit(const Ray& ray, double& tmin, ShadeRec& sr) const {

	double 		t;
	Vector3D	temp 	= ray.o - center;
	double 		a 		= ray.d * ray.d;
	double 		b 		= 2.0 * temp * ray.d;
	double 		c 		= temp * temp - radius * radius;
	double 		disc	= b * b - 4.0 * a * c;

	if (disc < 0.0)
		return(false);
	else {
		double e = sqrt(disc);
		double denom = 2.0 * a;
		t = (-b - e) / denom;    // smaller root

		if (t > kEpsilon) {
			tmin = t;
			sr.normal	 = (temp + t * ray.d) / radius;
			sr.local_hit_point = ray.o + t * ray.d;
			return (true);
		}

		t = (-b + e) / denom;    // larger root

		if (t > kEpsilon) {
			tmin = t;
			sr.normal   = (temp + t * ray.d) / radius;
			sr.local_hit_point = ray.o + t * ray.d;
			return (true);
		}
	}

	return (false);
}


3.point light

如下,以物体表面为原点发射一条 shadow ray 到光源,如果ray 和平面有相交,则可进一步判断是否在阴影区,如果没有相交,则不在阴影区。


bool
PointLight::in_shadow(const Ray& ray, const ShadeRec& sr) const {

	float t;
	int num_objects = sr.w.objects.size();
	float d = location.distance(ray.o);

	for(int j = 0; j < num_objects; j++)
		if (sr.w.objects[j]->shadow_hit(ray, t) && t < d)
			return true;

	return false;
}

4.phone

对于phone 模型,相交的判断,即只要判断 入射光线 w 和 法线 normal 的夹角。


RGBColor
Phong::shade(ShadeRec& sr) {

	Vector3D 	wo 			= -sr.ray.d;
	RGBColor 	L 			= ambient_brdf->rho(sr, wo) * sr.w.ambient_ptr->L(sr);
	int 		num_lights	= sr.w.lights.size();

	for (int j = 0; j < num_lights; j++) {
		Vector3D 	wi 		= sr.w.lights[j]->get_direction(sr);
		float 		ndotwi 	= sr.normal * wi;

		if (ndotwi > 0.0) {

			bool in_shadow = false;
			if (sr.w.lights[j]->casts_shadows()) {
				Ray shadowRay(sr.hit_point, wi);
				in_shadow = sr.w.lights[j]->in_shadow(shadowRay, sr);
			}

			if (!in_shadow)
				L += (	diffuse_brdf->f(sr, wo, wi)
						+ specular_brdf->f(sr, wo, wi)) * sr.w.lights[j]->L(sr) * ndotwi;
		}
	}

	return (L);
}



参考文献:

1. 《Ray Tracing from the ground up》

2. 清华大学胡事名 教学PPT


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不负初心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值