Java Swing游戏多光源灯光系统实现技术求助
Java Swing游戏多光源阴影系统实现方案
我正在为Java Swing游戏开发灯光系统,目前已实现两种带渐变效果的单光源阴影方案:
现有单光源实现
方案1:直接绘制径向渐变遮罩
Point2D center = new Point2D.Float(Main.localConfig.WINDOW_WIDTH/2, Main.localConfig.WINDOW_HEIGHT/2); float[] dist = {0.0f, 1.0f}; Color[] colors = {new Color(0.0f,0.0f,0.0f,0.0f), Color.BLACK}; RadialGradientPaint p = new RadialGradientPaint(center,100,dist,colors); g2D.setPaint(p); g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.95f)); g2D.fillRect(0, 0, Main.localConfig.WINDOW_WIDTH, Main.localConfig.WINDOW_HEIGHT); g2D.dispose();
方案2:LightArea类实现区域遮罩
public class LightArea { public BufferedImage Shadows; int circleSize; int circleCenterCordX, circleCenterCordY; public LightArea(int _circleSize, int _circleCenterCordX, int _circleCenterCordY){ this.circleSize = _circleSize; this.circleCenterCordX = _circleCenterCordX; this.circleCenterCordY = _circleCenterCordY; UpdateLight(); } public void UpdateLight(){ Shadows = new BufferedImage(Main.localConfig.WINDOW_WIDTH, Main.localConfig.WINDOW_HEIGHT, BufferedImage.TYPE_INT_ARGB); Graphics2D g2D = (Graphics2D) Shadows.getGraphics(); g2D.setColor(Color.BLACK); Area Rectangle = new Area(new Rectangle2D.Double(0, 0, Main.localConfig.WINDOW_WIDTH, Main.localConfig.WINDOW_HEIGHT)); Area Circle = new Area(new Ellipse2D.Double(circleCenterCordX, circleCenterCordY, circleSize, circleSize)); Rectangle.subtract(Circle); Color[] color = new Color[5]; float[] spacing = new float[5]; color[0] = new Color(0,0,0,0f); color[1] = new Color(0,0,0,0.25f); color[2] = new Color(0,0,0,0.50f); color[3] = new Color(0,0,0,0.75f); color[4] = new Color(0,0,0,0.95f); spacing[0] = 0f; spacing[1] = 0.25f; spacing[2] = 0.50f; spacing[3] = 0.75f; spacing[4] = 0.95f; RadialGradientPaint gradientPaint = new RadialGradientPaint(circleCenterCordX, circleCenterCordY, circleSize,spacing,color); g2D.setPaint(gradientPaint); g2D.fill(Circle); g2D.fill(Rectangle); g2D.dispose(); } public void Render(Graphics2D g2D) { g2D.drawImage(Shadows,0,0,null); } }
现有渲染方式
两种方案均通过继承JPanel的paintComponent方法调用渲染:
@Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2D = (Graphics2D) g; for (List<Node> tempInnerList : Map) for (Node tempNode : tempInnerList) tempNode.Render(g2D); }
遇到的问题
尝试调整AlphaComposite参数叠加多光源时,出现多种异常:
- 画面过亮无阴影
- 圆形边缘出现异常白色像素
- 光源衔接处亮度变暗,不符合“多光源叠加区域更亮”的预期
正确的多光源实现方案
核心思路
多光源阴影的正确逻辑应该是:
- 先绘制游戏场景的所有内容
- 创建一个全黑的遮罩层,覆盖整个窗口
- 对每个光源,使用**
AlphaComposite.DST_OUT**模式,在遮罩层上“挖”出光源的发光区域(渐变透明) - 最后将遮罩层叠加到场景上,实现多光源效果
具体实现代码
1. 修改LightArea类,改为生成光源的“发光区域”(而非阴影遮罩)
import java.awt.*; import java.awt.geom.Ellipse2D; import java.awt.image.BufferedImage; public class LightArea { public BufferedImage lightMask; int circleSize; int centerX, centerY; public LightArea(int size, int x, int y) { this.circleSize = size; this.centerX = x; this.centerY = y; updateLightMask(); } public void updateLightMask() { // 创建透明画布,只绘制光源的渐变发光区域 lightMask = new BufferedImage(Main.localConfig.WINDOW_WIDTH, Main.localConfig.WINDOW_HEIGHT, BufferedImage.TYPE_INT_ARGB); Graphics2D g2D = (Graphics2D) lightMask.getGraphics(); // 设置抗锯齿,避免边缘锯齿和白色异常像素 g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 构建径向渐变:中心完全透明(最亮),边缘逐渐变为不透明(阴影) float[] spacing = {0f, 0.5f, 0.8f, 1f}; Color[] colors = { new Color(0, 0, 0, 0f), // 中心:无遮罩,完全亮 new Color(0, 0, 0, 0.4f), // 中间:半遮罩,半亮 new Color(0, 0, 0, 0.8f), // 外围:强遮罩,较暗 new Color(0, 0, 0, 1f) // 边缘:全遮罩,完全暗 }; RadialGradientPaint gradient = new RadialGradientPaint( centerX, centerY, circleSize / 2f, spacing, colors, MultipleGradientPaint.CycleMethod.NO_CYCLE ); g2D.setPaint(gradient); // 修正坐标:以圆心为中心绘制圆形,避免光源偏移 g2D.fill(new Ellipse2D.Double( centerX - circleSize/2f, centerY - circleSize/2f, circleSize, circleSize )); g2D.dispose(); } public void applyLight(Graphics2D g2D) { // 使用DST_OUT模式:用光源区域减去遮罩层,实现"挖洞"效果 g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_OUT)); g2D.drawImage(lightMask, 0, 0, null); // 恢复默认合成模式,不影响后续绘制 g2D.setComposite(AlphaComposite.SrcOver); } }
2. 修改paintComponent方法,按正确流程渲染
import java.awt.*; import java.util.List; @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2D = (Graphics2D) g; // 步骤1:绘制游戏场景所有内容(地图、角色等) for (List<Node> tempInnerList : Map) { for (Node tempNode : tempInnerList) { tempNode.Render(g2D); } } // 步骤2:创建全黑遮罩层,控制整体场景暗度 g2D.setColor(new Color(0, 0, 0, 0.9f)); g2D.fillRect(0, 0, getWidth(), getHeight()); // 步骤3:叠加所有光源,挖出发光区域 // 假设你有一个List<LightArea> lights存储所有光源实例 for (LightArea light : lights) { light.applyLight(g2D); } }
关键细节说明
AlphaComposite.DST_OUT:该模式会用源像素(光源区域)的透明度减去目标像素(遮罩层)的透明度,实现“挖洞”效果。多个光源叠加时,重叠区域的遮罩被多次移除,亮度自然更高,符合预期。- 抗锯齿设置:添加抗锯齿渲染提示,彻底解决光源边缘的白色异常像素问题。
- 光源坐标修正:调整椭圆绘制坐标,确保光源以设置的中心点为圆心,避免偏移。
- 整体暗度控制:全黑遮罩的
alpha值(示例中0.9f)可自定义,值越高场景越暗,光源对比越强烈。
内容的提问来源于stack exchange,提问作者Alethia
相关产品推荐
相关产品推荐

