"Glossy" effect?

Hello,

Do you know a way to create a “glossy” effect in java?

Toine

Glossy effect? Example?

Depends upon what you mean. There are 3D lighting effects that might apply.

If you mean 2D, like a halo, one can overlay a semitransparent graphic.

If you mean like a shiny gel button, I did this by looking at photoshop “how to” articles, and translating the steps to Java. For example, to edit a graphic in Java, you would want to look into BufferedImage which allows you to do pixel-by-pixel editing, including the Alpha (transparency) values.

If you want a gradiency then there is GradientPaint:
http://download.oracle.com/javase/tutorial/2d/geometry/strokeandfill.html

Hello,

Thanks for reply, you can find example by typing “glossy effect” in Google Image.

@philfrei : have you a shreable code sample to illustrate what you did?

Regards,
Toine

[quote]@philfrei : have you a shreable code sample to illustrate what you did?
[/quote]
The glossy effect I did for Hexara is OK, not great. You can see it at www.hexara.com, either by looking at the game (in progress) itself or at the blog entry for July 2010. At that entry there is a clickable button on the right, copper colored, that clicks through some flat and somewhat glossy forms.

Below is a code sample for a circular gel. I don’t know how useful it is, because it assumes the existence of an oval gradient tool that I wrote. I tried to put in comments to explain what the data created by that tool looks like. There are probably lots of ways to make the code clearer or more efficient! And better settings for the gradients as well. Also, I cut and paste it out of the working program, so I hope I didn’t create any errors in the process. But at least you can see the basic idea in action. (And I can get feedback if there are better ways to do this!)

Here’s the basic concept. Let’s say you have an image that is just a flat color. To give it depth, there are three “classic” components that are added:

  1. Darkening around the edges. For example, take the R, the G, the B and subtract an amount that is calculated based upon the distance from the edge.

  2. Top light. For example, based upon the distance from the top edge, add a small amount to the R, G, & B components. A fancier system would have a way to do lighting from any angle, on the fly, but I haven’t advanced that far.

  3. Inner glow. This is usually accomplished by an oval that is placed in the lower center. The R, G, & B values are advanced as a function of how close they are to the center of this inner-glow region.

With a BufferedImage, you can create a “raster” and iterate through each pixel, putting in the base color + the modifications that arise from the three gradients.


	private int width;
	private int height;
	private BufferedImage baseImg;
	private int[] pixel = new int[4];
	
	public int getXMid(){return width/2;}
	public int getYMid(){return height/2;}
	
	// C O N S T R U C T O R //
	public RoundGel(int radius, Color color, String iconName){
		
		this.width = radius*2;
		this.height = width;
			
		int red = color.getRed();
		int green = color.getGreen();
		int blue = color.getBlue();
		int alpha = color.getAlpha();
		
		/*
		* The "alphaChannel" array will hold info used to create 
		* the base shape.
		* Outside the radius, integer = 0 (wholly transparent), 
		* inside the radius, integer = alpha (a value we passed
		* in the constructor as part of "color").
		* On the radius edge, I calculate an "antialiasing" int.  
		*/
		OvalBandMaskGradient aCh = new OvalBandMaskGradient(
				width, height,
				radius, 0, alpha,alpha,0,alpha);
		ArrayList<Integer> alphaChannel = aCh.getVector();
		
		/* 
		* The "darkenEdge" will hold data to be used in modifying 
		* the base color of the image. In this example, the amount of 
		* darkening will be a gradient that goes from 64 to 0 in the space
		* of 6 pixels from the edge.
		*/
		OvalBandMaskGradient deMask = new OvalBandMaskGradient(width, height,
				radius, (radius - 6), 64,0,64,0);
		ArrayList<Integer> darkenEdge = deMask.getVector();
		
		/*
		* The "innerGlow" array will hold data used to lighten the graphic
		* around the lower middle. In this example I go from 0 at the outer
		* edge to a boost of 72 at the center, as a gradient.
		*/
		int igWidth = width - width/3;
		int igHeight = height/3;
		OvalBandMaskGradient igMask = new OvalBandMaskGradient(
				igWidth, igHeight,
				igHeight/2, 0,
				0,64,0,64);
		ArrayList<Integer> innerGlow = igMask.getVector();
		/*
		* The "innerGlow" array fits "within" the main graphic, so
		* we get some numbers to help invoke it at the right
		* position as we iterate through the main graphic.
		*/
		int igXStart = (width - igWidth)/2;
		int igYStart = ((height - igHeight) - (height-igHeight)/6);
		int igIdx;
		
		// We will assemble the gel in this variable.
		baseImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		WritableRaster baseRaster = baseImg.getRaster();
		int idx;

		int whitelight; // used to calculate a gradient on the fly
		for (int jj=0; jj<height; ++jj) {
			for (int ii=0; ii<width; ++ii){
				
				idx = ii + jj*width;
				
				// core color and shape + edge darkening values
				pixel[0] = Math.max(0, red - darkenEdge.get(idx));
				pixel[1] = Math.max(0, green - darkenEdge.get(idx));
				pixel[2] = Math.max(0, blue - darkenEdge.get(idx));
				pixel[3] = alphaChannel.get(idx);
							
				// for top light, whitening caculated on the fly as
				// a function of the distance from the top, and
				// only applied above the midpoint
				if (jj < radius){
					whitelight = (radius-jj)*3;
					pixel[0] = Math.min(255, pixel[0] + whitelight);
					pixel[1] = Math.min(255, pixel[1] + whitelight);
					pixel[2] = Math.min(255, pixel[2] + whitelight);
				}

				// add inner glow component, if we are within the 
				// inner glow area
				if (ii>=igXStart && ii<(igXStart+igWidth)
						&& jj>=igYStart && jj<(igYStart+igHeight)){
					
					// get corresponding "innerGlow" index
					igIdx = (ii-igXStart) + ((jj-igYStart)*igWidth);

					pixel[0] = Math.min(255, pixel[0] + innerGlow.get(igIdx));
					pixel[1] = Math.min(255, pixel[1] + innerGlow.get(igIdx));
					pixel[2] = Math.min(255, pixel[2] + innerGlow.get(igIdx));
				}
								
				baseRaster.setPixel(ii, jj, pixel);
			}
		}
	}

There is also quite simply, like I stated before, GradientPaint which is very flexible in the number of ways you can add a glossy effect.

Some of what I did in the code example is covered in the “FAQ” for 2D in this site. [Edit: Looks like there are a lot of resources on the “Pixel Art” thread as well, even if they are several years old.]

When I was first asking around for how to do glossy gels (at other sites, before discovering JavaGaming) the most common answer I got was to just do it in Photoshop or some other art program, and import the graphic! That probably IS the easiest, most efficient way to go. It seems like it is a common enough need, though, that there would be some tool in Java that can spit them out easily. [Edit: I bet there IS, in the Pixel Art thread.]

But I didn’t want to buy PhotoShop or learn an art program and wanted to know how to do it myself, in Java, and understand better how it worked. Hence coding it pixel by pixel. At first, I tried to make it work with the GradientPaint and RadialGradientPaint, but because there were various odd-shaped parts that contribute to the effect (e.g., the “inner glow” according to Photoshop tutorials is usually an oval gradient, not circular) that I ended up creating a tool that generated gradients as an oval, and now routinely use it because it has anti-aliasing built in. But if there is an easier way to make a glossy gel button in Java, using the prebuilt gradient paints, I’d appreciate being shown how that would work.

Thanks for replies :smiley:

Hexara seems to be a good game project, congratulations! :wink:

About glossy effect, I wish something like:

It’s a bit like a piece of “plastic” and I would like to create this effect on any images.

Toine

@philfrei
You can draw any shape using the present GradientPaint or RadialGradientPaint.


g2d.setPaint(gradientPaint);
g2d.fill(new Ellipse2D.Double(x,y,width,height));

Is this what you were asking about?

Not exactly. It works for some things, but even with circular gradient paint with the high point in the center, if it is painted within an oval, the edges will all have different values, based on how far they are from the center. Does that make sense? It seems hard to explain.
Example: oval 60 wide, 30 tall. Circular gradient: go from 0 to 100 and back. If the gradient is set so that the left and right edges are 0 and the center 100, the gradient will only go down to 50 at the top and bottom edge. If the gradient is set so at the top and bottome it reaches 0, the gradient will also reach 0 at 15 and 45 on the vertical, giving one a circle within the oval. In other words, the oval shape is basically a “mask” over the gradient.
These plastic gels have tricky gradient shapes internally, where the gradient is steeper or shallower depending upon the direction.

Oh so you need RadialGradientPaint?
http://ra4king.is-a-geek.net/javadocs/java/awt/RadialGradientPaint.html