學習光線追蹤(19)---光源[1]

0.簡介

目前我們的程序能模擬一些反射和折射的效果,但是,目前物體可見主要還是由於物體自身發光,也就是材質的light屬性不爲0,從這裏開始,要徹底的實現光源發光,其餘物體依靠光源的光反射光線的方法顯示。

注:貼出源碼與最終源碼可能有所不同,以最終源碼爲準。

1.光源

定義一個光源很簡單,就是將光源材質的light設置一個大於0的數字,這就是光源,其餘的等於0的都是非光源。

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++;
				}
			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);
		}
		
	}

目前的光源是球形光源,球形光源的中,光發出中心就是球心,那麼每個物體上表面的點與球形光源的球心直接連線形成光線,這樣就可以實現光照效果。上述代碼中,先從物體集合中找到光源,然後計算構造好的指向光源的光線中途有沒有其餘物體遮擋,由於場景中的光源不一定只有一個,所以將構造的指向不同光源的光線最後都存儲到數組中。

3.光線顏色和強度計算


Ray Polygon::sample(Ray out, Ray reflect, Ray refract, vector<Ray> lightsRay)
{
	Ray res(out.direction, out.position, 0, vec3(0, 0, 0), out.polygon);
	if (m->light > 0)
	{
		res.intensity = (m->light);
		res.color = m->getColor(out.end.textureUV);
		return res;
	}
	//out光線帶的是對應物體的法向量值
	float cosa = abs(dot(out.normal,-out.direction));//光線入射角和麪法向量的cos值
	for (auto lightRay : lightsRay)
	{
		float cosl = abs(dot(out.normal, -lightRay.direction));
		//計算光源的發出中心
		//float cosl = abs(dot(out.normal, -normalize(((Polygon*)(lightRay.polygon))->getLightCenter()-lightRay.position)));
		res.color += (m->getColor(out.end.textureUV)*0.6f+lightRay.color*0.4f) * (m->light + std::fmaxf(cosl, 0) * lightRay.intensity * (1.0f - m->transparent)) * std::fmaxf(cosa, 0);
		res.intensity += std::fmaxf(cosl, 0) * lightRay.intensity * (1.0f - m->transparent)* std::fmaxf(cosa, 0);
	}
	res.intensity += reflect.intensity * m->getSpecular(out.end.textureUV);
	res.intensity += refract.intensity * m->transparent;

	if(reflect.polygon)
		res.color += reflect.color * m->getSpecular(out.end.textureUV);
	if (refract.polygon)
		res.color += refract.color * m->transparent;
	
	return res;
}

這裏新添加了一個函數參數,就是光線,現在一個物體的一個點接受來自反射,折射,和光源的光線,這裏把光線的強度添加了進來,還有光源的顏色也添加了進來。還要注意光線強度還要乘以光線與被照到表面的法向量的cos值,這樣能體現出離光源近亮,離光源遠就暗。

Ray Sphere::intersect(Ray & ray)
{
	Ray result(ray.direction,ray.position,ray.intensity, vec3(0,0,0), nullptr);
	//計算球和光線的向量
	vec3 v = ray.position - center;
	//如果光源在球體表面或者內部,要專門注意這種情況
	if (abs(length(v) - radius) < 0.001 && dot(normalize(v) , ray.direction) <= 0)
	{
		result.polygon = this;	
		float cosa = glm::dot(normalize((center - ray.position)), normalize(ray.direction));//normalize(abs((center - ray.position))*normalize(ray.direction));
		result.distance = 2 * radius * abs(cosa);
		result.end = ray.getEndPoint(result.distance);
		result.normal = -normalize(result.end.position - center);
		result.intensity = m->light;
		result.color = m->getColor(result.end.position);
		return result;
	}
	float disSubR = dot(v , v) - (radius * radius);
	float ray_v_dot = dot(ray.direction , v);

	if (ray_v_dot <= 0)
	{
		float discr = ray_v_dot * ray_v_dot - disSubR;
		if (discr >= 0)
		{
			result.polygon = this;
			result.distance = -ray_v_dot - sqrt(discr);
			result.end = ray.getEndPoint(result.distance);
			result.normal = normalize(result.end.position - center);
			result.intensity = m->light;
			result.color = m->getColor(result.end.position);
			return result;
		}
	}
	return result;
}

在球形和平面的這個碰撞檢測函數中,返回的光線結果帶有光源信息,就是直接將是否有光強,和物體表面顏色返回,這樣可以在與光線連接計算中直接獲得光線信息。函數參數也變成了引用,因爲我在程序性能檢測中發現函數傳參數佔用了不少時間。

4.效果

光源和陰影效果

5.源碼

由於這次和上次的源碼差距比較多,所以最後給出源碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章