简体   繁体   中英

Polygon vertices as UV coordinates

I am working on a 3D renderer in Java using the Graphics class, It is now capable of drawing any shape with coloured faces, however I was wondering if it was possible to texture the faces? I have seen a lot of people creating software renderers in Javascript so surely there is an equivalent function/method of however they are doing it in Java...
I have looked around so far but all I can find is Graphics.setClip(Shape), I don't think it would be suitable because it merely sets the background texture and wouldn't stretch the texture if a vertex moved - and that's just in 2D, it need to also stretch/skew the texture when it's at an angle to the camera (Think of the sides of a rotating cube).

I really have no idea where to start, I can't use XOR modes because of no skewing and I really wouldn't know how to do the math if I had to do it manually.
How do these Javascript software renderers do it so well?

You may be able to leverage java.awt.TexturePaint , illustrated here and here . In this context, you should know that TexturePaint aligns with the rendering surface's raster, rather than the shape's bounds.

图片

Addendum: While shading is a broad topic, also consider a pixel-based approach using gradient shading with alpha , illustrated in the KineticModel cited here . Note that such a gradient can be applied to the WritableRaster of a TexturePaint .

For non-affine transformations, see javax.media.jai.Warp.Warp , cited here .

I looked at doing this as a "fallback" for an OpenGL feature that would not run on some machines due to JOGL problems. I was unsuccessful. These were the unresolved questions that caused me to stop work:

  • Hidden surface removal. I could find no way to implement a z-buffer under Graphics2d primitives.

  • Perspective texture transforms. The AffineTransform available in Graphics2d has enough power to map textures if the image projection is parallel, but not perspective.

  • Mismatches in 2d clipping. The last operation in texturing will have to be clipping against a 2d mask. It turns out there is a bug in Graphics2d clipping. If you clip against perfectly adjoining 2d polygons, the clipped patches do not mate perfectly. Single pixels at the boundary are unshaded.

  • Performance. While recent versions of the Graphics2d pipeline try to use hardware acceleration if it's there, raw polygon rendering still tested an order of magnitude slower than JOGL, which was not good enough for my purposes.

The Javascript 3d libraries I've looked at are all built on WebGL, which in turn is implemented in HTML 5 Canvas objects. WebGL is an OpenGL API that must be implemented by the browser. Other 3d Javascript libraries use plug-ins to get at hardware-accelerated graphics. So they're not a useful source of information about how to do 3d in Swing.

Addition

Perhaps it is worth adding what I did do. To replace the 3d JOGL scene that the user can "fly through" by moving the camera, I chose a single, fixed viewpoint and "hard wired" the drawing order, effectively implementing the Painter's Algorithm with fixed logic to determine sort order, rendering more-or-less the same models as the JOGL view. I implemented Gouraud shading using gradient-filled polygons, which is where I found the clipping bug mentioned above. It all works and is running in hundreds of thousands of copies, but it's messy and fragile, and I wouldn't want to do it again.

I'm assuming you're only using the Swing/AWT frameworks for graphics. If this is wrong, please update your question.

If you're using Swing and the Graphics2D class (which is the class swing components use), you're dealing with a 2D framework. That just means that fancy 3D stuff isn't built in - you'll have to implement the transformations yourself (or start grabbing 3D classes to do your work).

So, you're on the right track - you'll have to set the clip first (so it fits your shape), then perform a rotation (so it appears at the correct angle).

That being said, doing basic rotational transformations isn't too difficult. There's a good outline of (basic) rotations here . Of course it gets a bit more complicated when you have rotations not based on only one axis. But as the article explains later on, if you multiply the matrices (Rx)(Ry)(Rz), you can use the resulting matrix to determine your pixel placement.

I created a quick example with a rotation on the Y-axis. Please note I made up a dumb algorithm ( Magic Vanishingpoint Technology® ) to give a vague illusion of depth. I'm assuming you already have something for that - It's probably more correct.

import java.awt.*;
import java.awt.image.*;
import java.io.*;

import javax.imageio.ImageIO;
import javax.swing.*;

public class Rotation3D extends JPanel{
    Image img;

    BufferedImage rotatedImage;
    final int ROTATION_DEGREES = 70;

    int vanishX = 0;
    int vanishY = 0;
    int vanishZ = -1000;

    public Rotation3D(){

        try {
            //Grabbed an image from the java folder - hopefully your computer has it
            img = ImageIO.read(new File(System.getProperty("java.home") + "/lib/deploy/splash.gif"));
            setPreferredSize(new Dimension(img.getWidth(this) * 2,img.getHeight(this) * 2));

            //Create a buffered image with the appropriate size, and draw the image on it
            BufferedImage shadedImage = new BufferedImage(img.getWidth(this), img.getWidth(this), BufferedImage.TYPE_INT_ARGB);
            shadedImage.getGraphics().drawImage(img, 0, 0, this);
            Raster r = shadedImage.getData();

            //Not really necessary unless you're using Magic Vanishingpoint Technology®
            vanishX = shadedImage.getWidth() /2;
            vanishY = shadedImage.getHeight() /2;

            //Create a Wraster for the transformed image
            WritableRaster wr = r.createCompatibleWritableRaster();

            //Do the transformation
            for(int i = 0; i < shadedImage.getWidth(); i++){
                for(int j = 0; j < shadedImage.getHeight(); j++){
                    //Remapping the pixel based on a matrix rotation
                    int[] result = r.getPixel(i, j, new int[4]);
                    Double radians = Math.toRadians(ROTATION_DEGREES);
                    Double newX, newY, newZ;
                    //newX = ((i-vanishX) * Math.cos(radians)) + vanishX; // places the rotation in the middle of the image
                    // x * cos(θ) + y * 0 + z * sin(θ)
                    newX = i * Math.cos(radians); //places the rotation in the y=0 axis
                    // x * 0 + y * 1 + z * 0
                    newY = j * 1.0;
                    // x * -sin(θ) + y * 0 + z * cos(θ)
                    newZ= i * Math.sin(radians) * -1;

                    //Apply Magic Vanishingpoint Technology®
                    //(Not actually trademarked or correct - just something thrown together)
                    if(newZ < vanishZ){
                        newX = 0.0;
                        newY = 0.0;
                    }else if(newZ < 0){
                        double magicVanish =  newZ / vanishZ;
                        newX += magicVanish * newX;
                        newY += magicVanish * newY;
                    }

                    //Print the pixel if it fits on the screen to the new Raster
                    if(newX > 0 && newX < shadedImage.getWidth() && newY > 0 && newY < shadedImage.getHeight())
                        wr.setPixel(newX.intValue(), newY.intValue(), result);
                }
            }

            //Create an image based on the raster.
            rotatedImage = new BufferedImage(img.getWidth(this), img.getWidth(this), BufferedImage.TYPE_INT_ARGB);
            rotatedImage.setData(wr);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.drawImage(rotatedImage, 0, 0, this);
    }

    public static void main(String[] args){
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new Rotation3D());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM