Browse Source

WebGLDeferredRenderer: merged with @MPanknin's spotlights.

Todo:

- physically based specular
- wrap around lighting
- light cone proxy instead of fullscreen quad
- light distance attenuation
- move spot angle cos out of shader
- move light direction out of shader
- shadow maps

See #2624
alteredq 12 years ago
parent
commit
133895ce2f
2 changed files with 231 additions and 2 deletions
  1. 169 2
      examples/js/ShaderDeferred.js
  2. 62 0
      examples/js/renderers/WebGLDeferredRenderer.js

+ 169 - 2
examples/js/ShaderDeferred.js

@@ -483,7 +483,7 @@ THREE.ShaderDeferred = {
 
 				// simple specular
 
-				//"vec3 specular = specularIntensity * max( pow( dotNormalHalf, shininess ), 0.0 ) * diffuse;",
+				//"vec3 specular = max( pow( dotNormalHalf, shininess ), 0.0 ) * diffuse;",
 
 				// physically based specular
 
@@ -506,7 +506,6 @@ THREE.ShaderDeferred = {
 
 				"}",
 
-
 			"}"
 
 		].join("\n"),
@@ -531,6 +530,174 @@ THREE.ShaderDeferred = {
 
 	},
 
+	"spotLight" : {
+
+		uniforms: {
+
+			samplerNormalDepth: { type: "t", value: null },
+			samplerColor: 		{ type: "t", value: null },
+			matView: 		{ type: "m4", value: new THREE.Matrix4() },
+			matProjInverse: { type: "m4", value: new THREE.Matrix4() },
+			viewWidth: 		{ type: "f", value: 800 },
+			viewHeight: 	{ type: "f", value: 600 },
+			lightPositionWS:{ type: "v3", value: new THREE.Vector3( 0, 1, 0 ) },
+			lightTargetWS: 	{ type: "v3", value: new THREE.Vector3( 0, 1, 0 ) },
+			lightColor: 	{ type: "c", value: new THREE.Color( 0x000000 ) },
+			lightIntensity: { type: "f", value: 1.0 },
+			lightDistance: 	{ type: "f", value: 1.0 },
+			lightAngle: 	{ type: "f", value: 1.0 }
+
+		},
+
+		fragmentShader : [
+
+			"uniform sampler2D samplerColor;",
+			"uniform sampler2D samplerNormalDepth;",
+
+			"uniform float viewHeight;",
+			"uniform float viewWidth;",
+
+		    "uniform float lightAngle;"+
+			"uniform float lightIntensity;"+
+			"uniform vec3 lightColor;",
+
+			"uniform mat4 matProjInverse;",
+
+			"varying vec3 vLightPositionVS;",
+			"varying vec3 vLightDirectionVS;",
+
+			"vec3 float_to_vec3( float data ) {",
+
+				"vec3 uncompressed;",
+				"uncompressed.x = fract( data );",
+				"float zInt = floor( data / 255.0 );",
+				"uncompressed.z = fract( zInt / 255.0 );",
+				"uncompressed.y = fract( floor( data - ( zInt * 255.0 ) ) / 255.0 );",
+				"return uncompressed;",
+
+			"}",
+
+			"void main() {",
+
+				"vec2 texCoord = gl_FragCoord.xy / vec2( viewWidth, viewHeight );",
+
+				"vec4 normalDepth = texture2D( samplerNormalDepth, texCoord );",
+				"float z = normalDepth.w;",
+
+				"if ( z == 0.0 ) discard;",
+
+				"float x = texCoord.x * 2.0 - 1.0;",
+				"float y = texCoord.y * 2.0 - 1.0;",
+
+				"vec4 projectedPos = vec4( x, y, z, 1.0 );",
+				"vec4 positionVS = matProjInverse * projectedPos;",
+				"positionVS.xyz /= positionVS.w;",
+				"positionVS.w = 1.0;",
+
+				// normal
+
+				"vec3 normal = normalDepth.xyz * 2.0 - 1.0;",
+
+				// color
+
+				"vec4 colorMap = texture2D( samplerColor, texCoord );",
+
+				"vec3 albedo = float_to_vec3( abs( colorMap.x ) );",
+				"vec3 specularColor = float_to_vec3( abs( colorMap.y ) );",
+				"float shininess = abs( colorMap.z );",
+				"float wrapAround = sign( colorMap.z );",
+				"float additiveSpecular = sign( colorMap.y );",
+
+				//
+
+				"vec3 surfToLight = normalize( vLightPositionVS.xyz - positionVS.xyz );",
+				"float dotProduct = max( dot( normal, surfToLight ), 0.0 );",
+
+				"float rho = dot( vLightDirectionVS, -surfToLight );",
+				"float rhoMax = cos( lightAngle * 0.5 );",
+
+				"if ( rho > rhoMax ) {",
+
+					"float theta = rhoMax + 0.0001;",
+					"float phi = rhoMax + 0.05;",
+					"float falloff = 4.0;",
+
+					"float spot = 0.0;",
+
+					"if ( rho >= phi ) {",
+
+						"spot = 1.0;",
+
+					"} else if ( rho <= theta ) {",
+
+						"spot = 0.0;",
+
+					"} else { ",
+
+						"spot = pow( ( rho - theta ) / ( phi - theta ), falloff );",
+
+					"}",
+
+					"float diffuse = spot * dotProduct;",
+
+					"vec3 halfVector = normalize( surfToLight - normalize( positionVS.xyz ) );",
+					"float dotNormalHalf = max( dot( normal, halfVector ), 0.0 );",
+
+					// simple specular
+
+					"vec3 specular = max( pow( dotNormalHalf, shininess ), 0.0 ) * spot * diffuse * specularColor;",
+
+					// combine
+
+					"vec3 light = lightIntensity * lightColor;",
+
+					"if ( additiveSpecular < 0.0 ) {",
+
+						"gl_FragColor = vec4( light * ( albedo * diffuse + specular ), 1.0 );",
+
+					"} else {",
+
+						"gl_FragColor = vec4( light * albedo * ( diffuse + specular ), 1.0 );",
+
+					"}",
+
+					"return;",
+
+
+				"}",
+
+				"gl_FragColor = vec4( 0.0 );",
+
+			"}"
+
+		].join("\n"),
+
+		vertexShader : [
+
+			"uniform vec3 lightPositionWS;",
+			"uniform vec3 lightTargetWS;",
+
+			"uniform mat4 matView;",
+
+			"varying vec3 vLightPositionVS;",
+			"varying vec3 vLightDirectionVS;",
+
+			"void main() { ",
+
+				"vec4 quadPosition = vec4( sign( position.xy ), 0.0, 1.0 );",
+				"gl_Position = quadPosition;",
+
+				"vLightPositionVS = vec3( matView * vec4( lightPositionWS, 1.0 ) );",
+
+				"vec3 lightDirectionWS = lightTargetWS - lightPositionWS;"+
+				"vLightDirectionVS = normalize( mat3( matView ) * lightDirectionWS );",
+
+			"}"
+
+		].join("\n")
+
+	},
+
 	"directionalLight" : {
 
 		uniforms: {

+ 62 - 0
examples/js/renderers/WebGLDeferredRenderer.js

@@ -49,6 +49,7 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	var emissiveLightShader = THREE.ShaderDeferred[ "emissiveLight" ];
 	var pointLightShader = THREE.ShaderDeferred[ "pointLight" ];
+	var spotLightShader = THREE.ShaderDeferred[ "spotLight" ];
 	var directionalLightShader = THREE.ShaderDeferred[ "directionalLight" ];
 
 	var compositeShader = THREE.ShaderDeferred[ "composite" ];
@@ -379,6 +380,56 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 	};
 
+	var createDeferredSpotLight = function ( light ) {
+
+		// setup light material
+
+		var materialLight = new THREE.ShaderMaterial( {
+
+			uniforms:       THREE.UniformsUtils.clone( spotLightShader.uniforms ),
+			vertexShader:   spotLightShader.vertexShader,
+			fragmentShader: spotLightShader.fragmentShader,
+
+			blending:		THREE.AdditiveBlending,
+			depthWrite:		false,
+			depthTest:		false,
+			transparent:	true
+
+		} );
+
+		// linear space
+
+		var intensity = light.intensity * light.intensity;
+
+		materialLight.uniforms[ "lightPositionWS" ].value.copy( light.matrixWorld.getPosition() );
+		materialLight.uniforms[ "lightTargetWS" ].value.copy( light.target.matrixWorld.getPosition() );
+		materialLight.uniforms[ "lightIntensity" ].value = intensity;
+		materialLight.uniforms[ "lightAngle" ].value = light.angle;
+		materialLight.uniforms[ "lightDistance" ].value = light.distance;
+		materialLight.uniforms[ "lightColor" ].value.copyGammaToLinear( light.color );
+
+		materialLight.uniforms[ "viewWidth" ].value = scaledWidth;
+		materialLight.uniforms[ "viewHeight" ].value = scaledHeight;
+
+		materialLight.uniforms[ 'samplerColor' ].value = compColor.renderTarget2;
+		materialLight.uniforms[ 'samplerNormalDepth' ].value = compNormalDepth.renderTarget2;
+
+		// create light proxy mesh
+
+		var meshLight = new THREE.Mesh( geometryLightPlane, materialLight );
+
+		// keep reference for color and intensity updates
+
+		meshLight.properties.originalLight = light;
+
+		// keep reference for size reset
+
+		resizableMaterials.push( materialLight );
+
+		return meshLight;
+
+	};
+
 	var createDeferredDirectionalLight = function ( light ) {
 
 		// setup light material
@@ -479,6 +530,11 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 			}
 
+		} else if ( object instanceof THREE.SpotLight ) {
+
+			var meshLight = createDeferredSpotLight( object );
+			lightSceneFullscreen.add( meshLight );
+
 		} else if ( object instanceof THREE.DirectionalLight ) {
 
 			var meshLight = createDeferredDirectionalLight( object );
@@ -635,6 +691,12 @@ THREE.WebGLDeferredRenderer = function ( parameters ) {
 
 				}
 
+			} else if ( originalLight instanceof THREE.SpotLight ) {
+
+				uniforms[ "lightPositionWS" ].value.copy( originalLight.matrixWorld.getPosition() );
+				uniforms[ "lightTargetWS" ].value.copy( originalLight.target.matrixWorld.getPosition() );
+				uniforms[ "lightAngle" ].value = originalLight.angle;
+
 			}
 
 		}