2014-01-06 59 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快得多。

希望這有助於