Random functions in raygen

I am using a few simple calculations to compute the direction of the ray inside the raygen program. The directions are supposed to be points on a unit circle. It works fine for the first ray but gives wrong values like 7.233e-44 or 0 for the rest of the rays. The direction calculation code works when I run it as a standalone program.

__raygen__rg(){
//some code
thrust::minstd_rand rng;
thrust::uniform_real_distribution<float> dist(0,1);
const float cosphi = (dist(rng)*2)-1;
const float phi = acos(cosphi);
const float theta = dist(rng)*2*M_PI;
const float X = sin(phi)*cos(theta);
const float Y = sin(phi)*sin(theta);
const float Z = cosphi;
const vec3f direction = vec3f(X,Y,Z);
//some code
}

OS:Ubuntu 18.04
CUDA: V10.1.243
OptiX: 7.0
GPU: GP100
DIsplay drive: 435.17

Hi a_s_d,

It clearly must be that the random number generator is not working the way you expect it to.

Don’t you need to call your rng function? For example: dist(rng()), rather than dist(rng)

Try printing out the numbers from rng before doing any math, to verify. Make sure they change for every thread.

Have you read about how to use the Thrust library inside CUDA device functions? I’ve never used it, but it looks like you need to specify a device execution policy. Have you done that?

There is a simple random number generator used in the OptiX SDK samples that you could use instead of Thrust to test with. You might also use some regular samples instead of random to make sure your code is working. For example optixGetLaunchIndex() divided by optixGetLaunchDimensions() will give you uniform regular samples.

Incidentally, you might want to use the single precision sinf/cosf/acosf since the versions without the ‘f’ prefix are all slower double precision functions.


David.

Hi David,
Thank you for your quick response.

Do you mean the one in the cuda subdirectory or the one in gdt?
I tried the random function from the cuda directory and it didn’t work either. There could be something wrong in my usage of it though, I am not really a programmer.

__raygen__rg(){
const int ix = optixGetLaunchIndex().x; //This is a single dimension launch.
uint32_t seed = tea<4>(ix,124);
const float r1 = rnd(seed);
const float r2 = rnd(seed);
//direction calculation
}

What happens when you use tea+rnd()? Are you checking the random numbers directly? Does your optixPathTracer_exp sample run correctly?

How are you getting your output that looks incorrect? Maybe the bug is in the connecting code that’s not shown here, and not in the random number generator. Your thrust usage looked correct, btw, dist(rng) is right, I guess I should try thrust.


David.

Yes. I am copying the random numbers and the computed directions directly to a buffer and analyzing them. When plotted, the direction points should form a uniform unit sphere. The random numbers for the first Ray are correct but the rest of the days tend to be very very close to zero. I am getting numbers in e-34 to e-44 ranges(sometimes positive powers). It is quite confusing as I can see that the code is working fine for the first ray. It is the same output for tea()+rnd. The pathtracer example works without any problem though.

I can recommend a few things to try.

Skip all the computation and set your output to be (r1,r2,0), make sure when you plot it, it looks like a flat square rather than a sphere.

If it’s a flat square, then the problem is not your random number generator. Change the output float3 to include one computation at a time until you can see which one causes the issue.

If it’s not a flat square, then either your random number generator is broken, or your indexing into your output buffer is wrong or something like that.

Make your launch very small and put a printf of the assigned variable after every single line. Inspect all values to see where things are going wrong.

Use cuda-gdb to step through your program line by line and see what’s wrong. The easiest way to do this is by converting your raygen program to a standard CUDA kernel and invoke it using the triple-chevron syntax <<< >>>, and compiling with -G.

You could post a complete raygen program that reproduces the problem, so I can see all of it. Despite my early confidence that it had something to do with the random number generator, it’s looking like the problem might be in some code you haven’t shown yet.


David.

In general the initial code is not the proper structure to produce different random numbers. The random number generator initialization needs to vary per launch index. Otherwise it would generate the same random number every time.

I haven’t used thrust inside OptiX device code either. Looking at the implementation you used, thrust::minstd_rand implements a simple linear congruential generator (LCG) and that can be done in two lines of device code instead.

The function float rng(unsigned int& prev) in here does that. Same as in the OptiX SDK examples.
https://github.com/nvpro-samples/optix_advanced_samples/blob/master/src/optixIntroduction/optixIntro_07/shaders/random_number_generators.h

Here’s some code implementing a uniform sampling of a sphere:
https://github.com/nvpro-samples/optix_advanced_samples/blob/master/src/optixIntroduction/optixIntro_07/shaders/light_sample.cu#L47

The code in post #3 looks correct and the problem would be somewhere else then.
Please provide the rest of the raygen code which writes the output and the host code which sets up that output buffer and reads it.

The complete raygen program:

extern "C" __global__ void __raygen__renderFrame()
    {
        const int ix = optixGetLaunchIndex().x;

        PhotonPRD prd;
        uint32_t seed = tea<4>(0,143);
        const float r1 = rnd(seed);
        const float r2 = rnd(seed);
        const float k1 = r1 * 2 * M_PI;
        const float k2 = (r2 *2) - 1;
        const float theta = k1;
        const float phi = acos(k2);
        const float X = sin(phi) * cos(theta);
        const float Y = sin(phi) * sin(theta);
        const float Z = cos(phi);
        const vec3f direction = vec3f(X,Y,Z);
        vec3f origin = vec3f(1.2f,1.2f,1.2f);
        prd.origin = origin;
        prd.done = 0;
        prd.absorbed = 0;
        prd.escaped = 0;
        prd.point = vec3f(0.0f,0.0f,0.0f);
        prd.probability = 0;
        prd.direction = direction;

        uint32_t u0,u1;
        packPointer( &prd,u0,u1);
        optixTrace(optixLaunchParams.traversable, origin, direction,0.0f,1e20f,0.0f,OptixVisibilityMask( 255 ), OPTIX_RAY_FLAG_DISABLE_ANYHIT, SURFACE_RAY_TYPE, RAY_TYPE_COUNT, SURFACE_RAY_TYPE,u0,u1);
        const vec3f interPoint = prd.point;
        optixLaunchParams.pointBuffer[ix] = direction;
    }

The code in the main.cpp:

sample.render();
sample.downloadPoints(points.data());

std::ofstream ofile;
ofile.open("output.csv", std::ios::app);
for(int i =0; i<5000;i++)
{
     ofile << points[i].x <<","<<points[i].y<<","<<points[i].z <<std::endl;
}
ofile.close();

The buffers for launchParameters(I am also using the CUDABuffer.h from Ingo Wald’s tutorial code):

LaunchParams launchParams;
CUDABuffer launchParamsBufer;
CUDABuffer pointBuffer;

My LaunchParams.h structure:

struct LaunchParams
    {

        vec3f *pointBuffer;
        uint32_t *events;
        vec2i size;

        OptixTraversableHandle traversable;

    };

The download function(used in main):

void Simulator::downloadPoints(vec3f h_points[])
    {
        pointBuffer.download(h_points,launchParams.size.x*launchParams.size.y);

    }

I don’t think the random function is the problem as when I tried to output (r1,r2,0), the first instance was correct but for the rest, even 0 has been replaced with some value.

My render function(used in main.cpp, above):

void Simulator::render()
    {
        if (launchParams.size.x == 0) return;

        launchParamsBuffer.upload(&launchParams,1);

        OPTIX_CHECK(optixLaunch(pipeline,stream,
                                launchParamsBuffer.d_pointer(),
                                launchParamsBuffer.sizeInBytes,
                                &sbt,
                                launchParams.size.x,
                                launchParams.size.y,
                                1
                                ));

        CUDA_SYNC_CHECK();

}

Is launchParams.size.y == 1?
Hmm, even if not it should have filled the first row with launchParams.size.x directions.

What happens if you do not call optixTrace()?
Have you tried adding an exception program? If there are exceptions the results will not be written to the output buffer.

Yes, the launchParams.size.y is 1. The result is the same even if I don’t call optixTrace(). It works for the first ray and then gives wrong values for the rest. I have an exception handling function. It doesn’t seem to catch any exceptions. I think I know the problem, I used points.shape() instead of 5000 in the for loop in main and I only got value for one ray. I think that only a single ray is actually being cast and I was reading garbage values for the rest. I will check my program once more.

It is launching properly(verified by printing launch ID), when I hard code the launch size into the optixLaunch function, but it is overwriting the output in the the buffer so I am only left with the output of a single ray.

Got it working perfectly. There was a type conversion error in the main program. Thank you for all the help.