🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Path tracing in Vulkan

Started by
116 comments, last by taby 8 months, 4 weeks ago

Fair enough! Haha.

Advertisement

I didn't find any free documents on how to implement spectral rendering, so I came up with my own solution. It uses a variable number of channels. There is a rainbow on the wall.

taby said:
I didn't find any free documents on how to implement spectral rendering

I believe PBRTv3 is freely available online - https://pbr-book.org/3ed-2018/contents

I do have a hard copy of 2nd edition here in my bookshelf, and was considering buying the 4th one. Or as I say - good night reading.

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

I added that to my bibliography! Looks like I've got reading to do. 🙂

I added in fog. It sort of works. Look at the rainbow mixing into the fog, underneath the left sphere.

Aressera said:

No because HSV is nonlinear and doesn't represent physical light quantities, while linear RGB or other linear spectral space does.

I dunno. It worked out fine for me.

void main() 
{
	const int channels = 10;

	const float max_hue = rgb2hsv(vec3(1.0, 0.0, 1.0)).x; // violet
	const float min_hue = rgb2hsv(vec3(1.0, 0.0, 0.0)).x; // red

	const float max_eta = 0.95;
	const float min_eta = 0.7;

	const float hue_diff = max_hue - min_hue;
	const float hue_step_size = hue_diff / (channels - 1);

	const float eta_diff = max_eta - min_eta;
	const float eta_step_size = eta_diff / (channels - 1);

	float curr_hue = min_hue;
	float curr_eta = min_eta;

	vec3 total = vec3(0.0);
	vec3 color = vec3(0.0);

	for(int i = 0; i < channels; i++, curr_hue += hue_step_size, curr_eta += eta_step_size)
	{
		prng_state = res.x * pixel_pos.y + pixel_pos.x;

		const float f = get_ray0(curr_hue, curr_eta);
	
		const vec3 mask = hsv2rgb(vec3(curr_hue, 1.0, 1.0));

		total += mask;
		color += f*mask;
	}

	color /= total;

	color = pow(color, vec3(2.2));


	imageStore(color_image, pixel_pos, vec4(color, 0.0));
}

“fine for me” may be good enough for you, but that doesn't make it correct. Doing path tracing or ray tracing in a nonlinear color space, especially HSV or HSL, is simply wrong. What does it mean to add two saturation or hue values? What happens when saturation goes over 1? The result is incorrect lighting.

To do it correctly, you would modify your loop to calculate each wavelength in the spectrum in a linear space, then do a weighted average across the spectrum with the CIE RGB color matching functions to get the linear RGB color.

vec3 colorRGB = vec3(0.0);
vec3 weightRGB = vec3(0.0);
for ( int i = 0; i < wavelengthCount; i++ )
{
	float wavelength = wavelengths[i];
	float intensity = calculatePixelIntensity( wavelength );
	
	colorRGB += intensity * CIE_RGB( wavelength );	
	weightRGB += CIE_RGB( wavelength );
}
colorRGB /= weightRGB;

@aressera Most of the cases I've seen were using random wavelengths, not looping through them (or rather - taking N different random wavelengths each time you solve for pixel). When you loop through them - isn't there going to be a problem with bias resulting due to discretization of visible spectrum?

My current blog on programming, linux and stuff - http://gameprogrammerdiary.blogspot.com

I am adding things using RGB. Just because you don't know how it works doesn't mean that it's an inferior solution. It works perfectly.

As for discretization, if you set the max hue to be blue instead of violet, and use 3 channels, you should get the standard chromatic aberration??? Now, I just use 10 - 20 channels, and end at violet

// Path trace code
			
float local_colour = 0;
float total = 0;
float ret_colour = 0; 
			
const float tmin = 0.001;
const float tmax = 10000.0;
traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, o.xyz, tmin, d.xyz, tmax, 0);

const vec3 mask = hsv2rgb(vec3(hue, 1.0, 1.0));

total += mask.r;
total += mask.g;
total += mask.b;

local_colour += rayPayload.colour.r*mask.r;
local_colour += rayPayload.colour.g*mask.g;
local_colour += rayPayload.colour.b*mask.b;
			
...
			
ret_colour += local_colour;
			
...

return ret_colour / total;

Vilem Otte said:
When you loop through them - isn't there going to be a problem with bias resulting due to discretization of visible spectrum?

You're right on this, but the effect is probably small for high enough number of wavelengths.

This topic is closed to new replies.

Advertisement