不确定在您的环境中,但在您的情况下我会:
-
渲染纹理球体(使用深度贴图)
球体必须以您的相机位置为中心,并且具有覆盖整个视图区域的大半径。为了避免出现在极地地区,您可以使用以下命令:
- 简单球体三角剖分 https://stackoverflow.com/a/29139125/2521214
-
然后渲染BSC
从点(点)开始。然而,如果您想要缩放(取消)缩放和/或更好地可视化星星的星等,那么您需要混合功能并将星星渲染为半透明圆盘面向相机(广告牌),其半径和强度取决于缩放和星等。
我通常将此纹理用于局部恒星(D=1/3 宽度,其余为日冕):
对于 BSC 星星(D = 几乎 100% 宽度):
The alpha
计算为颜色强度alpha=r+g+b/3
.
这样,视觉和物理双星将混合在一起,增加它们在现实中的视觉强度。这也将避免在任何视图更改期间由于非常接近的恒星之间的混叠而导致的闪烁。
这里是缩放的 GIF 动画(颜色会抖动,因此会出现绿色噪音),这样您就可以感受到它的样子:
[Edit1] 简单完整的 VCL C++ OpenGL 示例
我使用您链接中的深度地图。它们是用球面失真渲染的,因此球体三角测量没有意义(不会改进任何东西,因为源数据已经是错误的)。这意味着使用在极点上具有奇点的标准球形网格。这JPG由于有损压缩伪影弄乱了一切(尤其是在两极附近),文件无法使用。我用TIF并将所有纹理重新缩放为4096x2048
解决。较低的分辨率对我来说不合适。
之后只需将球体天空盒与每个纹理混合在一起即可。结果是这样的:
它显示了北极区域,因此您可以看到扭曲并没有那么严重(除非您进行粗略缩放)。
之后,您可以添加深度地图中不存在的星星。但由于深度图已经有了BSC我认为没有必要再次添加它(除非您想将渲染器校准为与创建深度贴图相同)。
按照此处的要求完整示例C++/GL它被写在BDS2006所以它是基于VCL Form应用程序,上面有单个20ms定时器。您可以忽略所有 VCL 内容(唯一使用的形式是位图加载器,我相信您已经拥有了)并仅使用您需要的事件代码。
//---------------------------------------------------------------------------
#include <vcl.h>
#include <Math.h>
#include <gl/gl.h>
#include <gl/glu.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
// key codes (Arrows + Space), pressed state
WORD key_left =37; bool _left =false;
WORD key_right=39; bool _right=false;
WORD key_up =38; bool _up =false;
WORD key_down =40; bool _down =false;
WORD key_reset=32; bool _reset=false;
//---------------------------------------------------------------------------
GLfloat rep[16],inv[16]; // camera matrix and its pseudo inverse
void pseudo_inverse(GLfloat *a,GLfloat *b) // a = inverse(b)
{
// this works only for orthonormal matrices with origin (0,0,0) and no projections
a[ 0]=b[ 0]; a[ 4]=b[ 1]; a[ 8]=b[ 2]; a[12]=b[ 3];
a[ 1]=b[ 4]; a[ 5]=b[ 5]; a[ 9]=b[ 6]; a[13]=b[ 7];
a[ 2]=b[ 8]; a[ 6]=b[ 9]; a[10]=b[10]; a[14]=b[11];
a[ 3]=b[12]; a[ 7]=b[13]; a[11]=b[14]; a[15]=b[15];
}
//---------------------------------------------------------------------------
const int nb=64; // slices
const int na=nb<<1; // points per equator
const int _skybox_textures=4;
class skybox
{
public:
bool _init; // has been initiated ?
GLfloat pos[na][nb][3]; // vertex
GLfloat txr[na][nb][2]; // texcoord
GLuint txrid[_skybox_textures]; // texture ids
skybox() { _init=false; }
~skybox() { if (_init) glDeleteTextures(_skybox_textures,txrid); }
void init(); // call after OpenGL is already working !!!
void draw();
};
void skybox::init()
{
if (!_init) { _init=true; glGenTextures(_skybox_textures,txrid); }
GLfloat x,y,z,a,b,da,db,r=99.9;
GLfloat tx0,tdx,ty0,tdy;// just correction if CLAMP_TO_EDGE is not available
int ia,ib;
// a,b to texture coordinate system
tx0=0.0;
ty0=0.5;
tdx=0.5/M_PI;
tdy=1.0/M_PI;
// load textures to GPU memory
Graphics::TBitmap *bmp=new Graphics::TBitmap; // new bmp
#ifndef GL_CLAMP_TO_EDGE
#define GL_CLAMP_TO_EDGE 0x812F
#endif
for (int i=0;i<_skybox_textures;i++)
{
Byte q;
unsigned int *pp;
int xs,ys,x,y,adr,*txr;
union { unsigned int c32; Byte db[4]; } c;
// load bmp from file
if (i==0) bmp->LoadFromFile("skybox_grid.bmp");
else if (i==1) bmp->LoadFromFile("skybox_sectors.bmp");
else if (i==2) bmp->LoadFromFile("skybox_figures.bmp");
else if (i==3) bmp->LoadFromFile("skybox_stars.bmp");
else break;
bmp->HandleType=bmDIB; // allow direct access to pixels
bmp->PixelFormat=pf32bit; // set pixel to 32bit so int is the same size as pixel
xs=bmp->Width; // resolution should be power of 2
ys=bmp->Height;
txr=new int[xs*ys]; // create 1D txr[] array and store texture in it in GL manner
for(adr=0,y=0;y<ys;y++)
{
pp=(unsigned int*)bmp->ScanLine[y];
for(x=0;x<xs;x++,adr++)
{
// rgb2bgr and copy bmp -> txr[]
c.c32=pp[x];
q =c.db[2];
c.db[2]=c.db[0];
c.db[0]=q;
txr[adr]=c.c32;
}
}
glEnable(GL_TEXTURE_2D); // copy txr[] to GL
glBindTexture(GL_TEXTURE_2D,txrid[i]);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xs, ys, 0, GL_RGBA, GL_UNSIGNED_BYTE, txr);
glDisable(GL_TEXTURE_2D);
delete[] txr; // release memory
}
delete bmp;
// generate sphere mesh
da=(2.0*M_PI)/GLfloat(na-1);
db= M_PI /GLfloat(nb-1);
for (ib=0,b=-0.5*M_PI;ib<nb;ib++,b+=db)
for (ia=0,a= 0.0 ;ia<na;ia++,a+=da)
{
x=cos(b)*cos(a);
y=cos(b)*sin(a);
z=sin(b);
pos[ia][ib][0]=r*x;
pos[ia][ib][1]=r*y;
pos[ia][ib][2]=r*z;
txr[ia][ib][0]=tx0+(a*tdx);
txr[ia][ib][1]=ty0+(b*tdy);
}
}
void skybox::draw()
{
if (!_init) return;
int i,ia,ib0,ib1;
// color table
GLfloat col[_skybox_textures][3]=
{
// R G B
{ 0.3,0.2,0.4 }, // Ra,Dec grid
{ 0.0,0.2,0.3 }, // sectors
{ 0.0,0.3,0.4 }, // figures
{ 1.0,1.0,1.0 }, // stars
};
// modlevie = inverse of camera matrix to allow local coordinate system rotations
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf(inv);
// set rendering pipeline
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
// render mesh once per each texture layer (stars are last)
for (i=0;i<_skybox_textures;i++)
{
glBindTexture(GL_TEXTURE_2D,txrid[i]);
glColor3fv(col[i]);
for (ib0=0,ib1=1;ib1<nb;ib0=ib1,ib1++)
{
glBegin(GL_QUAD_STRIP);
for (ia=0;ia<na;ia++)
{
glTexCoord2fv(txr[ia][ib0]);
glVertex3fv (pos[ia][ib0]);
glTexCoord2fv(txr[ia][ib1]);
glVertex3fv (pos[ia][ib1]);
}
glEnd();
}
}
// restore states ...
glEnable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
//---------------------------------------------------------------------------
skybox sky;
//---------------------------------------------------------------------------
int TForm1::ogl_init()
{
if (ogl_inicialized) return 1;
hdc = GetDC(Form1->Handle); // get device context
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory( &pfd, sizeof( pfd ) ); // set the pixel format for the DC
pfd.nSize = sizeof( pfd );
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 24;
pfd.iLayerType = PFD_MAIN_PLANE;
SetPixelFormat(hdc,ChoosePixelFormat(hdc, &pfd),&pfd);
hrc = wglCreateContext(hdc); // create current rendering context
if(hrc == NULL)
{
ShowMessage("Could not initialize OpenGL Rendering context !!!");
ogl_inicialized=0;
return 0;
}
if(wglMakeCurrent(hdc, hrc) == false)
{
ShowMessage("Could not make current OpenGL Rendering context !!!");
wglDeleteContext(hrc); // destroy rendering context
ogl_inicialized=0;
return 0;
}
ogl_resize();
glEnable(GL_DEPTH_TEST); // Zbuf
glDisable(GL_CULL_FACE); // vynechavaj odvratene steny
glDisable(GL_TEXTURE_2D); // pouzivaj textury, farbu pouzivaj z textury
glDisable(GL_BLEND); // priehladnost
glShadeModel(GL_SMOOTH); // gourard shading
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // background color
ogl_inicialized=1;
return 1;
}
//---------------------------------------------------------------------------
void TForm1::ogl_exit()
{
if (!ogl_inicialized) return;
wglMakeCurrent(NULL, NULL); // release current rendering context
wglDeleteContext(hrc); // destroy rendering context
ogl_inicialized=0;
}
//---------------------------------------------------------------------------
void TForm1::ogl_draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
sky.draw();
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
void TForm1::ogl_resize()
{
xs=ClientWidth;
ys=ClientHeight;
if (xs<=0) xs = 1; // Prevent a divide by zero
if (ys<=0) ys = 1;
if (!ogl_inicialized) return;
glViewport(0,0,xs,ys); // Set Viewport to window dimensions
glMatrixMode(GL_PROJECTION); // operacie s projekcnou maticou
glLoadIdentity(); // jednotkova matica projekcie
gluPerspective(60,float(xs)/float(ys),0.1,100.0); // matica=perspektiva,120 stupnov premieta z viewsize do 0.1
glMatrixMode(GL_TEXTURE); // operacie s texturovou maticou
glLoadIdentity(); // jednotkova matica textury
glMatrixMode(GL_MODELVIEW); // operacie s modelovou maticou
glLoadIdentity(); // jednotkova matica modelu (objektu)
ogl_draw();
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
ogl_inicialized=0;
hdc=NULL;
hrc=NULL;
ogl_init();
sky.init();
int i; // unit matrices at start
for (i=0;i<16;i++) rep[i]=0.0;
for (i=0;i<16;i+=5) rep[i]=1.0;
for (i=0;i<16;i++) inv[i]=rep[i];
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender) { ogl_exit(); }
void __fastcall TForm1::FormResize(TObject *Sender) { ogl_resize(); }
void __fastcall TForm1::Splitter1Moved(TObject *Sender){ ogl_resize(); }
void __fastcall TForm1::FormPaint(TObject *Sender) { ogl_draw(); }
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
GLfloat da=5.0; // angular turn speed in [deg/timer_iteration]
pseudo_inverse(inv,rep);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf(rep);
bool _redraw=false;
if (_left ) { _redraw=true; glRotatef(+da,0.0,1.0,0.0); }
if (_right) { _redraw=true; glRotatef(-da,0.0,1.0,0.0); }
if (_up ) { _redraw=true; glRotatef(+da,1.0,0.0,0.0); }
if (_down ) { _redraw=true; glRotatef(-da,1.0,0.0,0.0); }
if (_reset) { _redraw=true; glLoadIdentity(); }
if (_redraw)
{
glGetFloatv(GL_MODELVIEW_MATRIX,rep);
pseudo_inverse(inv,rep);
}
glPopMatrix();
if (_redraw) ogl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
{
if (Key==key_left ) _left =false;
if (Key==key_right) _right=false;
if (Key==key_up ) _up =false;
if (Key==key_down ) _down =false;
if (Key==key_reset) _reset=false;
Key=0; // key is handled
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
{
// on key down event
if (Key==key_left ) _left =true;
if (Key==key_right) _right=true;
if (Key==key_up ) _up =true;
if (Key==key_down ) _down =true;
if (Key==key_reset) _reset=true;
Key=0; // key is handled
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormActivate(TObject *Sender)
{
_left =false; // clear key flags after focus change
_right=false; // just to avoid constantly "pressed" keys
_up =false; // after window focus swaping during key press
_down =false; // many games are ignoring this and you need to
_reset=false; // press&release the stuck key again to stop movement ...
}
//---------------------------------------------------------------------------
这里编译了演示和包含纹理的完整源代码
- Win32 OpenGL 演示 https://ulozto.cz/!A9IEyOSbxNh5/gl-sphereskybox-zip
通过键盘箭头和空格进行控制。现在只需使用颜色、混合函数等即可。示例仅使用 OpenGL 1.0,没有扩展(除了CLAMP_TO_EDGE
).
您可以使用 MultiTexturing 和适当的组合功能将多次渲染交换为单通道,但我很长一段时间都没有使用该功能(因为我切换到GLSL相反)所以我没有信心为此添加代码。
玩得开心。