Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /usr/home/shadbb/domains/czub.info/public_html/wp-content/plugins/wp-syntax/wp-syntax.php on line 383 Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /usr/home/shadbb/domains/czub.info/public_html/wp-content/plugins/wp-syntax/wp-syntax.php on line 383 Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /usr/home/shadbb/domains/czub.info/public_html/wp-content/plugins/wp-syntax/wp-syntax.php on line 383

1. Wstęp

Raymarching jest to technika generowania obrazów 3D, bazująca na zupełnie innym podejściu niż grafika 3D generowana na podstawie obiektów trójwymiarowych. Ta technika używana jest szczególnie w intrach 4kb, gdzie cała grafika jest generowana przy pomocy PixelShadera.
Raymarching można go nazwać innym podejściem do Raytracingu, gdzie zamiast wyliczać punkt przecięcia promienia z obiektem na scenie wykonujemy spacer od obserwatora szukając miejsca przecięcia z obiektem sceny.

2. Algorytm Raymarching’u – spacer od obserwatora

Poniżej prezentuje kod shadera realizującego operację ray marchingu, przy jego pomocy otrzymujemy prosty sześcian (można zaszaleć).
W przykładowym kodzie najważniejsze są dwie funkcje:
rayMatching(vec3 ray, vec3 rayDir) – gdzie: ray – pozycja obserwatora (kamery), rayDir – kierunek patrzenia
map(vec3 p) – gdzie p – aktualny point w przestrzeni 3D.

#ifdef GL_ES
precision mediump float;
#endif
 
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
 
const float PI = 3.14159;
const int MAX_STEPS = 128;
const float STEPS_DELTA=1.0/32.;
const float MAX_DISTANCE = 100.0;
const float EPSILON = 0.01;
 
//-------------------------------------
// Struktury danych
//-------------------------------------
struct Material
{
	vec3 ambient;
	vec3 diffuse;
	vec3 specular;
	float shininess;
	int texture;
};
 
struct Object
{
	float distance;
	float step;
	vec3 pos;
	vec3 normal;
	bool hit;
	Material mat;
};
 
//-------------------------------------
// Obiekty 
//-------------------------------------
vec2 box(vec3 p, vec3 pos, vec3 size, float material){
	vec3 d = abs(p-pos) - size;
	float distance=min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0));
	return vec2(distance, material);
}
 
//-------------------------------------
// Mapowanie sceny
//-------------------------------------
//
// Zwraca wektor 2 wymiarowy gdzie:
// x: dystans
// y: id materiału
//
vec2 map(vec3 p)
{
	return box(p, vec3(0.0,0.0,0.0), vec3(1.0,1.0,1.0), 0.0);
}
 
//-------------------------------------
// Ray Marching
//-------------------------------------
vec3 pointNormal(vec3 pos)
{
	vec3 eps = vec3(EPSILON, 0.0, 0.0 );
	vec3 nor = vec3(
		map(pos+eps.xyy).x - map(pos-eps.xyy).x,
		map(pos+eps.yxy).x - map(pos-eps.yxy).x,
		map(pos+eps.yyx).x - map(pos-eps.yyx).x );
	return normalize(nor);
}
 
Object rayMarching(vec3 ray, vec3 rayDir){
 
	float distance = 0.0;
	Object sceneObject;
	vec2 mapOut;
 
	sceneObject.hit=false;
 
	for(int i=0; i < MAX_STEPS; ++i) {
 
		mapOut=map(ray);
		distance += mapOut.x;
		ray += rayDir * mapOut.x;
		sceneObject.step += STEPS_DELTA;;
		if(mapOut.x < EPSILON) {  			
			sceneObject.hit=true;
 			distance=0.0;
 			break;
  		}
 		if(distance > MAX_DISTANCE) { 
			distance=MAX_DISTANCE; 
			break; 
		}
	}
 
	sceneObject.distance=distance/MAX_DISTANCE;
 
	if (sceneObject.hit){
		sceneObject.normal=pointNormal(ray);
		sceneObject.pos=ray;
		sceneObject.mat.ambient=vec3(0.0);
		sceneObject.mat.diffuse=vec3(0.8);
		sceneObject.mat.specular=vec3(1.0);		
	}
	return sceneObject;
}
 
void main() {
 
	vec2 pos = (gl_FragCoord.xy*2.0 - resolution.xy) / resolution.y;
	vec2 uv = pos * vec2(resolution.x/resolution.y,1.0);
 
	//
	// Konfiguracja kamery
	//
	vec3 camPos = vec3(5.0, 2.0, 4.0);
	vec3 camTarget = vec3(0.0, 0.0, 0.0);
 
	//
	// Obliczenia kierunku kamery
	//
	vec3 camUp  = normalize(vec3(0.0, 1.0, 0.0));
	vec3 camDir = normalize(camTarget - camPos);
	vec3 camSide = cross(camDir, camUp);
	float focus = 1.8;
 
	vec3 rayDir = normalize(camSide * pos.x + camUp * pos.y + camDir * focus);
	vec3 ray = camPos;
 
	//
	// RayMarching
	//
	Object sceneObject = rayMarching(ray, rayDir);
	vec3 outColor= vec3(sceneObject.step);
	gl_FragColor=vec4(outColor.r,outColor.g,outColor.b,1.0);
}

Wynikiem pracy tego programu jest, sześcian gdzie im jaśniejszy kolor, tym więcej korków potrzeba było do znalezienia punktu przecięcia.

 

Kroki działania programu:
1. Na podstawie pozycji kamery i pozycji „na co patrzy” wyliczamy kierunek kamery

	vec3 camUp  = normalize(vec3(0.0, 1.0, 0.0));
	vec3 camDir = normalize(camTarget - camPos);
	vec3 camSide = cross(camDir, camUp);
	float focus = 1.8;
 
	vec3 rayDir = normalize(camSide * pos.x + camUp * pos.y + camDir * focus);
	vec3 ray = camPos;

 

2. Inicjujemy dane:

	float distance = 0.0;
	Object sceneObject;
	vec2 mapOut;
	sceneObject.hit=false;

 

3. Przeprowadzamy właściwą operację RayMarchingu. Bieżącą pozycję punktu w przestrzeni 3D, mapujemy na odległość. Po tym mamy 3 warunki wyjścia z pętli wykonującej operację RayMarchingu:

  1. Przekroczyliśmy maksymalną ilość kroków pętli
  2. Dystans jest mniejszy od zakładanej dokładności – znaleźliśmy punkt przecięcia. W tym przypadku wykonujemy obliczenie wektora normalnego, nadania koloru, wyliczenia światła, czy inne operacje na znalezionym punkcie przecięcia.
  3. Przekroczyliśmy maksymalny wyliczany dystans punktu od obserwatora, nie wykonujemy dalszych obliczeń
	for(int i=0; i < MAX_STEPS; ++i) {
 
		mapOut=map(ray);
		distance += mapOut.x;
		ray += rayDir * mapOut.x;
		sceneObject.step += STEPS_DELTA;;
		if(mapOut.x < EPSILON) {  			
			sceneObject.hit=true;
 			distance=0.0;
 			break;
  		}
 		if(distance > MAX_DISTANCE) { 
			distance=MAX_DISTANCE; 
			break; 
		}
	}

Graficzna wersja algorytmu:

 

Część II – obiekty podstawowe, modyfikatory

Część III – technikalia, kod shadera