Browse Source

Merge pull request #897 from julien-moreau/master

Materials Libray, added SkyMaterial
David Catuhe 9 năm trước cách đây
mục cha
commit
b8dd9d7b5e

+ 8 - 0
materialsLibrary/config.json

@@ -82,6 +82,14 @@
         "materials/triPlanar/triplanar.fragment.fx"
       ],
       "output": "babylon.triPlanarMaterial.js"
+    },
+    {
+      "file": "materials/sky/babylon.skyMaterial.ts",
+      "shaderFiles": [
+        "materials/sky/sky.vertex.fx",
+        "materials/sky/sky.fragment.fx"
+      ],
+      "output": "babylon.skyMaterial.js"
     }
   ],
   "build": {

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 228 - 0
materialsLibrary/dist/babylon.skyMaterial.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 0
materialsLibrary/dist/babylon.skyMaterial.min.js


+ 30 - 0
materialsLibrary/dist/dts/babylon.skyMaterial.d.ts

@@ -0,0 +1,30 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts" />
+declare module BABYLON {
+    class SkyMaterial extends Material {
+        luminance: number;
+        turbidity: number;
+        rayleigh: number;
+        mieCoefficient: number;
+        mieDirectionalG: number;
+        distance: number;
+        inclination: number;
+        azimuth: number;
+        private _sunPosition;
+        private _renderId;
+        private _defines;
+        private _cachedDefines;
+        constructor(name: string, scene: Scene);
+        needAlphaBlending(): boolean;
+        needAlphaTesting(): boolean;
+        getAlphaTestTexture(): BaseTexture;
+        private _checkCache(scene, mesh?, useInstances?);
+        isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean;
+        bindOnlyWorldMatrix(world: Matrix): void;
+        bind(world: Matrix, mesh?: Mesh): void;
+        getAnimatables(): IAnimatable[];
+        dispose(forceDisposeEffect?: boolean): void;
+        clone(name: string): SkyMaterial;
+        serialize(): any;
+        static Parse(source: any, scene: Scene, rootUrl: string): SkyMaterial;
+    }
+}

+ 287 - 0
materialsLibrary/materials/sky/babylon.skyMaterial.ts

@@ -0,0 +1,287 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON {
+    class SkyMaterialDefines extends MaterialDefines {
+        public CLIPPLANE = false;
+        public POINTSIZE = false;
+        public FOG = false;
+        public VERTEXCOLOR = false;
+        public VERTEXALPHA = false;
+
+        constructor() {
+            super();
+            this._keys = Object.keys(this);
+        }
+    }
+
+    export class SkyMaterial extends Material {
+        // Public members
+        public luminance: number = 1.0;
+		public turbidity: number = 10.0;
+		public rayleigh: number = 2.0;
+		public mieCoefficient: number = 0.005;
+		public mieDirectionalG: number = 0.8;
+        
+        public distance: number = 500;
+        public inclination: number = 0.49;
+		public azimuth: number = 0.25;
+        
+        // Private members
+        private _sunPosition: Vector3 = Vector3.Zero();
+        
+        private _renderId: number;
+        
+        private _defines = new SkyMaterialDefines();
+        private _cachedDefines = new SkyMaterialDefines();
+
+        constructor(name: string, scene: Scene) {
+            super(name, scene);
+        }
+
+        public needAlphaBlending(): boolean {
+            return (this.alpha < 1.0);
+        }
+
+        public needAlphaTesting(): boolean {
+            return false;
+        }
+
+        public getAlphaTestTexture(): BaseTexture {
+            return null;
+        }
+
+        // Methods   
+        private _checkCache(scene: Scene, mesh?: AbstractMesh, useInstances?: boolean): boolean {
+            if (!mesh) {
+                return true;
+            }
+            
+            if (mesh._materialDefines && mesh._materialDefines.isEqual(this._defines)) {
+                return true;
+            }
+
+            return false;
+        }
+
+        public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
+            if (this.checkReadyOnlyOnce) {
+                if (this._wasPreviouslyReady) {
+                    return true;
+                }
+            }
+
+            var scene = this.getScene();
+
+            if (!this.checkReadyOnEveryCall) {
+                if (this._renderId === scene.getRenderId()) {
+                    if (this._checkCache(scene, mesh, useInstances)) {
+                        return true;
+                    }
+                }
+            }
+
+            var engine = scene.getEngine();
+            this._defines.reset();
+
+            // Effect
+            if (scene.clipPlane) {
+                this._defines.CLIPPLANE = true;
+            }
+
+            // Point size
+            if (this.pointsCloud || scene.forcePointsCloud) {
+                this._defines.POINTSIZE = true;
+            }
+
+            // Fog
+            if (scene.fogEnabled && mesh && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE && this.fogEnabled) {
+                this._defines.FOG = true;
+            }
+            
+            // Attribs
+            if (mesh) {
+                if (mesh.useVertexColors && mesh.isVerticesDataPresent(VertexBuffer.ColorKind)) {
+                    this._defines.VERTEXCOLOR = true;
+
+                    if (mesh.hasVertexAlpha) {
+                        this._defines.VERTEXALPHA = true;
+                    }
+                }
+            }
+
+            // Get correct effect      
+            if (!this._defines.isEqual(this._cachedDefines)) {
+                this._defines.cloneTo(this._cachedDefines);
+                
+                scene.resetCachedMaterial();
+                
+                // Fallbacks
+                var fallbacks = new EffectFallbacks();             
+                if (this._defines.FOG) {
+                    fallbacks.addFallback(1, "FOG");
+                }
+
+                //Attributes
+                var attribs = [VertexBuffer.PositionKind];
+
+                if (this._defines.VERTEXCOLOR) {
+                    attribs.push(VertexBuffer.ColorKind);
+                }
+
+                // Legacy browser patch
+                var shaderName = "sky";
+                
+                var join = this._defines.toString();
+                this._effect = scene.getEngine().createEffect(shaderName,
+                    attribs,
+                    ["world", "viewProjection",
+                        "vFogInfos", "vFogColor", "pointSize", "vClipPlane",
+                        "luminance", "turbidity", "rayleigh", "mieCoefficient", "mieDirectionalG", "sunPosition"
+                    ],
+                    [],
+                    join, fallbacks, this.onCompiled, this.onError);
+            }
+            
+            if (!this._effect.isReady()) {
+                return false;
+            }
+
+            this._renderId = scene.getRenderId();
+            this._wasPreviouslyReady = true;
+
+            if (mesh) {
+                if (!mesh._materialDefines) {
+                    mesh._materialDefines = new SkyMaterialDefines();
+                }
+
+                this._defines.cloneTo(mesh._materialDefines);
+            }
+
+            return true;
+        }
+
+        public bindOnlyWorldMatrix(world: Matrix): void {
+            this._effect.setMatrix("world", world);
+        }
+
+        public bind(world: Matrix, mesh?: Mesh): void {
+            var scene = this.getScene();
+
+            // Matrices        
+            this.bindOnlyWorldMatrix(world);
+            this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
+
+            if (scene.getCachedMaterial() !== this) {
+                // Clip plane
+                if (scene.clipPlane) {
+                    var clipPlane = scene.clipPlane;
+                    this._effect.setFloat4("vClipPlane", clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.d);
+                }
+
+                // Point size
+                if (this.pointsCloud) {
+                    this._effect.setFloat("pointSize", this.pointSize);
+                }               
+            }
+
+            // View
+            if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE) {
+                this._effect.setMatrix("view", scene.getViewMatrix());
+            }
+            
+            // Fog
+            if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE) {
+                this._effect.setFloat4("vFogInfos", scene.fogMode, scene.fogStart, scene.fogEnd, scene.fogDensity);
+                this._effect.setColor3("vFogColor", scene.fogColor);
+            }
+            
+            // Sky
+            this._effect.setFloat("luminance", this.luminance);
+			this._effect.setFloat("turbidity", this.turbidity);
+			this._effect.setFloat("rayleigh", this.rayleigh);
+			this._effect.setFloat("mieCoefficient", this.mieCoefficient);
+			this._effect.setFloat("mieDirectionalG", this.mieDirectionalG);
+            
+            var theta = Math.PI * (this.inclination - 0.5);
+			var phi = 2 * Math.PI * (this.azimuth - 0.5);
+            
+            this._sunPosition.x = this.distance * Math.cos( phi );
+			this._sunPosition.y = this.distance * Math.sin( phi ) * Math.sin( theta );
+			this._sunPosition.z = this.distance * Math.sin( phi ) * Math.cos( theta );
+            
+			this._effect.setVector3("sunPosition", this._sunPosition);
+
+            super.bind(world, mesh);
+        }
+
+        public getAnimatables(): IAnimatable[] {
+            return [];
+        }
+
+        public dispose(forceDisposeEffect?: boolean): void {
+            super.dispose(forceDisposeEffect);
+        }
+
+        public clone(name: string): SkyMaterial {
+            var newMaterial = new SkyMaterial(name, this.getScene());
+
+            // Base material
+            this.copyTo(newMaterial);
+            
+            newMaterial.luminance = this.luminance;
+            newMaterial.turbidity = this.turbidity;
+            newMaterial.rayleigh = this.rayleigh;
+            newMaterial.mieCoefficient = this.mieCoefficient;
+            newMaterial.mieDirectionalG = this.mieDirectionalG;
+            newMaterial.distance = this.distance;
+            newMaterial.inclination = this.inclination;
+            newMaterial.azimuth = this.azimuth;
+            
+            return newMaterial;
+        }
+		
+		public serialize(): any {
+		
+            var serializationObject = super.serialize();
+            serializationObject.customType = "BABYLON.SkyMaterial";
+            
+            serializationObject.luminance = this.luminance;
+            serializationObject.turbidity = this.turbidity;
+            serializationObject.rayleigh = this.rayleigh;
+            serializationObject.mieCoefficient = this.mieCoefficient;
+            serializationObject.mieDirectionalG = this.mieDirectionalG;
+            serializationObject.distance = this.distance;
+            serializationObject.inclination = this.inclination;
+            serializationObject.azimuth = this.azimuth;
+
+            return serializationObject;
+        }
+
+        public static Parse(source: any, scene: Scene, rootUrl: string): SkyMaterial {
+            var material = new SkyMaterial(source.name, scene);
+
+            material.alpha = source.alpha;
+            material.id = source.id;
+            
+            Tags.AddTagsTo(material, source.tags);
+            material.backFaceCulling = source.backFaceCulling;
+            material.wireframe = source.wireframe;
+
+            if (source.checkReadyOnlyOnce) {
+                material.checkReadyOnlyOnce = source.checkReadyOnlyOnce;
+            }
+            
+            material.luminance = source.luminance;
+            material.turbidity = source.turbidity;
+            material.rayleigh = source.rayleigh;
+            material.mieCoefficient = source.mieCoefficient;
+            material.mieDirectionalG = source.mieDirectionalG;
+            material.distance = source.distance;
+            material.inclination = source.inclination;
+            material.azimuth = source.azimuth;
+
+            return material;
+        }
+    }
+} 
+

+ 211 - 0
materialsLibrary/materials/sky/sky.fragment.fx

@@ -0,0 +1,211 @@
+precision highp float;
+
+// Input
+varying vec3 vPositionW;
+
+#ifdef VERTEXCOLOR
+varying vec4 vColor;
+#endif
+
+#ifdef CLIPPLANE
+varying float fClipDistance;
+#endif
+
+// Sky
+uniform float luminance;
+uniform float turbidity;
+uniform float rayleigh;
+uniform float mieCoefficient;
+uniform float mieDirectionalG;
+uniform vec3 sunPosition;
+
+// Fog
+#ifdef FOG
+#define FOGMODE_NONE    0.
+#define FOGMODE_EXP     1.
+#define FOGMODE_EXP2    2.
+#define FOGMODE_LINEAR  3.
+#define E 2.71828
+
+uniform vec4 vFogInfos;
+uniform vec3 vFogColor;
+varying float fFogDistance;
+
+float CalcFogFactor()
+{
+	float fogCoeff = 1.0;
+	float fogStart = vFogInfos.y;
+	float fogEnd = vFogInfos.z;
+	float fogDensity = vFogInfos.w;
+
+	if (FOGMODE_LINEAR == vFogInfos.x)
+	{
+		fogCoeff = (fogEnd - fFogDistance) / (fogEnd - fogStart);
+	}
+	else if (FOGMODE_EXP == vFogInfos.x)
+	{
+		fogCoeff = 1.0 / pow(E, fFogDistance * fogDensity);
+	}
+	else if (FOGMODE_EXP2 == vFogInfos.x)
+	{
+		fogCoeff = 1.0 / pow(E, fFogDistance * fFogDistance * fogDensity * fogDensity);
+	}
+
+	return clamp(fogCoeff, 0.0, 1.0);
+}
+#endif
+
+// Constants
+const float e = 2.71828182845904523536028747135266249775724709369995957;
+const float pi = 3.141592653589793238462643383279502884197169;
+const float n = 1.0003;
+const float N = 2.545E25;
+const float pn = 0.035;
+
+const vec3 lambda = vec3(680E-9, 550E-9, 450E-9);
+
+const vec3 K = vec3(0.686, 0.678, 0.666);
+const float v = 4.0;
+
+const float rayleighZenithLength = 8.4E3;
+const float mieZenithLength = 1.25E3;
+const vec3 up = vec3(0.0, 1.0, 0.0);
+
+const float EE = 1000.0;
+const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;
+
+const float cutoffAngle = pi/1.95;
+const float steepness = 1.5;
+
+vec3 totalRayleigh(vec3 lambda)
+{
+	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));
+}
+
+vec3 simplifiedRayleigh()
+{
+	return 0.0005 / vec3(94, 40, 18);
+}
+
+float rayleighPhase(float cosTheta)
+{	 
+	return (3.0 / (16.0*pi)) * (1.0 + pow(cosTheta, 2.0));
+}
+
+vec3 totalMie(vec3 lambda, vec3 K, float T)
+{
+	float c = (0.2 * T ) * 10E-18;
+	return 0.434 * c * pi * pow((2.0 * pi) / lambda, vec3(v - 2.0)) * K;
+}
+
+float hgPhase(float cosTheta, float g)
+{
+	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));
+}
+
+float sunIntensity(float zenithAngleCos)
+{
+	return EE * max(0.0, 1.0 - exp(-((cutoffAngle - acos(zenithAngleCos))/steepness)));
+}
+
+float A = 0.15;
+float B = 0.50;
+float C = 0.10;
+float D = 0.20;
+float E = 0.02;
+float F = 0.30;
+float W = 1000.0;
+
+vec3 Uncharted2Tonemap(vec3 x)
+{
+	return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
+}
+
+void main(void) {
+	// Clip plane
+#ifdef CLIPPLANE
+	if (fClipDistance > 0.0)
+		discard;
+#endif
+
+	/**
+	*--------------------------------------------------------------------------------------------------
+	* Sky Color
+	*--------------------------------------------------------------------------------------------------
+	*/
+	const vec3 cameraPos = vec3(0.0, 0.0, 0.0);
+	float sunfade = 1.0 - clamp(1.0 - exp((sunPosition.y / 450000.0)), 0.0, 1.0);
+	float rayleighCoefficient = rayleigh - (1.0 * (1.0 - sunfade));
+	vec3 sunDirection = normalize(sunPosition);
+	float sunE = sunIntensity(dot(sunDirection, up));
+	vec3 betaR = simplifiedRayleigh() * rayleighCoefficient;
+	vec3 betaM = totalMie(lambda, K, turbidity) * mieCoefficient;
+	float zenithAngle = acos(max(0.0, dot(up, normalize(vPositionW - cameraPos))));
+	float sR = rayleighZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));
+	float sM = mieZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));
+	vec3 Fex = exp(-(betaR * sR + betaM * sM));
+	float cosTheta = dot(normalize(vPositionW - cameraPos), sunDirection);
+	float rPhase = rayleighPhase(cosTheta*0.5+0.5);
+	vec3 betaRTheta = betaR * rPhase;
+	float mPhase = hgPhase(cosTheta, mieDirectionalG);
+	vec3 betaMTheta = betaM * mPhase;
+	
+	vec3 Lin = pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * (1.0 - Fex),vec3(1.5));
+	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));
+
+	vec3 direction = normalize(vPositionW - cameraPos);
+	float theta = acos(direction.y);
+	float phi = atan(direction.z, direction.x);
+	vec2 uv = vec2(phi, theta) / vec2(2.0 * pi, pi) + vec2(0.5, 0.0);
+	vec3 L0 = vec3(0.1) * Fex;
+	
+	float sundisk = smoothstep(sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta);
+	L0 += (sunE * 19000.0 * Fex) * sundisk;
+	
+	vec3 whiteScale = 1.0/Uncharted2Tonemap(vec3(W));
+	vec3 texColor = (Lin+L0);   
+	texColor *= 0.04 ;
+	texColor += vec3(0.0,0.001,0.0025)*0.3;
+
+	float g_fMaxLuminance = 1.0;
+	float fLumScaled = 0.1 / luminance;     
+	float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (g_fMaxLuminance * g_fMaxLuminance)))) / (1.0 + fLumScaled); 
+
+	float ExposureBias = fLumCompressed;
+
+	vec3 curr = Uncharted2Tonemap((log2(2.0/pow(luminance,4.0)))*texColor);
+	vec3 skyColor = curr * whiteScale;
+
+	vec3 retColor = pow(skyColor,vec3(1.0/(1.2+(1.2*sunfade))));
+	
+	vec4 baseColor = vec4(retColor, 1.0);
+	/**
+	*--------------------------------------------------------------------------------------------------
+	* Sky Color
+	*--------------------------------------------------------------------------------------------------
+	*/
+	
+	// Alpha
+	float alpha = 1.0;
+
+#ifdef VERTEXCOLOR
+	baseColor.rgb *= vColor.rgb;
+#endif
+
+	// Lighting
+	vec3 diffuseBase = vec3(1.0, 1.0, 1.0);
+
+#ifdef VERTEXALPHA
+	alpha *= vColor.a;
+#endif
+
+	// Composition
+	vec4 color = vec4(baseColor.rgb, alpha);
+
+#ifdef FOG
+	float fog = CalcFogFactor();
+	color.rgb = fog * color.rgb + (1.0 - fog) * vFogColor;
+#endif
+
+	gl_FragColor = color;
+}

+ 59 - 0
materialsLibrary/materials/sky/sky.vertex.fx

@@ -0,0 +1,59 @@
+precision highp float;
+
+// Attributes
+attribute vec3 position;
+
+#ifdef VERTEXCOLOR
+attribute vec4 color;
+#endif
+
+// Uniforms
+uniform mat4 world;
+uniform mat4 viewProjection;
+
+#ifdef POINTSIZE
+uniform float pointSize;
+#endif
+
+// Output
+varying vec3 vPositionW;
+
+#ifdef VERTEXCOLOR
+varying vec4 vColor;
+#endif
+
+#ifdef CLIPPLANE
+uniform vec4 vClipPlane;
+varying float fClipDistance;
+#endif
+
+#ifdef FOG
+varying float fFogDistance;
+#endif
+
+void main(void) {
+	gl_Position = viewProjection * world * vec4(position, 1.0);
+	
+	vec4 worldPos = world * vec4(position, 1.0);
+	vPositionW = vec3(worldPos);
+
+	// Clip plane
+#ifdef CLIPPLANE
+	fClipDistance = dot(worldPos, vClipPlane);
+#endif
+
+	// Fog
+#ifdef FOG
+	fFogDistance = (view * worldPos).z;
+#endif
+
+	// Vertex color
+#ifdef VERTEXCOLOR
+	vColor = color;
+#endif
+
+	// Point size
+#ifdef POINTSIZE
+	gl_PointSize = pointSize;
+#endif
+}

+ 48 - 0
materialsLibrary/test/add/addsky.js

@@ -0,0 +1,48 @@
+window.prepareSky = function() {
+	var sky = new BABYLON.SkyMaterial("sky", scene);
+	sky.backFaceCulling = false;
+	
+	registerRangeUI("sky", "azimuth", 0, 1, function(value) {
+		sky.azimuth = value;
+	}, function() {
+		return sky.azimuth;
+	});
+	
+	registerRangeUI("sky", "inclination", 0, 1, function(value) {
+		sky.inclination = value;
+	}, function() {
+		return sky.inclination;
+	});
+	
+	registerRangeUI("sky", "luminance", 0, 2, function(value) {
+		sky.luminance = value;
+	}, function() {
+		return sky.luminance;
+	});
+	
+	registerRangeUI("sky", "mieDirectionalG", 0, 1, function(value) {
+		sky.mieDirectionalG = value;
+	}, function() {
+		return sky.mieDirectionalG;
+	});
+	
+	registerRangeUI("sky", "mieCoefficient", 0, 0.1, function(value) {
+		sky.mieCoefficient = value;
+	}, function() {
+		return sky.mieCoefficient;
+	});
+	
+	registerRangeUI("sky", "rayleigh", 0, 4, function(value) {
+		sky.rayleigh = value;
+	}, function() {
+		return sky.rayleigh;
+	});
+	
+	registerRangeUI("sky", "turbidity", 0, 20, function(value) {
+		sky.turbidity = value;
+	}, function() {
+		return sky.turbidity;
+	});
+		
+	return sky;
+}

+ 10 - 1
materialsLibrary/test/index.html

@@ -14,6 +14,7 @@
 	<script src="../dist/babylon.furMaterial.js"></script>
 	<script src="../dist/babylon.triPlanarMaterial.js"></script>
 	<script src="../dist/babylon.gradientMaterial.js"></script>
+	<script src="../dist/babylon.skyMaterial.js"></script>
 
 	<style>
 		html, body {
@@ -57,6 +58,7 @@
 	<script src="add/addfire.js"></script>
 	<script src="add/addtriplanar.js"></script>
 	<script src="add/addgradient.js"></script>
+	<script src="add/addsky.js"></script>
 	
 	<script>
 		if (BABYLON.Engine.isSupported()) {
@@ -201,13 +203,16 @@
 				
 				var triPlanar = prepareTriPlanar();
 				
+				var sky = prepareSky();
+				
 				// Default to std
 				var currentMaterial = std;
 				sphere.material = std;				
 				sphere.receiveShadows = true;
 
-				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur', 'triPlanar', 'gradient']).onFinishChange(function () {
+				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur', 'triPlanar', 'gradient', 'sky']).onFinishChange(function () {
 					water.enableRenderTargets(false);
+					skybox.material = skyboxMaterial;
 					
 					switch (options.material) {
 						case "simple":
@@ -242,6 +247,10 @@
 						case "gradient":
 							currentMaterial = gradient;
 							break;
+						case "sky":
+							skybox.setEnabled(true);
+							skybox.material = sky;
+							break;
 						default:
 							currentMaterial = std;
 							break;