Index: lightenv.h =================================================================== --- lightenv.h (revision 4538) +++ lightenv.h (working copy) @@ -41,6 +41,7 @@ Vec3f direction; float penumbraRadius; float umbraRadius; + float maxDepth; }; class LightingState Index: render.cpp =================================================================== --- render.cpp (revision 4538) +++ render.cpp (working copy) @@ -7795,10 +7795,21 @@ (float) sunDir.y, (float) sunDir.z); shadow.penumbraRadius = shadowRadius; + + // The umbra radius will be positive if the apparent size of the occluder + // is greater than the apparent size of the sun, zero if they're equal, + // and negative when the eclipse is partial. The absolute value of the + // umbra radius is the radius of the shadow region with constant depth: + // for total eclipses, this area is actually the umbra, with a depth of + // 1. For annular eclipses and transits, it is less than 1. shadow.umbraRadius = caster.getRadius() * (appOccluderRadius - appSunRadius) / appOccluderRadius; - shadows.push_back(shadow); + shadow.maxDepth = std::min(1.0f, square(appOccluderRadius / appSunRadius)); + // Ignore transits that don't produce a visible shadow. + if (shadow.maxDepth > 1.0f / 256.0f) + shadows.push_back(shadow); + return true; } } Index: renderglsl.cpp =================================================================== --- renderglsl.cpp (revision 4531) +++ renderglsl.cpp (working copy) @@ -648,17 +648,21 @@ Vec4f texGenS(sAxis.x, sAxis.y, sAxis.z, 0.5f); Vec4f texGenT(tAxis.x, tAxis.y, tAxis.z, 0.5f); - // r0 and r1 determine the size of the penumbra and the umbra - // shadow size. + // r0 and r1 determine the size of the planet's shadow and penumbra + // on the rings. + // TODO: A more accurate ring shadow calculation would set r1 / r0 + // to the ratio of the apparent sizes of the planet and sun as seen + // from the rings. Even more realism could be attained by letting + // this ratio vary across the rings, though it may not make enough + // of a visual difference to be worth the extra effort. float r0 = 0.24f; float r1 = 0.25f; float bias = 1.0f / (1.0f - r1 / r0); - /*float scale = -bias / r0; Unused*/ prog->shadows[li][0].texGenS = texGenS; prog->shadows[li][0].texGenT = texGenT; - prog->shadows[li][0].bias = bias; - prog->shadows[li][0].scale = -bias / r0; + prog->shadows[li][0].maxDepth = 1.0f; + prog->shadows[li][0].falloff = bias / r0; } glEnable(GL_BLEND); Index: shadermanager.cpp =================================================================== --- shadermanager.cpp (revision 4531) +++ shadermanager.cpp (working copy) @@ -7,9 +7,11 @@ // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. +#include "celutil/util.h" #include "gl.h" #include "glext.h" #include "shadermanager.h" +#include #include #include #include @@ -598,6 +600,19 @@ } +// Calculate the depth of an eclipse shadow at the current fragment. Eclipse +// shadows are circular, decreasing in depth from maxDepth at the center to +// zero at the edge of the penumbra. +// Eclipse shadows are approximate. They assume that the both the sun and +// occluding body are spherical. An oblate planet is treated as if its polar +// radius were equal to its equatorial radius. This produces quite accurate +// eclipses for major moons around giant planets, which orbit close to the +// equatorial plane of the planet. +// +// The radius of the shadow umbra and penumbra are computed accurately +// (to the limit of the spherical approximation.) The maximum shadow depth +// is also calculated accurately. However, the shadow falloff from from +// the umbra to the edge of the penumbra is approximated as linear. static string Shadow(unsigned int light, unsigned int shadow) { @@ -607,11 +622,23 @@ IndexedParameter("shadowTexGenS", light, shadow) + ") - 0.5;\n"; source += "shadowCenter.t = dot(vec4(position_obj, 1.0), " + IndexedParameter("shadowTexGenT", light, shadow) + ") - 0.5;\n"; - source += "shadowR = clamp(dot(shadowCenter, shadowCenter) * " + - IndexedParameter("shadowScale", light, shadow) + " + " + - IndexedParameter("shadowBias", light, shadow) + ", 0.0, 1.0);\n"; - source += "shadow *= sqrt(shadowR);\n"; + // The shadow shadow consists of a circular region of constant depth (maxDepth), + // surrounded by a ring of linear falloff from maxDepth to zero. For a total + // eclipse, maxDepth is zero. In reality, the falloff function is much more complex: + // to calculate the exact amount of sunlight blocked, we need to calculate the + // a circle-circle intersection area. + // (See http://mathworld.wolfram.com/Circle-CircleIntersection.html) + + // The code generated below will compute: + // r = 2 * sqrt(dot(shadowCenter, shadowCenter)); + // shadowR = clamp((r - 1) * shadowFalloff, 0, shadowMaxDepth) + source += "shadowR = clamp((2.0 * sqrt(dot(shadowCenter, shadowCenter)) - 1.0) * " + + IndexedParameter("shadowFalloff", light, shadow) + ", 0.0, " + + IndexedParameter("shadowMaxDepth", light, shadow) + ");\n"; + + source += "shadow *= 1.0 - shadowR;\n"; + return source; } @@ -1357,9 +1384,9 @@ source += "uniform vec4 " + IndexedParameter("shadowTexGenT", i, j) + ";\n"; source += "uniform float " + - IndexedParameter("shadowScale", i, j) + ";\n"; + IndexedParameter("shadowFalloff", i, j) + ";\n"; source += "uniform float " + - IndexedParameter("shadowBias", i, j) + ";\n"; + IndexedParameter("shadowMaxDepth", i, j) + ";\n"; } } } @@ -1724,9 +1751,9 @@ source += "uniform vec4 " + IndexedParameter("shadowTexGenT", i, j) + ";\n"; source += "uniform float " + - IndexedParameter("shadowScale", i, j) + ";\n"; + IndexedParameter("shadowFalloff", i, j) + ";\n"; source += "uniform float " + - IndexedParameter("shadowBias", i, j) + ";\n"; + IndexedParameter("shadowMaxDepth", i, j) + ";\n"; } } } @@ -2121,8 +2148,8 @@ { source << "uniform vec4 " << IndexedParameter("shadowTexGenS", i, j) << ";\n"; source << "uniform vec4 " << IndexedParameter("shadowTexGenT", i, j) << ";\n"; - source << "uniform float " << IndexedParameter("shadowScale", i, j) << ";\n"; - source << "uniform float " << IndexedParameter("shadowBias", i, j) << ";\n"; + source << "uniform float " << IndexedParameter("shadowFalloff", i, j) << ";\n"; + source << "uniform float " << IndexedParameter("shadowMaxDepth", i, j) << ";\n"; } } } @@ -2308,10 +2335,10 @@ vec4Param(IndexedParameter("shadowTexGenS", i, j)); shadows[i][j].texGenT = vec4Param(IndexedParameter("shadowTexGenT", i, j)); - shadows[i][j].scale = - floatParam(IndexedParameter("shadowScale", i, j)); - shadows[i][j].bias = - floatParam(IndexedParameter("shadowBias", i, j)); + shadows[i][j].falloff = + floatParam(IndexedParameter("shadowFalloff", i, j)); + shadows[i][j].maxDepth = + floatParam(IndexedParameter("shadowMaxDepth", i, j)); } } @@ -2527,20 +2554,13 @@ EclipseShadow& shadow = ls.shadows[li]->at(i); CelestiaGLProgramShadow& shadowParams = shadows[li][i]; - float R2 = 0.25f; - float umbra = shadow.umbraRadius / shadow.penumbraRadius; - umbra = umbra * umbra; - if (umbra < 0.0001f) - umbra = 0.0001f; - else if (umbra > 0.99f) - umbra = 0.99f; + // Compute shadow parameters: max depth of at the center of the shadow + // (always 1 if an eclipse is total) and the linear falloff + // rate from the center to the outer endge of the penumbra. + float u = shadow.umbraRadius / shadow.penumbraRadius; + shadowParams.falloff = -shadow.maxDepth / std::max(0.001f, 1.0f - std::fabs(u)); + shadowParams.maxDepth = shadow.maxDepth; - float umbraRadius = R2 * umbra; - float penumbraRadius = R2; - float shadowBias = 1.0f / (1.0f - penumbraRadius / umbraRadius); - shadowParams.bias = shadowBias; - shadowParams.scale = -shadowBias / umbraRadius; - // Compute the transformation to use for generating texture // coordinates from the object vertices. Point3f origin = shadow.origin * planetMat; Index: shadermanager.h =================================================================== --- shadermanager.h (revision 4531) +++ shadermanager.h (working copy) @@ -102,8 +102,8 @@ { Vec4ShaderParameter texGenS; Vec4ShaderParameter texGenT; - FloatShaderParameter scale; - FloatShaderParameter bias; + FloatShaderParameter falloff; + FloatShaderParameter maxDepth; }; class CelestiaGLProgram