3D planet uses now phong lighting, from PhracturedBlue

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@436 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
gewlitys
2002-12-18 16:06:51 +00:00
parent 1e4b7033ca
commit 03aea010fb
+114 -53
View File
@@ -59,8 +59,22 @@ DWORD **getpixelarray(FRAME FramePtr,int width, int height);
//RADIUS^2 //RADIUS^2
#define RADIUS_2 (RADIUS * RADIUS) #define RADIUS_2 (RADIUS * RADIUS)
#define DIAMETER (TWORADIUS + 1) #define DIAMETER (TWORADIUS + 1)
#define PHONG_BITS 24 #define DIFFUSE_BITS 24
#define GET_PHONG(val, ph) ((((val)<<PHONG_BITS)-((val)*ph))>>PHONG_BITS)
//#define GET_LIGHT(val, dif, sp) \
// ( (UBYTE)min ((sp) + \
// ( ( ( (DWORD)(val) << DIFFUSE_BITS ) - (DWORD)(val) * (dif) ) >> DIFFUSE_BITS ) \
// , 255) )
UBYTE GET_LIGHT (UBYTE val, DWORD dif, UBYTE sp)
{
DWORD i = (DWORD)val << DIFFUSE_BITS;
i -= val * dif;
i >>= DIFFUSE_BITS;
i += sp;
if (i > 255)
i = 255;
return ((UBYTE)i);
}
#ifndef M_TWOPI #ifndef M_TWOPI
#ifndef M_PI #ifndef M_PI
@@ -72,7 +86,8 @@ DWORD **getpixelarray(FRAME FramePtr,int width, int height);
#define M_DEG2RAD (M_TWOPI / 360.0) #define M_DEG2RAD (M_TWOPI / 360.0)
#endif #endif
DWORD phong[DIAMETER][DIAMETER]; DWORD light_diff[DIAMETER][DIAMETER];
UBYTE light_spec[DIAMETER][DIAMETER];
typedef struct typedef struct
{ {
POINT p[4]; POINT p[4];
@@ -80,6 +95,9 @@ typedef struct
} MAP3D_POINT; } MAP3D_POINT;
MAP3D_POINT map_rotate[DIAMETER][DIAMETER]; MAP3D_POINT map_rotate[DIAMETER][DIAMETER];
//POINT map_rotate[DIAMETER][DIAMETER]; //POINT map_rotate[DIAMETER][DIAMETER];
typedef struct {
double x, y, z;
} POINT3;
void void
RenderTopography (BOOLEAN Reconstruct) RenderTopography (BOOLEAN Reconstruct)
@@ -204,68 +222,110 @@ RenderTopography (BOOLEAN Reconstruct)
SetContext (OldContext); SetContext (OldContext);
} }
void P3mult (POINT3 *res, POINT3 *vec, double cnst)
{
res->x = vec->x * cnst;
res->y = vec->y * cnst;
res->z = vec->z * cnst;
}
void P3sub (POINT3 *res, POINT3 *v1, POINT3 *v2)
{
res->x = v1->x - v2->x;
res->y = v1->y - v2->y;
res->z = v1->z - v2->z;
}
double P3dot (POINT3 *v1, POINT3 *v2)
{
return (v1->x * v2->x + v1->y * v2->y + v1->z * v2->z);
}
void P3norm (POINT3 *res, POINT3 *vec)
{
double mag = sqrt (P3dot (vec, vec));
P3mult (res, vec, 1/mag);
}
// RenderPhongMask builds a shadow map for the rotating planet // RenderPhongMask builds a shadow map for the rotating planet
// loc indicates the planets position relavtive to the sun // loc indicates the planets position relavtive to the sun
static void static void
RenderPhongMask (POINT loc) RenderPhongMask (POINT loc)
{ {
POINT pt, light; POINT pt;
POINT3 light, view;
double lrad; double lrad;
int lmag;
DWORD step; DWORD step;
double lmag2;
int y, x; int y, x;
#define LIGHT_MULT 0.8 #define LIGHT_INTENS 0.4
#define AMBIENT_LIGHT 0.05 #define AMBIENT_LIGHT 0.1
#define LIGHT_RADIUS 1.6 #define MSHI 2
light.x = (int)(LIGHT_MULT * RADIUS * #define LIGHT_Z 1.2
cos (atan2 (-(double)loc.y, -(double)loc.x))); // lrad is the distance from the sun to the planet
light.y = (int)(LIGHT_MULT * RADIUS * lrad = sqrt (loc.x * loc.x + loc.y * loc.y);
sin (atan2 (-(double)loc.y, -(double)loc.x))); // light is the sun's position. the z-coordinate is whatever
// light.x=(int)(RADIUS*0.8); // looks good
// light.y=(int)(-RADIUS*0.6); light.x = -((double)loc.x);
// fprintf(stderr,"light: (%d,%d)->(%d,%d)\n",loc.x,loc.y,light.x,light.y); light.y = -((double)loc.y);
lmag = (int)(LIGHT_RADIUS * RADIUS); light.z = LIGHT_Z * lrad;
lmag2 = lmag * lmag; P3norm (&light, &light);
step = 1 << PHONG_BITS; // always view along the z-axis
// ideally use a view point, and have the view change per pixel
// but that is too much effort for now.
// the view MUST be normalized!
view.x = 0;
view.y = 0;
view.z = 1.0;
step = 1 << DIFFUSE_BITS;
for (pt.y = 0, y = -RADIUS; pt.y <= TWORADIUS; ++pt.y, y++) for (pt.y = 0, y = -RADIUS; pt.y <= TWORADIUS; ++pt.y, y++)
{ {
int y_2, deltay_2; DWORD y_2;
y_2 = y * y; y_2 = y * y;
deltay_2 = (y - light.y) * (y - light.y);
for (pt.x = 0, x = -RADIUS; pt.x <= TWORADIUS; ++pt.x, x++) for (pt.x = 0, x = -RADIUS; pt.x <= TWORADIUS; ++pt.x, x++)
{ {
int rad; DWORD x_2, rad_2, stepint;
DWORD stepint; POINT3 norm, rvec;
double lrad2; double diff, spec = 0.0, fb;
double intens; x_2 = x * x;
rad = x * x + y_2; rad_2 = x_2 + y_2;
if (rad <= RADIUS_2) if (rad_2 <= RADIUS_2)
{ {
lrad = ((x - light.x) * (x - light.x) + deltay_2); // norm is the sphere's surface normal.
lrad2 = lrad; norm.x = (double)x;
//lrad2=pow(lrad,1); norm.y = (double)y;
if (lrad2 >= lmag2) norm.z = (sqrt (RADIUS_2 - x_2) * sqrt (RADIUS_2 - y_2)) / RADIUS;
intens = AMBIENT_LIGHT; P3norm(&norm,&norm);
// diffuse component is norm dot light
diff =P3dot (&norm, &light);
// negative diffuse is bad
if(diff < 0)
diff = 0.0;
// specular highlight is the phong equation: (rvec dot view)^MSHI
// where rvec = (2*diff)*norm - light (reflection of light around norm)
P3mult (&rvec,&norm,2 * diff);
P3sub (&rvec, &rvec, &light);
fb = P3dot (&rvec, &view);
if (fb > 0.0)
spec = LIGHT_INTENS * pow (fb, MSHI);
else else
{ spec = 0;
intens = 1 * cos ((M_PI / 2) * (double)lrad2 / (double)lmag2); // adjust for the ambient light
if (intens < AMBIENT_LIGHT) if (diff < AMBIENT_LIGHT)
intens = AMBIENT_LIGHT; diff = AMBIENT_LIGHT;
} // stepint allows us multiply by a ratio without usig floating-point
stepint = step - (DWORD)(intens * step + 0.5); // instead of color*diff, we use ((color << 24) - stepint*color) >> 24
if(rad > (RADIUS - 1) * (RADIUS - 1)) stepint = step - (DWORD)(diff * step + 0.5);
// Now we antialias the edge of the spere to look nice
if(rad_2 > (RADIUS - 1) * (RADIUS - 1))
{ {
DWORD r; DWORD r;
r = rad - (RADIUS - 1) * (RADIUS - 1); r = rad_2 - (RADIUS - 1) * (RADIUS - 1);
stepint += (step >> 7) * (r + 1); stepint += (step >> 7) * (r + 1);
if (stepint > step) if (stepint > step)
stepint = step; stepint = step;
} }
} else } else
stepint = step; stepint = 1 << 31;
phong[pt.y][pt.x] = (int)stepint; light_diff[pt.y][pt.x] = (DWORD)stepint;
light_spec[pt.y][pt.x] = (UBYTE)(spec*255);
} }
} }
} }
@@ -512,7 +572,6 @@ RenderLevelMasks (int offset)
clock_t t1; clock_t t1;
t1 = clock (); t1 = clock ();
#endif #endif
rgba = (DWORD *)HMalloc (sizeof (DWORD *) * (DIAMETER) * (DIAMETER)); rgba = (DWORD *)HMalloc (sizeof (DWORD *) * (DIAMETER) * (DIAMETER));
p_rgba = rgba; p_rgba = rgba;
// Choose the correct Frame to wrte to // Choose the correct Frame to wrte to
@@ -523,12 +582,14 @@ RenderLevelMasks (int offset)
for (pt.x = 0, x = -RADIUS; pt.x <= TWORADIUS; ++pt.x, ++x) for (pt.x = 0, x = -RADIUS; pt.x <= TWORADIUS; ++pt.x, ++x)
{ {
UBYTE c[3]; UBYTE c[3];
int ph; DWORD diffus;
UBYTE spec;
COUNT i; COUNT i;
DWORD p1[4]; DWORD p1[4];
MAP3D_POINT *ppt = &map_rotate[pt.y][pt.x]; MAP3D_POINT *ppt = &map_rotate[pt.y][pt.x];
ph = phong[pt.y][pt.x]; diffus = light_diff[pt.y][pt.x];
if (ph < 1 << PHONG_BITS) spec = light_spec[pt.y][pt.x];
if (diffus < 1 << DIFFUSE_BITS)
{ {
if (ppt->m[0] == 0) if (ppt->m[0] == 0)
{ {
@@ -544,18 +605,18 @@ RenderLevelMasks (int offset)
for (i = 1; i < 4; i++) for (i = 1; i < 4; i++)
c[i-1] = get_avg_rgb (p1, ppt->m, i); c[i-1] = get_avg_rgb (p1, ppt->m, i);
} }
// Apply the lightinng model. This also bounds the sphere to make it circular // Apply the lighting model. This also bounds the sphere to make it circular
if (pSolarSysState->ShieldFrame) if (pSolarSysState->ShieldFrame)
{ {
c[2] = GET_PHONG (255, ph); c[2] = GET_LIGHT (255, diffus, spec);
c[1] = GET_PHONG (c[1] >> 1, ph); c[1] = GET_LIGHT ((UBYTE)(c[1] >> 1), diffus, spec);
c[0] = GET_PHONG (c[0] >> 1, ph); c[0] = GET_LIGHT ((UBYTE)(c[0] >> 1), diffus, spec);
} }
else else
{ {
c[2] = GET_PHONG (c[2], ph); c[2] = GET_LIGHT (c[2], diffus, spec);
c[1] = GET_PHONG (c[1], ph); c[1] = GET_LIGHT (c[1], diffus, spec);
c[0] = GET_PHONG (c[0], ph); c[0] = GET_LIGHT (c[0], diffus, spec);
} }
*p_rgba++ = frame_mapRGBA ( *p_rgba++ = frame_mapRGBA (
MaskFrame, c[2], c[1], c[0], (UBYTE)255); MaskFrame, c[2], c[1], c[0], (UBYTE)255);