我正在编写一个软件过滤器对象,并试图实现一个光绽放效果。我使用的是一种简单的两次卷积方法,除了效果半径很小,而且我似乎无法控制半径之外,这种方法效果很好。我玩过更大的盒子过滤器,并调整了各种像素的权重,但这些似乎都没有任何效果。效果似乎有一个最大大小(不是很大),然后对参数的所有更改都会使其变小。
我希望能够创建一个任意半径的光源。经过大量的实验和网上搜索,我开始怀疑这是否是不可能的。我一直在考虑其他方法——等离子体、梯度和各种播种方案——但我想先找到这条进入地面的路径。有人知道如何(在软件中)创建任意大小的光束吗?
javascript如下(这在HTML5画布上操作;如果需要,我可以在代码中添加注释):
// the kernel functions are called via Array.map on this.backBuffer.data, a canvas surface color array
this.kernelFirstPass = function(val, index, array)
{
if(index<pitch || index>=array.length-pitch || index%pitch<4 || index%pitch>pitch-5 || index%4==3)
return;
var c = 1,
l1 = 1,
l2 = 1,
l3 = 1,
r1 = 1,
r2 = 1,
r3 = 1;
var avg =
(
c*this.frontBuffer.data[index]+
l1*this.frontBuffer.data[index-4]+
l2*this.frontBuffer.data[index-8]+
l3*this.frontBuffer.data[index-12]+
l1*this.frontBuffer.data[index+4]+
l2*this.frontBuffer.data[index+8]+
l3*this.frontBuffer.data[index+12]
)/(c+l1+l2+l3+l1+l2+l3);
//this.frontBuffer.data[index] = avg;
array[index] = avg;
}
this.kernelSecondPass = function(val, index, array)
{
if(index<pitch || index>=array.length-pitch || index%pitch<4 || index%pitch>=pitch-4 || index%4==3)
return;
var c = 1,
l1 = 1,
l2 = 1,
l3 = 1,
r1 = 1,
r2 = 1,
r3 = 1;
var avg =
(
c*array[index]+
l1*array[index-pitch]+
l2*array[index-(pitch*2)]+
l3*array[index-(pitch*3)]+
l1*array[index+pitch]+
l2*array[index+(pitch*2)]+
l3*array[index+(pitch*3)]
)/(c+l1+l2+l3+l1+l2+l3);
array[index] = avg;
}
也许我在最初的问题中遗漏了一个重要的点,那就是解释我并没有试图模拟任何真实或特定的现象(我称之为"光"绽放可能于事无补)。在处理真实的光现象时,为了获得任意半径的半影,你可能需要一个任意半径的光源(即"完全饱和区域")。如果真的是这样的话,那么吉姆和茨库兹的解释似乎是合理的模拟方法。无论如何,这不是我想要实现的。我想独立于尺寸/强度等来控制花朵"梯度"部分的半径。源的。我希望能够在屏幕中央设置一个白色(最大值)像素,并让花朵长到我想要的地方,长到屏幕边缘或更远(如果我喜欢的话)。
为了获得良好的绽放效果,您应该使用高动态范围渲染。否则,你的白色会不够亮。
其原因是像素亮度通常在[0,1]范围内表示。因此,最大亮度被箝位为1。然而,在现实世界中,并没有真正的最大值。尽管真正明亮的灯光都被认为是"1",但像绽放这样的视觉副作用并不相同。
所以你要做的是允许真正明亮的区域超过最大亮度,至少对于bloom卷积。然后,在进行渲染时,根据需要钳制值。
完成后,您应该能够通过增加卷积中使用的Airy圆盘的大小来增加bloom半径。
tskuzy的答案的简单总结是:使用浮点缓冲区来存储布隆前的图像,然后卷积到第二个浮点缓冲区(从中将像素饱和为整数格式),或者在将每个输出像素直接存储在整数输出缓冲区之前,动态饱和以将其转换为整数。
Airy卷积必须有净空(即定点或浮点,而现在前者通常不值得用如此常见的快速FPU来麻烦),这样图像中较亮的点就会相应地在其邻近区域流血更多。
注意:色彩的动态饱和度并不像单独剪裁通道那么简单——如果你这样做,你可能会在剪裁的斑点周围出现色调失真和轮廓。