2014-01-06 31 views
3

我正在制作一款具有篝火对象的游戏。我想要做的是让每个篝火周围的所有像素变亮。但是,遍历每个像素并更改半径范围内的像素并不是那么有效,并且使游戏运行速度达到7 fps。关于如何使这个过程高效或以不同方式模拟光的想法?Java中的2D动态照明

我没有写对火灾的代码,但这个是基本的循环检查每个像素/更改基于它的一些亮度:

public static BufferedImage updateLightLevels(BufferedImage img, float light) 
{ 
    BufferedImage brightnessBuffer = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_4BYTE_ABGR); 

    brightnessBuffer.getGraphics().drawImage(img, 0, 0, null); 

    for(int i = 0; i < brightnessBuffer.getWidth(); i++) 
    { 
     for(int a = 0; a < brightnessBuffer.getHeight(); a++) 
     { 
      //get the color at the pixel 
      int rgb = brightnessBuffer.getRGB(i, a); 

      //check to see if it is transparent 
      int alpha = (rgb >> 24) & 0x000000FF; 

      if(alpha != 0) 
      { 
       //make a new color 
       Color rgbColor = new Color(rgb); 

       //turn it into an hsb color 
       float[] hsbCol = Color.RGBtoHSB(rgbColor.getRed(), rgbColor.getGreen(), rgbColor.getBlue(), null); 

       //lower it by the certain amount 
       //if the pixel is already darker then push it all the way to black 
       if(hsbCol[2] <= light) 
        hsbCol[2] -= (hsbCol[2]) - .01f; 
       else 
        hsbCol[2] -= light; 

       //turn the hsb color into a rgb color 
       int rgbNew = Color.HSBtoRGB(hsbCol[0], hsbCol[1], hsbCol[2]); 

       //set the pixel to the new color 
       brightnessBuffer.setRGB(i, a, rgbNew); 
      } 


     } 
    } 

    return brightnessBuffer; 
} 

我道歉,如果我的代码是不是干净,我自我教导。

+0

这个问题似乎是题外话,因为它更适合于http://codereview.stackexchange.com/ – jonrsharpe

+0

不过,我会建议你考虑对称性:如果在像素( x,y)在以(0,0)为中心的圆内,因此是(-x,y),(x,-y)和(-x,-y)。 – jonrsharpe

+0

不一定jonrsharpe,我认为替代方法在这里更重要,而不仅仅是代码打高尔夫他的实现。 – SpacePrez

回答

3

我可以给你很多方法。

您目前正在CPU上渲染,并且您正在检查每一个像素。这是硬核蛮力,蛮力并不是CPU最擅长的。它的工作原理,但正如你所看到的,表现是糟糕的。

我会点你在两个方向,将大规模提高你的表现:

方法1 - 剔除。每个像素是否真的需要计算其照明?如果您可以改为计算一般的“环境光”,那么您可以在该环境光线中绘制大部分像素,然后只计算最接近灯光的像素的真正适当光照;所以灯会产生一个淡入环境的“斑点”效应。这样,你一次只能对屏幕上的几个像素(每个灯周围的圆圈区域)执行检查。您发布的代码看起来像是绘制每个像素,我没有看到甚至应用了“圆圈”放置的位置。

编辑:

相反,通过灯扫描,并通过光位置的本地偏移只是循环。

for(Light l : Lights){ 

for(int x = l.getX() -LIGHT_DISTANCE, x< l.getX() + LIGHT_DISTANCE, y++){ 

for(int y = l.getY() - LIGHT_DISTANCE, y < l.getY() + LIGHT_DISTANCE, y++){ 

//calculate light 
int rgb = brightnessBuffer.getRGB(x, y); 
//do stuff 
} 

} 

您可能需要添加与方法的检查,以便重叠灯不引起一堆复查的,除非你想这种行为(理想情况下的像素会是两倍亮)

方法2 - 离线计算到GPU。有一个原因,我们有显卡;他们专门建立起来,可以对那些你真的需要蛮力的情况进行数字化处理。如果您可以将该过程作为着色器卸载到GPU,那么即使您在每个像素上运行几次,它也会运行licketysplit。然而,这需要您学习图形API,但如果您使用的是Java,则LibGDX使用GPU进行渲染并将一些着色器传递给GPU非常容易。

+0

那么这只是粗略的代码。如果我这样做,我不会这样做,我会在每个灯光位置交叉检查每个像素位置,然后根据距离光源的距离改变灯光。但正如你所说,表现是糟糕的。在你的第一种方法中,你将如何寻找最接近光线的像素,除了单步穿过像素来看它们是否接近之外,我想不出其他方法。 – Johndel

+0

更新了问题以回答您,因为评论格式很糟糕。 upvote怎么样? :P – SpacePrez

+0

没有足够的声望upvote:[但谢谢你。 – Johndel

0

我不确定你计算灯光值的方式,但我知道使用BufferedImage.getRGB()和BufferedImage.setRGB()方法非常慢。

我建议直接从数组访问BufferedImage的像素(快得多IMO)

做到这一点:

BufferedImage lightImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB); 
Raster r = lightImage.getRaster(); 
int[] lightPixels = ((DataBufferInt)r.getDataBuffer()).getData(); 

现在,改变这个阵列中的任何像素将显示在图像上。请注意,此数组中使用的值是以您定义图像时使用的任何格式的格式的颜色值。

在这种情况下它被TYPE_INT_ARGB意味着你将不得不设置coloar(RRGGBB * AA *)时

由于该阵列是一维数组,这是更难以以包括数α值使用x和y坐标访问像素。以下方法是更轻松地访问lightPixel数组中像素的实现。

public void setLight(int x, int y,int[] array,int width, int value){ 
    array[width*y+x] = value; 
} 

*注:宽度是你的水平的宽度,或二维数组你的水平可能存在的,如果它是一个二维数组的宽度。

您还可以使用类似的方法从lightPixel数组中获取像素,只是不包括并返回array[width*y+x]

如何使用setLight()和getLight()方法取决于您,但在遇到这种情况时,使用此方法比使用getRGB和setRGB快得多。

希望这有助于