Browse Source

Material: Add alpha to coverage define, add clipping & alpha test anti aliasing (#22172)

* add alpha-to-coverage define

* update clipping plane material to support alpha to coverage

* update cliping examples

* add early discard

* make standard material work

* update stencil example

* fix alphaToCoverage

* Add support for alphamaps

* remove a2c from stencil example, add condition so it works in WebGLPrograms

* add a2c to another example

* docs update, enable derivatives

* Update other materials

* screenshot update

* another screenshot update

* screenshot update

* Revert example change

* WebGLNodeBuilder: Fix compilation order from text replace

---------

Co-authored-by: sunag <[email protected]>
Garrett Johnson 1 year ago
parent
commit
6b31f885a9

+ 3 - 2
docs/api/en/materials/Material.html

@@ -45,8 +45,9 @@
 		<h3>[property:Boolean alphaToCoverage]</h3>
 		<h3>[property:Boolean alphaToCoverage]</h3>
 		<p>
 		<p>
 			Enables alpha to coverage. Can only be used with MSAA-enabled contexts
 			Enables alpha to coverage. Can only be used with MSAA-enabled contexts
-			(meaning when the renderer was created with `antialias` parameter set to
-			`true`). Default is `false`.
+			(meaning when the renderer was created with *antialias* parameter set to
+			`true`). Enabling this will smooth aliasing on clip plane edges and alphaTest-clipped edges.
+			Default is `false`.
 		</p>
 		</p>
 
 
 		<h3>[property:Float blendAlpha]</h3>
 		<h3>[property:Float blendAlpha]</h3>

+ 4 - 2
docs/api/zh/materials/Material.html

@@ -35,8 +35,10 @@
 
 
 <h3>[property:Boolean alphaToCoverage]</h3>
 <h3>[property:Boolean alphaToCoverage]</h3>
 <p>
 <p>
-启用alpha to coverage. 只能在开启了MSAA的渲染环境中使用 (当渲染器创建的时候*antialias* 属性要*true*才能使用).
-默认为 *false*.
+	Enables alpha to coverage. Can only be used with MSAA-enabled contexts
+	(meaning when the renderer was created with *antialias* parameter set to
+	`true`). Enabling this will smooth aliasing on clip plane edges and alphaTest-clipped edges.
+	Default is `false`.
 </p>
 </p>
 
 
 <h3>[property:Integer blendDst]</h3>
 <h3>[property:Integer blendDst]</h3>

+ 1 - 1
examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js

@@ -102,7 +102,7 @@ class WebGLNodeBuilder extends NodeBuilder {
 		this.addSlot( 'fragment', new SlotNode( {
 		this.addSlot( 'fragment', new SlotNode( {
 			node: normalView,
 			node: normalView,
 			nodeType: 'vec3',
 			nodeType: 'vec3',
-			source: getIncludeSnippet( 'clipping_planes_fragment' ),
+			source: 'void main() {',
 			target: 'vec3 TransformedNormalView = %RESULT%;',
 			target: 'vec3 TransformedNormalView = %RESULT%;',
 			inclusionType: 'append'
 			inclusionType: 'append'
 		} ) );
 		} ) );

BIN
examples/screenshots/webgl_shadowmap_pointlight.jpg


+ 16 - 3
examples/webgl_clipping.html

@@ -82,7 +82,9 @@
 
 
 					// ***** Clipping setup (material): *****
 					// ***** Clipping setup (material): *****
 					clippingPlanes: [ localPlane ],
 					clippingPlanes: [ localPlane ],
-					clipShadows: true
+					clipShadows: true,
+
+					alphaToCoverage: true,
 
 
 				} );
 				} );
 
 
@@ -108,7 +110,7 @@
 
 
 				// Renderer
 				// Renderer
 
 
-				renderer = new THREE.WebGLRenderer();
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
 				renderer.shadowMap.enabled = true;
 				renderer.shadowMap.enabled = true;
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setSize( window.innerWidth, window.innerHeight );
@@ -130,6 +132,9 @@
 				// GUI
 				// GUI
 
 
 				const gui = new GUI(),
 				const gui = new GUI(),
+					props = {
+						alphaToCoverage: true,
+					},
 					folderLocal = gui.addFolder( 'Local Clipping' ),
 					folderLocal = gui.addFolder( 'Local Clipping' ),
 					propsLocal = {
 					propsLocal = {
 
 
@@ -167,7 +172,6 @@
 						}
 						}
 
 
 					},
 					},
-
 					folderGlobal = gui.addFolder( 'Global Clipping' ),
 					folderGlobal = gui.addFolder( 'Global Clipping' ),
 					propsGlobal = {
 					propsGlobal = {
 
 
@@ -195,6 +199,15 @@
 
 
 					};
 					};
 
 
+				gui.add( props, 'alphaToCoverage' ).onChange( function ( value ) {
+
+					ground.material.alphaToCoverage = value;
+					ground.material.needsUpdate = true;
+
+					material.alphaToCoverage = value;
+					material.needsUpdate = true;
+
+				} );
 				folderLocal.add( propsLocal, 'Enabled' );
 				folderLocal.add( propsLocal, 'Enabled' );
 				folderLocal.add( propsLocal, 'Shadows' );
 				folderLocal.add( propsLocal, 'Shadows' );
 				folderLocal.add( propsLocal, 'Plane', 0.3, 1.25 );
 				folderLocal.add( propsLocal, 'Plane', 0.3, 1.25 );

+ 18 - 3
examples/webgl_clipping_intersection.html

@@ -29,7 +29,8 @@
 			const params = {
 			const params = {
 				clipIntersection: true,
 				clipIntersection: true,
 				planeConstant: 0,
 				planeConstant: 0,
-				showHelpers: false
+				showHelpers: false,
+				alphaToCoverage: true,
 			};
 			};
 
 
 			const clipPlanes = [
 			const clipPlanes = [
@@ -73,12 +74,13 @@
 
 
 					const geometry = new THREE.SphereGeometry( i / 30, 48, 24 );
 					const geometry = new THREE.SphereGeometry( i / 30, 48, 24 );
 
 
-					const material = new THREE.MeshLambertMaterial( {
+					const material = new THREE.MeshPhongMaterial( {
 
 
 						color: new THREE.Color().setHSL( Math.random(), 0.5, 0.5, THREE.SRGBColorSpace ),
 						color: new THREE.Color().setHSL( Math.random(), 0.5, 0.5, THREE.SRGBColorSpace ),
 						side: THREE.DoubleSide,
 						side: THREE.DoubleSide,
 						clippingPlanes: clipPlanes,
 						clippingPlanes: clipPlanes,
-						clipIntersection: params.clipIntersection
+						clipIntersection: params.clipIntersection,
+						alphaToCoverage: true,
 
 
 					} );
 					} );
 
 
@@ -101,6 +103,19 @@
 
 
 				const gui = new GUI();
 				const gui = new GUI();
 
 
+				gui.add( params, 'alphaToCoverage' ).onChange( function ( value ) {
+
+					group.children.forEach( c => {
+
+						c.material.alphaToCoverage = Boolean( value );
+						c.material.needsUpdate = true;
+
+					} );
+
+					render();
+
+				} );
+
 				gui.add( params, 'clipIntersection' ).name( 'clip intersection' ).onChange( function ( value ) {
 				gui.add( params, 'clipIntersection' ).name( 'clip intersection' ).onChange( function ( value ) {
 
 
 					const children = group.children;
 					const children = group.children;

+ 9 - 0
src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl.js

@@ -1,7 +1,16 @@
 export default /* glsl */`
 export default /* glsl */`
 #ifdef USE_ALPHATEST
 #ifdef USE_ALPHATEST
 
 
+	#ifdef ALPHA_TO_COVERAGE
+
+	diffuseColor.a = smoothstep( alphaTest, alphaTest + fwidth( diffuseColor.a ), diffuseColor.a );
+	if ( diffuseColor.a == 0.0 ) discard;
+
+	#else
+
 	if ( diffuseColor.a < alphaTest ) discard;
 	if ( diffuseColor.a < alphaTest ) discard;
 
 
+	#endif
+
 #endif
 #endif
 `;
 `;

+ 56 - 11
src/renderers/shaders/ShaderChunk/clipping_planes_fragment.glsl.js

@@ -3,29 +3,74 @@ export default /* glsl */`
 
 
 	vec4 plane;
 	vec4 plane;
 
 
-	#pragma unroll_loop_start
-	for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {
+	#ifdef ALPHA_TO_COVERAGE
 
 
-		plane = clippingPlanes[ i ];
-		if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;
+		float distanceToPlane, distanceGradient;
+		float clipOpacity = 1.0;
 
 
-	}
-	#pragma unroll_loop_end
+		#pragma unroll_loop_start
+		for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {
+
+			plane = clippingPlanes[ i ];
+			distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w;
+			distanceGradient = fwidth( distanceToPlane ) / 2.0;
+			clipOpacity *= smoothstep( - distanceGradient, distanceGradient, distanceToPlane );
+
+			if ( clipOpacity == 0.0 ) discard;
+
+		}
+		#pragma unroll_loop_end
+
+		#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES
+
+			float unionClipOpacity = 1.0;
+
+			#pragma unroll_loop_start
+			for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {
+
+				plane = clippingPlanes[ i ];
+				distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w;
+				distanceGradient = fwidth( distanceToPlane ) / 2.0;
+				unionClipOpacity *= 1.0 - smoothstep( - distanceGradient, distanceGradient, distanceToPlane );
 
 
-	#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES
+			}
+			#pragma unroll_loop_end
 
 
-		bool clipped = true;
+			clipOpacity *= 1.0 - unionClipOpacity;
+
+		#endif
+
+		diffuseColor.a *= clipOpacity;
+
+		if ( diffuseColor.a == 0.0 ) discard;
+
+	#else
 
 
 		#pragma unroll_loop_start
 		#pragma unroll_loop_start
-		for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {
+		for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {
 
 
 			plane = clippingPlanes[ i ];
 			plane = clippingPlanes[ i ];
-			clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;
+			if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;
 
 
 		}
 		}
 		#pragma unroll_loop_end
 		#pragma unroll_loop_end
 
 
-		if ( clipped ) discard;
+		#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES
+
+			bool clipped = true;
+
+			#pragma unroll_loop_start
+			for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {
+
+				plane = clippingPlanes[ i ];
+				clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;
+
+			}
+			#pragma unroll_loop_end
+
+			if ( clipped ) discard;
+
+		#endif
 
 
 	#endif
 	#endif
 
 

+ 1 - 2
src/renderers/shaders/ShaderLib/depth.glsl.js

@@ -62,9 +62,8 @@ varying vec2 vHighPrecisionZW;
 
 
 void main() {
 void main() {
 
 
-	#include <clipping_planes_fragment>
-
 	vec4 diffuseColor = vec4( 1.0 );
 	vec4 diffuseColor = vec4( 1.0 );
+	#include <clipping_planes_fragment>
 
 
 	#if DEPTH_PACKING == 3200
 	#if DEPTH_PACKING == 3200
 
 

+ 1 - 2
src/renderers/shaders/ShaderLib/distanceRGBA.glsl.js

@@ -58,9 +58,8 @@ varying vec3 vWorldPosition;
 
 
 void main () {
 void main () {
 
 
-	#include <clipping_planes_fragment>
-
 	vec4 diffuseColor = vec4( 1.0 );
 	vec4 diffuseColor = vec4( 1.0 );
+	#include <clipping_planes_fragment>
 
 
 	#include <map_fragment>
 	#include <map_fragment>
 	#include <alphamap_fragment>
 	#include <alphamap_fragment>

+ 1 - 1
src/renderers/shaders/ShaderLib/linedashed.glsl.js

@@ -48,6 +48,7 @@ varying float vLineDistance;
 
 
 void main() {
 void main() {
 
 
+	vec4 diffuseColor = vec4( diffuse, opacity );
 	#include <clipping_planes_fragment>
 	#include <clipping_planes_fragment>
 
 
 	if ( mod( vLineDistance, totalSize ) > dashSize ) {
 	if ( mod( vLineDistance, totalSize ) > dashSize ) {
@@ -57,7 +58,6 @@ void main() {
 	}
 	}
 
 
 	vec3 outgoingLight = vec3( 0.0 );
 	vec3 outgoingLight = vec3( 0.0 );
-	vec4 diffuseColor = vec4( diffuse, opacity );
 
 
 	#include <logdepthbuf_fragment>
 	#include <logdepthbuf_fragment>
 	#include <map_fragment>
 	#include <map_fragment>

+ 1 - 2
src/renderers/shaders/ShaderLib/meshbasic.glsl.js

@@ -70,9 +70,8 @@ uniform float opacity;
 
 
 void main() {
 void main() {
 
 
-	#include <clipping_planes_fragment>
-
 	vec4 diffuseColor = vec4( diffuse, opacity );
 	vec4 diffuseColor = vec4( diffuse, opacity );
+	#include <clipping_planes_fragment>
 
 
 	#include <logdepthbuf_fragment>
 	#include <logdepthbuf_fragment>
 	#include <map_fragment>
 	#include <map_fragment>

+ 1 - 1
src/renderers/shaders/ShaderLib/meshlambert.glsl.js

@@ -84,9 +84,9 @@ uniform float opacity;
 
 
 void main() {
 void main() {
 
 
+	vec4 diffuseColor = vec4( diffuse, opacity );
 	#include <clipping_planes_fragment>
 	#include <clipping_planes_fragment>
 
 
-	vec4 diffuseColor = vec4( diffuse, opacity );
 	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
 	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
 	vec3 totalEmissiveRadiance = emissive;
 	vec3 totalEmissiveRadiance = emissive;
 
 

+ 1 - 2
src/renderers/shaders/ShaderLib/meshmatcap.glsl.js

@@ -71,9 +71,8 @@ varying vec3 vViewPosition;
 
 
 void main() {
 void main() {
 
 
-	#include <clipping_planes_fragment>
-
 	vec4 diffuseColor = vec4( diffuse, opacity );
 	vec4 diffuseColor = vec4( diffuse, opacity );
+	#include <clipping_planes_fragment>
 
 
 	#include <logdepthbuf_fragment>
 	#include <logdepthbuf_fragment>
 	#include <map_fragment>
 	#include <map_fragment>

+ 3 - 1
src/renderers/shaders/ShaderLib/meshnormal.glsl.js

@@ -67,12 +67,14 @@ uniform float opacity;
 
 
 void main() {
 void main() {
 
 
+	vec4 diffuseColor = vec4( 0.0, 0.0, 0.0, opacity );
+
 	#include <clipping_planes_fragment>
 	#include <clipping_planes_fragment>
 	#include <logdepthbuf_fragment>
 	#include <logdepthbuf_fragment>
 	#include <normal_fragment_begin>
 	#include <normal_fragment_begin>
 	#include <normal_fragment_maps>
 	#include <normal_fragment_maps>
 
 
-	gl_FragColor = vec4( packNormalToRGB( normal ), opacity );
+	gl_FragColor = vec4( packNormalToRGB( normal ), diffuseColor.a );
 
 
 	#ifdef OPAQUE
 	#ifdef OPAQUE
 
 

+ 1 - 1
src/renderers/shaders/ShaderLib/meshphong.glsl.js

@@ -86,9 +86,9 @@ uniform float opacity;
 
 
 void main() {
 void main() {
 
 
+	vec4 diffuseColor = vec4( diffuse, opacity );
 	#include <clipping_planes_fragment>
 	#include <clipping_planes_fragment>
 
 
-	vec4 diffuseColor = vec4( diffuse, opacity );
 	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
 	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
 	vec3 totalEmissiveRadiance = emissive;
 	vec3 totalEmissiveRadiance = emissive;
 
 

+ 1 - 1
src/renderers/shaders/ShaderLib/meshphysical.glsl.js

@@ -157,9 +157,9 @@ varying vec3 vViewPosition;
 
 
 void main() {
 void main() {
 
 
+	vec4 diffuseColor = vec4( diffuse, opacity );
 	#include <clipping_planes_fragment>
 	#include <clipping_planes_fragment>
 
 
-	vec4 diffuseColor = vec4( diffuse, opacity );
 	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
 	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
 	vec3 totalEmissiveRadiance = emissive;
 	vec3 totalEmissiveRadiance = emissive;
 
 

+ 1 - 1
src/renderers/shaders/ShaderLib/meshtoon.glsl.js

@@ -80,9 +80,9 @@ uniform float opacity;
 
 
 void main() {
 void main() {
 
 
+	vec4 diffuseColor = vec4( diffuse, opacity );
 	#include <clipping_planes_fragment>
 	#include <clipping_planes_fragment>
 
 
-	vec4 diffuseColor = vec4( diffuse, opacity );
 	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
 	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
 	vec3 totalEmissiveRadiance = emissive;
 	vec3 totalEmissiveRadiance = emissive;
 
 

+ 1 - 1
src/renderers/shaders/ShaderLib/points.glsl.js

@@ -63,10 +63,10 @@ uniform float opacity;
 
 
 void main() {
 void main() {
 
 
+	vec4 diffuseColor = vec4( diffuse, opacity );
 	#include <clipping_planes_fragment>
 	#include <clipping_planes_fragment>
 
 
 	vec3 outgoingLight = vec3( 0.0 );
 	vec3 outgoingLight = vec3( 0.0 );
-	vec4 diffuseColor = vec4( diffuse, opacity );
 
 
 	#include <logdepthbuf_fragment>
 	#include <logdepthbuf_fragment>
 	#include <map_particle_fragment>
 	#include <map_particle_fragment>

+ 1 - 1
src/renderers/shaders/ShaderLib/sprite.glsl.js

@@ -59,10 +59,10 @@ uniform float opacity;
 
 
 void main() {
 void main() {
 
 
+	vec4 diffuseColor = vec4( diffuse, opacity );
 	#include <clipping_planes_fragment>
 	#include <clipping_planes_fragment>
 
 
 	vec3 outgoingLight = vec3( 0.0 );
 	vec3 outgoingLight = vec3( 0.0 );
-	vec4 diffuseColor = vec4( diffuse, opacity );
 
 
 	#include <logdepthbuf_fragment>
 	#include <logdepthbuf_fragment>
 	#include <map_fragment>
 	#include <map_fragment>

+ 2 - 1
src/renderers/webgl/WebGLProgram.js

@@ -141,7 +141,7 @@ function getToneMappingFunction( functionName, toneMapping ) {
 function generateExtensions( parameters ) {
 function generateExtensions( parameters ) {
 
 
 	const chunks = [
 	const chunks = [
-		( parameters.extensionDerivatives || !! parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.normalMapTangentSpace || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '',
+		( parameters.extensionDerivatives || !! parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.normalMapTangentSpace || parameters.clearcoatNormalMap || parameters.flatShading || parameters.alphaToCoverage || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '',
 		( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '',
 		( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '',
 		( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '',
 		( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '',
 		( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : ''
 		( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : ''
@@ -765,6 +765,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
 			parameters.useFog && parameters.fog ? '#define USE_FOG' : '',
 			parameters.useFog && parameters.fog ? '#define USE_FOG' : '',
 			parameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '',
 			parameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '',
 
 
+			parameters.alphaToCoverage ? '#define ALPHA_TO_COVERAGE' : '',
 			parameters.map ? '#define USE_MAP' : '',
 			parameters.map ? '#define USE_MAP' : '',
 			parameters.matcap ? '#define USE_MATCAP' : '',
 			parameters.matcap ? '#define USE_MATCAP' : '',
 			parameters.envMap ? '#define USE_ENVMAP' : '',
 			parameters.envMap ? '#define USE_ENVMAP' : '',

+ 4 - 1
src/renderers/webgl/WebGLPrograms.js

@@ -200,6 +200,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 
 
 			supportsVertexTextures: SUPPORTS_VERTEX_TEXTURES,
 			supportsVertexTextures: SUPPORTS_VERTEX_TEXTURES,
 			outputColorSpace: ( currentRenderTarget === null ) ? renderer.outputColorSpace : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ),
 			outputColorSpace: ( currentRenderTarget === null ) ? renderer.outputColorSpace : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ),
+			alphaToCoverage: !! material.alphaToCoverage,
 
 
 			map: HAS_MAP,
 			map: HAS_MAP,
 			matcap: HAS_MATCAP,
 			matcap: HAS_MATCAP,
@@ -245,7 +246,7 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 
 
 			gradientMap: HAS_GRADIENTMAP,
 			gradientMap: HAS_GRADIENTMAP,
 
 
-			opaque: material.transparent === false && material.blending === NormalBlending,
+			opaque: material.transparent === false && material.blending === NormalBlending && material.alphaToCoverage === false,
 
 
 			alphaMap: HAS_ALPHAMAP,
 			alphaMap: HAS_ALPHAMAP,
 			alphaTest: HAS_ALPHATEST,
 			alphaTest: HAS_ALPHATEST,
@@ -552,6 +553,8 @@ function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities
 			_programLayers.enable( 18 );
 			_programLayers.enable( 18 );
 		if ( parameters.decodeVideoTexture )
 		if ( parameters.decodeVideoTexture )
 			_programLayers.enable( 19 );
 			_programLayers.enable( 19 );
+		if ( parameters.alphaToCoverage )
+			_programLayers.enable( 20 );
 
 
 		array.push( _programLayers.mask );
 		array.push( _programLayers.mask );