还是那句老话,自己动手丰衣足食!SkyDome在许多的书中都有提及,有的还提供了源代码,可以我就是喜欢自己的东西。拿上手术刀,对其进行了一番改造!
OpenGL中实现天空的方法不少,如直接的,背景涂成蓝色;好点的,使用天空盒,可是寻找合适的贴图会把你累坏;较好的,我想还是使用天空顶吧(SkyDome)。当我为天空的效果感到无助的时候,在某处遇到了SkyDome,它可以实现我需要的效果。如可以均匀雾化,不会在某个点上有堆积;可以实现地平线上的过渡色,这一直是我想要的。你需要对SkyDome有一些了解,但是不需要了解具体的过程。我在程序中没有使用纹理贴在SkyDome上,而是另外创建了一个云层,这样的效果决定会比直接在SkyDome上贴图好,因为我还没有解决纹理不变形;况且独立的云层可以实现多种效果。废话少说,在我的程序中实现的天空顶的效果如图1,图2是加有云层的效果。后面是代码,希望可以作为您的参考。Source Code


#ifndef SKYDOME_H_
#define SKYDOME_H_
#ifndef PI
#define PI 3.1415926535897f
#endif
#ifndef DTOR
#define DTOR (PI/180.0f)
#endif
#ifndef SQR
#define SQR(x) (x*x)
#endif
typedef struct {
float x,y,z;
unsigned int color;
float u, v;
int flag;
}VERTEX_SKY;
class CSkyDome
{
public:
CSkyDome();
~CSkyDome();
void GenerateDome(float radius, float dtheta, float dphi, float hTile, float vTile);
int RenderSkyDome(float ,float ,float);
protected:
void ReleaseDome();
private:
VERTEX_SKY *Vertices;
int NumVertices;
float mY;
};
#endif
skydome.cpp
#include "stdafx.h"
#include <math.h>
#include "SkyDome.h"
CSkyDome::CSkyDome()
{
}
CSkyDome::~CSkyDome()
{
//ReleaseDome();
}
void CSkyDome::GenerateDome(float radius, float dtheta, float dphi, float hTile, float vTile)
{
int theta, phi;
// Make sure our vertex array is clear
/*if (Vertices)
{
delete Vertices;
Vertices = NULL;
NumVertices = 0;
}*/
// Initialize our Vertex array
NumVertices = (int)((360/dtheta)*(90/dphi)*4);
Vertices = new VERTEX_SKY[NumVertices];
ZeroMemory(Vertices, sizeof(VERTEX_SKY)*NumVertices);
// Used to calculate the UV coordinates
float vx, vy, vz, mag;
// Generate the dome
int n = 0;
for (phi=0; phi <= 90 - dphi; phi += (int)dphi)
{
for (theta=0; theta <= 360 - dtheta; theta += (int)dtheta)
{
// Calculate the vertex at phi, theta
Vertices[n].x = radius * sinf(phi*DTOR) * cosf(DTOR*theta);
Vertices[n].y = radius * sinf(phi*DTOR) * sinf(DTOR*theta);
Vertices[n].z = radius * cosf(phi*DTOR);
// Create a vector from the origin to this vertex
vx = Vertices[n].x;
vy = Vertices[n].y;
vz = Vertices[n].z;
// Normalize the vector
mag = (float)sqrt(SQR(vx)+SQR(vy)+SQR(vz));
vx /= mag;
vy /= mag;
vz /= mag;
// Calculate the spherical texture coordinates
Vertices[n].u = hTile * (float)(atan2(vx, vz)/(PI*2)) + 0.5f;
Vertices[n].v = vTile * (float)(asinf(vy) / PI) + 0.5f;
n++;
// Calculate the vertex at phi+dphi, theta
Vertices[n].x = radius * sinf((phi+dphi)*DTOR) * cosf(theta*DTOR);
Vertices[n].y = radius * sinf((phi+dphi)*DTOR) * sinf(theta*DTOR);
Vertices[n].z = radius * cosf((phi+dphi)*DTOR);
// Calculate the texture coordinates
vx = Vertices[n].x;
vy = Vertices[n].y;
vz = Vertices[n].z;
mag = (float)sqrt(SQR(vx)+SQR(vy)+SQR(vz));
vx /= mag;
vy /= mag;
vz /= mag;
Vertices[n].u = hTile * (float)(atan2(vx, vz)/(PI*2)) + 0.5f;
Vertices[n].v = vTile * (float)(asinf(vy) / PI) + 0.5f;
n++;
// Calculate the vertex at phi, theta+dtheta
Vertices[n].x = radius * sinf(DTOR*phi) * cosf(DTOR*(theta+dtheta));
Vertices[n].y = radius * sinf(DTOR*phi) * sinf(DTOR*(theta+dtheta));
Vertices[n].z = radius * cosf(DTOR*phi);
// Calculate the texture coordinates
vx = Vertices[n].x;
vy = Vertices[n].y;
vz = Vertices[n].z;
mag = (float)sqrt(SQR(vx)+SQR(vy)+SQR(vz));
vx /= mag;
vy /= mag;
vz /= mag;
Vertices[n].u = hTile * (float)(atan2(vx, vz)/(PI*2)) + 0.5f;
Vertices[n].v = vTile * (float)(asinf(vy) / PI) + 0.5f;
n++;
if (phi > -90 && phi < 90)
{
// Calculate the vertex at phi+dphi, theta+dtheta
Vertices[n].x = radius * sinf((phi+dphi)*DTOR) * cosf(DTOR*(theta+dtheta));
Vertices[n].y = radius * sinf((phi+dphi)*DTOR) * sinf(DTOR*(theta+dtheta));
Vertices[n].z = radius * cosf((phi+dphi)*DTOR);
// Calculate the texture coordinates
vx = Vertices[n].x;
vy = Vertices[n].y;
vz = Vertices[n].z;
mag = (float)sqrt(SQR(vx)+SQR(vy)+SQR(vz));
vx /= mag;
vy /= mag;
vz /= mag;
Vertices[n].u = hTile * (float)(atan2(vx, vz)/(PI*2)) + 0.5f;
Vertices[n].v = vTile * (float)(asinf(vy) / PI) + 0.5f;
n++;
}
}
}
//得到最低点
float minZ = Vertices[0].z;
for(int j=1;j<NumVertices;j++)
{
Vertices[j].flag = 0;
if(Vertices[j].z < minZ) minZ = Vertices[j].z;
}
mY = minZ;
for(j=0;j<NumVertices;j++)
{
if(Vertices[j].z == minZ) Vertices[j].flag=1;
}
// Fix the problem at the seam
for (int i=0; i < NumVertices-3; i++)
{
if (Vertices[i].u - Vertices[i+1].u > 0.9f)
Vertices[i+1].u += 1.0f;
if (Vertices[i+1].u - Vertices[i].u > 0.9f)
Vertices[i].u += 1.0f;
if (Vertices[i].u - Vertices[i+2].u > 0.9f)
Vertices[i+2].u += 1.0f;
if (Vertices[i+2].u - Vertices[i].u > 0.9f)
Vertices[i].u += 1.0f;
if (Vertices[i+1].u - Vertices[i+2].u > 0.9f)
Vertices[i+2].u += 1.0f;
if (Vertices[i+2].u - Vertices[i+1].u > 0.9f)
Vertices[i+1].u += 1.0f;
if (Vertices[i].v - Vertices[i+1].v > 0.8f)
Vertices[i+1].v += 1.0f;
if (Vertices[i+1].v - Vertices[i].v > 0.8f)
Vertices[i].v += 1.0f;
if (Vertices[i].v - Vertices[i+2].v > 0.8f)
Vertices[i+2].v += 1.0f;
if (Vertices[i+2].v - Vertices[i].v > 0.8f)
Vertices[i].v += 1.0f;
if (Vertices[i+1].v - Vertices[i+2].v > 0.8f)
Vertices[i+2].v += 1.0f;
if (Vertices[i+2].v - Vertices[i+1].v > 0.8f)
Vertices[i+1].v += 1.0f;
}
}
int CSkyDome::RenderSkyDome(float x,float y,float z)
{
glPushMatrix();
//glTranslatef(0.0f, -100.0f, 0.0f);
glTranslatef(x, y-100, z);
//glRotatef(timeGetTime()/2000.0f,0.0f, 1.0f, 0.0f);
glRotatef(270, 1.0f, 0.0f, 0.0f);
glColor4f(1,1,1,1);
glBegin(GL_TRIANGLE_STRIP);
//glBegin(GL_LINE_STRIP);
for (int i=0; i < NumVertices; i++)
{
if(Vertices[i].flag==1) //最后一圈顶点
//glColor3f(0.9f,0.9f,1.0f);
glColor3f(0.95f,0.95f,1.0f);
else
//glColor3f(0.5f, 0.7f, 0.8f);
glColor3f(0.2f, 0.5f, 1.0f);
glTexCoord2f(Vertices[i].u, Vertices[i].v);
glVertex3f(Vertices[i].x, Vertices[i].y, Vertices[i].z);
}
glEnd();
//闭合底部
glColor3f(0.9f,0.9f,1.0f);
glBegin(GL_POLYGON);
for(i=0;i<NumVertices;i++)
{
if(Vertices[i].flag == 1)
{
glTexCoord2f(Vertices[i].u, Vertices[i].v);
glVertex3f(Vertices[i].x, Vertices[i].y, Vertices[i].z);
}
}
glEnd();
glPopMatrix();
return 1;
}
void CSkyDome::ReleaseDome()
{
if (Vertices)
{
delete Vertices;
Vertices = NULL;
}
}
Code