You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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参数叠加多光源时,出现多种异常:

  • 画面过亮无阴影
  • 圆形边缘出现异常白色像素
  • 光源衔接处亮度变暗,不符合“多光源叠加区域更亮”的预期

正确的多光源实现方案

核心思路

多光源阴影的正确逻辑应该是:

  1. 先绘制游戏场景的所有内容
  2. 创建一个全黑的遮罩层,覆盖整个窗口
  3. 对每个光源,使用**AlphaComposite.DST_OUT**模式,在遮罩层上“挖”出光源的发光区域(渐变透明)
  4. 最后将遮罩层叠加到场景上,实现多光源效果

具体实现代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 21:28:10