【问题标题】:Java Raytracer: Multithreaded CPU rendering of different parts of the screen per threadJava Raytracer:每个线程对屏幕不同部分的多线程 CPU 渲染
【发布时间】:2014-09-29 22:05:50
【问题描述】:

所以我一直在使用纯 Java 开发一个简单的实时光线追踪器,现在我的大部分东西都能正常工作,从漫射照明和镜面高光到光线反射和折射,我开始想知道我是否可以制作一款简约的冒险游戏使用我的这个新引擎。唯一阻止我这样做的是在高于 512x512 的分辨率下表现不佳。

因此,我决定阅读一些有关 Java 中的多线程的知识,因为我以前的项目从来不需要多个线程,所以我以前没有关于该主题的经验。因此,在那之后,我尝试了各种实现多线程渲染的方法,最终达到了 4 个线程分别渲染屏幕的给定部分并将其传递给代表屏幕的缓冲图像的像素数据的地步。我对此很满意,现在唯一真正的问题是将渲染与更新整个图像同步,我尝试了多种方法来做到这一点,但无法提出任何好的建议,它总是闪烁或有一些噪音或者是块状的,因为即使刷新屏幕,也一直在渲染内容。

这是我在github上的项目源代码,渲染在Engine.java中完成:https://github.com/Harha/JRay

我试图通过谷歌和直接从stackoverflow寻找答案,但没有运气,过去似乎没有人真正公开做过这样的事情,或者我只是不擅长搜索东西。无论如何,我没有想法,想知道这里是否有人可以提出一些明智的答案,可用于将所有渲染线程与更新位图的主线程同步。

在此先感谢,我绝不想被人喂食,但正如我所说,我真的对此一无所知...

编辑:这是我的固定运行循环。

/*
 * Main run method.
 * Handles the update and render methods.
 */
public void run() {
    for (int i = 0; i < THREAD_COUNT; i++) {
        updateDelta(i);
        lastFPS[i] = getTime();
    }
    requestFocus();
    while (running) {
        updateDelta(0);
        update(delta[0]);
        runRenderThreads();
        try {
            e.awaitTermination(10, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        render();
    }
    stop();
}

渲染线程等待 10 毫秒来完成给定的任务,如果超过了这个时间,它只会将图像渲染到 render() 中的缓冲区并重新开始,而不关心任务是否堆积。

【问题讨论】:

    标签: java rendering real-time raytracing


    【解决方案1】:

    光线追踪实际上是多线程的主要示例,因为任务分配简单且性能要求高,因此以前肯定已经做过。

    我建议您使用double buffering。也就是说,您不是直接在屏幕上绘制,而是在一个不可见的缓冲区上绘制,并为那里的每个线程分配相同数量的像素。您等到每个线程完成,然后将可见缓冲区与不可见缓冲区交换并从新图像开始。如果仍然闪烁,请尝试垂直同步。

    伪代码

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    
    public class Raytracer
    {
        final static int THREADS = 4;
    
        public static void main(String[] args) throws InterruptedException
        {
            ExecutorService renderPool = Executors.newFixedThreadPool(THREADS);
            while(true)
            {
                for(int i=0;i<THREADS;i++)
                {
                    renderPool.execute(createRenderThread(i));
                }
                if(renderPool.awaitTermination(1,TimeUnit.MINUTES))
                {
                    // delay here in case you want to artifically limit the frame rate  
                    // v-sync if necessary here
                    swapBuffers(); // should be available from your graphics library, e.g. open GL
                }
            }
    
        }
    
        private static Runnable createRenderThread(final int i)
        {
            return new Runnable()
            {
                @Override public void run()
                {
                    for(int x=..;x<..;x++)
                        for(int y=..;y<..;y++)
                            render(x,y);
                    // do something ...
                }
            };
        }
    
    }
    

    【讨论】:

    • 嗯,这很简单...我的屏幕上现在有完美平滑的位图输出,我只是不知道应该如何检查执行程序是否已完成任务与否。谢谢,虽然那个伪代码有点不必要,但无论如何你真的很好。如果可以的话,我会支持你。
    【解决方案2】:

    除了双缓冲之外,您还应确保在缓冲区完成后使用synchronized 交换缓冲区。

    您可以考虑使用显卡进行渲染,但它会比使用 CPU 快得多。

    【讨论】:

    • 感谢您的提示。但是,我不知道应该如何使用同步来进行缓冲区交换?我的意思是,在我的情况下,只有一个线程会并且可以访问最终执行缓冲区交换的渲染方法,那么我怎么可能从中受益呢?同步不是主要是为了防止方法被多个线程同时执行吗?
    • 我的意思是在交换缓冲区之前确保渲染线程全部完成,但看起来你已经完成了所有工作。
    • (我不会经常重新创建线程,我会在后台运行渲染线程并使用同步块来防止交换中断)
    猜你喜欢
    • 2013-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-10
    • 1970-01-01
    • 1970-01-01
    • 2012-11-03
    • 2010-10-13
    相关资源
    最近更新 更多