1 // renderglsl.cpp
2 //
3 // Functions for rendering objects using dynamically generated GLSL shaders.
4 //
5 // Copyright (C) 2006-2007, Chris Laurel <claurel@shatters.net>
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11
12 #include <algorithm>
13 #include <cstdio>
14 #include <cstring>
15 #include <cassert>
16
17 #ifndef _WIN32
18 #ifndef TARGET_OS_MAC
19 #include <config.h>
20 #endif
21 #endif /* _WIN32 */
22
23 #include <celutil/debug.h>
24 #include <celmath/frustum.h>
25 #include <celmath/distance.h>
26 #include <celmath/intersect.h>
27 #include <celutil/utf8.h>
28 #include <celutil/util.h>
29 #include "gl.h"
30 #include "astro.h"
31 #include "glext.h"
32 #include "vecgl.h"
33 #include "glshader.h"
34 #include "shadermanager.h"
35 #include "spheremesh.h"
36 #include "lodspheremesh.h"
37 #include "model.h"
38 #include "regcombine.h"
39 #include "vertexprog.h"
40 #include "texmanager.h"
41 #include "meshmanager.h"
42 #include "render.h"
43 #include "renderinfo.h"
44 #include "renderglsl.h"
45
46
47 using namespace std;
48
49
50 const double AtmosphereExtinctionThreshold = 0.05;
51
52
53 // Render a planet sphere with GLSL shaders
renderSphere_GLSL(const RenderInfo & ri,const LightingState & ls,RingSystem * rings,Atmosphere * atmosphere,float cloudTexOffset,float radius,unsigned int textureRes,int renderFlags,const Mat4f & planetMat,const Frustum & frustum,const GLContext & context)54 void renderSphere_GLSL(const RenderInfo& ri,
55 const LightingState& ls,
56 RingSystem* rings,
57 Atmosphere* atmosphere,
58 float cloudTexOffset,
59 float radius,
60 unsigned int textureRes,
61 int renderFlags,
62 const Mat4f& planetMat,
63 const Frustum& frustum,
64 const GLContext& context)
65 {
66 Texture* textures[MAX_SPHERE_MESH_TEXTURES] =
67 { NULL, NULL, NULL, NULL, NULL, NULL };
68 unsigned int nTextures = 0;
69
70 glDisable(GL_LIGHTING);
71
72 ShaderProperties shadprop;
73 shadprop.nLights = min(ls.nLights, MaxShaderLights);
74
75 // Set up the textures used by this object
76 if (ri.baseTex != NULL)
77 {
78 shadprop.texUsage = ShaderProperties::DiffuseTexture;
79 textures[nTextures++] = ri.baseTex;
80 }
81
82 if (ri.bumpTex != NULL)
83 {
84 shadprop.texUsage |= ShaderProperties::NormalTexture;
85 textures[nTextures++] = ri.bumpTex;
86 if (ri.bumpTex->getFormatOptions() & Texture::DXT5NormalMap)
87 shadprop.texUsage |= ShaderProperties::CompressedNormalTexture;
88 }
89
90 if (ri.specularColor != Color::Black)
91 {
92 shadprop.lightModel = ShaderProperties::PerPixelSpecularModel;
93 if (ri.glossTex == NULL)
94 {
95 shadprop.texUsage |= ShaderProperties::SpecularInDiffuseAlpha;
96 }
97 else
98 {
99 shadprop.texUsage |= ShaderProperties::SpecularTexture;
100 textures[nTextures++] = ri.glossTex;
101 }
102 }
103 else if (ri.lunarLambert != 0.0f)
104 {
105 // TODO: Lunar-Lambert model and specular color should not be mutually exclusive
106 shadprop.lightModel = ShaderProperties::LunarLambertModel;
107 }
108
109 if (ri.nightTex != NULL)
110 {
111 shadprop.texUsage |= ShaderProperties::NightTexture;
112 textures[nTextures++] = ri.nightTex;
113 }
114
115 if (ri.overlayTex != NULL)
116 {
117 shadprop.texUsage |= ShaderProperties::OverlayTexture;
118 textures[nTextures++] = ri.overlayTex;
119 }
120
121 if (rings != NULL && (renderFlags & Renderer::ShowRingShadows) != 0)
122 {
123 Texture* ringsTex = rings->texture.find(textureRes);
124 if (ringsTex != NULL)
125 {
126 glx::glActiveTextureARB(GL_TEXTURE0_ARB + nTextures);
127 ringsTex->bind();
128 nTextures++;
129
130 // Tweak the texture--set clamp to border and a border color with
131 // a zero alpha.
132 float bc[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
133 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bc);
134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
135 GL_CLAMP_TO_BORDER_ARB);
136 glx::glActiveTextureARB(GL_TEXTURE0_ARB);
137
138 shadprop.texUsage |= ShaderProperties::RingShadowTexture;
139 }
140 }
141
142 if (atmosphere != NULL)
143 {
144 if (renderFlags & Renderer::ShowAtmospheres)
145 {
146 // Only use new atmosphere code in OpenGL 2.0 path when new style parameters are defined.
147 // ... but don't show atmospheres when there are no light sources.
148 if (atmosphere->mieScaleHeight > 0.0f && shadprop.nLights > 0)
149 shadprop.texUsage |= ShaderProperties::Scattering;
150 }
151
152 if ((renderFlags & Renderer::ShowCloudMaps) != 0 &&
153 (renderFlags & Renderer::ShowCloudShadows) != 0)
154 {
155 Texture* cloudTex = NULL;
156 if (atmosphere->cloudTexture.tex[textureRes] != InvalidResource)
157 cloudTex = atmosphere->cloudTexture.find(textureRes);
158
159 // The current implementation of cloud shadows is not compatible
160 // with virtual or split textures.
161 bool allowCloudShadows = true;
162 for (unsigned int i = 0; i < nTextures; i++)
163 {
164 if (textures[i] != NULL &&
165 (textures[i]->getLODCount() > 1 ||
166 textures[i]->getUTileCount(0) > 1 ||
167 textures[i]->getVTileCount(0) > 1))
168 {
169 allowCloudShadows = false;
170 }
171 }
172
173 // Split cloud shadows can't cast shadows
174 if (cloudTex != NULL)
175 {
176 if (cloudTex->getLODCount() > 1 ||
177 cloudTex->getUTileCount(0) > 1 ||
178 cloudTex->getVTileCount(0) > 1)
179 {
180 allowCloudShadows = false;
181 }
182 }
183
184 if (cloudTex != NULL && allowCloudShadows && atmosphere->cloudShadowDepth > 0.0f)
185 {
186 shadprop.texUsage |= ShaderProperties::CloudShadowTexture;
187 textures[nTextures++] = cloudTex;
188 glx::glActiveTextureARB(GL_TEXTURE0_ARB + nTextures);
189 cloudTex->bind();
190 glx::glActiveTextureARB(GL_TEXTURE0_ARB);
191 }
192 }
193 }
194
195 // Set the shadow information.
196 // Track the total number of shadows; if there are too many, we'll have
197 // to fall back to multipass.
198 unsigned int totalShadows = 0;
199 for (unsigned int li = 0; li < ls.nLights; li++)
200 {
201 if (ls.shadows[li] && !ls.shadows[li]->empty())
202 {
203 unsigned int nShadows = (unsigned int) min((size_t) MaxShaderShadows, ls.shadows[li]->size());
204 shadprop.setShadowCountForLight(li, nShadows);
205 totalShadows += nShadows;
206 }
207 }
208
209 // Get a shader for the current rendering configuration
210 CelestiaGLProgram* prog = GetShaderManager().getShader(shadprop);
211 if (prog == NULL)
212 return;
213
214 prog->use();
215
216 #ifdef USE_HDR
217 prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black, ri.nightLightScale);
218 #else
219 prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black);
220 #endif
221
222 prog->eyePosition = ls.eyePos_obj;
223 prog->shininess = ri.specularPower;
224 if (shadprop.lightModel == ShaderProperties::LunarLambertModel)
225 prog->lunarLambert = ri.lunarLambert;
226
227 if (shadprop.texUsage & ShaderProperties::RingShadowTexture)
228 {
229 float ringWidth = rings->outerRadius - rings->innerRadius;
230 prog->ringRadius = rings->innerRadius / radius;
231 prog->ringWidth = radius / ringWidth;
232 }
233
234 if (shadprop.texUsage & ShaderProperties::CloudShadowTexture)
235 {
236 prog->shadowTextureOffset = cloudTexOffset;
237 prog->cloudHeight = 1.0f + atmosphere->cloudHeight / radius;
238 }
239
240 if (shadprop.hasScattering())
241 {
242 prog->setAtmosphereParameters(*atmosphere, radius, radius);
243 }
244
245 if (shadprop.shadowCounts != 0)
246 prog->setEclipseShadowParameters(ls, radius, planetMat);
247
248 glColor(ri.color);
249
250 unsigned int attributes = LODSphereMesh::Normals;
251 if (ri.bumpTex != NULL)
252 attributes |= LODSphereMesh::Tangents;
253 g_lodSphere->render(context,
254 attributes,
255 frustum, ri.pixWidth,
256 textures[0], textures[1], textures[2], textures[3]);
257
258 glx::glUseProgramObjectARB(0);
259 }
260
261
262 /*! Render a mesh object
263 * Parameters:
264 * tsec : animation clock time in seconds
265 */
renderGeometry_GLSL(Geometry * geometry,const RenderInfo & ri,ResourceHandle texOverride,const LightingState & ls,const Atmosphere * atmosphere,float geometryScale,int renderFlags,const Mat4f & planetMat,double tsec)266 void renderGeometry_GLSL(Geometry* geometry,
267 const RenderInfo& ri,
268 ResourceHandle texOverride,
269 const LightingState& ls,
270 const Atmosphere* atmosphere,
271 float geometryScale,
272 int renderFlags,
273 const Mat4f& planetMat,
274 double tsec)
275 {
276 glDisable(GL_LIGHTING);
277
278 GLSL_RenderContext rc(ls, geometryScale, planetMat);
279
280 if (renderFlags & Renderer::ShowAtmospheres)
281 {
282 rc.setAtmosphere(atmosphere);
283 }
284
285 rc.setCameraOrientation(ri.orientation);
286 rc.setPointScale(ri.pointScale);
287
288 // Handle extended material attributes (per model only, not per submesh)
289 rc.setLunarLambert(ri.lunarLambert);
290
291 // Handle material override; a texture specified in an ssc file will
292 // override all materials specified in the geometry file.
293 if (texOverride != InvalidResource)
294 {
295 Mesh::Material m;
296 m.diffuse = ri.color;
297 m.specular = ri.specularColor;
298 m.specularPower = ri.specularPower;
299 m.maps[Mesh::DiffuseMap] = texOverride;
300 rc.setMaterial(&m);
301 rc.lock();
302 geometry->render(rc, tsec);
303 }
304 else
305 {
306 geometry->render(rc, tsec);
307 }
308
309 glx::glUseProgramObjectARB(0);
310 }
311
312
313 /*! Render a mesh object without lighting.
314 * Parameters:
315 * tsec : animation clock time in seconds
316 */
renderGeometry_GLSL_Unlit(Geometry * geometry,const RenderInfo & ri,ResourceHandle texOverride,float geometryScale,int,const Mat4f &,double tsec)317 void renderGeometry_GLSL_Unlit(Geometry* geometry,
318 const RenderInfo& ri,
319 ResourceHandle texOverride,
320 float geometryScale,
321 int /* renderFlags */,
322 const Mat4f& /* planetMat */,
323 double tsec)
324 {
325 glDisable(GL_LIGHTING);
326
327 GLSLUnlit_RenderContext rc(geometryScale);
328
329 rc.setPointScale(ri.pointScale);
330
331 // Handle material override; a texture specified in an ssc file will
332 // override all materials specified in the model file.
333 if (texOverride != InvalidResource)
334 {
335 Mesh::Material m;
336 m.diffuse = ri.color;
337 m.specular = ri.specularColor;
338 m.specularPower = ri.specularPower;
339 m.maps[Mesh::DiffuseMap] = texOverride;
340 rc.setMaterial(&m);
341 rc.lock();
342 geometry->render(rc, tsec);
343 }
344 else
345 {
346 geometry->render(rc, tsec);
347 }
348
349 glx::glUseProgramObjectARB(0);
350 }
351
352
353 // Render the cloud sphere for a world a cloud layer defined
renderClouds_GLSL(const RenderInfo & ri,const LightingState & ls,Atmosphere * atmosphere,Texture * cloudTex,Texture * cloudNormalMap,float texOffset,RingSystem * rings,float radius,unsigned int textureRes,int renderFlags,const Mat4f & planetMat,const Frustum & frustum,const GLContext & context)354 void renderClouds_GLSL(const RenderInfo& ri,
355 const LightingState& ls,
356 Atmosphere* atmosphere,
357 Texture* cloudTex,
358 Texture* cloudNormalMap,
359 float texOffset,
360 RingSystem* rings,
361 float radius,
362 unsigned int textureRes,
363 int renderFlags,
364 const Mat4f& planetMat,
365 const Frustum& frustum,
366 const GLContext& context)
367 {
368 Texture* textures[MAX_SPHERE_MESH_TEXTURES] =
369 { NULL, NULL, NULL, NULL, NULL, NULL };
370 unsigned int nTextures = 0;
371
372 glDisable(GL_LIGHTING);
373
374 ShaderProperties shadprop;
375 shadprop.nLights = ls.nLights;
376
377 // Set up the textures used by this object
378 if (cloudTex != NULL)
379 {
380 shadprop.texUsage = ShaderProperties::DiffuseTexture;
381 textures[nTextures++] = cloudTex;
382 }
383
384 if (cloudNormalMap != NULL)
385 {
386 shadprop.texUsage |= ShaderProperties::NormalTexture;
387 textures[nTextures++] = cloudNormalMap;
388 if (cloudNormalMap->getFormatOptions() & Texture::DXT5NormalMap)
389 shadprop.texUsage |= ShaderProperties::CompressedNormalTexture;
390 }
391
392 if (rings != NULL && (renderFlags & Renderer::ShowRingShadows) != 0)
393 {
394 Texture* ringsTex = rings->texture.find(textureRes);
395 if (ringsTex != NULL)
396 {
397 glx::glActiveTextureARB(GL_TEXTURE0_ARB + nTextures);
398 ringsTex->bind();
399 nTextures++;
400
401 // Tweak the texture--set clamp to border and a border color with
402 // a zero alpha.
403 float bc[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
404 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bc);
405 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
406 GL_CLAMP_TO_BORDER_ARB);
407 glx::glActiveTextureARB(GL_TEXTURE0_ARB);
408
409 shadprop.texUsage |= ShaderProperties::RingShadowTexture;
410 }
411 }
412
413 if (atmosphere != NULL)
414 {
415 if (renderFlags & Renderer::ShowAtmospheres)
416 {
417 // Only use new atmosphere code in OpenGL 2.0 path when new style parameters are defined.
418 // ... but don't show atmospheres when there are no light sources.
419 if (atmosphere->mieScaleHeight > 0.0f && shadprop.nLights > 0)
420 shadprop.texUsage |= ShaderProperties::Scattering;
421 }
422 }
423
424 // Set the shadow information.
425 // Track the total number of shadows; if there are too many, we'll have
426 // to fall back to multipass.
427 unsigned int totalShadows = 0;
428 for (unsigned int li = 0; li < ls.nLights; li++)
429 {
430 if (ls.shadows[li] && !ls.shadows[li]->empty())
431 {
432 unsigned int nShadows = (unsigned int) min((size_t) MaxShaderShadows, ls.shadows[li]->size());
433 shadprop.setShadowCountForLight(li, nShadows);
434 totalShadows += nShadows;
435 }
436 }
437
438 // Get a shader for the current rendering configuration
439 CelestiaGLProgram* prog = GetShaderManager().getShader(shadprop);
440 if (prog == NULL)
441 return;
442
443 prog->use();
444
445 prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black);
446 prog->eyePosition = ls.eyePos_obj;
447 prog->ambientColor = Vec3f(ri.ambientColor.red(), ri.ambientColor.green(),
448 ri.ambientColor.blue());
449 prog->textureOffset = texOffset;
450
451 float cloudRadius = radius + atmosphere->cloudHeight;
452
453 if (shadprop.hasScattering())
454 {
455 prog->setAtmosphereParameters(*atmosphere, radius, cloudRadius);
456 }
457
458 if (shadprop.texUsage & ShaderProperties::RingShadowTexture)
459 {
460 float ringWidth = rings->outerRadius - rings->innerRadius;
461 prog->ringRadius = rings->innerRadius / cloudRadius;
462 prog->ringWidth = 1.0f / (ringWidth / cloudRadius);
463 }
464
465 if (shadprop.shadowCounts != 0)
466 prog->setEclipseShadowParameters(ls, cloudRadius, planetMat);
467
468 unsigned int attributes = LODSphereMesh::Normals;
469 if (cloudNormalMap != NULL)
470 attributes |= LODSphereMesh::Tangents;
471 g_lodSphere->render(context,
472 attributes,
473 frustum, ri.pixWidth,
474 textures[0], textures[1], textures[2], textures[3]);
475
476 prog->textureOffset = 0.0f;
477
478 glx::glUseProgramObjectARB(0);
479 }
480
481
482 // Render the sky sphere for a world with an atmosphere
483 void
renderAtmosphere_GLSL(const RenderInfo & ri,const LightingState & ls,Atmosphere * atmosphere,float radius,const Mat4f &,const Frustum & frustum,const GLContext & context)484 renderAtmosphere_GLSL(const RenderInfo& ri,
485 const LightingState& ls,
486 Atmosphere* atmosphere,
487 float radius,
488 const Mat4f& /*planetMat*/,
489 const Frustum& frustum,
490 const GLContext& context)
491 {
492 // Currently, we just skip rendering an atmosphere when there are no
493 // light sources, even though the atmosphere would still the light
494 // of planets and stars behind it.
495 if (ls.nLights == 0)
496 return;
497
498 glDisable(GL_LIGHTING);
499
500 ShaderProperties shadprop;
501 shadprop.nLights = ls.nLights;
502
503 shadprop.texUsage |= ShaderProperties::Scattering;
504 shadprop.lightModel = ShaderProperties::AtmosphereModel;
505
506 // Get a shader for the current rendering configuration
507 CelestiaGLProgram* prog = GetShaderManager().getShader(shadprop);
508 if (prog == NULL)
509 return;
510
511 prog->use();
512
513 prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black);
514 prog->ambientColor = Vec3f(0.0f, 0.0f, 0.0f);
515
516 float atmosphereRadius = radius + -atmosphere->mieScaleHeight * (float) log(AtmosphereExtinctionThreshold);
517 float atmScale = atmosphereRadius / radius;
518
519 prog->eyePosition = Point3f(ls.eyePos_obj.x / atmScale, ls.eyePos_obj.y / atmScale, ls.eyePos_obj.z / atmScale);
520 prog->setAtmosphereParameters(*atmosphere, radius, atmosphereRadius);
521
522 #if 0
523 // Currently eclipse shadows are ignored when rendering atmospheres
524 if (shadprop.shadowCounts != 0)
525 prog->setEclipseShadowParameters(ls, radius, planetMat);
526 #endif
527
528 glPushMatrix();
529 glScalef(atmScale, atmScale, atmScale);
530 glFrontFace(GL_CW);
531 glEnable(GL_BLEND);
532 glDepthMask(GL_FALSE);
533 glBlendFunc(GL_ONE, GL_SRC_ALPHA);
534
535 g_lodSphere->render(context,
536 LODSphereMesh::Normals,
537 frustum,
538 ri.pixWidth,
539 NULL);
540
541 glDisable(GL_BLEND);
542 glDepthMask(GL_TRUE);
543 glFrontFace(GL_CCW);
544 glPopMatrix();
545
546
547 glx::glUseProgramObjectARB(0);
548
549 //glx::glActiveTextureARB(GL_TEXTURE0_ARB);
550 //glEnable(GL_TEXTURE_2D);
551 }
552
553
renderRingSystem(float innerRadius,float outerRadius,float beginAngle,float endAngle,unsigned int nSections)554 static void renderRingSystem(float innerRadius,
555 float outerRadius,
556 float beginAngle,
557 float endAngle,
558 unsigned int nSections)
559 {
560 float angle = endAngle - beginAngle;
561
562 glBegin(GL_QUAD_STRIP);
563 for (unsigned int i = 0; i <= nSections; i++)
564 {
565 float t = (float) i / (float) nSections;
566 float theta = beginAngle + t * angle;
567 float s = (float) sin(theta);
568 float c = (float) cos(theta);
569 glTexCoord2f(0, 0.5f);
570 glVertex3f(c * innerRadius, 0, s * innerRadius);
571 glTexCoord2f(1, 0.5f);
572 glVertex3f(c * outerRadius, 0, s * outerRadius);
573 }
574 glEnd();
575 }
576
577
578 // Render a planetary ring system
renderRings_GLSL(RingSystem & rings,RenderInfo & ri,const LightingState & ls,float planetRadius,float planetOblateness,unsigned int textureResolution,bool renderShadow,unsigned int nSections)579 void renderRings_GLSL(RingSystem& rings,
580 RenderInfo& ri,
581 const LightingState& ls,
582 float planetRadius,
583 float planetOblateness,
584 unsigned int textureResolution,
585 bool renderShadow,
586 unsigned int nSections)
587 {
588 float inner = rings.innerRadius / planetRadius;
589 float outer = rings.outerRadius / planetRadius;
590 Texture* ringsTex = rings.texture.find(textureResolution);
591
592 ShaderProperties shadprop;
593 // Set up the shader properties for ring rendering
594 {
595 shadprop.lightModel = ShaderProperties::RingIllumModel;
596 shadprop.nLights = min(ls.nLights, MaxShaderLights);
597
598 if (renderShadow)
599 {
600 // Set one shadow (the planet's) per light
601 for (unsigned int li = 0; li < ls.nLights; li++)
602 shadprop.setShadowCountForLight(li, 1);
603 }
604
605 if (ringsTex)
606 shadprop.texUsage = ShaderProperties::DiffuseTexture;
607 }
608
609
610 // Get a shader for the current rendering configuration
611 CelestiaGLProgram* prog = GetShaderManager().getShader(shadprop);
612 if (prog == NULL)
613 return;
614
615 prog->use();
616
617 prog->eyePosition = ls.eyePos_obj;
618 prog->ambientColor = Vec3f(ri.ambientColor.red(), ri.ambientColor.green(),
619 ri.ambientColor.blue());
620 prog->setLightParameters(ls, ri.color, ri.specularColor, Color::Black);
621
622 for (unsigned int li = 0; li < ls.nLights; li++)
623 {
624 const DirectionalLight& light = ls.lights[li];
625
626 // Compute the projection vectors based on the sun direction.
627 // I'm being a little careless here--if the sun direction lies
628 // along the y-axis, this will fail. It's unlikely that a
629 // planet would ever orbit underneath its sun (an orbital
630 // inclination of 90 degrees), but this should be made
631 // more robust anyway.
632 Vec3f axis = Vec3f(0, 1, 0) ^ light.direction_obj;
633 float cosAngle = Vec3f(0.0f, 1.0f, 0.0f) * light.direction_obj;
634 /*float angle = (float) acos(cosAngle); Unused*/
635 axis.normalize();
636
637 float tScale = 1.0f;
638 if (planetOblateness != 0.0f)
639 {
640 // For oblate planets, the size of the shadow volume will vary
641 // based on the light direction.
642
643 // A vertical slice of the planet is an ellipse
644 float a = 1.0f; // semimajor axis
645 float b = a * (1.0f - planetOblateness); // semiminor axis
646 float ecc2 = 1.0f - (b * b) / (a * a); // square of eccentricity
647
648 // Calculate the radius of the ellipse at the incident angle of the
649 // light on the ring plane + 90 degrees.
650 float r = a * (float) sqrt((1.0f - ecc2) /
651 (1.0f - ecc2 * square(cosAngle)));
652
653 tScale *= a / r;
654 }
655
656 // The s axis is perpendicular to the shadow axis in the plane of the
657 // of the rings, and the t axis completes the orthonormal basis.
658 Vec3f sAxis = axis * 0.5f;
659 Vec3f tAxis = (axis ^ light.direction_obj) * 0.5f * tScale;
660 Vec4f texGenS(sAxis.x, sAxis.y, sAxis.z, 0.5f);
661 Vec4f texGenT(tAxis.x, tAxis.y, tAxis.z, 0.5f);
662
663 // r0 and r1 determine the size of the planet's shadow and penumbra
664 // on the rings.
665 // TODO: A more accurate ring shadow calculation would set r1 / r0
666 // to the ratio of the apparent sizes of the planet and sun as seen
667 // from the rings. Even more realism could be attained by letting
668 // this ratio vary across the rings, though it may not make enough
669 // of a visual difference to be worth the extra effort.
670 float r0 = 0.24f;
671 float r1 = 0.25f;
672 float bias = 1.0f / (1.0f - r1 / r0);
673
674 prog->shadows[li][0].texGenS = texGenS;
675 prog->shadows[li][0].texGenT = texGenT;
676 prog->shadows[li][0].maxDepth = 1.0f;
677 prog->shadows[li][0].falloff = bias / r0;
678 }
679
680 glEnable(GL_BLEND);
681 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
682
683 if (ringsTex != NULL)
684 ringsTex->bind();
685 else
686 glDisable(GL_TEXTURE_2D);
687
688 renderRingSystem(inner, outer, 0, (float) PI * 2.0f, nSections);
689 renderRingSystem(inner, outer, (float) PI * 2.0f, 0, nSections);
690
691 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
692
693 glx::glUseProgramObjectARB(0);
694 }
695