Opengl 路径跟踪器的简单渐进式渲染窗口

Opengl 路径跟踪器的简单渐进式渲染窗口,opengl,rendering,maya,raytracing,Opengl,Rendering,Maya,Raytracing,希望每个人都做得很好。出于好奇,我已经思考了一段时间。现在已经将Maya与Arnold一起使用了一段时间。只是出于爱好,大多数渲染都比较简单,可以与我的路径跟踪器进行比较。我意识到他们的渲染器有一个非常好的功能,可以让你在渲染时看到图像。。。。循序渐进。它似乎从较低的采样和aa量开始,然后随着自动增加这些参数而重新渲染图像。我认为这真的很酷。这是一种在渲染达到最高质量之前显示预览的好方法。这让我非常有兴趣对我正在使用的路径跟踪器执行同样的操作。目前它是等待整个渲染完成,然后在驱动器上保存一个简单

希望每个人都做得很好。出于好奇,我已经思考了一段时间。现在已经将Maya与Arnold一起使用了一段时间。只是出于爱好,大多数渲染都比较简单,可以与我的路径跟踪器进行比较。我意识到他们的渲染器有一个非常好的功能,可以让你在渲染时看到图像。。。。循序渐进。它似乎从较低的采样和aa量开始,然后随着自动增加这些参数而重新渲染图像。我认为这真的很酷。这是一种在渲染达到最高质量之前显示预览的好方法。这让我非常有兴趣对我正在使用的路径跟踪器执行同样的操作。目前它是等待整个渲染完成,然后在驱动器上保存一个简单的ppm文件。 我现在的问题是……有人知道这样的事情怎么做吗?我已经尽了最大的努力找到了答案,我得到的唯一信息是OpenGL不知何故参与其中。我不想创建与Maya相同的东西。只是在渲染开始时弹出一个简单的窗口,并逐步使图像变得更好。 再说一次……这比其他任何东西都更令人好奇。尽管我认为这真的很酷
谢谢:)

它在任何方面都不限于OpenGL。将渲染器设计为在单独的线程(可能是多个线程,甚至多台计算机)中运行,并逐步将部分结果发送到主线程。然后,主线程创建一个窗口,在这些结果出现时显示这些结果。这里没有魔法。

预览图像只是蒙特卡罗渲染中的第一轮样本(阿诺德是)。这种“开始时有噪音”,然后“提高质量”不一定是预期的功能。它存在于所有蒙特卡罗渲染器中,因为执行无偏采样的性质意味着您从一些采样开始,其中许多采样可能不准确(在最终图像中产生噪声)。然后,随着越来越多的样本发射到场景中(对于每个像素),结果最终将收敛到预期结果(噪声将减少,不准确的样本贡献越来越少)

蒙特卡罗渲染将永远进行渲染,但是在一定数量的样本之后,每个贡献都将很小,因此会被忽略(取决于实际结果)。这就是为什么图像开始有噪声(样本不多,且大量样本不准确),然后随着越来越多的样本用于估计像素颜色,图像质量逐渐提高的原因

渐进式采样是另一种优化,旨在减少收敛结果所需的时间。i、 e如前所述,在可能贡献更多的区域触发采样(即采样像素时差异越大,需要的精度越高,因此为此像素计算更多采样)

请继续关注每一个像素的划痕。这是一个极好的资源

p.p.S还可以使用OpenGL来辅助纹理/图像分析(决定哪些像素需要采样更多等等),或者通过将几何图形绘制到屏幕外缓冲区来加速相交测试(这只是我过去使用的两种方法)。然而,这将取决于实施。默认情况下,OpenGL不提供任何光线跟踪系统所需的内容

关于显示渲染

首先创建一个与所需输出图像尺寸相同的帧缓冲区。这将有效地成为未压缩的RGB24或RGBA32图像。使用与所需显示输出相关的格式(这样可以在有限的延迟时间内完成复制,并且直接显示不需要转换/处理)。Is还将包括另一位元信息,每个像素跟踪像素当前使用的采样数。这允许结果填充相互排斥的帧缓冲区。i、 e您可以在需要的区域激发更多像素(自适应),并在需要时选择显示帧缓冲区的内容,同时继续在同一渲染上下文中采样像素(渐进)

该帧缓冲区应在主循环的每个周期中保持,以便将每个循环的结果累积到帧缓冲区中。要计算单个像素的结果,它通常是该像素的所有采样的总和除以像素的标准抖动网格采样的采样总数(其他采样方法可能会相应地对采样进行加权)

要向用户显示此图像,这取决于您使用的api,但您只需以显示图像/位图的相同方式显示帧缓冲区即可。我个人的做法:

  • 在openGL中绘制一个带纹理的四边形,使用帧缓冲区作为纹理(因此需要使用每帧帧帧缓冲区的内容更新纹理)

  • 使用windows gdi将DIB位图渲染到控件

  • 输出为未压缩图像格式(可快速转换为二进制PPM或TGA/tiff/未压缩位图,用于直接复制帧缓冲区的内容)或压缩图像,如png或jpg

下面是一些代码,实现取决于您选择使用哪种api,不过希望这是一个伪代码,足以详细描述正在发生的事情。这是c风格的

声明/定义

// number of pixels in the resultant final render image.
unsigned int numberOfPixels = imageWidth * imageHeight;

// number of channels of ouput image (also ray result) RGB 3 channels.
unsiged int channelSize = 3; 

// RGB pixel. Each channel/colour component is in the range 0 <= ... <= 1
struct pixel
{
    float red;
    float green;
    float blue;
};

// framebuffer, 3 channels RGB
pixel frameBuffer[numberOfPixels];

// framebuffer meta data. Number of samples for each pixel.
int pixelSampleCount[numberOfPixels];
//结果最终渲染图像中的像素数。
无符号整数numberOfPixels=图像宽度*图像高度;
//输出图像(也是光线结果)RGB 3通道的通道数。
未设定的int channelSize=3;

//RGB像素。每个通道/颜色分量在0范围内。堆栈溢出用于特定编程问题。问“如何完成这个大项目”并不具体。明白了。已经找到了一篇关于这个问题的文章。现在就读。谢谢你的帮助。哦,好的。我在读一篇关于ScratchaPixel的文章,似乎自适应采样也涉及到了这一点
// your init routine
...   

    for (unsiged int p = 0; p < numberOfPixels; ++p )
    {
        // initialise the framebuffer to black (0,0,0)
        frameBuffer[p].red = 0.0;
        frameBuffer[p].green = 0.0;
        frameBuffer[p].blue = 0.0;

        // set the sample count to 0
        pixelSampleCount[p] = 0;
    }

...
// your main loop
...

// Main loop...each cycle we will cast a single sample for each pixel. Of course you can get as many sample results as you want if you
// intelligently manage the casting (adaptive), ensure each cast knows which pixel it is contributing to, so when it comes to accumulation of
// this sample result, it can be amended to the correct pixel and the correct sample count incremented as you add to the framebuffer.

for ( unsigned int x = 0; x < imageWidth; ++x )
{
    for ( unsigned int y = 0; y < imageHeight; ++y )
    {
         // get the result of the sample for this pixel (e.g cast the ray for this pixel, jittered according to sampling method). Ultimately
         // each sample needs to be different (preferably unique and random) from the previous cycle and will return a different result.
         pixel castResult = GetSampleResult(x, y, ... );    // aka cast the ray and get the resultant 'colour'

         // Get the current pixel from the frame buffer read to ammend it with the new sample/contribution. 
         unsigned int currentPixelIndex = (y * imageWidth) + x;
         pixel& pixelOfSample = frameBuffer[currentPixelIndex];

         // to correctly accumulate this sample, we must first multiply (scale up) each colour component
         // by the number of samples/contributions to this pixel. We can then add the sample result and divide
         // (scale down) the result (sum of all samples now) by the new number of samples.
         pixelOfSample.red = ( (pixelOfSample.red * pixelSampleCount[currentPixelIndex]) + castResult.red ) / ( pixelSampleCount[currentPixelIndex] + 1 );

        // repeat this for the rest of the components in the pixel, i.e for green and blue in this case.
        pixelOfSample.green = ( (pixelOfSample.green * pixelSampleCount[currentPixelIndex]) + castResult.green ) / ( pixelSampleCount[currentPixelIndex] + 1 );
        pixelOfSample.blue = ( (pixelOfSample.blue * pixelSampleCount[currentPixelIndex]) + castResult.blue ) / ( pixelSampleCount[currentPixelIndex] + 1 );

          // increment the sample count for this pixel.
          ++pixelSampleCount[currentPixelIndex];
     }
}

// And then send this to your output gdi/opengl/image output etc.
// For displaying direct in gdi, use BitBlt(...) with SRCCOPY.
// For displaying in OpenGL use glTexture2D(...)

glTexture2D(...);    // for OpenGL
BitBlt(...);         // for win gdi

// The next loop you can simply display the framebuffer (it would look the same as previous cycle) or you can fire a load of rays and then add this to your framebuffer and display that, giving you a different display.

...