Skip to content

Commit 16a8419

Browse files
brianchirlsmrdoob
authored andcommitted
Optimize Sky Shader (mrdoob#9684)
- Move computation from fragment shader to vertex shader wherever possible - Eliminate some dead shader code and comments - Fix spelling of "Rayleigh"
1 parent c1b8d4c commit 16a8419

2 files changed

Lines changed: 100 additions & 137 deletions

File tree

examples/js/SkyShader.js

Lines changed: 97 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ THREE.ShaderLib[ 'sky' ] = {
2020

2121
luminance: { value: 1 },
2222
turbidity: { value: 2 },
23-
reileigh: { value: 1 },
23+
rayleigh: { value: 1 },
2424
mieCoefficient: { value: 0.005 },
2525
mieDirectionalG: { value: 0.8 },
2626
sunPosition: { value: new THREE.Vector3() }
@@ -29,7 +29,51 @@ THREE.ShaderLib[ 'sky' ] = {
2929

3030
vertexShader: [
3131

32+
"uniform vec3 sunPosition;",
33+
"uniform float rayleigh;",
34+
"uniform float turbidity;",
35+
"uniform float mieCoefficient;",
36+
3237
"varying vec3 vWorldPosition;",
38+
"varying vec3 vSunDirection;",
39+
"varying float vSunfade;",
40+
"varying vec3 vBetaR;",
41+
"varying vec3 vBetaM;",
42+
"varying float vSunE;",
43+
44+
"const vec3 up = vec3(0.0, 1.0, 0.0);",
45+
46+
// constants for atmospheric scattering
47+
"const float e = 2.71828182845904523536028747135266249775724709369995957;",
48+
"const float pi = 3.141592653589793238462643383279502884197169;",
49+
50+
// mie stuff
51+
// K coefficient for the primaries
52+
"const float v = 4.0;",
53+
"const vec3 K = vec3(0.686, 0.678, 0.666);",
54+
55+
// see http://blenderartists.org/forum/showthread.php?321110-Shaders-and-Skybox-madness
56+
// A simplied version of the total Reayleigh scattering to works on browsers that use ANGLE
57+
"const vec3 simplifiedRayleigh = 0.0005 / vec3(94, 40, 18);",
58+
59+
// wavelength of used primaries, according to preetham
60+
"const vec3 lambda = vec3(680E-9, 550E-9, 450E-9);",
61+
62+
// earth shadow hack
63+
"const float cutoffAngle = pi/1.95;",
64+
"const float steepness = 1.5;",
65+
"const float EE = 1000.0;",
66+
67+
"float sunIntensity(float zenithAngleCos)",
68+
"{",
69+
"return EE * max(0.0, 1.0 - pow(e, -((cutoffAngle - acos(zenithAngleCos))/steepness)));",
70+
"}",
71+
72+
"vec3 totalMie(vec3 lambda, float T)",
73+
"{",
74+
"float c = (0.2 * T ) * 10E-18;",
75+
"return 0.434 * c * pi * pow((2.0 * pi) / lambda, vec3(v - 2.0)) * K;",
76+
"}",
3377

3478
"void main() {",
3579

@@ -38,113 +82,73 @@ THREE.ShaderLib[ 'sky' ] = {
3882

3983
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
4084

85+
"vSunDirection = normalize(sunPosition);",
86+
87+
"vSunE = sunIntensity(dot(vSunDirection, up));",
88+
89+
"vSunfade = 1.0-clamp(1.0-exp((sunPosition.y/450000.0)),0.0,1.0);",
90+
91+
"float rayleighCoefficient = rayleigh - (1.0 * (1.0-vSunfade));",
92+
93+
// extinction (absorbtion + out scattering)
94+
// rayleigh coefficients
95+
"vBetaR = simplifiedRayleigh * rayleighCoefficient;",
96+
97+
// mie coefficients
98+
"vBetaM = totalMie(lambda, turbidity) * mieCoefficient;",
99+
41100
"}",
42101

43102
].join( "\n" ),
44103

45104
fragmentShader: [
46105

47-
"uniform sampler2D skySampler;",
48-
"uniform vec3 sunPosition;",
49106
"varying vec3 vWorldPosition;",
50-
51-
"vec3 cameraPos = vec3(0., 0., 0.);",
52-
"// uniform sampler2D sDiffuse;",
53-
"// const float turbidity = 10.0; //",
54-
"// const float reileigh = 2.; //",
55-
"// const float luminance = 1.0; //",
56-
"// const float mieCoefficient = 0.005;",
57-
"// const float mieDirectionalG = 0.8;",
107+
"varying vec3 vSunDirection;",
108+
"varying float vSunfade;",
109+
"varying vec3 vBetaR;",
110+
"varying vec3 vBetaM;",
111+
"varying float vSunE;",
58112

59113
"uniform float luminance;",
60-
"uniform float turbidity;",
61-
"uniform float reileigh;",
62-
"uniform float mieCoefficient;",
63114
"uniform float mieDirectionalG;",
64115

65-
"// constants for atmospheric scattering",
66-
"const float e = 2.71828182845904523536028747135266249775724709369995957;",
67-
"const float pi = 3.141592653589793238462643383279502884197169;",
116+
"const vec3 cameraPos = vec3(0., 0., 0.);",
68117

69-
"const float n = 1.0003; // refractive index of air",
70-
"const float N = 2.545E25; // number of molecules per unit volume for air at",
71-
"// 288.15K and 1013mb (sea level -45 celsius)",
72-
"const float pn = 0.035; // depolatization factor for standard air",
73-
74-
"// wavelength of used primaries, according to preetham",
75-
"const vec3 lambda = vec3(680E-9, 550E-9, 450E-9);",
118+
// constants for atmospheric scattering
119+
"const float pi = 3.141592653589793238462643383279502884197169;",
76120

77-
"// mie stuff",
78-
"// K coefficient for the primaries",
79-
"const vec3 K = vec3(0.686, 0.678, 0.666);",
80-
"const float v = 4.0;",
121+
"const float n = 1.0003;", // refractive index of air
122+
"const float N = 2.545E25;", // number of molecules per unit volume for air at
123+
// 288.15K and 1013mb (sea level -45 celsius)
81124

82-
"// optical length at zenith for molecules",
125+
// optical length at zenith for molecules
83126
"const float rayleighZenithLength = 8.4E3;",
84127
"const float mieZenithLength = 1.25E3;",
85128
"const vec3 up = vec3(0.0, 1.0, 0.0);",
86129

87-
"const float EE = 1000.0;",
88130
"const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;",
89-
"// 66 arc seconds -> degrees, and the cosine of that",
90-
91-
"// earth shadow hack",
92-
"const float cutoffAngle = pi/1.95;",
93-
"const float steepness = 1.5;",
94-
95-
96-
"vec3 totalRayleigh(vec3 lambda)",
97-
"{",
98-
"return (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn));",
99-
"}",
100-
101-
// see http://blenderartists.org/forum/showthread.php?321110-Shaders-and-Skybox-madness
102-
"// A simplied version of the total Reayleigh scattering to works on browsers that use ANGLE",
103-
"vec3 simplifiedRayleigh()",
104-
"{",
105-
"return 0.0005 / vec3(94, 40, 18);",
106-
// return 0.00054532832366 / (3.0 * 2.545E25 * pow(vec3(680E-9, 550E-9, 450E-9), vec3(4.0)) * 6.245);
107-
"}",
131+
// 66 arc seconds -> degrees, and the cosine of that
108132

109133
"float rayleighPhase(float cosTheta)",
110-
"{ ",
111-
"return (3.0 / (16.0*pi)) * (1.0 + pow(cosTheta, 2.0));",
112-
"// return (1.0 / (3.0*pi)) * (1.0 + pow(cosTheta, 2.0));",
113-
"// return (3.0 / 4.0) * (1.0 + pow(cosTheta, 2.0));",
114-
"}",
115-
116-
"vec3 totalMie(vec3 lambda, vec3 K, float T)",
117134
"{",
118-
"float c = (0.2 * T ) * 10E-18;",
119-
"return 0.434 * c * pi * pow((2.0 * pi) / lambda, vec3(v - 2.0)) * K;",
135+
"return (3.0 / (16.0*pi)) * (1.0 + pow(cosTheta, 2.0));",
120136
"}",
121137

122138
"float hgPhase(float cosTheta, float g)",
123139
"{",
124140
"return (1.0 / (4.0*pi)) * ((1.0 - pow(g, 2.0)) / pow(1.0 - 2.0*g*cosTheta + pow(g, 2.0), 1.5));",
125141
"}",
126142

127-
"float sunIntensity(float zenithAngleCos)",
128-
"{",
129-
// This function originally used `exp(n)`, but it returns an incorrect value
130-
// on Samsung S6 phones. So it has been replaced with the equivalent `pow(e, n)`.
131-
// See https://github.com/mrdoob/three.js/issues/8382
132-
"return EE * max(0.0, 1.0 - pow(e, -((cutoffAngle - acos(zenithAngleCos))/steepness)));",
133-
"}",
134-
135-
"// float logLuminance(vec3 c)",
136-
"// {",
137-
"// return log(c.r * 0.2126 + c.g * 0.7152 + c.b * 0.0722);",
138-
"// }",
143+
// Filmic ToneMapping http://filmicgames.com/archives/75
144+
"const float A = 0.15;",
145+
"const float B = 0.50;",
146+
"const float C = 0.10;",
147+
"const float D = 0.20;",
148+
"const float E = 0.02;",
149+
"const float F = 0.30;",
139150

140-
"// Filmic ToneMapping http://filmicgames.com/archives/75",
141-
"float A = 0.15;",
142-
"float B = 0.50;",
143-
"float C = 0.10;",
144-
"float D = 0.20;",
145-
"float E = 0.02;",
146-
"float F = 0.30;",
147-
"float W = 1000.0;",
151+
"const float whiteScale = 1.0748724675633854;", // 1.0 / Uncharted2Tonemap(1000.0)
148152

149153
"vec3 Uncharted2Tonemap(vec3 x)",
150154
"{",
@@ -154,83 +158,44 @@ THREE.ShaderLib[ 'sky' ] = {
154158

155159
"void main() ",
156160
"{",
157-
"float sunfade = 1.0-clamp(1.0-exp((sunPosition.y/450000.0)),0.0,1.0);",
158-
159-
"// luminance = 1.0 ;// vWorldPosition.y / 450000. + 0.5; //sunPosition.y / 450000. * 1. + 0.5;",
160-
161-
"// gl_FragColor = vec4(sunfade, sunfade, sunfade, 1.0);",
162-
163-
"float reileighCoefficient = reileigh - (1.0* (1.0-sunfade));",
164-
165-
"vec3 sunDirection = normalize(sunPosition);",
166-
167-
"float sunE = sunIntensity(dot(sunDirection, up));",
168-
169-
"// extinction (absorbtion + out scattering) ",
170-
"// rayleigh coefficients",
171-
172-
// "vec3 betaR = totalRayleigh(lambda) * reileighCoefficient;",
173-
"vec3 betaR = simplifiedRayleigh() * reileighCoefficient;",
174-
175-
"// mie coefficients",
176-
"vec3 betaM = totalMie(lambda, K, turbidity) * mieCoefficient;",
177-
178-
"// optical length",
179-
"// cutoff angle at 90 to avoid singularity in next formula.",
161+
// optical length
162+
// cutoff angle at 90 to avoid singularity in next formula.
180163
"float zenithAngle = acos(max(0.0, dot(up, normalize(vWorldPosition - cameraPos))));",
181164
"float sR = rayleighZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));",
182165
"float sM = mieZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));",
183166

167+
// combined extinction factor
168+
"vec3 Fex = exp(-(vBetaR * sR + vBetaM * sM));",
184169

185-
186-
"// combined extinction factor ",
187-
"vec3 Fex = exp(-(betaR * sR + betaM * sM));",
188-
189-
"// in scattering",
190-
"float cosTheta = dot(normalize(vWorldPosition - cameraPos), sunDirection);",
170+
// in scattering
171+
"float cosTheta = dot(normalize(vWorldPosition - cameraPos), vSunDirection);",
191172

192173
"float rPhase = rayleighPhase(cosTheta*0.5+0.5);",
193-
"vec3 betaRTheta = betaR * rPhase;",
174+
"vec3 betaRTheta = vBetaR * rPhase;",
194175

195176
"float mPhase = hgPhase(cosTheta, mieDirectionalG);",
196-
"vec3 betaMTheta = betaM * mPhase;",
197-
177+
"vec3 betaMTheta = vBetaM * mPhase;",
198178

199-
"vec3 Lin = pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * (1.0 - Fex),vec3(1.5));",
200-
"Lin *= mix(vec3(1.0),pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * Fex,vec3(1.0/2.0)),clamp(pow(1.0-dot(up, sunDirection),5.0),0.0,1.0));",
179+
"vec3 Lin = pow(vSunE * ((betaRTheta + betaMTheta) / (vBetaR + vBetaM)) * (1.0 - Fex),vec3(1.5));",
180+
"Lin *= mix(vec3(1.0),pow(vSunE * ((betaRTheta + betaMTheta) / (vBetaR + vBetaM)) * Fex,vec3(1.0/2.0)),clamp(pow(1.0-dot(up, vSunDirection),5.0),0.0,1.0));",
201181

202-
"//nightsky",
182+
//nightsky
203183
"vec3 direction = normalize(vWorldPosition - cameraPos);",
204184
"float theta = acos(direction.y); // elevation --> y-axis, [-pi/2, pi/2]",
205185
"float phi = atan(direction.z, direction.x); // azimuth --> x-axis [-pi/2, pi/2]",
206186
"vec2 uv = vec2(phi, theta) / vec2(2.0*pi, pi) + vec2(0.5, 0.0);",
207-
"// vec3 L0 = texture2D(skySampler, uv).rgb+0.1 * Fex;",
208187
"vec3 L0 = vec3(0.1) * Fex;",
209188

210-
"// composition + solar disc",
211-
"//if (cosTheta > sunAngularDiameterCos)",
189+
// composition + solar disc
212190
"float sundisk = smoothstep(sunAngularDiameterCos,sunAngularDiameterCos+0.00002,cosTheta);",
213-
"// if (normalize(vWorldPosition - cameraPos).y>0.0)",
214-
"L0 += (sunE * 19000.0 * Fex)*sundisk;",
215-
191+
"L0 += (vSunE * 19000.0 * Fex)*sundisk;",
216192

217-
"vec3 whiteScale = 1.0/Uncharted2Tonemap(vec3(W));",
218-
219-
"vec3 texColor = (Lin+L0); ",
220-
"texColor *= 0.04 ;",
221-
"texColor += vec3(0.0,0.001,0.0025)*0.3;",
222-
223-
"float g_fMaxLuminance = 1.0;",
224-
"float fLumScaled = 0.1 / luminance; ",
225-
"float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (g_fMaxLuminance * g_fMaxLuminance)))) / (1.0 + fLumScaled); ",
226-
227-
"float ExposureBias = fLumCompressed;",
193+
"vec3 texColor = (Lin+L0) * 0.04 + vec3(0.0, 0.0003, 0.00075);",
228194

229195
"vec3 curr = Uncharted2Tonemap((log2(2.0/pow(luminance,4.0)))*texColor);",
230196
"vec3 color = curr*whiteScale;",
231197

232-
"vec3 retColor = pow(color,vec3(1.0/(1.2+(1.2*sunfade))));",
233-
198+
"vec3 retColor = pow(color,vec3(1.0/(1.2+(1.2*vSunfade))));",
234199

235200
"gl_FragColor.rgb = retColor;",
236201

@@ -256,7 +221,6 @@ THREE.Sky = function () {
256221
var skyGeo = new THREE.SphereBufferGeometry( 450000, 32, 15 );
257222
var skyMesh = new THREE.Mesh( skyGeo, skyMat );
258223

259-
260224
// Expose variables
261225
this.mesh = skyMesh;
262226
this.uniforms = skyUniforms;

examples/webgl_shaders_sky.html

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
<script src="js/Detector.js"></script>
5151
<script src="js/libs/dat.gui.min.js"></script>
5252

53-
5453
<script>
5554

5655

@@ -84,7 +83,7 @@
8483

8584
var effectController = {
8685
turbidity: 10,
87-
reileigh: 2,
86+
rayleigh: 2,
8887
mieCoefficient: 0.005,
8988
mieDirectionalG: 0.8,
9089
luminance: 1,
@@ -99,7 +98,7 @@
9998

10099
var uniforms = sky.uniforms;
101100
uniforms.turbidity.value = effectController.turbidity;
102-
uniforms.reileigh.value = effectController.reileigh;
101+
uniforms.rayleigh.value = effectController.rayleigh;
103102
uniforms.luminance.value = effectController.luminance;
104103
uniforms.mieCoefficient.value = effectController.mieCoefficient;
105104
uniforms.mieDirectionalG.value = effectController.mieDirectionalG;
@@ -122,7 +121,7 @@
122121
var gui = new dat.GUI();
123122

124123
gui.add( effectController, "turbidity", 1.0, 20.0, 0.1 ).onChange( guiChanged );
125-
gui.add( effectController, "reileigh", 0.0, 4, 0.001 ).onChange( guiChanged );
124+
gui.add( effectController, "rayleigh", 0.0, 4, 0.001 ).onChange( guiChanged );
126125
gui.add( effectController, "mieCoefficient", 0.0, 0.1, 0.001 ).onChange( guiChanged );
127126
gui.add( effectController, "mieDirectionalG", 0.0, 1, 0.001 ).onChange( guiChanged );
128127
gui.add( effectController, "luminance", 0.0, 2 ).onChange( guiChanged );

0 commit comments

Comments
 (0)