【问题标题】:Scrolling starfield with gl_points in pyglet在pyglet中使用gl_points滚动星空
【发布时间】:2012-09-10 07:52:15
【问题描述】:

对于我正在开发的游戏,我想实现一个滚动星空。到目前为止,所有游戏都是从 OpenGL 原语绘制的,我想继续这样做,gl_points 似乎很合适。

我正在使用 pyglet (python),并且知道我可以存储一大堆的位置或点更新它们并手动移动它们,但我希望有一个更简洁的替代方案。

编辑: 回应伊恩马利特 我想我想问的是如果我生成了一堆点,有什么方法可以将它们粘贴到某种表面或缓冲区上并在后台滚动。

此外,在这个阶段我想要生成什么样的星域,我正在尝试为自上而下的游戏创建一个简单的单层,几乎就像你在小行星中所拥有的那样

【问题讨论】:

    标签: python opengl pyglet


    【解决方案1】:

    无限的程序化星空模式

    特色:视差滚动

    基本配方是:

    • 播种随机生成器(每帧相同的种子)
    • 生成 N 个随机点 (X,Y) 和一个“Z”值(例如每个点 0.5 ... 2)

    • 翻译每个点的位置:(X,Y) += scrollXY * Z
      这是棘手的部分。 每个点都有不同的 Z 值,因此它们以不同的速度滚动。这称为“视差滚动”,效果惊人。

    • 环绕点(以屏幕大小为模)。

    每帧使用相同的种子会给你一个随机的星空模式,但每帧都是持久的(仅由滚动值改变)。

    趣事

    这个算法可以很容易地修改,以便您不必将所有 N 个位置实际存储在内存中。您只需要一个生成器,它可以为您提供随机的基础 (X,Y) 坐标(和深度),并由一个常数值加上一些星号作为种子。

    伪代码:

    for (int i=0; i<1000; ++i) {
    
        RandomGenerator rnd;
        rnd.seed(SomeConstantSeed + i*10293);
    
        // determine i-th star's position
        var basePosition = vec2(rnd.next(800), rnd.next(600));
        var depth = rnd.next(0.5, 2);
    
        // parallax scrolling and wrapping
        var realPosition = basePosition + scrollXY * depth; 
        var wrappedPosition = realPosition modulo vec2(800, 600);
    
        // these are our coordinates! let's draw
        renderStar(wrappedPosition);
    }
    

    在这里,每颗星星都是按需生成的,我们不必在帧之间将其值保存在内存中。随机生成器种子保持不变就足以在每一帧生成相同的(但动画的)星场。

    您可以使用相同的技术为每颗星分配不同的随机(但持久)颜色。


    如何使用OpenGL高效实现?

    最简单、最有效的方法是在顶点着色器中动态生成位置。

    • 在 VBO 中准备星形几何体。它可以是你想要的任何东西:一个顶点(对于一个点精灵)、一个带纹理的四边形,任何东西。

    • 使用glDrawArraysInstanced 渲染它,其中primcount 是您想要的星数

    • 在您的顶点着色器中,根据上面的伪代码实现循环体,用gl_InstanceID 替换i。然后通过滚动值(作为 uniform vec2 变量传递)改变每个顶点的输入 XY 屏幕位置(VS 中的属性变量),如上所述。

    一些脚注:

    • 您将需要在顶点着色器中使用随机生成器。使用一维噪声纹理或have a look here 实现它。
    • 如果您的星星的几何图形多于一个点(即您使用四边形,而不是点精灵),您需要更加小心屏幕环绕以避免出现星形环绕的某些顶点而其他顶点不环绕的情况。这有点棘手,但可行。使用点精灵更简单。

    【讨论】:

    • 这是一个非常好的和详细的答案,我标记了 Ian Mallett 的答案,因为我最终发现了如何对纹理进行 blit 然后将其包裹起来。不过这是一个更好的答案,我希望在我的 open gl 技能有所提高时能够使用此方法。
    【解决方案2】:

    你能澄清一下是什么样的星空吗? 2D 滚动(对于侧面或顶部滚动游戏,可能有不同的层)或 3D(就像在难以置信的快速宇宙飞船中真正飞过星空)?

    在前者中,纹理(或叠加混合的纹理层)可能是最干净、最快的方法。 [编辑:纹理是迄今为止最好的方法,但如果您真的不想使用纹理,您可以执行以下操作,这是下一个最好的方法:

    • 制作静态 VBO 或显示点列表,其大小可能是滚动方向所需大小的六倍(例如,如果您正在运行 800x600 屏幕并且您正在水平滚动,则在 4800x600 上生成点网格)。
    • 将这些点绘制两次,偏移宽度和滚动变量。例如,让 x 成为您的滚动变量(它从 0 开始,然后递增直到达到 4800(点的宽度),然后回绕并从 0 重新开始)。每一帧,用 glTranslatef(x,0,0) 绘制你的点。然后用 glTranslatef(x+4800,0,0) 再次绘制它们。
    • 通过这种方式,您的积分将看似连续滚动过去。常数(我在上面选择了六个)对算法并不重要。值越大,重复次数越少,但性能越慢。
    • 您也可以尝试使用不同的滚动常数多次执行上述所有操作,以产生深度错觉(具有多个图层)。

    ]

    在后者中,有很多我能想到的聪明算法,但我建议只使用原始点(如果你喜欢的话,点精灵);如果将 GPU 放在静态 VBO 或显示列表中,GPU 可以处理此问题。如果您的积分很小,您一次可能会抛出几千个积分,而不会对性能造成任何明显影响。

    【讨论】:

    • 我想我想问的是,如果我生成了一堆点,有没有办法可以将它们粘贴到某种表面或缓冲区上并在后台滚动它。此外,至于我在这个阶段想要生成什么样的星域,我正在尝试为自上而下的游戏创建一个简单的单层,几乎就像你在小行星中所拥有的那样。除非绝对必须,否则我想远离使用纹理。
    • 抱歉,远离纹理,我的意思是远离加载图像
    • 您无需加载图像即可获得纹理。创建一个 FBO 并使用它将您的点渲染到纹理。然后在背景中绘制该纹理。纹理缓冲区、表面等。这对你来说可能太高级了(FBO 可能很棘手),所以请在我的编辑中查看上述算法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多