О методе

  • Описание
  • Литература
  • Словарик

Трассировка фотонов

  • Процесс
  • Отражения
  • Русская рулетка

Фотонная карта

  • Фотон
  • Фотонная карта
  • Поиск фотонов

Разное

  • Расчёт освещения
  • Код на C++
  • Улучшения

Сайт

  • Об авторе
  • Контакты
  • ФПМиКТ
  • ВГПУ
 

Реализация фотонной карты на C++


/*  (c) 2001,  Henrik Wann Jensen  */
			

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h" 


/****************************/
/*  Photon map constructor  */
/****************************/
Photon_map :: Photon_map( const int max_phot ) {
	stored_photons = 0;
	prev_scale = 1;
	max_photons = max_phot;

	photons = (Photon*)malloc( sizeof( Photon ) * ( max_photons + 1 ) );

	if (photons == NULL) {
		//f_debug = fopen("debug.txt", "a");
		fprintf(stderr, "Out of memory initializing photon map\n");
		//fclose(f_debug);
		exit(-1);
	}

	bbox_min[0] = bbox_min[1] = bbox_min[2] = 1e8f;
	bbox_max[0] = bbox_max[1] = bbox_max[2] = -1e8f;

	/*  Initialize direction convertion tables  */
	for (int i = 0; i < 256; i++) {
		double angle = double(i)*(1.0/256.0)*M_PI;
		costheta[i] = cos( angle );
		sintheta[i] = sin( angle );
		cosphi[i]   = cos( 2.0*angle );
		sinphi[i]   = sin( 2.0*angle );
	}
}


Photon_map :: ~Photon_map() {
	free( photons );
}


/*  direction of a photon  */
void Photon_map :: photon_dir( float *dir, const Photon *p ) const {
	dir[0] = sintheta[p->theta]*cosphi[p->phi];
	dir[1] = sintheta[p->theta]*sinphi[p->phi];
	dir[2] = costheta[p->theta];
}


/*  Irradiance estimate at pos  */
void Photon_map :: irradiance_estimate(
	float irrad[3],					// returned irradiance
	const float pos[3],				// surface position
	const float normal[3],			// surface normal vector at pos
	const float max_dist,			// max distance to look for photons
	const int nPhotons ) const		// number of used photons
{
	irrad[0] = irrad[1] = irrad[2] = 0.0;

	NearestPhotons np;
	np.dist2 = (float*)alloca( sizeof(float)*( nPhotons + 1 ) );
	np.index = (const Photon**)alloca( sizeof(Photon*)*( nPhotons + 1 ) );

	np.pos[0] = pos[0];
	np.pos[1] = pos[1];
	np.pos[2] = pos[2];
	np.max = nPhotons;
	np.found = 0;
	np.got_heap = 0;
	np.dist2[0] = max_dist*max_dist;

	locate_photons( &np, 1 );		// locating the nearest photons

	if( np.found < 8 ) {
		return;
	}

	float pdir[3];

	/*  Sum irradiance from all photons  */
	for (int i = 1; i <= np.found; i++) {
		const Photon *p = np.index[i];
		// Some additional code here:
		photon_dir( pdir, p );
		if ( (pdir[0] * normal[0] + pdir[1] * normal[1] + pdir[2] * normal[2]) < 0.0f ) {
			irrad[0] += p->power[0];
			irrad[1] += p->power[1];
			irrad[2] += p->power[2];
		}
		// End of additional code segment
	}

	const float tmp = (1.0f/M_PI)/(np.dist2[0]);	// estimate of density

	irrad[0] *= tmp;
	irrad[1] *= tmp;
	irrad[2] *= tmp;
}


/*  Find nearest photons in the map  */
void Photon_map :: locate_photons(
	NearestPhotons *const np,
	const int index ) const
{
	const Photon *p = &photons[index];
	float dist1;


	if (index < half_stored_photons) {
		dist1 = np->pos[p->plane] - p->pos[p->plane];

		if (dist1 > 0.0) {	// if sidt1 is positive search right plane
			locate_photons( np, 2 * index + 1 );
			if ( dist1 * dist1 < np-> dist2[0] ) {
				locate_photons( np, 2*index );
			}
		} else {			// dist1 is negative search left first
			locate_photons( np, 2*index + 1 );
			if ( dist1 * dist1 < np->dist2[0] ) {
				locate_photons( np, 2*index + 1 );
			}
		}
	}

	/* Compute squared distance between current photon and np->pos  */
	dist1 = p->pos[0] - np->pos[0];
	float dist2 = dist1 * dist1;
	dist1 = p->pos[1] - np->pos[1];
	dist2 += dist1*dist1;
	dist1 = p->pos[2] - np->pos[2];
	dist2 += dist1 * dist1;

	if ( dist2 < np->dist2[0] ) {
		// photon is found, let's insert it in the candidate list

		if ( np->found < np->max ) {
			// heap is not full; use array
			np->found++;
			np->dist2[np->found] = dist2;
			np->index[np->found] = p;
		} else {
			int j;
			int parent;

			if (np->got_heap == 0) {
				// we need to build the heap
				float dst2;
				const Photon *phot;
				int half_found = np->found >> 1;
				for (int k = half_found; k >=1; k--) {
					parent = k;
					phot = np->index[k];
					dst2 = np->dist2[k];
					while ( parent <= half_found ) {
						j = parent + parent;
						if (j < np->found && np->dist2[j] < np->dist2[j + 1]) {
							j++;
						}
						if (dst2 >= np->dist2[j]) {
							break;
						}
						np->dist2[parent] = np->dist2[j];
						np->index[parent] = np->index[j];
						parent = j;
					}
					np->dist2[parent] = dst2;
					np->index[parent] = phot;
				}
				np->got_heap = 1;
			}

			// insert new photon into max heap
			// delete largest element, insert new and reorder the heap

			parent = 1;
			j = 2;

			while (j <= np->found ) {
				if ( j < np->found && np->dist2[j] < np->dist2[j + 1] ) {
					j++;
				}
				if ( dist2 > np->dist2[j] ) {
					break;
				}
				np->dist2[parent] = np->dist2[j];
				np->index[parent] = np->index[j];
				parent = j;
				j += j;
			}
			np->index[parent] = p;
			np->dist2[parent] = dist2;

			np->dist2[0] = np->dist2[1];
		}
	}
}


/*  Store photon in the flat array  */
void Photon_map :: store(
	const float power[3],
	const float pos[3],
	const float dir[3] ) 
{
	if (stored_photons > max_photons) {
		return;
	}

	stored_photons++;
	Photon *const node = &photons[stored_photons];

	for (int i = 0; i < 3; i++) {
		node->pos[i] = pos[i];

		if (node->pos[i] < bbox_min[i]) {
			bbox_min[i] = node->pos[i];
		}
		if (node->pos[i] > bbox_max[i]) {
			bbox_max[i] = node->pos[i];
		}

		node->power[i] = power[i];
	}

	int theta = int( acos(dir[2]) * (256.0/M_PI) );
	if (theta > 255) {
		node->theta = 255;
	} else {
		node->theta = (unsigned char) theta;
	}

	int phi = int( atan2(dir[1], dir[0]) * (256.0/(2.0 * M_PI)) );
	if (phi > 255) {
		node->phi = 255;
	} else if(phi < 0) {
		node->phi = (unsigned char) (phi + 256);
	} else {
		node->phi = (unsigned char) phi;
	}
}


/*  Scale the power of all photons once they have been emitted from the light source            */
/*  This function is called after each light source is processed; scale = 1/(#emitted photons)  */
void Photon_map :: scale_photon_power( const float scale ) {
	for (int i = prev_scale; i <= stored_photons; i++) {
		photons[i].power[0] *= scale;
		photons[i].power[1] *= scale;
		photons[i].power[2] *= scale;
	}
	prev_scale = stored_photons;
}


/*  Create left-balanced kd-tree from the flat photon array        */
/*  This function is called before using photon map for rendering  */
void Photon_map :: balance(void) {
	if (stored_photons > 1) {
		// allocating two temporary arrays for the balancing procedure
		Photon **pa1 = (Photon**)malloc( sizeof(Photon*) * (stored_photons + 1) );
		Photon **pa2 = (Photon**)malloc( sizeof(Photon*) * (stored_photons + 1) );

		for (int i = 0; i <= stored_photons; i++) {
			pa2[i] = &photons[i];
		}

		balance_segment( pa1, pa2, 1, 1, stored_photons );
		free(pa2);

		// reorganize balanced kd-tree (make a heap)
		int d;
		int j = 1;
		int foo = 1;
		Photon foo_photon = photons[j];

		for (int i = 1; i <= stored_photons; i++) {
			d = pa1[j] - photons;
			pa1[j] = NULL;
			if (d != foo) {
				photons[j] = photons[d];
			} else {
				photons[j] = foo_photon;
			
				if (i < stored_photons) {
					for (foo; foo <= stored_photons; foo++) {
						if (pa1[foo] != NULL) {
							break;
						}
					}
					foo_photon = photons[foo];
					j = foo;
				}
				continue;
			}
			j = d;
		}
		free(pa1);
	}

	half_stored_photons = stored_photons/2 - 1;
}


#define swap(ph, a, b) { Photon *ph2 = ph[a]; ph[a] = ph[b]; ph[b] = ph2; }


/*  Median splits the photon array into top and bottom separated pieces  */
void Photon_map :: median_split(
	Photon **p,
	const int start,		// start of photon block in array
	const int end,			// end of photon block in array
	const int median,		// desired median number
	const int axis )		// axis to split along
{
	int left = start;
	int right = end;

	while (right > left) {
		const float v = p[right]->pos[axis];
		int i = left - 1;
		int j = right;
		for(;;) {
			while ( p[++i]->pos[axis] < v) {
			}
			while ( p[--j]->pos[axis] > v && j > left ) {
			}
			if (i >= j) {
				break;
			}
			swap(p, i, j);
		}

		swap(p, i, right);
		if ( i >= median ) {
			right = i - 1;
		}
		if ( i <= median ) {
			left = i + 1;
		}
	}
}


void Photon_map :: balance_segment(	// C6
	Photon **pbal,
	Photon **porg,
	const int index,
	const int start,
	const int end )
{
	// compute new median 
	int median = 1;
	while ((4 * median) <= (end - start + 1)) {
		median += median;
	}

	if ((3*median) <= (end - start + 1)) {
		median += median;
		median += start - 1;
	} else {
		median = end - median + 1;
	}

	// find axis to split along
	int axis = 2;
	if ((bbox_max[0] - bbox_min[0]) > (bbox_max[1] - bbox_min[1]) &&
		(bbox_max[0] - bbox_min[0]) > (bbox_max[2] - bbox_min[2])) {
		axis = 0;
	} else if ((bbox_max[1] - bbox_min[1]) > (bbox_max[2] - bbox_min[2])) {
		axis = 1;
	}

	// partition photon block around the median
	median_split( porg, start, end, median, axis );
	
	pbal[index] = porg[median];
	pbal[index]->plane = axis;

	// recursively balance the left and right block
	if (median > start) {
		// balance left segment
		if ( start < median - 1 ) {
			const float tmp = bbox_max[axis];
			bbox_max[axis] = pbal[index]->pos[axis];
			balance_segment( pbal, porg, 2*index, start, median - 1 );
			bbox_max[axis] = tmp;
		} else {
			pbal[2*index] = porg[start];
		}
	}

	if ( median < end ) {
		// balance right segment
		if ( median + 1 < end ) {
			const float tmp = bbox_min[axis];
			bbox_min[axis] = pbal[index]->pos[axis];
			balance_segment( pbal, porg, 2*index + 1, median + 1, end );
			bbox_min[axis] = tmp;
		} else {
			pbal[2*index + 1] = porg[end];
		}
	}
}
			
			

Перейти к оглавлению
 
 
 

© 2011. Feel free to copy from this site. Design by fpm08siv ^^
www.stats.lt - Puslapiu statistika, reitingai, skaitliukas