我该如何渲染2D手电筒效果?

我想在2D游戏中制作手电筒效果。 我的手电筒表示为从特定角度的实体延伸的线段。 手电筒可以指向任何方向。 手电筒的强度也不同(手电筒光束的长度)。

我有一个问题,试图找出最好(最简单?)和最灵活的方式来渲染手电筒效果。 特别是平铺地图。

我可以想到两种方法。 但我不知道实现它们:

  1. 仅绘制平铺地图的圆锥/圆弧部分
  2. 用黑色纹理覆盖屏幕,并用黑色纹理打孔。 这样我就可以改变洞的属性。

如果libGDX能够做到这一点,那么我对这个/这些被调用的内容一无所知我一无所知。

第一个称为模板缓冲区。 但是你会发现很难达到软效果。 – 简单。

第二:你只需要一个带光的纹理。 可以绘制完全黑色区域,重复亮光精灵周围的黑色精灵(左,右,上,下)。 或者您可以将其与模板缓冲区混合使用。 或者您可以仔细计算纹理坐标,并使用GL.clamp_to_edge传播所有黑色像素。 根据渲染场景的方式,您可以先使用Alpha信息渲染光源,然后混合场景(根据dst_alpha变暗)。 – 并不难实现。

第三个是对着色器的研究(GLES 2.0)。 您可以渲染网格以填充孔屏幕并使用某些着色器计算使其变暗。 – 这是最灵活的选择,也是最困难的选择(无论如何都远离火箭科学)。

有些选项比其他选项听起来更好,但提供您提供的信息,我无法告诉您。 你有一个很好的起点,从哪里开始研究。

**如果您选择使用纹理,请根据电池考虑使用几种不同的纹理。 你可以稍微缩放它们或者对它们进行着色,但是使用不同的纹理会更灵活。

如果图形元素是BufferedImageGraphics2D实例,您可能会像这样接近它。

手电筒使用白炽(黄色)光束

手电筒使用卤素(蓝色)光束

 import java.awt.*; import java.awt.geom.*; import java.awt.image.BufferedImage; import javax.swing.*; public class FlashLight { public static void main(String[] args) throws Exception { Robot robot = new Robot(); int w = 500, h = 200; Rectangle rect = new Rectangle(0, 0, w, h); final BufferedImage bi = robot.createScreenCapture(rect); final BufferedImage bi2 = FlashLight.draw( bi, 10, 180, 420, 90, .3, new Color(255, 255, 120, 15), new Color(0, 0, 0, 220)); final BufferedImage bi3 = FlashLight.draw( bi, 10, 180, 420, 90, .3, new Color(180, 250, 255, 15), new Color(0, 0, 0, 220)); Runnable r = new Runnable() { @Override public void run() { JPanel gui = new JPanel(new GridLayout(3,0,2,2)); gui.add(new JLabel(new ImageIcon(bi2))); gui.add(new JLabel(new ImageIcon(bi))); gui.add(new JLabel(new ImageIcon(bi3))); JOptionPane.showMessageDialog(null,gui); } }; // Swing GUIs should be created and updated on the EDT // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html SwingUtilities.invokeLater(r); } public static BufferedImage draw( BufferedImage source, double x1, double y1, double x2, double y2, double beamWidth, Color beamColor, Color darknessColor) { RenderingHints hints = new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); BufferedImage bi = new BufferedImage( source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g = bi.createGraphics(); g.setRenderingHints(hints); g.drawImage(source, 0, 0, null); // Create a conical shape to constrain the beam double distance = Math.sqrt(Math.pow(x1 - x2, 2d) + Math.pow(y1 - y2, 2d)); double tangent = (y2 - y1) / (x2 - x1); double theta = Math.atan(tangent); System.out.println( "distance: " + distance + " tangent: " + tangent + " theta: " + theta); double minTheta = theta + beamWidth / 2; double maxTheta = theta - beamWidth / 2; double xMin = x1 + distance * Math.cos(minTheta); double yMin = y1 + distance * Math.sin(minTheta); double xMax = x1 + distance * Math.cos(maxTheta); double yMax = y1 + distance * Math.sin(maxTheta); Polygon beam = new Polygon(); beam.addPoint((int) x1, (int) y1); beam.addPoint((int) xMax, (int) yMax); beam.addPoint((int) xMin, (int) yMin); g.setColor(beamColor); GradientPaint gp = new GradientPaint( (int)x1,(int)y1, beamColor, (int)x2,(int)y2, darknessColor); g.setClip(beam); g.setPaint(gp); g.fillRect(0, 0, bi.getWidth(), bi.getHeight()); // create an area the size of the image, but lacking the beam area Area darknessArea = new Area(new Rectangle(0, 0, bi.getWidth(), bi.getHeight())); darknessArea.subtract(new Area(beam)); g.setColor(darknessColor); g.setClip(darknessArea); g.fillRect(0, 0, bi.getWidth(), bi.getHeight()); // fill in the beam edges with black (mostly to smooth lines) g.setClip(null); g.setColor(Color.BLACK); g.setStroke(new BasicStroke(2)); g.draw(new Line2D.Double(x1,y1,xMin,yMin)); g.draw(new Line2D.Double(x1,y1,xMax,yMax)); g.dispose(); return bi; } }