function WebGLBackground( renderer, cubemaps, state, objects, premultipliedAlpha ) {

    	const clearColor = new Color( 0x000000 );
    	let clearAlpha = 0;

    	let planeMesh;
    	let boxMesh;

    	let currentBackground = null;
    	let currentBackgroundVersion = 0;
    	let currentTonemapping = null;

    	function render( renderList, scene ) {

    		let forceClear = false;
    		let background = scene.isScene === true ? scene.background : null;

    		if ( background && background.isTexture ) {

    			background = cubemaps.get( background );

    		}

    		// Ignore background in AR
    		// TODO: Reconsider this.

    		const xr = renderer.xr;
    		const session = xr.getSession && xr.getSession();

    		if ( session && session.environmentBlendMode === 'additive' ) {

    			background = null;

    		}

    		if ( background === null ) {

    			setClear( clearColor, clearAlpha );

    		} else if ( background && background.isColor ) {

    			setClear( background, 1 );
    			forceClear = true;

    		}

    		if ( renderer.autoClear || forceClear ) {

    			renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );

    		}

    		if ( background && ( background.isCubeTexture || background.mapping === CubeUVReflectionMapping ) ) {

    			if ( boxMesh === undefined ) {

    				boxMesh = new Mesh(
    					new BoxGeometry( 1, 1, 1 ),
    					new ShaderMaterial( {
    						name: 'BackgroundCubeMaterial',
    						uniforms: cloneUniforms( ShaderLib.cube.uniforms ),
    						vertexShader: ShaderLib.cube.vertexShader,
    						fragmentShader: ShaderLib.cube.fragmentShader,
    						side: BackSide,
    						depthTest: false,
    						depthWrite: false,
    						fog: false
    					} )
    				);

    				boxMesh.geometry.deleteAttribute( 'normal' );
    				boxMesh.geometry.deleteAttribute( 'uv' );

    				boxMesh.onBeforeRender = function ( renderer, scene, camera ) {

    					this.matrixWorld.copyPosition( camera.matrixWorld );

    				};

    				// enable code injection for non-built-in material
    				Object.defineProperty( boxMesh.material, 'envMap', {

    					get: function () {

    						return this.uniforms.envMap.value;

    					}

    				} );

    				objects.update( boxMesh );

    			}

    			boxMesh.material.uniforms.envMap.value = background;
    			boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1;

    			if ( currentBackground !== background ||
    				currentBackgroundVersion !== background.version ||
    				currentTonemapping !== renderer.toneMapping ) {

    				boxMesh.material.needsUpdate = true;

    				currentBackground = background;
    				currentBackgroundVersion = background.version;
    				currentTonemapping = renderer.toneMapping;

    			}

    			// push to the pre-sorted opaque render list
    			renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null );

    		} else if ( background && background.isTexture ) {

    			if ( planeMesh === undefined ) {

    				planeMesh = new Mesh(
    					new PlaneGeometry( 2, 2 ),
    					new ShaderMaterial( {
    						name: 'BackgroundMaterial',
    						uniforms: cloneUniforms( ShaderLib.background.uniforms ),
    						vertexShader: ShaderLib.background.vertexShader,
    						fragmentShader: ShaderLib.background.fragmentShader,
    						side: FrontSide,
    						depthTest: false,
    						depthWrite: false,
    						fog: false
    					} )
    				);

    				planeMesh.geometry.deleteAttribute( 'normal' );

    				// enable code injection for non-built-in material
    				Object.defineProperty( planeMesh.material, 'map', {

    					get: function () {

    						return this.uniforms.t2D.value;

    					}

    				} );

    				objects.update( planeMesh );

    			}

    			planeMesh.material.uniforms.t2D.value = background;

    			if ( background.matrixAutoUpdate === true ) {

    				background.updateMatrix();

    			}

    			planeMesh.material.uniforms.uvTransform.value.copy( background.matrix );

    			if ( currentBackground !== background ||
    				currentBackgroundVersion !== background.version ||
    				currentTonemapping !== renderer.toneMapping ) {

    				planeMesh.material.needsUpdate = true;

    				currentBackground = background;
    				currentBackgroundVersion = background.version;
    				currentTonemapping = renderer.toneMapping;

    			}


    			// push to the pre-sorted opaque render list
    			renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null );

    		}

    	}

    	function setClear( color, alpha ) {

    		state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha );

    	}

    	return {

    		getClearColor: function () {

    			return clearColor;

    		},
    		setClearColor: function ( color, alpha = 1 ) {

    			clearColor.set( color );
    			clearAlpha = alpha;
    			setClear( clearColor, clearAlpha );

    		},
    		getClearAlpha: function () {

    			return clearAlpha;

    		},
    		setClearAlpha: function ( alpha ) {

    			clearAlpha = alpha;
    			setClear( clearColor, clearAlpha );

    		},
    		render: render

    	};

    }
Uniforms library for shared webgl shaders