Real-Time raytracing tutorials

The Partridge Family were neither partridges nor a family. Discuss.
WarriorMario
Posts: 4
Joined: February 9th, 2015, 4:10 pm

Real-Time raytracing tutorials

Post by WarriorMario » February 4th, 2017, 10:58 pm

Hey everyone!

I've been following Chili for as long as I've been coding in C++ (slightly less than 3 years now) and now I'm a second year game programming student. I remember my intake assignment which I might upload the coming week. I made it with Chili's framework and I thought I was using polymorphism correctly but after a month at my study I learned that it was bad and that I did not understood how to use it that well. I also did not understand some other stuff that well but hey, I probably should not have skipped half of the intermediate tutorials :lol:

Anyways I continued and I quickly figured out that besides the polymorphism part and some other stuff I actually was quite a capable programmer that learned C++ super fast compared to others. Chili's foundations are basically the first year of my study and beyond besides GPU programming ;)

Now that was some background info but lets get to the tutorial part. I've been thinking about doing some raytracing tutorials for a while since that's something I'm quite good at (getting 'interactive framerates' and near to the 30 FPS on the CPU )and my teachers would love to see me do some tutorials to show my learnings. Now I could have rushed those tutorials just to show my learnings to my teachers but I also wanted to learn how to do some proper tutorials. So here I go:

[First tutorial - Basic Setup]
https://www.youtube.com/watch?v=AHe8PUr7Jqw
It will guide you quickly through a very basic raytracer setup in slightly modified version of Chili's framework.

[Second tutorial - Lighting]
https://www.youtube.com/watch?v=5apJJKd4z-s
Camera movement, basic diffuse lighting (NdotL) and shadows!

Any feedback would be appreciated!
Last edited by WarriorMario on March 7th, 2017, 9:32 pm, edited 1 time in total.

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: Real-Time raytracing tutorials

Post by chili » February 5th, 2017, 4:56 am

The long-awaited Raytracing tutorial! I'll be checking this one out soon, and I'll post here again after I've gotten a chance to watch it.
Chili

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Real-Time raytracing tutorials

Post by albinopapa » February 5th, 2017, 5:25 am

Good stuff, subscribed and will be following.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
chili
Site Admin
Posts: 3948
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: Real-Time raytracing tutorials

Post by chili » February 9th, 2017, 3:22 am

The graphics were really well done, nice job. Now I need you to make scenes for me in my 3D Fundamentals series. :lol:

I expected there to be more code / episodes / time required before any results could be achieved, so nice work in setting it up such that you can get a simple scene rendered after only 10 min of tutorial.
Great math fundamentals explained here too. Not just useful for RT graphics, but also 3D physics, things like raycasting for picking objects or doing tests etc.

Looking forward to more of these, and I'm def. gonna give you a holla next time I do an update video for the channel. Keep up the good work bro, and thanks for sharing!
Chili

WarriorMario
Posts: 4
Joined: February 9th, 2015, 4:10 pm

Re: Real-Time raytracing tutorials

Post by WarriorMario » February 10th, 2017, 3:23 pm

Thanks for the positive responses! Hopefully I will be able to pump out the next video soon.
chili wrote:I expected there to be more code / episodes / time required before any results could be achieved, so nice work in setting it up such that you can get a simple scene rendered after only 10 min of tutorial.
Great math fundamentals explained here too. Not just useful for RT graphics, but also 3D physics, things like raycasting for picking objects or doing tests etc.
Yeah my goal was below 10 minutes and I got close ^^ As for the math, I didn't really want to go too indepth on it but then I kind off did... Glad that it turned out well though :D
chili wrote:Looking forward to more of these, and I'm def. gonna give you a holla next time I do an update video for the channel. Keep up the good work bro, and thanks for sharing!
That would be awesome!

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Real-Time raytracing tutorials

Post by albinopapa » February 13th, 2017, 9:06 pm

Hey, thanks for sharing. I've finally gotten around to watching the vid and poking around the code. I have messed around with ray tracers from others and haven't been able to break it down into consumable bytes :). I have a couple of questions if you are up for the challenge.
WarriorMario wrote:(getting 'interactive framerates' and near to the 30 FPS on the CPU )
How did you get that framerate? SIMD? Multi-threading? Both?

I've tried using SSE using the "as-is" layout of Vec3 ( using alignas(16) to properly align the structs ) and it was still pretty slow. I used std::async with multiple thread counts and it does help, but still nowhere near 30 ( I didn't actually test the framerates using these methods, but it was choppy ).

The next step was to restructure it so that four rays would be cast at a time. This works very well. I get around 25 fps single threaded and about 40 using std::async with multiple threads. The only problem I'm having is dealing with the cases where some rays hit and some rays don't. For instance, if I'm checking one of the spheres and say two of the rays hit the sphere, but the other two rays hit the back wall, I'm having troubles combining the results so that I can tell which objects were hit.

Any help would be appreciated.

Just to clarify:

Code: Select all

for( int i = 0; i < m_spheres.numSpheres; ++i )
{
	const Vec3p mSpherePosition( m_spheres.position[ i ].Load() );
	const __m128 mSphereRadius( _mm_load_ps1( &m_spheres.radius[ i ] ) );

	const auto mHitMask = RaySphereIntersection(
		mSpherePosition, mSphereRadius, mCamPos, mRayDirection, mRayLength );
	const auto mHitMaskI = _mm_castps_si128( mHitMask );

	const auto res = _mm_movemask_ps( mHitMask );				
				
	if( res != 0 )
	{
		hitIndex = i;
	}
}

if( hitIndex != -1 )
{
	for( int i = 0; i < 2; ++i )
	{
		// The mRayLength holds the lengths of the four rays so mHitPoint are the four points of 
		// intersection
		const auto mHitPoint = mCamPos + ( mRayDirection * mRayLength );
		
		const auto mLightPosition = mLightPositions[ i ];
		const auto mLightRayDelta = ( mLightPosition - mHitPoint );
		const auto mLightRayDirection = Normalize( mLightRayDelta );
		auto mLightRayLength = Dot( mLightRayDelta, mLightRayDirection );

		// If any of the spheres occlude the light
		bool occluded = false;

		for( int i = 0; i < m_spheres.numSpheres; ++i )
		{
			// Vec3p is a struct with three __m128 members ( X, Y, Z ).
			// When constructed with a single __m128 member, the first three elements are 
			// Swizzled/Shuffled so that X = {xxxx} Y = {yyyy} Z = {zzzz} the fourth SIMD element is 
			// ignored
			
			const Vec3p mSpherePosition( m_spheres.position[ i ].Load() );
			const __m128 mSphereRadius( _mm_load_ps1( &m_spheres.radius[ i ] ) );
			const auto occMask = RaySphereIntersection(
				mSpherePosition, mSphereRadius, mHitPoint,
				mLightRayDirection, mLightRayLength );

			const auto res = _mm_movemask_ps( occMask );
			occluded = res != 0;
			if( occluded ) break;
		}
		if( occluded == false )
		{
			const Vec3p mSpherePosition = m_spheres.position[ hitIndex ].Load();

			const auto mNormal = Normalize( mHitPoint - mSpherePosition );
			const auto mDot = Dot( mNormal, mLightRayDirection );
			const auto mD = _mm_max_ps( _mm_setzero_ps(), mDot );
			const auto &sphereColor = m_spheres.color[ hitIndex ];
			Vec3p mSphereColor(
				Vec4( sphereColor.y, sphereColor.z, sphereColor.w, sphereColor.x
				).Load() );

			finalColor = finalColor + (
				Vec3p( mLightColors[ i ].x * mSphereColor.x,
					   mLightColors[ i ].y * mSphereColor.y,
					   mLightColors[ i ].z * mSphereColor.z ) * mD );
		}
	}
}
See, here's what I'm confused on. Even if this was x86 code, you don't do any comparisons on which object was hit first. The spheres are pretty much laid out in back to front order. So even if there are multiple collisions, you are relying on the fact that the last sphere checked that collides with the ray is going to be the sphere you use for your color calculations.

Unfortunately, even knowing this, I don't know where to go from here to check for closest collisions and what info I would need to carry over. The hitIndex isn't going to help here.

Using the current code, the render looks like the pixels are 8x8 blocks.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Real-Time raytracing tutorials

Post by albinopapa » February 13th, 2017, 10:14 pm

Thought I'd share the code I'm using. It's not very pretty, but I think most of it is pretty much the same as you had it...naming wise. The render code is in SimdRenderer::Render

I threw all the Simd operator overloads and helper structs in Vec3.h/.cpp.
Attachments
RayTracerWM.zip
(255.07 KiB) Downloaded 122 times
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

WarriorMario
Posts: 4
Joined: February 9th, 2015, 4:10 pm

Re: Real-Time raytracing tutorials

Post by WarriorMario » February 13th, 2017, 11:14 pm

albinopapa wrote:How did you get that framerate? SIMD? Multi-threading? Both?
First of all, the raytracer could only handle triangles, basic light, relflections and refractions. So it was not a path tracer. If someone managed to get a path tracer with a somewhat complex scene running at 30 fps then we basically 'solved' graphics for games.

I might do a branch in the series to do some path tracing as well. Not sure about the targetted fps as path tracers aren't really about getting fps.

As for the raytracer I actually got the 30 fps without the reflections and refractions but nonetheless it takes some optimisation. SIMD is one thing but not the first thing, it was actually the last thing I did to optimise it. What I used was (in chronological order):
  • BVH, could also be used for spheres or any kind of geometry but it works best with big numbers
  • Multithreading which gives you roughly 80%(or 100% can't exactly remember but I will test this soon) performance increase per core and 20% increase per hyper threaded core. Not sure about async but it doesn't seem like it is performant.
  • Ray packets, you are somewhat heading in this direction with your 4 rays but it misses some crucial things.
  • SIMD, SSE and AVX(my laptop actually supports this :D )
Maybe I forgot something but as I will cover this in the tutorial series we'll find that out soon enough.
As for your performance issues, how many spheres are you using in your scene?
albinopapa wrote: See, here's what I'm confused on. Even if this was x86 code, you don't do any comparisons on which object was hit first. The spheres are pretty much laid out in back to front order. So even if there are multiple collisions, you are relying on the fact that the last sphere checked that collides with the ray is going to be the sphere you use for your color calculations.

Seems like you went ahead of the initial tutorial, anyways the comparison is actually done in the sphere intersection test. Whenever you calculate the new length of the ray segment you check whether that is smaller than the old length of the ray segment(the ray segment does not intersect the sphere because it didn't reach that far) and whether it is bigger than 0(we don't want to handle this, yet).

Now what would happen if we intersect two spheres? The first is closer than the second so we will reduce the ray's length. Now we test the second sphere and we do potentially intersect it but our new length will be bigger than our current length so we did not reach it and thus did not intersect it.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Real-Time raytracing tutorials

Post by albinopapa » February 14th, 2017, 1:19 am

Despite not having 32 cores, supplying async with 32 batches and letting her rip, I get close to 60 FPS with the SIMD changes.

I did a bunch of masking together sphere positions, radii and colors and almost have it working. The lines where the walls join are better, but the two spheres are still blocky.

Note: I think chili said std::async works from a thread pool so whenver I say thread, I mean batch maybe?
Here are a few test scores
( I split the batches into Graphics::ScreenHeight / numThreads )

original x86
1 thread = 8 fps
2 thread = 14 fps
4+ thread = 18 fps ( I only have a quad core, raising the count only stabilizes the fps )

SOA SIMD
1 thread = 26 fps
2 thread = 50 fps
4 thread = 56 fps
8+ thread = 60 fps

Next task, going to check out C++ AMP.

Specs
AMD 7870K A10 APU
3.8GHz
12 GB 1333 Ram
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

WarriorMario
Posts: 4
Joined: February 9th, 2015, 4:10 pm

Re: Real-Time raytracing tutorials

Post by WarriorMario » March 7th, 2017, 9:32 pm

Second tutorial is up!

Post Reply