简体   繁体   中英

Rotating Star Field | Immitating 3D Space on a 2D Plane

I am working to simulate a 3D star field on a 2 dimensional surface where the viewer can turn in only the x direction. With a normal 2D star field of course you could simply modify X / Y positions of the stars to simulate movement; but what I am looking to find is a clearly defined model where the velocities of the stars give the illusion of rotating around the view.

Below is an example of what I mean. Where r1 , r2 , and r3 are all star depths that wrap around the viewer. The wrapping effect (and warping) is where I am stuck.

3D视图

There are two approaches I am considering, but am currently working toward the first since it appears to be the more attainable of the two.

  1. as stars get closer to the edge where motion is occurring, speed up their velocities.
  2. as stars get closer to the top and bottom of the screen, the stars curve inward.

or

  1. use some sort of "lense effect" (matrix projection magic?) where stars appear to curve around the screen. Not sure how to go about this one.

Part of my problem is not knowing the exact terminology I'm looking for here. I'm a programmer, not a mathematician and I believe this question may fall more so into the latter realm. This question was asked here but I don't particularly like the solution in terms of the visualized result, so I'm curious if there is anyone out there with alternative solutions.

If you want to go with such approach then circular trajectories does not feel right... I would try something like rounded diamond shape:

弹道

You can use cubics for this (the blue points are the control points of interpolation cubic) that you init at start when new star is born. And then just iterate its parameter t from -1 to +2... or use 3 consequent cubics (piecewise interpolation)

However to be much closer to real 3D simply move your stars in straight lines anti parallel to ship movement and simply project their position based on distance to ship d :

// camera basis vectors
X = vec3(-sin(yaw),0.0,+cos(yaw)); // view right direction (or left)
Y = vec3(        0,  1,        0); // view up direction (or left)
Z = vec3( cos(yaw),0.0, sin(yaw)); // view forward direction
// camera local coordinates assuming its placed at (0,0,0)
x' = dot(vec3(x,y,z),X);
y' = dot(vec3(x,y,z),Y);
z' = dot(vec3(x,y,z),Z);
// perspective projection
d=c1/max(c0,abs(z));
x' *= d;
y' *= d;

Where yaw is turn angle, (x,y,z) is star coordinate and c0 is min distance constant to avoid division by zero, c1 is scaling constant like zoom ... You just generate radom star positions around your ship and iterate their z coordinate once it crosses some threshold remove that star and generate another one...

Here small C++/VCL example of this:

//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include "GLSL_math.h"
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
//---------------------------------------------------------------------------
// https://stackoverflow.com/a/71950012/2521214
//---------------------------------------------------------------------------
const int stars=500;
vec3 star[stars];
float yaw=0.0;          // camera view angle
float forw=0.0;         // ship movement angle
float speed=0.02;
float dyaw=0.0;         // camera turning speed
//---------------------------------------------------------------------------
void stars_init()
    {
    int i;
    vec3 p;
    Randomize();
    for (i=0;i<stars;i++)
        {
        p=vec3(Random(),Random(),Random()); // range < 0, 1>
        p=p+p-vec3(1.0,1.0,1.0);                // range <-1,+1>
        star[i]=p;
        }
    }
//---------------------------------------------------------------------------
void stars_update()
    {
    int i;
    vec3 p;
    // update camera direction
    yaw=fmod(yaw+dyaw,2.0*M_PI);
    // ship speed vector
    vec3 dp=speed*vec3( cos(forw),0.0, sin(forw));
    // process stars
    for (i=0;i<stars;i++)
        {
        p=star[i]-dp;                           // apply speed
        while (length(p)>1.0)                   // if out of range
            {
            // create new star
            p=vec3(Random(),Random(),Random()); // range < 0, 1>
            p=p+p-vec3(1.0,1.0,1.0);            // range <-1,+1>
            }
        star[i]=p;                              // store new position
        }
    }
//---------------------------------------------------------------------------
void stars_draw()
    {
    int i,xx=0,yy=0;
    vec3 X,Y,Z;
    float x,y,z,d;
    // camera basis vectors (3x3 rotation matrix)
    X = vec3(-sin(yaw),0.0,+cos(yaw)); // view right direction (or left)
    Y = vec3(        0,  1,        0); // view up direction (or left)
    Z = vec3( cos(yaw),0.0, sin(yaw)); // view forward direction
    // stars
    for (i=0;i<stars;i++)
        {
        // camera local coordinates assuming its placed at (0,0,0)
        x = dot(star[i],X);
        y = dot(star[i],Y);
        z = dot(star[i],Z);
        // behind camera?
        if (z<0) continue;
        // perspective projection
        d=200.0/max(0.001,fabs(z));
        x*=d; xx=Main->xs2+x;
        y*=d; yy=Main->ys2-y;
        // screen cliping & render pixel
        if ((xx>=0)&&(xx<Main->xs)&&(yy>=0)&&(yy<Main->ys))
         Main->pyx[yy][xx]=0x00FFFFFF;
        }
    }
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void TMain::draw()
    {
    double x,y,z,r=5,*p;
    // clear buffer
    bmp->Canvas->Brush->Color=clBlack;
    bmp->Canvas->FillRect(TRect(0,0,xs,ys));

    // stars
    stars_update();
    stars_draw();
    Caption=floor(yaw*180.0/M_PI);

    // render backbuffer
    Main->Canvas->Draw(0,0,bmp);
    }
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
    {
    bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    pyx=NULL;
    stars_init();
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
    {
    if (pyx) delete[] pyx;
    delete bmp;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
    {
    xs=ClientWidth;  xs2=xs>>1;
    ys=ClientHeight; ys2=ys>>1;
    bmp->Width=xs;
    bmp->Height=ys;
    if (pyx) delete[] pyx;
    pyx=new int*[ys];
    for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y];
    }
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
    {
    draw();
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
    {
    if (Key==37) dyaw=-2.0*M_PI/180.0;
    if (Key==39) dyaw=+2.0*M_PI/180.0;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
    {
    if (Key==37) dyaw=0.0;
    if (Key==39) dyaw=0.0;
    }
//---------------------------------------------------------------------------

Where Main->xs,Main->ys is the resolution of window Main->xs2,Main->ys2 is half of it (center of screen) and Main->pyx[y][x] is direct pixel access to my backbuffer bitmap... The only important stuff are star_init,star_update,star_draw functions just change the VCL rendering to your kind... The camera turning is controled by dyaw variable set by keyboard events... The vecto rmath is GLSL like syntax but you can use any(like GLM) even hardcode as I do not use any wild stuff...

Here preview for 90 deg rotations:

预习

just play with the constants to match your needs and screen resolution...

This approach can use any rotation not just yaw ... Now you can play with star colors You can use this:

And add red/blue shift based on z = dot(star[i],Z); speed for thet you can use this:

You can also render Lines instead of dots to get the feeling of different speeds

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