【问题标题】:2D Dynamic Lighting in JavaJava 中的 2D 动态光照
【发布时间】:2014-01-20 11:12:26
【问题描述】:

我正在制作一个包含篝火对象的游戏。我想要做的是照亮每个篝火周围圆圈中的所有像素。但是,循环遍历每个像素并更改半径内的像素并不是那么有效,并且会使游戏以约 7 fps 的速度运行。关于如何提高此过程效率或以不同方式模拟光线的想法?

我还没有为火灾编写代码,但这是检查每个像素/根据数字更改其亮度的基本循环:

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;
}

如果我的代码不干净,我很抱歉,我是自学的。

【问题讨论】:

  • 这个问题似乎离题了,因为它更适合codereview.stackexchange.com
  • 但是,我建议您考虑对称性:如果 (x, y) 处的像素在以 (0, 0) 为中心的圆内,那么 (-x, y), (x , -y) 和 (-x, -y)。
  • 不一定是 jonrsharpe ,我认为这里的替代方法比代码打他的实现更重要。

标签: java 2d lighting


【解决方案1】:

我可以给你很多方法。

您当前正在 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 变得非常轻松。

【讨论】:

  • 那只是粗略的代码。如果我不是那样做,我会交叉检查每个像素位置和每个灯光位置,然后根据与光源的距离改变灯光。但正如你所说,性能很糟糕。在您的第一种方法中,您将如何找到最接近光线的像素,除了逐步查看像素以查看它们是否接近之外,我想不出其他方法。
  • 更新了问题来回答你,因为评论格式很糟糕。赞一个怎么办? :P
【解决方案2】:

我不确定您计算光照值的方式,但我知道使用 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,这意味着您在设置颜色时必须在数字中包含 alpha 值 (RRGGBB*AA*)

由于此数组是一维数组,因此使用 x 和 y 坐标访问像素更加困难。下面的方法是一种更容易从 lightPixels 数组中访问像素的实现。

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

*注意:width 是你的关卡的宽度,或者你的关卡可能存在的二维数组的宽度,如果它是一个二维数组的话。

也可以用类似的方法从lightPixels数组中获取像素,只是排除,返回array[width*y+x]

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

希望对你有帮助

【讨论】:

    猜你喜欢
    • 2015-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多