希望每个人都做得很好.Bon对此有一段时间的思考,出于好奇而不是任何事情.Been将Maya和Arnold一起使用了一段时间。只是为了爱好的东西,大多是简单的渲染来与我的路径追踪器进行比较.I意识到他们的渲染器有这个非常好的功能,可以让你看到它渲染的图像....渐进。它似乎从较低的采样和一个量开始,然后重新渲染图像,因为它自动增加这些参数。我想它真的很酷。并且在它们达到最高质量之前显示渲染预览的一种很好的方式。这让我非常感兴趣为我正在处理的路径跟踪器做同样的事情。目前它等待整个渲染完成之后它在你的驱动器上保存了一个简单的ppm文件。 我现在的问题是.....有谁知道这样的事情怎么办?我尽力找到并且我想出的唯一信息是OpenGL以某种方式参与其中。我不打算创建与Maya相同的东西。只是一个简单的窗口,当渲染开始并逐步制作时弹出图像更好。 再次......这比其他任何东西都更好奇。我觉得它真的很酷 谢谢:))
答案 0 :(得分:0)
它不以任何方式限制于OpenGL。您将渲染器设计为在单独的线程(可能是多个线程甚至多个机器)中运行,并逐步将部分结果发送到主线程。然后主线程创建一个窗口,显示这些结果。这里没有魔法。
答案 1 :(得分:0)
预览图像只是蒙特卡罗渲染中的第一轮样本(Arnold是)。这开始吵闹,然后提高质量'不一定是预期的功能。它与所有monte carlo渲染器一起存在,因为执行无偏差采样的性质意味着你从一些样本开始,其中许多可能是不准确的(在最终图像中产生噪声)。然后,随着越来越多的样本被射入场景(对于每个像素),结果将最终收敛于预期结果(噪声将减少并且不准确的样本贡献越来越少)。
蒙特卡罗渲染将永久地进行渲染,但是在一定数量的样本之后,每个贡献都将是次要的,因此被忽略(确定实际结果)。这就是为什么图像开始有噪声(不是很多样本,以及大量不准确的样本),然后逐渐提高其质量,因为越来越多的样本用于估计像素颜色。
渐进式采样是另一种优化,旨在减少收敛结果所需的时间。即如上所述,在可能贡献更多的区域中发射样本(即采样像素时差异更大,需要更高的精度,因此为此像素计算更多样本)。
P.S继续看着Scratch-a-pixel。它是一个很好的资源。
PPS OpenGL也可用于帮助进行纹理/图像分析(决定哪些像素要采样甚至更多等),或用于通过将几何图形绘制到屏幕外缓冲区来加速相交测试(仅使用我使用的两种方式)它在过去)。然而,这将取决于实施。默认情况下,OpenGL不提供光线跟踪系统所需的任何内容。
关于显示渲染。
首先创建一个与您需要的输出图像尺寸相同的帧缓冲区。这将有效地是未压缩的RGB24或RGBA32图像。使用与所需显示输出相关的格式(因此可以在有限延迟的情况下完成复制,并且不需要直接显示的转换/处理)。还将包括另一位元信息,每个像素跟踪像素当前使用的样本数。这允许结果填充帧缓冲器彼此互斥。即你可以在你需要的区域(自适应)发射更多像素,你可以选择在需要时显示帧缓冲的内容,同时继续在同一渲染上下文中逐帧采样(渐进)。
这个帧缓冲区应该在主循环的每个循环中保持不变,以便每个循环的结果被累积到帧缓冲区中。为了计算单个像素的结果,它通常是该像素的所有样本的总和除以样本的总数(其他采样方法可能相应地对样本进行加权),用于像素的标准抖动网格采样。
要将此图像呈现给用户,这取决于您使用的api,但是您需要以相同的方式显示帧缓冲区以显示图像/位图。我亲自做过的方式:
使用framebuffer作为纹理在openGL中绘制纹理四边形(因此您需要使用每帧的帧缓冲内容更新纹理)。
使用windows gdi将DIB位图呈现给控件。
输出为未压缩的图像格式(可以快速完成二进制PPM,或TGA / tiff /未压缩位图直接复制帧缓冲区的内容)或压缩图像,如png或jpg。
以下是一些代码,实现取决于您选择使用哪种api,但希望这足以伪以描述具体细节。它是c-esque。
声明/定义
// 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];
然后在你的init方法中。要初始化帧缓冲区,将其设置为黑色图像(这很重要,因为我们要将第一个样本添加到0,0,0)。
// 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.
...
N.B GL和gdi都以不同的方式期待图像。因此,您可能需要将图像水平翻转为正确的方向。这取决于你在内部存储帧缓冲的方式以及用于显示帧缓冲的api。
这有希望展示如何编写一个系统,该系统将逐步显示图像的内容,因为计算的图像越来越多。它适用于任何形式的光线跟踪(如前所述,monte carlo会根据模拟的性质产生噪声,偏差渲染可能会或可能不会取决于它们的工作方式。通常它不会比图像的抗锯齿更多,虽然有偏差的渲染可能会出现噪音。)