Skip to content

Commit aa150f1

Browse files
levinli303Copilot
andcommitted
Render rings with BJ Jonsson radial profiles
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 357341a commit aa150f1

5 files changed

Lines changed: 111 additions & 21 deletions

File tree

src/celengine/body.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ struct RingSystem
102102
float innerRadius;
103103
float outerRadius;
104104
Color color{ 1.0f, 1.0f, 1.0f };
105+
Color unlitColor{ 1.0f, 0.97075f, 0.952f };
105106
celestia::util::TextureHandle texture{ celestia::util::TextureHandle::Invalid };
107+
celestia::util::TextureHandle colorTexture{ celestia::util::TextureHandle::Invalid };
106108
};
107109

108110
// Object class enumeration:

src/celengine/shadermanager.cpp

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1856,6 +1856,8 @@ buildRingsVertexShader(const ShaderProperties& props, bool fisheyeEnabled)
18561856
GLFragmentShader
18571857
buildRingsFragmentShader(const ShaderProperties& props)
18581858
{
1859+
bool useProfiles = util::is_set(props.texUsage, TexUsage::RingColorTexture);
1860+
18591861
std::string source(VersionHeader);
18601862
source += CommonHeader;
18611863
source += FragmentHeader;
@@ -1873,6 +1875,12 @@ buildRingsFragmentShader(const ShaderProperties& props)
18731875
source += DeclareUniform("diffTex", Shader_Sampler2D);
18741876
}
18751877

1878+
if (useProfiles)
1879+
{
1880+
source += DeclareUniform("ringColorTex", Shader_Sampler2D);
1881+
source += DeclareUniform("ringUnlitColor", Shader_Vector3);
1882+
}
1883+
18761884
if (props.hasEclipseShadows())
18771885
{
18781886
source += DeclareInput("shadowDepths", Shader_Vector4);
@@ -1901,7 +1909,24 @@ buildRingsFragmentShader(const ShaderProperties& props)
19011909
source += "color = texture(diffTex, diffTexCoord.st);\n";
19021910
else
19031911
source += "color = vec4(1.0);\n";
1904-
source += DeclareLocal("opticalDepth", Shader_Float, "color.a");
1912+
1913+
if (useProfiles)
1914+
{
1915+
// BJ Jónsson radial-profile encoding:
1916+
// diffTex.r = backscattered brightness (lit side, viewer ~ sun direction)
1917+
// diffTex.g = forward-scattered brightness (lit side, viewer opposite sun)
1918+
// diffTex.b = unlit-side brightness (transparent areas glow as sunlight filters through)
1919+
// diffTex.a = transparency (1 - opticalDepth)
1920+
// ringColorTex supplies the lit-side color profile.
1921+
source += DeclareLocal("ringColor", Shader_Vector3,
1922+
"texture(ringColorTex, diffTexCoord.st).rgb");
1923+
source += DeclareLocal("opticalDepth", Shader_Float, "1.0 - color.a");
1924+
}
1925+
else
1926+
{
1927+
source += DeclareLocal("opticalDepth", Shader_Float, "color.a");
1928+
}
1929+
19051930
if (props.hasEclipseShadows())
19061931
{
19071932
// Temporaries required for shadows
@@ -1917,15 +1942,35 @@ buildRingsFragmentShader(const ShaderProperties& props)
19171942
// Ring plane normal pointing toward the viewer
19181943
source += DeclareLocal("ringNormal", Shader_Vector3,
19191944
"vec3(0.0, sign(eyeDir.y), 0.0)");
1945+
if (useProfiles)
1946+
{
1947+
source += DeclareLocal("phase", Shader_Float);
1948+
source += DeclareLocal("tint", Shader_Vector3);
1949+
}
19201950

19211951
for (unsigned i = 0; i < props.nLights; i++)
19221952
{
19231953
// litSide is 1 when viewer and light are on the same side of the rings, 0 otherwise
19241954
source += "litSide = 1.0 - step(0.0, " + LightProperty(i, "direction") + ".y * eyeDir.y);\n";
1925-
//source += assign("litSide", 1.0f - step(0.0f, sh_vec3("eyePosition")["y"]));
19261955

1927-
source += "intensity = (dot(" + LightProperty(i, "direction") + ", eyeDir) + 1.0) * 0.5;\n";
1928-
source += "intensity = mix(intensity, intensity * (1.0 - opticalDepth), litSide);\n";
1956+
if (useProfiles)
1957+
{
1958+
// phase: 0 at backscatter (light & eye aligned), 1 at forward scatter.
1959+
// BJ's forward-scattered profile was captured at phase 139 degrees,
1960+
// so remap so that phase=139 degrees saturates to the G channel.
1961+
source += "phase = clamp((1.0 - dot(" + LightProperty(i, "direction")
1962+
+ ", eyeDir)) * 0.5 * (180.0 / 139.0), 0.0, 1.0);\n";
1963+
// Lit-side brightness blends backscatter and forward-scatter profiles.
1964+
// Unlit-side brightness comes from the dedicated profile.
1965+
source += "intensity = mix(mix(color.r, color.g, phase), color.b, litSide);\n";
1966+
// Apply unlit color tint when viewer sees the unlit side.
1967+
source += "tint = mix(ringColor, ringColor * ringUnlitColor, litSide);\n";
1968+
}
1969+
else
1970+
{
1971+
source += "intensity = (dot(" + LightProperty(i, "direction") + ", eyeDir) + 1.0) * 0.5;\n";
1972+
source += "intensity = mix(intensity, intensity * (1.0 - opticalDepth), litSide);\n";
1973+
}
19291974

19301975
// Specular highlight from sunlight reflecting off icy ring particles.
19311976
// Only contributes when the viewer and the light are on the same side
@@ -1935,32 +1980,27 @@ buildRingsFragmentShader(const ShaderProperties& props)
19351980
+ LightProperty(i, "direction") + " + eyeDir), ringNormal)), 32.0);\n";
19361981
source += "specFactor *= (1.0 - litSide) * opticalDepth;\n";
19371982

1983+
std::string lightTerm = useProfiles
1984+
? "(intensity * tint + vec3(specFactor))"
1985+
: "(intensity + specFactor)";
1986+
19381987
if (props.getEclipseShadowCountForLight(i) > 0)
19391988
{
19401989
source += "shadow = 1.0;\n";
19411990
source += Shadow(i, 0);
19421991
source += "shadow = min(1.0, shadow + step(0.0, " + ShadowDepth(i) + "));\n";
1943-
#if 0
1944-
source += "diff.rgb += (shadow * " + SeparateDiffuse(i) + ") * " +
1945-
FragLightProperty(i, "color") + ";\n";
1946-
#endif
1947-
source += "diff.rgb += shadow * (intensity + specFactor) * " + LightProperty(i, "diffuse") + ";\n";
1992+
source += "diff.rgb += shadow * " + lightTerm + " * " + LightProperty(i, "diffuse") + ";\n";
19481993
}
19491994
else
19501995
{
1951-
source += "diff.rgb += (intensity + specFactor) * " + LightProperty(i, "diffuse") + ";\n";
1952-
#if 0
1953-
source += SeparateDiffuse(i) + " = (dot(" +
1954-
LightProperty(i, "direction") + ", eyeDir) + 1.0) * 0.5;\n";
1955-
#endif
1956-
#if 0
1957-
source += "diff.rgb += " + SeparateDiffuse(i) + " * " +
1958-
FragLightProperty(i, "color") + ";\n";
1959-
#endif
1996+
source += "diff.rgb += " + lightTerm + " * " + LightProperty(i, "diffuse") + ";\n";
19601997
}
19611998
}
19621999

1963-
source += "fragColor = vec4(color.rgb * diff.rgb, opticalDepth);\n";
2000+
if (useProfiles)
2001+
source += "fragColor = vec4(diff.rgb, opticalDepth);\n";
2002+
else
2003+
source += "fragColor = vec4(color.rgb * diff.rgb, opticalDepth);\n";
19642004

19652005
source += "}\n";
19662006

@@ -2873,6 +2913,8 @@ CelestiaGLProgram::initParameters()
28732913
{
28742914
ringWidth = floatParam("ringWidth");
28752915
ringRadius = floatParam("ringRadius");
2916+
if (util::is_set(props.texUsage, TexUsage::RingColorTexture))
2917+
ringUnlitColor = vec3Param("ringUnlitColor");
28762918
}
28772919

28782920
textureOffset = floatParam("texCoordOffset");
@@ -2987,6 +3029,13 @@ CelestiaGLProgram::initSamplers()
29873029
if (GLint slot = glGetUniformLocation(program.getID(), "shadowMapTex0"); slot != -1)
29883030
glUniform1i(slot, nSamplers);
29893031
}
3032+
3033+
if (util::is_set(props.texUsage, TexUsage::RingColorTexture))
3034+
{
3035+
if (GLint slot = glGetUniformLocation(program.getID(), "ringColorTex"); slot != -1)
3036+
glUniform1i(slot, nSamplers);
3037+
nSamplers++;
3038+
}
29903039
}
29913040

29923041
void

src/celengine/shadermanager.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ enum class TexUsage : std::uint32_t
7171
StaticPointSize = 0x10000,
7272
LineAsTriangles = 0x20000,
7373
TextureCoordTransform = 0x40000,
74+
RingColorTexture = 0x80000,
7475
};
7576

7677
ENUM_CLASS_BITWISE_OPS(TexUsage);
@@ -257,6 +258,7 @@ class CelestiaGLProgram
257258
FloatShaderParameter ringRadius;
258259
Vec4ShaderParameter ringPlane;
259260
Vec3ShaderParameter ringCenter;
261+
Vec3ShaderParameter ringUnlitColor;
260262

261263
// Mix of Lambertian and "lunar" (Lommel-Seeliger) photometric models.
262264
// 0 = pure Lambertian, 1 = L-S

src/celengine/solarsys.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,12 +1325,22 @@ void ReadRings(Body* body,
13251325
if (auto color = ringsData->getColor("Color"); color.has_value())
13261326
rings->color = *color;
13271327

1328+
if (auto unlitColor = ringsData->getColor("UnlitColor"); unlitColor.has_value())
1329+
rings->unlitColor = *unlitColor;
1330+
13281331
if (auto textureName = GetFilename(*ringsData, "Texture"sv, "Invalid filename in rings Texture\n");
13291332
textureName.has_value())
13301333
{
13311334
rings->texture = texturePaths.getHandle(*textureName, path);
13321335
}
13331336

1337+
if (auto colorTextureName = GetFilename(*ringsData, "ColorTexture"sv,
1338+
"Invalid filename in rings ColorTexture\n");
1339+
colorTextureName.has_value())
1340+
{
1341+
rings->colorTexture = texturePaths.getHandle(*colorTextureName, path);
1342+
}
1343+
13341344
if (newRings != nullptr)
13351345
bodyFeaturesManager->setRings(body, std::move(newRings));
13361346
}

src/celrender/ringrenderer.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include <celcompat/numbers.h>
2020
#include <celengine/body.h>
21+
#include <celengine/glsupport.h>
2122
#include <celengine/lightenv.h>
2223
#include <celengine/render.h>
2324
#include <celengine/renderinfo.h>
@@ -44,6 +45,7 @@ struct RingVertex
4445
ShaderProperties
4546
createShaderProperties(const LightingState& ls,
4647
const Texture* ringsTex,
48+
const Texture* colorTex,
4749
bool renderShadow)
4850
{
4951
// Set up the shader properties for ring rendering
@@ -61,6 +63,12 @@ createShaderProperties(const LightingState& ls,
6163
if (ringsTex != nullptr)
6264
shadprop.texUsage = TexUsage::DiffuseTexture;
6365

66+
// BJ Jónsson radial-profile mode: enabled when both the profile texture
67+
// (RGBA: back/forward/unlit/transparency) and a separate color texture
68+
// are supplied.
69+
if (ringsTex != nullptr && colorTex != nullptr)
70+
shadprop.texUsage |= TexUsage::RingColorTexture;
71+
6472
return shadprop;
6573
}
6674

@@ -158,8 +166,10 @@ RingRenderer::renderRings(const RingSystem& rings,
158166
float inner = rings.innerRadius / planetRadius;
159167
float outer = rings.outerRadius / planetRadius;
160168
Texture* ringsTex = renderer.getTextureManager()->find(rings.texture);
169+
Texture* colorTex = renderer.getTextureManager()->find(rings.colorTexture);
170+
bool useProfiles = ringsTex != nullptr && colorTex != nullptr;
161171

162-
ShaderProperties shadprop = createShaderProperties(ls, ringsTex, renderShadow);
172+
ShaderProperties shadprop = createShaderProperties(ls, ringsTex, colorTex, renderShadow);
163173

164174
// Get a shader for the current rendering configuration
165175
auto* prog = renderer.getShaderManager().getShader(shadprop);
@@ -171,15 +181,32 @@ RingRenderer::renderRings(const RingSystem& rings,
171181

172182
prog->eyePosition = ls.eyePos_obj;
173183
prog->ambientColor = ri.ambientColor.toVector3();
174-
prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black);
184+
// In BJ-profile mode the ColorTexture supplies the ring tint, so don't
185+
// double-tint by baking ri.color into light.diffuse.
186+
Color lightDiffuseTint = useProfiles ? Color::White : ri.color;
187+
prog->setLightParameters(ls, lightDiffuseTint, ri.specularColor, Color::Black);
175188

176189
prog->ringRadius = inner;
177190
prog->ringWidth = outer - inner;
191+
if (useProfiles)
192+
prog->ringUnlitColor = rings.unlitColor.toVector3();
178193

179194
setUpShadowParameters(prog, ls, planetOblateness);
180195

196+
unsigned int texUnit = 0;
181197
if (ringsTex != nullptr)
198+
{
199+
glActiveTexture(GL_TEXTURE0 + texUnit);
182200
ringsTex->bind();
201+
texUnit++;
202+
}
203+
if (useProfiles)
204+
{
205+
glActiveTexture(GL_TEXTURE0 + texUnit);
206+
colorTex->bind();
207+
texUnit++;
208+
}
209+
glActiveTexture(GL_TEXTURE0);
183210

184211
// Determine level of detail
185212
std::uint32_t nSections = BaseSectionCount;

0 commit comments

Comments
 (0)