Jon Baker, Graphics Programming

    home

    writings

AirplaneMode Seeding and Determinism

 At some point I want to start making some prints of raytraced or other generated images - not really something I have short term plans for, but it's an idea I want to pursue down the road. In any event, as an exercise to this end, I started messing with a method to generate scenes for AirplaneMode using deterministic random numbers, by using a Wang hash. This basically means that for some given seed value, which would seed the Wang hash, the scene and camera generation would be deterministic, and repeatable for any given output.

simple example

 This Wang hash is a common method for RNG - especially common in shaders, due to the simplicity of the code and inexpensive cost of computation. It doesn't have great guarantees on uniformity, etc, but it is often "good enough" for simple cases when you just need to be able to pull out some random numbers. Basically, the following is a very simple C++ class implementation:


	class wang {
	public:
		wang()             : seed( 0 ) {}
		wang( uint32_t s ) : seed( s ) {}
		uint32_t seed;
		void hash() {
			seed = ( seed ^ 12345391 ) * 2654435769;
			seed ^= ( seed << 6 ) ^ ( seed >> 26 );
			seed *= 2654435769;
			seed += ( seed << 5 ) ^ ( seed >> 12 );
		}

		// return a value in the range 0.0 - 1.0
		// cycle the hash, then divide seed by max value of uint32_t
		float getNum() {
			hash();
			return float( seed ) / 4294967295.0f;
		}
	};


 This is used for the scene generation and camera setup - for example, placing the individual primitives, setting the values for the vectors used by the LookAt() function for the camera. I still use the std::random header for the per thread RNG during the rendering, but it could just as well have a wang object per thread, with whatever neccesary mapping that I would use the std::random distributions for - it's just a little easier to use the standard library mapping functions.

simple example

 And what shakes out is a program where I can feed in this uint32_t value, and get a deterministic result - scene geometry, camera settings, all identical. If there's a good seed, I can re-run it with the same seed value, at a higher resolution and sample count. I ran this program in batch mode for a weekend while I was traveling, and produced some 5k outputs - upon return, sorted through them to find ones that I liked, and kept them for later - this is the reason why you'll notice a fair amount of noise in the outputs on this page - they were created with 64spp at a resolution of 640x480, essentially as ( large ) thumbnail previews. The seed value was encoded in the filename - by doing this, it is easily repeatable by putting this value into the wang hash at the beginning of the program. With the current setup, I was seeing a little less than 10% of the results being something I liked enough to maybe try to render again, which is still a pretty big pile to sift through.

simple example

 I will be revisiting this again in the future, to try to improve on this as a generative art tool with a preview function. I want to add more different scene geometry than the subdivided AABBs and spheres ( triangles, cylinders, etc ) - as it stands, making significant changes to the generation function will break the seeding system, unless it is implemented as additional primitives appended to the list after the current scene and camera generation. Since I'm the only user, and I have a version history in git, I'm not really worried about breaking changes like this. Another aspect to improve upon will be a BVH system for faster scene intersection that scales to larger numbers of primitives.


Last updated 3/8/2021