【问题标题】:Java2D Alpha Mapping imagesJava2D Alpha 映射图像
【发布时间】:2014-03-09 09:02:52
【问题描述】:

使用 Graphics2D,我如何获取一张黑白图像,并使用它来定义应该在另一张图像上渲染什么和不应该渲染什么?

例如,如果我有一张图片,比如说,一块田地,那块田地上有一头奶牛,而在另一张相同尺寸的图片上,我在黑色背景上画了一个白色框,与奶牛的坐标相同,当用 Java 渲染的图像将是全黑的,除了我有白盒子的那头牛?

【问题讨论】:

  • 蒙版的实际颜色应该有什么影响,还是总是either完全黑色完全白色像素?您对如何创建或表示蒙版有任何影响(即蒙版也可以存储为 alpha 值吗?)。如果将面具表示为Shape(这会容易得多),或者它必须是图像,这对您有好处吗?您想创建蒙版图像(然后绘制它),还是要绘制原始图像,在绘制时应用蒙版? (后者会困难得多)
  • 它不一定是什么,如果你有更好的方法,请推荐一些东西。我只是想用它来创建光照贴图,这样我就可以有一个瓦片级别,然后我的光照贴图和瓦片亮度取决于光照贴图。有人告诉我 Alpha Composites 是一种很好的方法。

标签: java swing graphics


【解决方案1】:

编辑:基于聊天中的长时间讨论,很明显存在对意图的误解,最初的问题来自XY-Problem:如何用蒙版图像合成图像的问题是仅针对实际问题的一种解决方案尝试 - 即在平铺贴图上绘制一些阴影/灯光效果。帖子的原始版本可以在修订历史中看到。

实际目标显然是在图像上添加“灯光效果”。这是一个如何实现的示例:

  • 原始图像在背景中绘制
  • 在图像上绘制了“阴影图像”。

“阴影图像”最初是几乎不透明、几乎黑色的图像。灯光被绘制到此图像中,带有RadialGradientPaint。选择这种油漆的颜色,以便它们使阴影图像不那么不透明,并且在应该有灯光的地方减少黑色。这会导致这些区域看起来很亮,而其他部分则保持黑暗。

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class LightEffectTest2
{
    public static void main(String args[])
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new LightEffectTest2();
            }
        });
    }


    public LightEffectTest2()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new LightEffectPanel2());
        f.setSize(600,600);
        f.setVisible(true);
    }
}


class LightEffectPanel2 extends JPanel implements MouseMotionListener
{
    private Point point = new Point(0,0);
    private BufferedImage image;
    private BufferedImage shadow;

    public LightEffectPanel2()
    {
        image = createExampleImage(600,600);
        shadow = new BufferedImage(image.getWidth(), image.getHeight(),
            BufferedImage.TYPE_INT_ARGB);

        addMouseMotionListener(this);
    }

    private static BufferedImage createExampleImage(int w, int h)
    {
        BufferedImage image = new BufferedImage(w, h, 
            BufferedImage.TYPE_INT_ARGB);
        Graphics g = image.getGraphics();
        Random random = new Random(0);
        for (int i=0; i<200; i++)
        {
            int x = random.nextInt(w);
            int y = random.nextInt(h);
            Color c = new Color(
                random.nextInt(255),
                random.nextInt(255),
                random.nextInt(255));
            g.setColor(c);
            g.fillOval(x-20, y-20, 40, 40);
        }
        g.dispose();
        return image;
    }


    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        g.drawImage(image, 0,0,null);

        drawLights();

        g.drawImage(shadow, 0,0, null);
    }

    private void drawLights()
    {
        Graphics2D g = shadow.createGraphics();
        g.setComposite(AlphaComposite.Src);
        g.setColor(new Color(0,0,16,240));
        g.fillRect(0,0,getWidth(),getHeight());

        drawLight(g, new Point(100,100));
        drawLight(g, point);

        g.dispose();
    }

    private void drawLight(Graphics2D g, Point pt)
    {
        float radius = 100;
        g.setComposite(AlphaComposite.DstOut);
        Point2D center = new Point2D.Float(pt.x, pt.y);
        float[] dist = {0.0f, 1.0f};
        Color[] colors = {new Color(255,255,255,255), new Color(0,0,0,0) };
        RadialGradientPaint p =
            new RadialGradientPaint(
                center, radius, dist, colors, CycleMethod.NO_CYCLE);
        g.setPaint(p);
        g.fillOval(pt.x-(int)radius,pt.y-(int)radius,(int)radius*2,(int)radius*2);
    }



    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        point = e.getPoint();
        repaint();
    }
}

(后期)编辑对于 cmets 中的请求:

要添加另一个阴影(不管现有的灯光),可以创建一个drawShadow 方法,在灯光绘制后重新应用阴影。它基本上使用了另一个RadialGradientPaint,它部分“恢复”了原始的、不透明的、暗影图像。

(这里的阴影被赋予了更清晰的边框,以使效果更明显)

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class LightEffectTest3
{
    public static void main(String args[])
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new LightEffectTest3();
            }
        });
    }


    public LightEffectTest3()
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new LightEffectPanel3());
        f.setSize(600,600);
        f.setVisible(true);
    }
}


class LightEffectPanel3 extends JPanel implements MouseMotionListener
{
    private Point point = new Point(0,0);
    private BufferedImage image;
    private BufferedImage shadow;

    public LightEffectPanel3()
    {
        image = createExampleImage(600,600);
        shadow = new BufferedImage(image.getWidth(), image.getHeight(),
            BufferedImage.TYPE_INT_ARGB);

        addMouseMotionListener(this);
    }

    private static BufferedImage createExampleImage(int w, int h)
    {
        BufferedImage image = new BufferedImage(w, h, 
            BufferedImage.TYPE_INT_ARGB);
        Graphics g = image.getGraphics();
        Random random = new Random(0);
        for (int i=0; i<200; i++)
        {
            int x = random.nextInt(w);
            int y = random.nextInt(h);
            Color c = new Color(
                random.nextInt(255),
                random.nextInt(255),
                random.nextInt(255));
            g.setColor(c);
            g.fillOval(x-20, y-20, 40, 40);
        }
        g.dispose();
        return image;
    }


    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D)gr;
        g.drawImage(image, 0,0,null);

        drawLights();

        g.drawImage(shadow, 0,0, null);
    }

    private void drawLights()
    {
        Graphics2D g = shadow.createGraphics();
        g.setComposite(AlphaComposite.Src);
        g.setColor(new Color(0,0,16,240));
        g.fillRect(0,0,getWidth(),getHeight());

        drawLight(g, new Point(200,200));
        drawLight(g, point);
        drawShadow(g, new Point(250,250));

        g.dispose();
    }

    private void drawLight(Graphics2D g, Point pt)
    {
        float radius = 150;
        g.setComposite(AlphaComposite.DstOut);
        Point2D center = new Point2D.Float(pt.x, pt.y);
        float[] dist = {0.0f, 1.0f};
        Color[] colors = {new Color(255,255,255,255), new Color(0,0,0,0) };
        RadialGradientPaint p =
            new RadialGradientPaint(
                center, radius, dist, colors, CycleMethod.NO_CYCLE);
        g.setPaint(p);
        g.fillOval(pt.x-(int)radius,pt.y-(int)radius,
            (int)radius*2,(int)radius*2);
    }

    private void drawShadow(Graphics2D g, Point pt)
    {
        float radius = 75;
        g.setComposite(AlphaComposite.DstOver);
        Point2D center = new Point2D.Float(pt.x, pt.y);
        float[] dist = {0.0f, 0.7f, 1.0f};
        Color[] colors = { 
            new Color(0,0,0,200),
            new Color(0,0,0,150),
            new Color(255,255,255,0) };
        RadialGradientPaint p =
            new RadialGradientPaint(
                center, radius, dist, colors, CycleMethod.NO_CYCLE);
        g.setPaint(p);
        g.fillOval(pt.x-(int)radius,pt.y-(int)radius,
            (int)radius*2,(int)radius*2);
    }



    @Override
    public void mouseDragged(MouseEvent e) {
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        point = e.getPoint();
        repaint();
    }
}

【讨论】:

  • 所以我绘制基础图像,将合成设置为 DstOut,然后绘制蒙版?
  • 在这种情况下,是的。这取决于您如何定义掩码。但是当应该可见的部分由 opacity 0 定义,而应该隐藏的部分由 opacity 255 定义时,则可以这样。
  • 大多数图像编辑软件将 alpha 映射表示为黑白图像,这一定是我感到困惑的地方。
  • 我正在像这样绘制光照贴图:lightMap.getGraphics().setColor(new Color(0, 0, 0, 0)); lightMap.getGraphics().fillRect(0, 0, 1080, 720); lightMap.getGraphics().setColor(new Color(0, 0, 0, 0)); lightMap.getGraphics().fillRect(300, 300, 300, 300); 屏幕仍然是全黑的?
  • @KisnardOnline 关于图像,我曾经创建过这个例子stackoverflow.com/a/23971327/3182664,我基本上将它们组合起来构建codereview.stackexchange.com/a/83455/41175BUT后者使用多个灯光来实现(有点)柔和的阴影,而且这个在实践中使用起来太慢了。 (然而,将这两种方法与 single 光(因此是“硬阴影”)结合起来是完全可行的。也许有一天我会把所有这些都扔到 GitHub 上,似乎有这样的需求....
猜你喜欢
  • 2012-06-29
  • 1970-01-01
  • 2011-03-17
  • 1970-01-01
  • 2014-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-11
相关资源
最近更新 更多