[Piglit] [PATCH] arb_es2_compatibility-releaseshadercompiler-ge7: test glReleaseShaderCompiler crash

Brian Paul brianp at vmware.com
Tue Dec 18 14:43:12 PST 2012


This tests exercises a Mesa crash found with Google Earth v7.
Specifically, Google Earth is calling glReleaseShaderCompiler() between
glCompileShader() and glLinkProgram().

It appears that glReleaseShaderCompiler() is freeing some data
structures that are needed later by the linker.

The test passes with NVIDIA's driver.
---
 tests/all.tests                                    |    1 +
 tests/spec/arb_es2_compatibility/CMakeLists.gl.txt |    1 +
 ...b_es2_compatibility-releaseshadercompiler-ge7.c |  132 ++++
 tests/spec/arb_es2_compatibility/ge7.frag          |  710 +++++++++++++++++
 tests/spec/arb_es2_compatibility/ge7.vert          |  814 ++++++++++++++++++++
 5 files changed, 1658 insertions(+), 0 deletions(-)
 create mode 100644 tests/spec/arb_es2_compatibility/arb_es2_compatibility-releaseshadercompiler-ge7.c
 create mode 100644 tests/spec/arb_es2_compatibility/ge7.frag
 create mode 100644 tests/spec/arb_es2_compatibility/ge7.vert

diff --git a/tests/all.tests b/tests/all.tests
index c823bcf..b168e69 100644
--- a/tests/all.tests
+++ b/tests/all.tests
@@ -867,6 +867,7 @@ add_plain_test(arb_es2_compatibility, 'arb_es2_compatibility-getshaderprecisionf
 add_plain_test(arb_es2_compatibility, 'arb_es2_compatibility-maxvectors')
 add_plain_test(arb_es2_compatibility, 'arb_es2_compatibility-shadercompiler')
 add_plain_test(arb_es2_compatibility, 'arb_es2_compatibility-releaseshadercompiler')
+add_plain_test(arb_es2_compatibility, 'arb_es2_compatibility-releaseshadercompiler-ge7')
 add_plain_test(arb_es2_compatibility, 'arb_es2_compatibility-fixed-type')
 add_plain_test(arb_es2_compatibility, 'fbo-missing-attachment-clear')
 arb_es2_compatibility['FBO blit to missing attachment (ES2 completeness rules)'] = PlainExecTest(['fbo-missing-attachment-blit', '-auto', 'es2', 'to'])
diff --git a/tests/spec/arb_es2_compatibility/CMakeLists.gl.txt b/tests/spec/arb_es2_compatibility/CMakeLists.gl.txt
index 99937fa..fe69ea3 100644
--- a/tests/spec/arb_es2_compatibility/CMakeLists.gl.txt
+++ b/tests/spec/arb_es2_compatibility/CMakeLists.gl.txt
@@ -15,6 +15,7 @@ piglit_add_executable (arb_es2_compatibility-depthrangef arb_es2_compatibility-d
 piglit_add_executable (arb_es2_compatibility-drawbuffers arb_es2_compatibility-drawbuffers.c)
 piglit_add_executable (arb_es2_compatibility-maxvectors arb_es2_compatibility-maxvectors.c)
 piglit_add_executable (arb_es2_compatibility-releaseshadercompiler arb_es2_compatibility-releaseshadercompiler.c)
+piglit_add_executable (arb_es2_compatibility-releaseshadercompiler-ge7 arb_es2_compatibility-releaseshadercompiler-ge7.c)
 piglit_add_executable (arb_es2_compatibility-shadercompiler arb_es2_compatibility-shadercompiler.c)
 piglit_add_executable (arb_es2_compatibility-getshaderprecisionformat arb_es2_compatibility-getshaderprecisionformat.c)
 piglit_add_executable (arb_es2_compatibility-fixed-type arb_es2_compatibility-fixed-type.c)
diff --git a/tests/spec/arb_es2_compatibility/arb_es2_compatibility-releaseshadercompiler-ge7.c b/tests/spec/arb_es2_compatibility/arb_es2_compatibility-releaseshadercompiler-ge7.c
new file mode 100644
index 0000000..112bed0
--- /dev/null
+++ b/tests/spec/arb_es2_compatibility/arb_es2_compatibility-releaseshadercompiler-ge7.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Eric Anholt <eric at anholt.net>  (original program)
+ *    Brian Paul  (this variation on Eric's program)
+ *
+ */
+
+/** @file arb_es2_compatibility-releasecompiler-ge7.c
+ *
+ * This tests glReleaseShaderCompiler() between compilation and linking.
+ *
+ * At the time of writing, this causes Mesa to crash with Google Earth
+ * version 7 because the linker is trying to use data that was
+ * generated by the compiler but freed by glReleaseShaderCompiler().
+ *
+ * This test passes with NVIDIA's driver.
+ *
+ * The vertex/fragment shaders are captured from Google Earth v7.
+ * Simpler shaders don't always trigger the bug we're testing for.
+ */
+
+#include "piglit-util-gl-common.h"
+
+PIGLIT_GL_TEST_CONFIG_BEGIN
+
+	config.supports_gl_compat_version = 10;
+
+	config.window_width = 100;
+	config.window_height = 100;
+	config.window_visual = PIGLIT_GL_VISUAL_RGB | PIGLIT_GL_VISUAL_ALPHA | PIGLIT_GL_VISUAL_DOUBLE;
+
+PIGLIT_GL_TEST_CONFIG_END
+
+#ifdef GL_ARB_ES2_compatibility
+
+void
+draw(void)
+{
+	GLuint vs, fs, prog;
+
+	vs = piglit_compile_shader(GL_VERTEX_SHADER,
+				   "spec/arb_es2_compatibility/ge7.vert");
+	fs = piglit_compile_shader(GL_FRAGMENT_SHADER,
+				   "spec/arb_es2_compatibility/ge7.frag");
+
+	/* NOTE: we're calling glReleaseShaderCompiler() between compiling
+	 * and linking.
+	 */
+	glReleaseShaderCompiler();
+
+	prog = piglit_link_simple_program(vs, fs);
+	piglit_link_check_status(prog);
+
+	glDeleteShader(vs);
+	glDeleteShader(fs);
+
+	glUseProgram(prog);
+
+	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+	glDeleteProgram(prog);
+}
+#endif
+
+enum piglit_result
+piglit_display(void)
+{
+#ifdef GL_ARB_ES2_compatibility
+	static const float green[] = {0.0, 1.0, 0.0, 0.0};
+	GLboolean pass = GL_TRUE;
+
+	glClearColor(0.5, 0.5, 0.5, 0.0);
+	glClear(GL_COLOR_BUFFER_BIT);
+
+	draw();
+
+	pass &= piglit_probe_pixel_rgba(piglit_width / 4, piglit_height / 2,
+					green);
+
+	assert(!glGetError());
+
+	piglit_present_results();
+
+	return pass ? PIGLIT_PASS : PIGLIT_FAIL;
+#else
+	return PIGLIT_SKIP;
+#endif /* GL_ARB_ES2_compatibility */
+}
+
+void
+piglit_init(int argc, char **argv)
+{
+#ifdef GL_ARB_ES2_compatibility
+	static const float verts[] = {
+		-1.0,  1.0, 0.0, 1.0,
+		-1.0, -1.0, 0.0, 1.0,
+		+0.0,  1.0, 0.0, 1.0,
+		+0.0, -1.0, 0.0, 1.0,
+	};
+
+	piglit_require_gl_version(20);
+
+	if (!piglit_is_extension_supported("GL_ARB_ES2_compatibility")) {
+		printf("Requires ARB_ES2_compatibility\n");
+		piglit_report_result(PIGLIT_SKIP);
+	}
+
+	glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4,
+			      verts);
+	glEnableVertexAttribArray(0);
+#endif
+}
diff --git a/tests/spec/arb_es2_compatibility/ge7.frag b/tests/spec/arb_es2_compatibility/ge7.frag
new file mode 100644
index 0000000..2d354ea
--- /dev/null
+++ b/tests/spec/arb_es2_compatibility/ge7.frag
@@ -0,0 +1,710 @@
+//!GL2
+#define GE_PIXEL_SHADER
+#define GE_PRECISION mediump
+#define ATMOSPHERE_OFF
+#define GROUND
+#define SUN_OFF
+#define OVERLAY
+#define ENABLE_VERTEX_REJECT
+#if defined(GL_ES)
+precision GE_PRECISION float;
+#else
+#define lowp
+#define mediump
+#define highp
+#endif
+
+uniform mat4 ig_ModelViewMatrix;
+uniform mat4 ig_ModelViewProjectionMatrix;
+uniform mat4 ig_TextureMatrix;
+uniform vec4 ig_LightDirectionInModelSpace;
+uniform vec4 ig_env_atmosphere_custom_state_value;
+uniform lowp vec4 ig_env_atmosphere_custom_color_value;
+uniform lowp float alpha_ref_value;
+
+float saturate(float v) { return clamp(v, 0.0, 1.0); }
+vec2 saturate(vec2 v) { return clamp(v, 0.0, 1.0); }
+vec3 saturate(vec3 v) { return clamp(v, 0.0, 1.0); }
+vec4 saturate(vec4 v) { return clamp(v, 0.0, 1.0); }
+
+// Returns the quadrant portion of the given vertex flags.
+float getQuadrant(vec4 vertexQuadrant) {
+  return dot(vertexQuadrant, vec4(1.0, 2.0, 4.0, 8.0));
+}
+
+// Both getMasked methods assume that quadrantMask has a single 1 and the
+// rest are 0.
+
+vec4 getMaskedVertex(vec4 pos, vec4 quadrantMask, vec4 vertexQuadrant) {
+  return pos * dot(quadrantMask, vertexQuadrant);
+}
+
+float getMaskedAlpha(vec4 quadrantAlphas, vec4 vertexQuadrant) {
+  return dot(quadrantAlphas, vertexQuadrant);
+}
+
+// Computes a vertex color to show which quadrant a vertex is in.  Also marks
+// vertices that would be rejected and error conditions (e.g. invalid
+// quadrant).
+vec4 getDebugVertexColor(vec4 quadrantMask, vec4 vertexQuadrant) {
+  vec4 color;
+  float quadrant = getQuadrant(vertexQuadrant);
+  if (quadrant == 1.0) {
+    // Red
+    color = vec4(1.0, 0.0, 0.0, 1.0);
+  } else if (quadrant == 2.0) {
+    // Green
+    color = vec4(0.0, 1.0, 0.0, 1.0);
+  } else if (quadrant == 4.0) {
+    // Blue
+    color = vec4(0.0, 0.0, 1.0, 1.0);
+  } else if (quadrant == 8.0) {
+    // Cyan
+    color = vec4(0.0, 1.0, 1.0, 1.0);
+  } else if (quadrant == 0.0) {
+    // Vertex didn't have a quadrant bit set.  Mark as saturated Yellow.
+    color = vec4(10.0, 10.0, 0.0, 1.0);
+  } else if (quadrant == 15.0) {
+    // All quadrant bits were set.  Mark as White.
+    color = vec4(10.0, 10.0, 10.0, 1.0);
+  } else {
+    // More than one quadrant bit was set or a quadrant bit was set to
+    // something other than 1.0, so flag verts in saturated Magenta.
+    color = vec4(10.0, 0.0, 10.0, 1.0);
+  }
+  // Make hidden quadrants dimmer.
+  if (dot(quadrantMask, vertexQuadrant) == 0.)
+    color *= .5;
+  return color;
+}
+
+#ifdef DEBUG_QUADRANT_COLORS
+// Used by the pixel shader to show quadrants, culled verts and error
+// conditions when DEBUG_QUADRANT_COLORS is defined.
+varying vec4 vout_debug_color;
+#endif
+
+// The \"discard\" instruction is not available for vertex shaders, so
+// this function needs to be excluded when parsing a vertex shader.
+#ifdef GE_PIXEL_SHADER
+
+// When debugging is on, modulates the pixel color by the debug color output
+// from the vertex shader.  Otherwise, the given pixel color is returned
+// unchanged.
+vec4 computeDebugPixelColor(vec4 pixelShaderColor) {
+// OGL ES 2.0 does not support alpha test, so need to do it in the
+// shader.
+#if defined(GL_ES) && !defined(FORCE_NO_ALPHA_TEST)
+// Terrain and sky don't use alpha test.
+#if defined(OVERLAY) || defined(MODEL)
+  // alpha_ref_value is set by igVisualContext, and will be -1 if alpha test
+  // is off.
+  if (pixelShaderColor.a < alpha_ref_value) {
+    discard;
+  }
+#endif  // defined(OVERLAY) || defined(MODEL)
+#endif  // defined(GL_ES) && !defined(FORCE_NO_ALPHA_TEST)
+
+#ifdef DEBUG_QUADRANT_COLORS
+  return pixelShaderColor * vout_debug_color;
+#else
+  return pixelShaderColor;
+#endif
+}
+
+#endif  // GE_PIXEL_SHADER
+// Constant g for phase function used for Mie scattering.
+const float gMieConst = -0.990;
+const float gMieConst2 = gMieConst * gMieConst;
+
+varying vec4 vout_texCoordAndFogFactor;
+#if defined(QUADRANT_ALPHAS)
+varying float vout_alpha;
+#endif
+
+uniform lowp sampler2D groundTexture;
+uniform lowp vec4 groundColor;
+
+#if defined(OVERLAY)
+uniform vec4 groundOverlayExtent;
+float GetMaskedOverlayAlpha(float alpha, vec2 texCoord) {
+  return ((texCoord.s - groundOverlayExtent.x) *
+          (groundOverlayExtent.y - texCoord.s) >= 0. &&
+          (texCoord.t - groundOverlayExtent.z) *
+          (groundOverlayExtent.w - texCoord.t) >= 0. ? alpha : 0.0);
+}
+
+#endif // defined(OVERLAY)
+
+#if defined(USE_IMPROVED_SHADER_VARIATION)
+// x: atmosphere density.
+// y: atmosphere angle falloff.
+// z: night intensity.
+// w: camera exposure.
+uniform vec4 atmosphereTweaks;
+
+#endif // defined(USE_IMPROVED_SHADER_VARIATION)
+
+//", '=' <repeats 30 times>, " ground sun off
+#if defined(ATMOSPHERE_ON) && defined(GROUND) && defined(SUN_OFF)
+
+#if !defined(USE_SIMPLIFIED_SHADER_VARIATION)
+varying vec4 vout_blendTexCoordAndRayleighT;
+#endif
+
+uniform lowp sampler2D groundRayleighMap;
+uniform vec4 groundSunOffPixelParams;
+uniform lowp vec4 groundFogColor;
+
+#if defined(DEBUG_GE_BUILD)
+uniform lowp vec4 sidedb_color;
+#endif
+
+void AtmosphereGroundSunOffFragShader() {
+  // Get the texture color for the object.
+  lowp vec4 texColor = texture2D(groundTexture, vout_texCoordAndFogFactor.st);
+
+#if defined(DEBUG_GE_BUILD)
+  if (sidedb_color.a > 0.0) {
+    texColor = mix(texColor, sidedb_color, 0.1);
+  }
+#endif
+  texColor *= groundColor;
+
+  lowp vec4 color;
+  #if defined(USE_SIMPLIFIED_SHADER_VARIATION)
+    color = texColor;
+  #else
+    // The rayleigh map's s-coordinate depends on camera height and is computed
+    // on the CPU.
+    float rayleighS = groundSunOffPixelParams.x;
+    lowp vec4 rayleigh = texture2D(groundRayleighMap, vec2(rayleighS, vout_blendTexCoordAndRayleighT.z));
+    texColor.rgb += rayleigh.rgb;
+    lowp float fogFactor = vout_texCoordAndFogFactor.w;
+    color.rgb = mix(groundFogColor.rgb, texColor.rgb, fogFactor);
+    color.a = texColor.a;
+  #endif
+
+  gl_FragColor = computeDebugPixelColor(color);
+}
+
+//", '=' <repeats 30 times>, " ground sun off overlay
+#if defined(OVERLAY)
+uniform lowp sampler2D groundRayleighOverlayMap;
+
+void AtmosphereGroundSunOffOverlayFragShader() {
+  // Get the texture color for the object. The projective component is to
+  // allow us to do Arbitrary Ground Overlays.
+  lowp vec4 texColor =
+      texture2DProj(groundTexture, vout_texCoordAndFogFactor.stp) *
+      groundColor;
+  // Note that we do not allow texture blend for overlays.
+  // Perform texture edge clamping for ground overlays by setting alpha to zero
+  // outside of texture coordinate range [0, 1]
+  vec2 stOverZ = vout_texCoordAndFogFactor.xy / vout_texCoordAndFogFactor.z;
+  texColor.a = GetMaskedOverlayAlpha(texColor.a, stOverZ);
+
+  // The rayleigh map's s-coordinate depends on camera height and is computed
+  // on the CPU.
+  float rayleighS = groundSunOffPixelParams.x;
+  lowp vec4 rayleigh = texture2D(
+      groundRayleighOverlayMap,
+      vec2(rayleighS, vout_blendTexCoordAndRayleighT.z));
+  texColor.rgb += rayleigh.rgb;
+  lowp float fogFactor = vout_texCoordAndFogFactor.w;
+  lowp vec4 color;
+  color.rgb = mix(groundFogColor.rgb, texColor.rgb, fogFactor);
+  color.a = texColor.a;
+  gl_FragColor = computeDebugPixelColor(color);
+}
+#endif // defined(OVERLAY)
+#endif // defined(ATMOSPHERE_ON) && defined(GROUND) && defined(SUN_OFF)
+
+//", '=' <repeats 30 times>, " ground sun on (default)
+#if defined(ATMOSPHERE_ON) && defined(GROUND) && defined(SUN_ON) && !defined(USE_IMPROVED_SHADER_VARIATION)
+varying vec3 vout_rayleigh;
+varying vec3 vout_adjustedMie;
+uniform vec4 fogColor;
+
+#if defined(MODEL)
+uniform float has_texture;
+uniform lowp vec4 global_color;
+#endif
+
+void AtmosphereGroundSunOnFragShader() {
+  // If texturing is off, use white as diffuse color. Otherwise use color
+  // from texture.
+#if defined(MODEL)
+  lowp vec4 texColor = mix(
+      global_color,
+      texture2D(groundTexture, vout_texCoordAndFogFactor.st) * global_color,
+      has_texture);
+#else
+  lowp vec4 texColor = ig_env_atmosphere_custom_state_value.y != 0.0 ?
+      texture2D(groundTexture, vout_texCoordAndFogFactor.st) * groundColor :
+      groundColor;
+#endif  // defined(MODEL)
+  vec3 scatteredColor = vout_rayleigh.rgb + texColor.rgb * vout_adjustedMie.rgb;
+  float fogFactor = vout_texCoordAndFogFactor.w;
+  vec4 color;
+  color.rgb = mix(fogColor.rgb, scatteredColor, fogFactor);
+  color.a = texColor.a;
+  gl_FragColor = computeDebugPixelColor(color);
+}
+
+
+//", '=' <repeats 29 times>, " ground sun on overlay (default)
+#if defined(OVERLAY)
+// Similar to above, but sets output alpha to zero when the projected texture
+// coordinates are outside of the [0,1] range.
+void AtmosphereGroundSunOnOverlayFragShader() {
+  // Get the texture color for the object. The projective component is to
+  // allow us to do Arbitrary Ground Overlays.
+  // If texturing is off, use white as diffuse color. Otherwise use color
+  // from texture.
+  lowp vec4 texColor = (ig_env_atmosphere_custom_state_value.y == 0.0) ?
+      groundColor :
+      (texture2DProj(groundTexture, vout_texCoordAndFogFactor.xyz) *
+       groundColor);
+  // Perform texture edge clamping for ground overlays by setting alpha to zero
+  // outside of texture coordinate range [0, 1]
+  vec2 stOverZ = vout_texCoordAndFogFactor.xy / vout_texCoordAndFogFactor.z;
+  texColor.a = GetMaskedOverlayAlpha(texColor.a, stOverZ);
+
+  vec3 scatteredColor = vout_rayleigh.rgb + texColor.rgb * vout_adjustedMie.rgb;
+  float fogFactor = vout_texCoordAndFogFactor.w;
+  vec4 color;
+  color.rgb = mix(fogColor.rgb, scatteredColor, fogFactor);
+  color.a = texColor.a;
+  gl_FragColor = computeDebugPixelColor(color);
+}
+#endif // defined(OVERLAY)
+#endif // defined(ATMOSPHERE_ON) && defined(GROUND) && defined(SUN_ON) && !defined(USE_IMPROVED_SHADER_VARIATION)
+
+//", '=' <repeats 30 times>, " ground sun on (improved)
+#if defined(ATMOSPHERE_ON) && defined(GROUND) && defined(SUN_ON) && defined(USE_IMPROVED_SHADER_VARIATION)
+uniform vec3 worldOriginInView;
+// The direction vector to the light source.
+uniform vec4 cameraToSunDirAndExposure;
+// xyz: Extra ambient color that is stronger during sunsets (and sunrises).
+// w: density used in fog exp2 function.
+uniform vec4 sunsetAmbientAndFogDensity;
+uniform vec4 fogColor;
+
+varying vec4 vout_viewPosAndNormalDotSun;
+#if defined(MORPH_VERTICES)
+varying vec4 vout_blendTexCoord;
+#endif // defined(MORPH_VERTICES)
+
+// Implements the abstract function defined in atmosphere.glsllib.
+// Returns a fading factor to attenuate the atmospheric effects when the camera
+// altitude decreases, unless the view zenith angle gets close to the horizon
+// angle.
+float atmosphereTweak(float r, float mu) {
+  float r0 = max(kMinCameraRadius, cameraAndSunState.x);
+  float muHoriz = -sqrt(1.0 - 1.0 / (r * r));
+  float angleFalloff = pow((1.0 + mu) / (1.0 + muHoriz), atmosphereTweaks.y);
+  return clamp(angleFalloff, 0.1, 1.0) * atmosphereTweaks.x;
+}
+
+vec4 ImprovedAtmosphereGroundSunOnAboveWater(vec4 texColor) {
+  // Computes the ground albedo, i.e. the proportion of incident ligth reflected
+  // by the ground, for each r,g,b wavelength, divided by pi. Ideally the ground
+  // texture would directly contain this physical quantity, but in fact it
+  // contains some uncalibrated values. Assuming that these values correctly
+  // represent the albedo, and that they are simply gamma corrected as in almost
+  // all images with a gamma value of 2.2, we can compute the albedo with a
+  // pow(color.rgb, 2.2) or approximate it with a square to avoid a costly pow
+  // function:
+  vec3 albedoOverPi = texColor.rgb * texColor.rgb * (0.55 / kPi);
+
+  // Computes the fragment position in geocentric coordinates.
+  vec3 groundPos = vout_viewPosAndNormalDotSun.xyz - worldOriginInView.xyz;
+
+  // Computes the radiance of the Sun reaching groundPos, relatively to the
+  // outer Sun radiance. This is the transmittance of the atmosphere from the
+  // ground to the top atmosphere boundary in the Sun direction.
+  float gR = length(groundPos);
+  float gMuS = dot(groundPos, cameraToSunDirAndExposure.xyz) / gR;
+  vec3 sunRadiance = atmoTex(gR, gMuS).rgb;
+
+  // Computes the irradiance due to the sky dome (excluding the Sun).
+  vec3 skyIrradiance = skyTex(gR, gMuS) * atmosphereTweaks.x;
+  // Tweaks the sky irradiance to avoid a dark ground during night.
+  float night = smoothstep(0.0, 0.2, -gMuS) * atmosphereTweaks.z;
+  vec3 gSkyIrradiance = max(skyIrradiance, vec3(0.002, 0.005, 0.01) * night);
+
+  // Computes the radiance reflected by the ground (using a Lambertian BRDF).
+  vec3 groundL = albedoOverPi *
+      (max(vout_viewPosAndNormalDotSun.w, 0.0) * sunRadiance + gSkyIrradiance);
+
+  vec3 pixel = aerialPerspective(groundL, -worldOriginInView, groundPos,
+      cameraToSunDirAndExposure.xyz, atmosphereTweaks.w);
+  return vec4(pixel, texColor.a);
+}
+
+vec4 ImprovedAtmosphereGroundSunOnBelowWater(vec4 texColor) {
+  // The following shading is completely ad-hoc and not physically-based at all.
+  // For instance distant water is always blue, even at night, which is of
+  // course wrong. Also the Sun light is not attenuated with depth, so that the
+  // sea bottom is always visible (in reality everything becomes completely dark
+  // at a few dozens meters below the surface).
+  texColor.rgb *= 0.66 * max(vout_viewPosAndNormalDotSun.w, 0.33);
+  float fogDistSq = dot(vout_viewPosAndNormalDotSun.xyz,
+      vout_viewPosAndNormalDotSun.xyz);
+  float fogFactor = exp(-2.0 * sunsetAmbientAndFogDensity.w * sqrt(fogDistSq));
+  return vec4(mix(fogColor.rgb, texColor.rgb, fogFactor), texColor.a);
+}
+
+void ImprovedAtmosphereGroundSunOnFragShader() {
+#if defined(OVERLAY)
+  // Get the texture color for the object. The projective component is to
+  // allow us to do Arbitrary Ground Overlays.
+  // If texturing is off, use white as diffuse color. Otherwise use color
+  // from texture.
+  lowp vec4 texColor = (ig_env_atmosphere_custom_state_value.y == 0.0) ?
+      vec4(1.0) : texture2DProj(groundTexture, vout_texCoordAndFogFactor.xyz);
+  // Perform texture edge clamping for ground overlays by setting alpha to zero
+  // outside of texture coordinate range [0, 1]
+  vec2 stOverZ = vout_texCoordAndFogFactor.xy / vout_texCoordAndFogFactor.z;
+  texColor.a = GetMaskedOverlayAlpha(texColor.a, stOverZ);
+#else
+  // If texturing is off, use white as diffuse color. Otherwise use color
+  // from texture.
+  vec4 texColor;
+  if (ig_env_atmosphere_custom_state_value.y != 0.0) {
+    texColor = texture2D(groundTexture, vout_texCoordAndFogFactor.st);
+    #if defined(MORPH_VERTICES)
+      float blend_param = blendUnpopPixelParams[0];
+      if (blend_param != 0.) {
+        // We are blending between 2 textures.
+        vec4 blendBlendColor = texture2D(blendTexture, vout_blendTexCoord.st);
+        // Compute final pixel color, blend between texture 1 and texture 2
+        texColor = mix(texColor, blendBlendColor, blend_param);
+      }
+    #endif // defined(MORPH_VERTICES)
+  } else {
+    texColor = vec4(1.0);
+  }
+#endif
+
+  vec4 color;
+  // TODO(ebruneton): water level may not always be exactly at r=1,
+  // how can we detect that?
+  if (cameraAndSunState.x < 1.0) {
+    color = ImprovedAtmosphereGroundSunOnBelowWater(texColor);
+  } else {
+    color = ImprovedAtmosphereGroundSunOnAboveWater(texColor);
+  }
+  #if defined(MORPH_VERTICES)
+    float unpop_alpha = blendUnpopPixelParams[1];
+    color.a *= unpop_alpha;
+  #endif // defined(MORPH_VERTICES)
+  gl_FragColor = computeDebugPixelColor(color);
+}
+#endif // defined(ATMOSPHERE_ON) && defined(GROUND) && defined(SUN_ON) && defined(USE_IMPROVED_SHADER_VARIATION)
+
+//", '=' <repeats 32 times>, " sky sun off", '=' <repeats 26 times>, "
+#if defined(ATMOSPHERE_ON) && defined(SKY) && defined(SUN_OFF)
+varying vec4 vout_rayleighT;
+uniform vec4 skySunOffPixelParams;
+uniform lowp sampler2D skyMap;
+
+void AtmosphereSkySunOffFragShader() {
+  // The rayleigh map's s-coordinate depends on camera height and is computed
+  // on the CPU.
+  float rayleighS = skySunOffPixelParams.x;
+  lowp vec4 color = texture2D(skyMap, vec2(rayleighS, vout_rayleighT.x));
+  gl_FragColor = color;
+}
+#endif // defined(ATMOSPHERE_ON) && defined(SKY) && defined(SUN_OFF)
+
+//", '=' <repeats 32 times>, " sky sun on (default)", '=' <repeats 22 times>, "
+#if defined(ATMOSPHERE_ON) && defined(SKY) && defined(SUN_ON) && !defined(USE_IMPROVED_SHADER_VARIATION)
+varying vec4 vout_rayleighColorAndSkyOpacity;
+varying vec4 vout_vertToCameraDir;
+// The direction vector to the light source in View coordinate system.
+uniform vec4 cameraToSunDirAndExposure;
+// xyz: Attribute for Mie color corresponding to the brightest spot in the
+//      sky (i.e. usually the sun's front color during the day, and black
+//      during the night).
+//      This is computed on the CPU in double floating precision to avoid
+//      precision problems on the GPU.
+// w: Sun strength: [0, 1] where 0 = no sun, 1 = full strength.
+uniform vec4 brightestMieColorAndSunStrength;
+
+// Calculates the Rayleigh phase function.
+float getRayleighPhase(float cosAngle2) {
+  return 0.75 + 0.75 * cosAngle2;
+}
+
+// Calculates the Mie phase function.
+float getMiePhase(float cosAngle, float cosAngle2) {
+  return 1.5 * ((1.0 - gMieConst2) / (2.0 + gMieConst2)) * (1.0 + cosAngle2) /
+    pow(1.0 + gMieConst2 - 2.0 * gMieConst * cosAngle, 1.5);
+}
+
+// Returns the luminance of an RGB color.
+float getLuminance(vec3 color) {
+  const vec3 luminance = vec3(0.3, 0.59, 0.11);
+  return dot(luminance, color);
+}
+
+void AtmosphereSkySunOnFragShader() {
+  // Variables needed to compute Rayleigh and Mie phases.
+  float cosAngle = dot(cameraToSunDirAndExposure.xyz,
+      vout_vertToCameraDir.xyz) / length(vout_vertToCameraDir.xyz);
+  // Tweak cosAngle by sunStrength. This dims the sun strength for lower
+  // values of sunStrength.
+  cosAngle *= brightestMieColorAndSunStrength.w;
+  float cosAngle2 = cosAngle * cosAngle;
+  // Compute final color using Rayleigh and Mie phases.
+  // The alpha component gets overwritten later.
+  vec4 color;
+  color.rgb = getRayleighPhase(cosAngle2) * vout_rayleighColorAndSkyOpacity.rgb +
+    getMiePhase(cosAngle, cosAngle2) * brightestMieColorAndSunStrength.rgb;
+  // Tone HDR using constant exposure for the whole scene.
+  color.rgb = vec3(1.0) - exp(color.rgb * -cameraToSunDirAndExposure.w);
+  // Use color's luminance to compute its opacity.
+  float luminanceAlphaComponent = 2.0 * getLuminance(color.rgb);
+  color.a = luminanceAlphaComponent + vout_rayleighColorAndSkyOpacity.a;
+  gl_FragColor = color;
+}
+#endif // defined(ATMOSPHERE_ON) && defined(SKY) && defined(SUN_ON) && !defined(USE_IMPROVED_SHADER_VARIATION)
+
+//", '=' <repeats 32 times>, " sky sun on (improved)", '=' <repeats 22 times>, "
+#if defined(ATMOSPHERE_ON) && defined(SKY) && defined(SUN_ON) && defined(USE_IMPROVED_SHADER_VARIATION)
+
+// The radius of the atmosphere mesh, divided by the Earth radius.
+const float kAtmosphereMeshRadius = (6378.0 + 170.0) / 6378.0;
+
+uniform vec3 worldOriginInView;
+// The direction vector to the light source.
+uniform vec4 cameraToSunDirAndExposure;
+
+varying vec3 vout_viewPos;  // Vertex position in view coordinates.
+varying vec3 vout_sunPos;  // Vertex position in Sun coordinates.
+
+// Implements the abstract function defined in atmosphere.glsllib.
+float atmosphereTweak(float r, float mu) {
+  return atmosphereTweaks.x;
+}
+
+// Computes the final pixel color and alpha value from its radiance. The color
+// is computed with a tone mapping operator to map the high dynamic range of the
+// radiance values to the low dynamic range of the screen. The alpha value is
+// used to approximate the correct compositing, which should in theory occur
+// before tone mapping, and not after as we do for stars (see below). This alpha
+// value is computed with an adhoc formula so that stars only appear in the very
+// dark areas of the sky (i.e at night).
+vec4 fragColor(vec3 pixelL) {
+  pixelL = toneMapping(pixelL, atmosphereTweaks.w);
+  return vec4(pixelL, dot(pixelL, vec3(2.0)));
+}
+
+void ImprovedAtmosphereSkySunOnFragShader() {
+  // Computes the view ray direction in view and Sun coordinates.
+  vec3 viewViewRayDir = normalize(vout_viewPos);
+  vec3 sunViewRayDir = normalize(vout_sunPos);
+
+  // Computes the radiance of the background behind the atmosphere. This should
+  // includes the Sun, the Moon and the stars, but here we only take the Sun
+  // into account. The Moon would not be difficult to handle, but rendering the
+  // stars here would require a very high res skymap. Instead, we render them
+  // with points, before rendering the sky, and we approximate the correct
+  // compositing, toneMapping(starL * transmittance + inscatterL), with alpha
+  // blending, i.e. with toneMapping(inscatterL) + (1 - a) * starL. We compute
+  // alpha with an adhoc formula based on toneMapping(inscatterL) -- see the
+  // fragColor function.
+  vec3 backgroundL = outerSunRadiance(sunViewRayDir);
+
+  // Computes the camera to Earth center distance r, the view direction zenith
+  // angle cosinus mu, the Sun zenith angle cosinus muS, and the phase angle
+  // cosinus nu.
+  float r = cameraAndSunState.x;
+  float rMu = -dot(worldOriginInView.xyz, viewViewRayDir);
+  float rMuS = -dot(worldOriginInView.xyz, cameraToSunDirAndExposure.xyz);
+  float nu = dot(viewViewRayDir, cameraToSunDirAndExposure.xyz);
+  bool hitAtmosphere = true;
+
+  // If the camera is outside the atmosphere, moves it to the entry point of the
+  // view ray inside the atmosphere, if it exists (otherwise the view ray does
+  // not traverse the atmosphere, and so we can return the background radiance).
+  if (r > kAtmosphereRadius) {
+    // Computes the distance from the view ray to the Earth center, squared.
+    float hSq = r * r - rMu * rMu;
+    // Computes the distance to the view ray entry point in the atmosphere.
+    float delta = kAtmosphereRadius * kAtmosphereRadius - hSq;
+    float distanceToAtmosphereEntryPoint = -rMu - sqrt(delta);
+    if (delta < 0.0 || distanceToAtmosphereEntryPoint < 0.0) {
+      // The view ray does not hit the atmosphere, return the background color,
+      // i.e the Sun color. In order to avoid discontinuities when switching
+      // from the Sun color computed here, to the Sun color resulting from the
+      // Sun billboard, we use an alpha value going from 1 at kAtmosphereRadius
+      // to 0 at kAtmosphereMeshRadius.
+      const float kAtmosphereRadiusSq = kAtmosphereRadius * kAtmosphereRadius;
+      const float kMeshRadiusSq = kAtmosphereMeshRadius * kAtmosphereMeshRadius;
+      float a = (kMeshRadiusSq - hSq) / (kMeshRadiusSq - kAtmosphereRadiusSq);
+      gl_FragColor = vec4(toneMapping(backgroundL, atmosphereTweaks.w) * a, a);
+      hitAtmosphere = false;
+    } else {
+      // Moves the camera to the view ray entry point inside the atmosphere.
+      r = kAtmosphereRadius;
+      rMu += distanceToAtmosphereEntryPoint;
+      rMuS += distanceToAtmosphereEntryPoint * nu;
+    }
+  }
+
+  if (hitAtmosphere) {
+    // Computes the light which is scattered towards the viewer along the view
+    // ray, and the transmittance of the atmosphere along this ray.
+    vec3 transmittance;
+    vec3 inscatterL = inscatter(r, rMu / r, rMuS / r, nu, atmosphereTweaks.w,
+        transmittance);
+
+    // The pixel radiance is the background radiance, multiplied by the
+    // atmosphere transmittance and augmented by the inscattered light.
+    vec3 pixelL = backgroundL * transmittance + inscatterL;
+
+    gl_FragColor = fragColor(pixelL);
+  }
+}
+#endif // defined(ATMOSPHERE_ON) && defined(SKY) && defined(SUN_ON) && defined(USE_IMPROVED_SHADER_VARIATION)
+
+// ", '=' <repeats 65 times>, "
+#if defined(ATMOSPHERE_OFF) && defined(SUN_OFF)
+varying vec4 vout_groundTexCoord;  // Transformed texture coordinate.
+
+void NoAtmosphereSunOffFragShader() {
+  vec4 diffuseColor = texture2D(groundTexture, vout_groundTexCoord.st);
+  gl_FragColor = computeDebugPixelColor(diffuseColor);
+}
+#endif // defined(ATMOSPHERE_OFF) && defined(SUN_OFF)
+
+// ", '=' <repeats 68 times>, "
+#if defined(ATMOSPHERE_OFF) && defined(SUN_ON)
+varying vec4 vout_transformedCoords;
+varying float vout_diffuse_factor;
+
+void NoAtmosphereSunOnFragShader() {
+  // Get the texture color for the object.
+  vec4 diffuseColor = texture2D(groundTexture, vout_transformedCoords.xy);
+  diffuseColor.rgb *= vout_diffuse_factor;
+  gl_FragColor = computeDebugPixelColor(diffuseColor);
+}
+#endif // defined(ATMOSPHERE_OFF) && defined(SUN_ON)
+
+// ", '=' <repeats 67 times>, "
+#if defined(ATMOSPHERE_OFF) && defined(GROUND) && defined(OVERLAY)
+varying vec4 vout_texCoord;
+
+void NoAtmosphereGroundOverlayFragShader() {
+  // Get the texture color for the object. The projective component is to
+  // allow us to do Arbitrary Ground Overlays.
+  vec4 texColor = (texture2DProj(groundTexture, vout_texCoord.xyw)) *
+      groundColor;
+
+  // Compute projected texture coordinates
+  vec2 stOverW = vout_texCoord.xy / vout_texCoord.w;
+  // Zero out alpha outside of texture edges.
+  texColor.a = GetMaskedOverlayAlpha(texColor.a, stOverW);
+  gl_FragColor = computeDebugPixelColor(texColor);
+}
+#endif // defined(ATMOSPHERE_OFF) && defined(GROUND) && defined(OVERLAY)
+
+// ", '=' <repeats 64 times>, "
+// Forward-declare top-level functions.  (This may fix some pre-processor issues
+// when loading the shaders on Mali embedded GPUs.)
+void AtmosphereGroundSunOnOverlayFragShader();
+void AtmosphereGroundSunOnFragShader();
+void AtmosphereGroundSunOffOverlayFragShader();
+void AtmosphereGroundSunOffFragShader();
+void AtmosphereSkySunOnFragShader();
+void AtmosphereSkySunOffFragShader();
+void ImprovedAtmosphereGroundSunOnFragShader();
+void ImprovedAtmosphereSkySunOnFragShader();
+void NoAtmosphereSunOnFragShader();
+void NoAtmosphereSunOffFragShader();
+void NoAtmosphereGroundOverlayFragShader();
+
+void main()
+{
+  // All #elif directives have been replaced with #else + nested #if.
+  // Do not use #elif directives in shader files because they cause errors
+  // when loaded on Mali-400 based devices.  See bug #6662252.
+#if defined(ATMOSPHERE_ON)
+  #if defined(GROUND)
+    #if defined(SUN_ON)
+      #if defined(OVERLAY)
+        #if defined(USE_IMPROVED_SHADER_VARIATION)
+          ImprovedAtmosphereGroundSunOnFragShader();
+        #else
+          AtmosphereGroundSunOnOverlayFragShader();
+        #endif
+      #else
+        #if defined(USE_IMPROVED_SHADER_VARIATION)
+          ImprovedAtmosphereGroundSunOnFragShader();
+        #else
+          AtmosphereGroundSunOnFragShader();
+        #endif
+      #endif
+    #else
+      #if defined(SUN_OFF)
+        #if defined(OVERLAY)
+          AtmosphereGroundSunOffOverlayFragShader();
+        #else
+          AtmosphereGroundSunOffFragShader();
+        #endif
+      #else
+        #error Invalid shader config 1
+      #endif
+    #endif
+    #if defined(QUADRANT_ALPHAS)
+      gl_FragColor.a *= vout_alpha;
+    #endif
+  #else
+    #if defined(SKY)
+      #if defined(SUN_ON)
+        #if defined(USE_IMPROVED_SHADER_VARIATION)
+          ImprovedAtmosphereSkySunOnFragShader();
+        #else
+          AtmosphereSkySunOnFragShader();
+        #endif
+      #else
+        #if defined(SUN_OFF)
+          AtmosphereSkySunOffFragShader();
+        #else
+          #error Invalid shader config 2
+        #endif
+      #endif
+    #else
+      #if defined(USE_IMPROVED_SHADER_VARIATION)
+        gl_FragColor = computeInscatter(gl_FragCoord.xy, atmosphereTweaks.w);
+      #else
+        #error Invalid shader config 3
+      #endif
+    #endif
+  #endif
+#elif defined(ATMOSPHERE_OFF)
+    #if defined(GROUND) && defined(OVERLAY)
+      NoAtmosphereGroundOverlayFragShader();
+    #else
+      #if defined(SUN_ON)
+        NoAtmosphereSunOnFragShader();
+      #elif defined(SUN_OFF)
+        NoAtmosphereSunOffFragShader();
+      #else
+        #error Invalid shader config 4
+      #endif
+    #endif
+    #if defined(QUADRANT_ALPHAS)
+      gl_FragColor.a *= vout_alpha;
+    #endif
+#else
+  #error Invalid shader config 6
+#endif
+
+   // Added by BrianP
+   gl_FragColor = vec4(0, 1, 0, 0);
+}
+
diff --git a/tests/spec/arb_es2_compatibility/ge7.vert b/tests/spec/arb_es2_compatibility/ge7.vert
new file mode 100644
index 0000000..2fc7a32
--- /dev/null
+++ b/tests/spec/arb_es2_compatibility/ge7.vert
@@ -0,0 +1,814 @@
+//!GL2
+#define GE_VERTEX_SHADER
+#define GE_PRECISION mediump
+#define ATMOSPHERE_OFF
+#define GROUND
+#define SUN_OFF
+#define OVERLAY
+#define ENABLE_VERTEX_REJECT
+#if defined(GL_ES)
+precision GE_PRECISION float;
+#else
+#define lowp
+#define mediump
+#define highp
+#endif
+
+uniform mat4 ig_ModelViewMatrix;
+uniform mat4 ig_ModelViewProjectionMatrix;
+uniform mat4 ig_TextureMatrix;
+uniform vec4 ig_LightDirectionInModelSpace;
+uniform vec4 ig_env_atmosphere_custom_state_value;
+uniform lowp vec4 ig_env_atmosphere_custom_color_value;
+uniform lowp float alpha_ref_value;
+
+float saturate(float v) { return clamp(v, 0.0, 1.0); }
+vec2 saturate(vec2 v) { return clamp(v, 0.0, 1.0); }
+vec3 saturate(vec3 v) { return clamp(v, 0.0, 1.0); }
+vec4 saturate(vec4 v) { return clamp(v, 0.0, 1.0); }
+
+// Returns the quadrant portion of the given vertex flags.
+float getQuadrant(vec4 vertexQuadrant) {
+  return dot(vertexQuadrant, vec4(1.0, 2.0, 4.0, 8.0));
+}
+
+// Both getMasked methods assume that quadrantMask has a single 1 and the
+// rest are 0.
+
+vec4 getMaskedVertex(vec4 pos, vec4 quadrantMask, vec4 vertexQuadrant) {
+  return pos * dot(quadrantMask, vertexQuadrant);
+}
+
+float getMaskedAlpha(vec4 quadrantAlphas, vec4 vertexQuadrant) {
+  return dot(quadrantAlphas, vertexQuadrant);
+}
+
+// Computes a vertex color to show which quadrant a vertex is in.  Also marks
+// vertices that would be rejected and error conditions (e.g. invalid
+// quadrant).
+vec4 getDebugVertexColor(vec4 quadrantMask, vec4 vertexQuadrant) {
+  vec4 color;
+  float quadrant = getQuadrant(vertexQuadrant);
+  if (quadrant == 1.0) {
+    // Red
+    color = vec4(1.0, 0.0, 0.0, 1.0);
+  } else if (quadrant == 2.0) {
+    // Green
+    color = vec4(0.0, 1.0, 0.0, 1.0);
+  } else if (quadrant == 4.0) {
+    // Blue
+    color = vec4(0.0, 0.0, 1.0, 1.0);
+  } else if (quadrant == 8.0) {
+    // Cyan
+    color = vec4(0.0, 1.0, 1.0, 1.0);
+  } else if (quadrant == 0.0) {
+    // Vertex didn't have a quadrant bit set.  Mark as saturated Yellow.
+    color = vec4(10.0, 10.0, 0.0, 1.0);
+  } else if (quadrant == 15.0) {
+    // All quadrant bits were set.  Mark as White.
+    color = vec4(10.0, 10.0, 10.0, 1.0);
+  } else {
+    // More than one quadrant bit was set or a quadrant bit was set to
+    // something other than 1.0, so flag verts in saturated Magenta.
+    color = vec4(10.0, 0.0, 10.0, 1.0);
+  }
+  // Make hidden quadrants dimmer.
+  if (dot(quadrantMask, vertexQuadrant) == 0.)
+    color *= .5;
+  return color;
+}
+
+#ifdef DEBUG_QUADRANT_COLORS
+// Used by the pixel shader to show quadrants, culled verts and error
+// conditions when DEBUG_QUADRANT_COLORS is defined.
+varying vec4 vout_debug_color;
+#endif
+
+// The \"discard\" instruction is not available for vertex shaders, so
+// this function needs to be excluded when parsing a vertex shader.
+#ifdef GE_PIXEL_SHADER
+
+// When debugging is on, modulates the pixel color by the debug color output
+// from the vertex shader.  Otherwise, the given pixel color is returned
+// unchanged.
+vec4 computeDebugPixelColor(vec4 pixelShaderColor) {
+// OGL ES 2.0 does not support alpha test, so need to do it in the
+// shader.
+#if defined(GL_ES) && !defined(FORCE_NO_ALPHA_TEST)
+// Terrain and sky don't use alpha test.
+#if defined(OVERLAY) || defined(MODEL)
+  // alpha_ref_value is set by igVisualContext, and will be -1 if alpha test
+  // is off.
+  if (pixelShaderColor.a < alpha_ref_value) {
+    discard;
+  }
+#endif  // defined(OVERLAY) || defined(MODEL)
+#endif  // defined(GL_ES) && !defined(FORCE_NO_ALPHA_TEST)
+
+#ifdef DEBUG_QUADRANT_COLORS
+  return pixelShaderColor * vout_debug_color;
+#else
+  return pixelShaderColor;
+#endif
+}
+
+#endif  // GE_PIXEL_SHADER
+#if defined(PACKED_VERTS)
+#define igv_Vertex ig_VertexAttr0
+#define igv_MultiTexCoord0 ig_VertexAttr1
+attribute vec3 igv_Vertex;
+attribute vec2 igv_MultiTexCoord0;
+#else
+#define igv_Vertex ig_Vertex
+#define igv_MultiTexCoord0 ig_MultiTexCoord0
+attribute vec4 igv_Vertex;
+attribute vec4 igv_MultiTexCoord0;
+#endif
+
+attribute vec3 ig_Normal;
+attribute vec4 ig_Color;
+
+uniform vec3 worldOriginInView;
+uniform vec4 dirToCameraAndFogDensity;
+uniform vec4 groundSunOffVertexParams;
+uniform vec4 projScalingFactor;
+uniform vec4 quadrantMask;
+
+#if defined(SUN_ON)
+// The direction vector to the light source.
+uniform vec4 cameraToSunDirAndExposure;
+// .y has Normalized Camera Dot Light
+// .z has Max Camera Angle subtended by planet
+// .w has Camera Height * Camera Height
+uniform vec4 cameraLightInfo;
+// .x has ground shader start depth
+// .y has scatter power coefficient
+// .z has rayleighToneDown Coefficient
+// .w has atmosphere rayleigh opacity alpha
+uniform vec4 startupConsts;
+// xyz: Extra ambient color that is stronger during sunsets (and sunrises).
+// w: density used in fog exp2 function.
+uniform vec4 sunsetAmbientAndFogDensity;
+#endif
+
+#if defined(ATMOSPHERE_ON) && defined(GROUND)
+// XYZ: The x, y, and w-coordinates of the projective rayleighTure coordinate.
+// We use projective rayleighTures for Arbitrary Ground Overlays.
+// W: fog_factor between 0 (no fog) and 1 (full fog).
+varying vec4 vout_texCoordAndFogFactor;
+#endif // defined(ATMOSPHERE_ON) && defined(GROUND)
+
+#if defined(QUADRANT_ALPHAS)
+uniform vec4 quadrantAlphas;  // Alpha 0-1 per quadrant.
+// TODO(jrohlf): Combine with vec4 if possible.
+varying float vout_alpha;  // Single alpha for entire tri, 0-1.
+#endif
+
+//", '=' <repeats 22 times>, " Constants ", '=' <repeats 33 times>, "
+// The number of sample points taken along the ray.
+const int samples = 2;
+// Reciprocal of the sun light color.
+const vec3 invWavelength = 1.0 /
+    vec3(0.17850625, 0.10556001, 0.050906640625);
+// Altitude of Atmosphere in km
+const float atmosphereAltiude = 170.0;
+// Radius of planet in km
+const float planetRadius = 6378.0;
+// Relative Radius of the planet.
+const float innerRadius = 1.0;
+const float innerRadius2 = innerRadius * innerRadius;
+// Radius of the outer atmosphere.
+const float outerRadius = innerRadius + (atmosphereAltiude / planetRadius);
+const float outerRadius2 = outerRadius * outerRadius;
+// Rayleigh scattering constant.
+const float kr = 0.0025;
+// Brightness of the sun.
+const float eSun = 25.0;
+// Mie scattering constant.
+const float km = 0.001;
+// Pi.
+const float pi = 3.1415926535897932384626433832795;
+// Convenience constants.
+const float krESun = kr * eSun;
+const float kmESun = km * eSun;
+const float kr4Pi = kr * 4.0 * pi;
+const float km4Pi = km * 4.0 * pi;
+
+// The scale depth (the altitude at which the average atmospheric density is
+// found).
+const float scaleDepth = 0.15;
+const float invScaleDepth = 1.0 / scaleDepth;
+// Reciprocal of the atmosphere's depth.
+const float atmosphereDepthInv = 1.0 / (outerRadius - innerRadius);
+const float scaleOverScaleDepth = atmosphereDepthInv / scaleDepth;
+
+// In Fixed function, we use a trick of Sun's color to be of intensity
+// 3.0 to get better correlation between time of day and nDotL.
+// Using the same trick in shaders.
+const float kSunColor = 3.0;
+
+// Transparent color used for hiding white seams behind terrain cracks.
+// Note: all four components must be 0.0 due to the (more or less) additive
+// blending function.
+const vec4 kTransparent = vec4(0.0);
+
+//", '=' <repeats 22 times>, " Functions ", '=' <repeats 30 times>, "
+// The scale equation calculated by Vernier's Graphical Analysis.
+float scale(float cosAngle) {
+  float x = 1.0 - cosAngle;
+  return scaleDepth *
+    exp(-0.00287 + x * (0.459 + x * (3.83 + x * (-6.80 + x * 5.25))));
+}
+
+// Returns the near intersection point of a line and a sphere centered around
+// the origin.
+float getNearIntersection(vec3 vertPos, vec3 rayDir, float distance2, float fRadius2) {
+  float b = 2.0 * dot(vertPos, rayDir);
+  float c = distance2 - fRadius2;
+  float determ = max(0.0, b * b - 4.0 * c);
+  return 0.5 * (-b - sqrt(determ));
+}
+
+// Returns the far intersection point of a line and a sphere centered around
+// the origin.
+float getFarIntersection(vec3 vertPos, vec3 rayDir, float distance2, float fRadius2) {
+  float b = 2.0 * dot(vertPos, rayDir);
+  float c = distance2 - fRadius2;
+  float determ = max(0.0, b * b - 4.0 * c);
+  return 0.5 * (-b + sqrt(determ));
+}
+
+// Returns the luminance of an RGB color.
+float getLuminance(vec3 color) {
+  const vec3 luminance = vec3(0.3, 0.59, 0.11);
+  return dot(luminance, color);
+}
+
+//", '=' <repeats 30 times>, " ground sun off vertex
+#if defined(ATMOSPHERE_ON) && defined(GROUND) && defined(SUN_OFF)
+
+// Multiplier for rayleigh result; when 0, scattering calculation are omitted
+uniform vec4 rayleighAttenuator;
+
+#if !defined(USE_SIMPLIFIED_SHADER_VARIATION)
+// Texcoord for blending between 2 textures on same geometry and rayleighT
+// param stuffed in z component.
+varying vec4 vout_blendTexCoordAndRayleighT;
+#endif
+
+// Vertex shader program.
+void AtmosphereGroundSunOffVertexShader() {
+  vec4 position = vec4(igv_Vertex.xyz, 1.0);
+  vec4 texCoord = vec4(igv_MultiTexCoord0.xy, 0.0, 1.0);
+
+#if !defined(USE_SIMPLIFIED_SHADER_VARIATION)
+  // We project the vertex direction (from the planet origin) onto the
+  // camera direction (from the planet origin) to compute the rayleigh
+  // map's t-coordinate.
+  vec3 posInView = (ig_ModelViewMatrix * projScalingFactor.x * position).xyz;
+  vec3 dirToPos = normalize(posInView - worldOriginInView);
+  vec3 dirToCamera = dirToCameraAndFogDensity.xyz;
+  float projected_length = dot(dirToCamera, dirToPos);
+  float rayleighTTranslation = groundSunOffVertexParams.x;
+  float rayleighTScale = groundSunOffVertexParams.y;
+  float rayleighT = (projected_length + rayleighTTranslation) *
+      rayleighTScale;
+  rayleighT = rayleighT * rayleighAttenuator.x + (1.0 - rayleighAttenuator.x);
+#endif
+
+  vec4 vert_pos_clip = ig_ModelViewProjectionMatrix * position;
+
+#if !defined(USE_SIMPLIFIED_SHADER_VARIATION)
+  // Compute fog factor.
+  float fogDensity = dirToCameraAndFogDensity.w;
+  float fogTemp = fogDensity * vert_pos_clip.z;
+  float fogFactor = exp(-(fogTemp * fogTemp));
+  vout_texCoordAndFogFactor.w = fogFactor;
+#endif
+
+  vec4 transformedTexCoord = ig_TextureMatrix * texCoord;
+  vout_texCoordAndFogFactor.xyz = transformedTexCoord.xyw;
+#if !defined(USE_SIMPLIFIED_SHADER_VARIATION)
+  vout_blendTexCoordAndRayleighT.x = rayleighT;
+  vout_blendTexCoordAndRayleighT.y = rayleighT;
+  vout_blendTexCoordAndRayleighT.z = rayleighT;
+  vout_blendTexCoordAndRayleighT.w = rayleighT;
+#endif
+
+  gl_Position = vert_pos_clip;
+}
+#endif // defined(ATMOSPHERE_ON) && defined(GROUND) && defined(SUN_OFF)
+
+//", '=' <repeats 33 times>, " Ground sun on
+
+#if defined(ATMOSPHERE_ON) && defined(GROUND) && defined(SUN_ON)
+
+// has_lighting tracks the igVisualContext lighting enabled state.
+// Diorama buildings that do not have normals have lighting turned off in the
+// scene-graph and should do lighting using the vertex normal.
+// Detecting 0-length normals in the shader does not appear to
+// work for diormam buildings (perhaps because the normal data is garbage,
+// instead of being absent like it is in the terrain data).
+// TODO(baustin): Remove the has_lighting test once the 0-length check can be
+// made to work for diorama buildings.
+uniform float has_lighting;
+
+#if defined(USE_IMPROVED_SHADER_VARIATION)
+// Vertex position in view coordinates and dot product between normal and Sun.
+varying vec4 vout_viewPosAndNormalDotSun;
+#else
+// Vertex shader's output.
+// RGB: The Rayleigh color.
+varying vec3 vout_rayleigh;
+// RGB: The adjusted Mie color.
+varying vec3 vout_adjustedMie;
+#endif
+
+// Vertex shader program.
+void AtmosphereGroundSunOnVertexShader() {
+  vec4 position = vec4(igv_Vertex.xyz, 1.0);
+  vec4 texCoord = vec4(igv_MultiTexCoord0.xy, 0.0, 1.0);
+  vec3 normal = ig_Normal;
+
+  // projScalingFactor is some kind of hack to handle poor precision on
+  // some platforms, I dunno the details.
+  mat4 hackedModelView = ig_ModelViewMatrix * projScalingFactor.x;
+
+  highp vec4 posInView = hackedModelView * position;
+  // Compute vertex's color using its position projected at sea level.
+  // This avoids artifacts when the vertex is outside the frustum and makes
+  // it easier to compute the distance to the horizon.
+  highp vec3 unprojectedVertPos = posInView.xyz - worldOriginInView;
+  highp vec3 vertNorm = normalize(unprojectedVertPos);
+
+  // If normals are not supplied or lighting is turned off, use sphere normals.
+  highp vec3 normToUse = vertNorm;
+  if (has_lighting == 1.0) {
+    highp float normalLen = dot(normal.xyz, normal.xyz);
+    // Not sure how fast this if-test is.
+    // TODO(jrohlf,baustin): See if ? or mix() might be faster (mix() isn't
+    // working for diorama buildings, possibly because the normals aren't
+    // normalized.
+    if (normalLen > 0.0) {
+      // NOTE: normal xform should use inverse transpose but I don't think we
+      // have any scales in modelview.
+      normToUse = (hackedModelView * vec4(normal, 0.0)).xyz;
+      normToUse = normalize(normToUse);
+    }
+  }
+
+  highp float nDotL = dot(normToUse, cameraToSunDirAndExposure.xyz);
+
+#if !defined(USE_IMPROVED_SHADER_VARIATION)
+  // Multiply by Sun Color which is really high intensity and saturate
+  nDotL = saturate(kSunColor * nDotL);
+
+  // Create new coordinate system with the view orientation but world origin.
+  // All computation is done in this new coordinate system.
+  highp vec3 cameraPos = -worldOriginInView;
+  highp float cameraHeight = cameraLightInfo.x;
+  highp float cameraHeight2 = cameraLightInfo.w;
+
+  // Get the ray from the camera to the vertex and its length.
+  highp vec3 rayDir = unprojectedVertPos - cameraPos;
+  highp float farDist = length(rayDir);
+  rayDir /= farDist;
+
+  highp vec3 startPos = cameraPos;
+  // Initial scattering offset.
+  highp float startDepth = startupConsts.x;
+  // We use power function to create gradient for limiting rayleigh
+  // affect closer horizon. Lower the exponent, more scattering we
+  // get all over planet. To get scattering glow on planet when viewed
+  // from distance, horizonExp depends on altitude.
+  highp float horizonExp = startupConsts.y;
+  // Since we have not using exposure function in fragment shader for
+  // ground, rayleigh component adds to the existing color and the result
+  // tends to saturate much too soon. Hence we add a tonedown scale which
+  // is function of altitude and proximity to horizon.
+  highp float rayleighToneDownScale = startupConsts.z;
+
+  // Calculate where our ray hits the atmosphere to find the point where
+  // we start accumulating its color.
+  // TODO(quarup): Get rid of this conditional.
+  if (cameraHeight >= outerRadius) {
+    // Camera is outside the atmosphere.
+    // Calculate the closest intersection of the ray with the outer atmosphere
+    // (which is the near point of the ray passing through the atmosphere).
+    highp float nearDist = getNearIntersection(cameraPos, rayDir, cameraHeight2, outerRadius2);
+    // Ray's start position within the atmosphere.
+    startPos += rayDir * nearDist;
+    // Ray's distance within the atmosphere.
+    farDist -= nearDist;
+  }
+
+  // Initialize constants needed for the scattering equations.
+  // vertNorm is normalized
+  highp float cameraAngle = max(0.0, dot(-rayDir, vertNorm));
+  highp float lightAngle = dot(cameraToSunDirAndExposure.xyz, vertNorm);
+  highp float cameraScale = scale(cameraAngle);
+  highp float lightScale = scale(lightAngle);
+  highp float cameraOffset = startDepth * cameraScale;
+  highp float scaleSum = lightScale + cameraScale;
+
+  // Initialize the scattering loop variables.
+  highp float sampleLength = farDist / float(samples);
+  highp float scaledLength = sampleLength * atmosphereDepthInv;
+  highp vec3 sampleRay = rayDir * sampleLength;
+  highp vec3 samplePoint = startPos + sampleRay * 0.5;
+
+  // Now loop through sample points on the ray and accumulate the color
+  // using the scattering equations (including surface-scattering).
+  highp vec3 frontColor = vec3(0.0, 0.0, 0.0);
+  highp vec3 attenuate;
+  for(int i = 0; i < samples; i++) {
+    highp float sampleHeight = length(samplePoint);
+    highp float sampleDepth =
+        exp(scaleOverScaleDepth * (innerRadius - sampleHeight));
+    highp float sampleScatter = sampleDepth * scaleSum - cameraOffset;
+    attenuate = exp(-sampleScatter * (invWavelength * kr4Pi + km4Pi));
+    frontColor += attenuate * (sampleDepth * scaledLength);
+    samplePoint += sampleRay;
+  }
+
+  // Compute how \"close\" the vertex is to the horizon. Vertices close
+  // to the horizon correspond to one, and vertices under the camera
+  // correspond to zero.
+  highp float closeToHorizon = 1.0 - cameraAngle;
+  highp float closeToCamera = 1.0 - closeToHorizon*closeToHorizon;
+  // Compute rayleigh scale factor. Higher closer to horizon and
+  // at higher altitudes.
+  highp float scaledRayleighFactor = rayleighToneDownScale * pow(closeToHorizon, horizonExp);
+
+  // Base ambient color for dark shaded ground objects.
+  highp vec3 kBaseAmbientColor = vec3(0.12, 0.12, 0.15);
+  // Add ambient light to geometry with low N.L shading. This allows the
+  // user to always see the dark areas of mountains, buildings, etc.
+  highp float darkShadedAmbientFactor = 1.0 - nDotL;
+  // Compute night ambient color factor so that it is strongest at night
+  // (i.e. low attenuate luminance) and weakest during the day
+  // (i.e. high attenuate luminance). This allows the user to always see
+  // ground objects at night.
+  highp float nightAmbientTemp = getLuminance(attenuate);
+  highp float nightAmbientFactor = 1.0 - nightAmbientTemp * nightAmbientTemp;
+  // Compute ambient contribution. Make ambient light stronger
+  // for nearby vertices, which creates a \"head lamp\" effect.
+  highp float ambientFactor =
+    closeToCamera * (darkShadedAmbientFactor + nightAmbientFactor);
+  highp vec3 headlampAmbientContribution = kBaseAmbientColor * ambientFactor;
+  // The extra sunset ambient contribution keeps the planet from looking too
+  // dark during sunset and sunrise.
+  highp vec3 sunsetAmbient = sunsetAmbientAndFogDensity.xyz;
+  highp vec3 ambientContribution = headlampAmbientContribution + sunsetAmbient;
+
+  // Compute how close to noon it is.
+  highp float closeToNoon = cameraLightInfo.y;
+  // When it is close to noon, add light close to the camera in order
+  // to show full contrast of the textures near the user.
+  highp float closeToNoonFactor = closeToCamera * closeToNoon;
+
+  // Compute fog factor.
+  highp float fogDensity = sunsetAmbientAndFogDensity.w;
+  highp float fogTemp = fogDensity * farDist;
+  highp float fogFactor = exp(-(fogTemp * fogTemp));
+#endif
+
+  // Transform vertex position.
+  gl_Position = ig_ModelViewProjectionMatrix * position;
+#if defined(USE_IMPROVED_SHADER_VARIATION)
+  vout_viewPosAndNormalDotSun = vec4(posInView.xyz, nDotL);
+  float fogFactor = 0.0;
+#else
+  // Compute Rayleigh color and make it strongest at the horizon.
+  const vec3 kMaxRayleighContribution = vec3(0.5, 0.5, 0.8);
+  vout_rayleigh.rgb = min(kMaxRayleighContribution,
+       frontColor * (invWavelength * krESun + kmESun) * scaledRayleighFactor);
+  // Compute Mie color.
+  highp vec3 mieColor = attenuate;
+  highp vec3 adjustedMieColor = saturate(
+    (mieColor + vec3(closeToNoonFactor)) * nDotL + ambientContribution);
+  vout_adjustedMie.rgb = adjustedMieColor;
+#endif
+  // Set transformed texture coordinates.
+  highp vec4 transformedTexCoord = ig_TextureMatrix * texCoord;
+  vout_texCoordAndFogFactor.xyz = transformedTexCoord.xyw;
+  vout_texCoordAndFogFactor.w = fogFactor;
+}
+#endif // defined(ATMOSPHERE_ON) && defined(GROUND) && defined(SUN_ON)
+
+//", '=' <repeats 38 times>, " sky sun off
+
+#if defined(ATMOSPHERE_ON) && defined(SKY) && defined(SUN_OFF)
+
+uniform vec4 skySunOffVertexParams;
+
+// X: Rayleigh map's t-coordinate.
+varying vec4 vout_rayleighT;
+
+// Vertex shader program.
+void AtmosphereSkySunOffVertexShader() {
+  vec4 position = vec4(igv_Vertex.xyz, 1.0);
+
+  // From the position's Y-coordinate (in atmosphere space), we can
+  // compute its rayleigh color from the rayleigh map.
+  float rayleighTTranslation = skySunOffVertexParams.x;
+  float rayleighTScale = skySunOffVertexParams.y;
+  // Look-up's t-coordinate.
+  float rayleighT = (position.y + rayleighTTranslation) * rayleighTScale;
+  gl_Position = ig_ModelViewProjectionMatrix * position;
+
+  // Clamp vertex depth to the far plane. This forces the sky to always
+  // be drawn even if the far plane is closer than the outer atmosphere.
+  // Some drivers have problems when z == w, so ensure z is strictly smaller.
+  gl_Position.z = min(gl_Position.z, gl_Position.w * 0.99999);
+
+  vout_rayleighT = vec4(rayleighT);
+}
+#endif // defined(ATMOSPHERE_ON) && defined(SKY) && defined(SUN_OFF)
+
+//", '=' <repeats 39 times>, " sky sun on
+#if defined(ATMOSPHERE_ON) && defined(SKY) && defined(SUN_ON)
+
+#if defined(USE_IMPROVED_SHADER_VARIATION)
+// Transformation from view to Sun coordinates. This coordinate system is
+// centered on the Earth and its z axis points towards the Sun.
+uniform mat4 viewToSun;
+
+varying vec3 vout_viewPos;  // Vertex position in view coordinates.
+varying vec3 vout_sunPos;  // Vertex position in Sun coordinates.
+#else
+varying vec4 vout_rayleighColorAndSkyOpacity;  // The Rayleigh color
+varying vec4 vout_vertToCameraDir;  // Dir from ray to camera.
+#endif
+
+// Vertex shader program.
+void AtmosphereSkySunOnVertexShader() {
+
+  vec4 position = vec4(igv_Vertex.xyz, 1.0);
+  // projScalingFactor is some kind of hack to handle poor precision on
+  // some platforms, I dunno the details.
+  mat4 hackedModelView = ig_ModelViewMatrix * projScalingFactor.x;
+
+#if !defined(USE_IMPROVED_SHADER_VARIATION)
+  // Make up a new coordinate system with the view orientation but world origin.
+  // All computation is done in this new coordinate system.
+  vec3 cameraPos = -worldOriginInView;
+  float cameraHeight = cameraLightInfo.x;
+  float cameraHeightInv = 1.0 / cameraHeight;
+  float cameraHeight2 = cameraLightInfo.w;
+  vec4 posInView = hackedModelView * position;
+  vec3 vertPos = posInView.xyz - worldOriginInView;
+
+  // Get the ray from the camera to the vertex and its length (which is the
+  // far point of the ray passing through the atmosphere).
+  vec3 rayDir = vertPos - cameraPos;
+  float farDist = length(rayDir);
+  rayDir /= farDist;
+
+  vec3 startPos;
+  float startAngle;
+  float startOffset;
+  // Calculate where our ray hits the atmosphere to find the point where
+  // we start accumulating its color.
+  // TODO(quarup): Get rid of this conditional.
+  if (cameraHeight < outerRadius) {
+    // If we're inside the atmosphere, then start accumulating from the
+    // camera position.
+    startPos = cameraPos;
+    // Initial scattering constants.
+    float startDepth = exp(scaleOverScaleDepth * (innerRadius - cameraHeight));
+    startAngle = dot(rayDir, startPos) * cameraHeightInv;
+    startOffset = startDepth * scale(startAngle);
+  } else {
+    // Camera is outside the atmosphere.
+    // Calculate the closest intersection of the ray with the outer atmosphere
+    // (which is the near point of the ray passing through the atmosphere).
+    float nearDist = getNearIntersection(cameraPos, rayDir, cameraHeight2, outerRadius2);
+    // Ray's start position within the atmosphere.
+    startPos = cameraPos + rayDir * nearDist;
+    // Ray's distance within the atmosphere.
+    farDist -= nearDist;
+    // Initial scattering constants.
+    startAngle = dot(rayDir, startPos) / outerRadius;
+    float startDepth = exp(-invScaleDepth);
+    startOffset = startDepth * scale(startAngle);
+  }
+
+  // Initialize the scattering loop variables.
+  float sampleLength = farDist / float(samples);
+  float scaledLength = sampleLength * atmosphereDepthInv;
+  vec3 sampleRay = rayDir * sampleLength;
+  vec3 samplePoint = startPos + sampleRay * 0.5;
+
+  // Now loop through sample points on the ray and accumulate the color
+  // using the scattering equations.
+  vec3 frontColor = vec3(0.0, 0.0, 0.0);
+  vec3 attenuate;
+  for(int i = 0; i < samples; i++) {
+    // TODO(quarup): Simplify this.
+    float sampleHeight = length(samplePoint);
+    float sampleDepth = exp(scaleOverScaleDepth * (innerRadius - sampleHeight));
+    float lightAngle = dot(cameraToSunDirAndExposure.xyz, samplePoint), sampleHeight;
+    float cameraAngle = dot(rayDir, samplePoint) / sampleHeight;
+    float sampleScatter =
+      (startOffset + sampleDepth*(scale(lightAngle) - scale(cameraAngle)));
+    attenuate = exp(-sampleScatter * (invWavelength * kr4Pi + km4Pi));
+    frontColor += attenuate * (sampleDepth * scaledLength);
+    samplePoint += sampleRay;
+  }
+#endif
+
+  // Finally, scale the Mie and Rayleigh colors and set up the varying
+  // variables for the pixel shader.
+  gl_Position = ig_ModelViewProjectionMatrix * position;
+
+  // Clamp vertex depth to the far plane. This forces the sky to always
+  // be drawn even if the far plane is closer than the outer atmosphere.
+  // Some drivers have problems when z == w, so ensure z is strictly smaller.
+  gl_Position.z = min(gl_Position.z, gl_Position.w * 0.99999);
+
+#if defined(USE_IMPROVED_SHADER_VARIATION)
+  vout_viewPos = (hackedModelView * position).xyz;
+  vout_sunPos = (viewToSun * vec4(vout_viewPos, 0.0)).xyz;
+#else
+  // Handle White Seams here.
+  // Idea is that we make the part of sphere occluded by planet transparent.
+  // Therefore terrain cracks show the color buffer's clear color instead.
+  // (Note: opaque colors (instead of transparent) are distorted by the HDR
+  //  tonemap computation and the (more or less) additive blending function.)
+  // cosAngleByPlanet: cosine of angle subtended by planet at camera
+  // cosVertAngle : cosine of angle subtended by current vertex at camera
+  float cosAngleByPlanet = cameraLightInfo.z;
+  float cosVertAngle = -dot(rayDir, cameraPos) * cameraHeightInv;
+  vec4 computedRayleighColorAndSkyOpacity =
+      vec4(frontColor * (invWavelength * krESun), startupConsts.w);
+  vout_rayleighColorAndSkyOpacity = cosVertAngle > cosAngleByPlanet ?
+      kTransparent : computedRayleighColorAndSkyOpacity;
+  vout_vertToCameraDir.xyz = cameraPos - vertPos;
+  // Fix for a driver bug on ATI Mobility FireGL V3200 (Driver 8.362.0.0)
+  // for OpenGL mode.
+  // Problem: The textures looked distorted.
+  // Reason: Two sets of shaders (atmosphere_ground and atomsphere_model_space)
+  //         used different length of TEXCOORD1 with my last change. With this
+  //         change, TEXCOORD1.w in atmosphere_groud fragment shader was getting
+  //         0 irrespective of whatever value you set in vertexshader. It seems
+  //         that some validation for length of texture coordinates was missing
+  //         in driver when fragment programs were switched.
+  // Workaround: Add TEXCOORD1.w back in all shaders. Avoid any computation by
+  //", ' ' <repeats 13 times>, "just setting it to 1.0
+  vout_vertToCameraDir.w = 1.0;
+#endif
+}
+#endif // defined(ATMOSPHERE_ON) && defined(SKY) && defined(SUN_ON)
+
+// ", '=' <repeats 74 times>, "
+#if defined(ATMOSPHERE_ON)
+  #if defined(GROUND)
+    void AtmosphereGroundVertexShader() {
+    #if defined(SUN_ON)
+      AtmosphereGroundSunOnVertexShader();
+    #elif defined(SUN_OFF)
+      AtmosphereGroundSunOffVertexShader();
+    #else
+      #error Invalid shader config 1
+    #endif
+    #if defined(ENABLE_VERTEX_REJECT)
+      gl_Position = getMaskedVertex(gl_Position, quadrantMask, ig_Color);
+    #endif
+    #if defined(DEBUG_QUADRANT_COLORS)
+      vout_debug_color = getDebugVertexColor(quadrantMask, ig_Color);
+    #endif
+    #if defined(QUADRANT_ALPHAS)
+      vout_alpha = getMaskedAlpha(quadrantAlphas, ig_Color);
+    #endif
+    }
+  #elif defined(SKY)
+    void AtmosphereSkyVertexShader() {
+    #if defined(SUN_ON)
+      AtmosphereSkySunOnVertexShader();
+    #elif defined(SUN_OFF)
+      AtmosphereSkySunOffVertexShader();
+    #else
+      #error Invalid shader config 2
+    #endif
+    }
+  #elif defined(USE_IMPROVED_SHADER_VARIATION)
+    void AtmosphereLookupTableShader() {
+      gl_Position = vec4(igv_Vertex.xy, 0.0, 1.0);
+    }
+  #endif
+#endif // ATMOSPHERE_ON
+
+
+#if defined(ATMOSPHERE_OFF)
+//", '=' <repeats 35 times>, " Sun off
+#if defined(SUN_OFF)
+varying vec4 vout_groundTexCoord;  // Transformed texture coordinate.
+
+// Vertex shader program.
+void NoAtmosphereSunOffVertexShader() {
+  vec4 position = vec4(igv_Vertex.xyz, 1.0);
+  vec4 groundTexCoord = vec4(igv_MultiTexCoord0.xy, 0.0, 1.0);
+
+  // Transform coordinate and texture coordinates.
+  gl_Position = ig_ModelViewProjectionMatrix * position;
+  vout_groundTexCoord = ig_TextureMatrix * groundTexCoord;
+}
+#endif // SUNOFF
+
+//", '=' <repeats 35 times>, " Sun on
+#if defined(SUN_ON)
+// xy: base texture coordinates.
+// zw: blend texture coordinates.
+varying vec4 vout_transformedCoords;  // Transformed texture coordinate.
+// x: diffuse factor that contains N.L and ambient contribution.
+varying float vout_diffuse_factor;
+
+// Vertex shader program.
+void NoAtmosphereSunOnVertexShader() {
+  vec4 position = vec4(igv_Vertex.xyz, 1.0);
+  vec4 groundTexCoord = vec4(igv_MultiTexCoord0.xy, 0.0, 1.0);
+  vec3 normal = ig_Normal;
+
+  // normalLen should be 0 if normals not supplied or 1 if supplied - assumes
+  // normals are normalized. This allows us to use multiplies instead
+  // of slow if or ?. TODO(jrohlf): See if ? might be faster.
+  highp float normalLen = dot(normal.xyz, normal.xyz);
+  // If normals not supplied use sphere normals.
+  // NOTE: normal xform should use inverse transpose but I don't think we
+  // have any scales in modelview.
+  highp vec3 normToUse = mix(
+    (ig_ModelViewMatrix * position).xyz - worldOriginInView,
+    (ig_ModelViewMatrix * vec4(normal, 0.)).xyz,
+    normalLen);
+  normToUse = normalize(normToUse);
+  highp float nDotL = dot(normToUse, cameraToSunDirAndExposure.xyz);
+  // When atmosphere shaders are off, the ambient contribution comes in
+  // the \"exposure\" variable.
+  float ambient_light = cameraToSunDirAndExposure.w;
+  // Transform coordinate and texture coordinates.
+  gl_Position = ig_ModelViewProjectionMatrix * position;
+  vout_transformedCoords.xy = (ig_TextureMatrix * groundTexCoord).xy;
+  vout_transformedCoords.zw = vec2(0.0, 0.0);
+  vout_diffuse_factor = min(1.0, ambient_light + max(0.0, nDotL));
+}
+#endif // SUN_ON
+
+//", '=' <repeats 26 times>, " Ground overlay no atmosphere
+#if defined(GROUND) && defined(OVERLAY)
+varying vec4 vout_texCoord;
+void NoAtmosphereGroundOverlayVertexShader() {
+  vec4 position = vec4(igv_Vertex.xyz, 1.0);
+  vec4 texCoord = vec4(igv_MultiTexCoord0.xy, 0.0, 1.0);
+
+  // Very simple pixel shader, handles modelview/projection matrix and
+  // texture matrix just like fixed function, but when specifying a pixel
+  // shader, we must have a vertex shader.
+  gl_Position = ig_ModelViewProjectionMatrix * position;
+  vout_texCoord = ig_TextureMatrix * texCoord;
+}
+#endif // GROUND AND OVERLAY
+#endif // ATMOSPHERE_OFF
+
+// ", '=' <repeats 74 times>, "
+void main() {
+#if defined(ATMOSPHERE_ON)
+  #if defined(GROUND)
+    AtmosphereGroundVertexShader();
+  #elif defined(SKY)
+    AtmosphereSkyVertexShader();
+  #elif defined(USE_IMPROVED_SHADER_VARIATION)
+    AtmosphereLookupTableShader();
+  #else
+    #error Invalid shader config 3
+  #endif
+#elif defined(ATMOSPHERE_OFF)
+    #if defined(GROUND) && defined(OVERLAY)
+      NoAtmosphereGroundOverlayVertexShader();
+    #else
+      #if defined(SUN_ON)
+        NoAtmosphereSunOnVertexShader();
+      #elif defined(SUN_OFF)
+        NoAtmosphereSunOffVertexShader();
+      #else
+        #error Invalid shader config 4
+      #endif
+    #endif
+    #if defined(ENABLE_VERTEX_REJECT)
+      gl_Position =
+        getMaskedVertex(gl_Position, quadrantMask, ig_Color);
+    #endif
+    #if defined(DEBUG_QUADRANT_COLORS)
+      vout_debug_color = getDebugVertexColor(quadrantMask, ig_Color);
+    #endif
+    #if defined(QUADRANT_ALPHAS)
+      vout_alpha = getMaskedAlpha(quadrantAlphas, ig_Color);
+    #endif
+#else
+  #error Invalid shader config 6
+#endif
+
+   // Added by BrianP:
+   gl_Position = gl_Vertex;
+}
+
-- 
1.7.3.4



More information about the Piglit mailing list