多边形的顶点作为UV坐标

6
我正在使用Java的Graphics类开发3D渲染器。它已经能够绘制任何有彩色表面的形状,但我想知道是否可以纹理这些表面?我看到很多人在Javascript中创建软件渲染器,因此在Java中肯定也有相应的函数/方法......
目前我找到的都是Graphics.setClip(Shape),但我认为它不适合,因为它只设置背景纹理,如果一个顶点移动了,它就不能拉伸纹理;而且这只是2D的情况,当表面与相机呈角度时它还需要拉伸/扭曲纹理(想象一下旋转立方体的侧面)。
我真的不知道从哪里开始。因为不存在扭曲,所以我不能使用XOR模式,如果必须手动计算,我也不知道该怎么做。
这些Javascript软件渲染器是如何做到这么好的呢?
3个回答

7
你可以利用 java.awt.TexturePaint,这里有相关的示例herehere。在这个上下文中,你需要知道TexturePaint与渲染表面的光栅对齐,而不是形状的边界。

image

补充说明:虽然阴影是一个广泛的主题,但也可以考虑使用基于像素的渐变阴影方法,其中包括alpha,如所示在KineticModel中引用这里。请注意,这样的渐变可以应用于TexturePaintWritableRaster
对于非仿射变换,请参见javax.media.jai.Warp.Warp,引用这里

5

我曾考虑将此作为OpenGL功能的“备用方案”,因为由于JOGL问题,某些机器上无法运行。但是我没有成功。以下是导致我停止工作的未解决问题:

  • 隐藏表面移除。我找不到在Graphics2d基元下实现z缓冲区的方法。

  • 透视纹理变换。Graphics2d中可用的AffineTransform具有足够的能力来映射纹理,如果图像投影是平行的,但不是透视的。

  • 2D剪辑不匹配。纹理最后一步操作将必须针对2D遮罩进行裁剪。结果发现Graphics2d剪辑存在错误。如果您针对完全相邻的2D多边形进行剪辑,则剪辑的补丁不完全匹配。边界处的单个像素未着色。

  • 性能。虽然Graphics2d管道的最新版本会尝试使用硬件加速(如果有),但原始多边形渲染仍然比JOGL测试慢一个数量级,这对我的目的来说不够好。

我看过的Javascript 3d库都是基于WebGL构建的,而WebGL又是在HTML 5 Canvas对象中实现的。WebGL是一个OpenGL API,必须由浏览器实现。其他3d Javascript库使用插件来获取硬件加速图形。所以它们对于如何在Swing中进行3D不是一个有用的信息来源。 补充 也许值得补充一下我做了什么。为了替换用户可以通过移动相机“飞行”的3D JOGL场景,我选择了单个固定视点,并“硬连线”绘图顺序,有效地实现了具有固定逻辑以确定排序顺序的Painter's Algorithm,渲染几乎与JOGL视图相同的模型。我使用渐变填充多边形实现Gouraud着色,这就是我发现上面提到的剪辑错误的地方。这一切都运作良好,在成千上万的副本中运行,但很混乱和脆弱,我不想再做一遍。

好的,这怎么回答我的问题了呢?你有没有研究过LWJGL?它可以在任何带有OpenGL的机器上工作,因为它基本上是一个Java到OGL的C++管道补丁。我还发现了一种使用面法线进行隐藏表面消除的方法,不需要Z缓冲区。 - Lee Fogg
@LeeAllan 只使用面法线进行剔除只适用于一个凸物体。当物体互相遮挡时,它没有任何帮助。我见过的Javascript渲染器使用3D画布对象,其底层使用OpenGL或类似技术。如果您提供一个不使用这种技术的示例,我会看一下。LWJGL也有自己的问题。 - Gene
2
@LeeAllan 对不起,我应该补充一下你是对的:我没有直接回答你的问题。我的意图是让你避免浪费时间,因为我花了3-4个小时的学习时间才得出上述结论。另外,我还在上面添加了有关JavaScript 3D渲染的注释。 - Gene
a) 从零开始编写软件渲染器(毫无意义,因为你应该使用例如Mesa OpenGL软件驱动程序);b) 使用渲染器解析和渲染数据(毫无意义,因为你既不能获得足够的帧速率,也不能获得质量好的硬件渲染器或良好的SR);c) 使用2D框架显示它,导致进一步的线程/效率问题(同样显然是毫无意义的)。简而言之,你只是在浪费时间而已。 - user719662
对不起之前说话语气有些严厉,但如果你真的想要正确地给一个多边形贴纹理的话,你必须使用线性代数 - 严格来说,你需要为每个多边形顶点定义纹理的UV坐标,进行R2->R3转换到XYZ空间,然后你需要维护模型视图和变换矩阵,并对它们进行乘法,然后使用适当的插值算法(例如双线性)来匹配最终屏幕XY坐标,有效地重新将空间变换为Z2 - …任何其他方法都无法让你真正了解在严肃的应用中如何进行纹理贴图。 - user719662
显示剩余3条评论

2

我假设你只使用Swing/AWT框架进行图形处理。如果我的假设不正确,请更新你的问题。

如果你正在使用Swing和Graphics2D类(这是Swing组件使用的类),那么你正在处理一个2D框架。这意味着高级的3D特效没有内置,你需要自己实现变换(或者开始抓取3D类来完成工作)。

所以,你已经在正确的轨道上 - 你必须先设置剪辑(使其适应你的形状),然后执行旋转(使其出现在正确的角度)。

话虽如此,进行基本的旋转变换并不太困难。这里有一个关于(基本)旋转的好概述。点击这里。当然,当你有不仅基于单一轴的旋转时,它会变得更加复杂。但正如文章后面解释的那样,如果你将矩阵(Rx)(Ry)(Rz)相乘,就可以使用得到的矩阵确定像素的位置。

我创建了一个在Y轴上旋转的快速示例。请注意,我想出了一个愚蠢的算法(奇幻消失点技术®),给了一个模糊的深度感。我假设你已经有了一些东西-这可能更正确。

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);
    }
}

1
我确实只是使用Swing。 这段代码非常有趣,有潜力绘制3D基元,但我感觉有两件事会干扰;为什么旋转超过180度的时候看起来和前180度不同?它不能更快吗?..否则我认为它无法以超过5FPS的速度绘制超过10个面的模型。 - Lee Fogg
除非您开始使用图形卡进行处理,否则您将无法获得太多FPS(因为它将在您的CPU上运行)。OpenGL实际上利用了您的图形卡-这就是为什么它运行得更快的原因。至于旋转/ 180°,对我来说,像300这样的角度似乎工作得很好。您不会获得深度感,因为我的Magic Vanishingpoint Technology®并不完美,但它确实可以旋转。 - Nick Rippe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接