mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-12-25 12:23:49 +01:00
1209 lines
31 KiB
JavaScript
Executable File
1209 lines
31 KiB
JavaScript
Executable File
/**
|
|
* @author alteredq / http://alteredqualia.com/
|
|
* @author MPanknin / http://www.redplant.de/
|
|
*/
|
|
|
|
THREE.WebGLDeferredRenderer = function ( parameters ) {
|
|
|
|
var _this = this;
|
|
|
|
var pixelWidth = parameters.width !== undefined ? parameters.width : 800;
|
|
var pixelHeight = parameters.height !== undefined ? parameters.height : 600;
|
|
var currentScale = parameters.scale !== undefined ? parameters.scale : 1;
|
|
|
|
var scaledWidth = Math.floor( currentScale * pixelWidth );
|
|
var scaledHeight = Math.floor( currentScale * pixelHeight );
|
|
|
|
var brightness = parameters.brightness !== undefined ? parameters.brightness : 1;
|
|
var tonemapping = parameters.tonemapping !== undefined ? parameters.tonemapping : THREE.SimpleOperator;
|
|
var antialias = parameters.antialias !== undefined ? parameters.antialias : false;
|
|
|
|
this.renderer = parameters.renderer;
|
|
|
|
if ( this.renderer === undefined ) {
|
|
|
|
this.renderer = new THREE.WebGLRenderer( { antialias: false } );
|
|
this.renderer.setSize( pixelWidth, pixelHeight );
|
|
this.renderer.setClearColor( 0x000000, 0 );
|
|
|
|
this.renderer.autoClear = false;
|
|
|
|
}
|
|
|
|
this.domElement = this.renderer.domElement;
|
|
|
|
//
|
|
|
|
var gl = this.renderer.context;
|
|
|
|
//
|
|
|
|
var currentCamera = null;
|
|
var projectionMatrixInverse = new THREE.Matrix4();
|
|
|
|
var positionVS = new THREE.Vector3();
|
|
var directionVS = new THREE.Vector3();
|
|
var tempVS = new THREE.Vector3();
|
|
|
|
var rightVS = new THREE.Vector3();
|
|
var normalVS = new THREE.Vector3();
|
|
var upVS = new THREE.Vector3();
|
|
|
|
//
|
|
|
|
var geometryLightSphere = new THREE.SphereGeometry( 1, 16, 8 );
|
|
var geometryLightPlane = new THREE.PlaneBufferGeometry( 2, 2 );
|
|
|
|
var black = new THREE.Color( 0x000000 );
|
|
|
|
var colorShader = THREE.ShaderDeferred[ "color" ];
|
|
var normalDepthShader = THREE.ShaderDeferred[ "normalDepth" ];
|
|
|
|
//
|
|
|
|
var emissiveLightShader = THREE.ShaderDeferred[ "emissiveLight" ];
|
|
var pointLightShader = THREE.ShaderDeferred[ "pointLight" ];
|
|
var spotLightShader = THREE.ShaderDeferred[ "spotLight" ];
|
|
var directionalLightShader = THREE.ShaderDeferred[ "directionalLight" ];
|
|
var hemisphereLightShader = THREE.ShaderDeferred[ "hemisphereLight" ];
|
|
var areaLightShader = THREE.ShaderDeferred[ "areaLight" ];
|
|
|
|
var compositeShader = THREE.ShaderDeferred[ "composite" ];
|
|
|
|
//
|
|
|
|
var compColor, compNormal, compDepth, compLight, compFinal;
|
|
var passColor, passNormal, passDepth, passLightFullscreen, passLightProxy, compositePass;
|
|
|
|
var effectFXAA;
|
|
|
|
//
|
|
|
|
var lightSceneFullscreen, lightSceneProxy;
|
|
|
|
//
|
|
|
|
var resizableMaterials = [];
|
|
|
|
//
|
|
|
|
var invisibleMaterial = new THREE.ShaderMaterial();
|
|
invisibleMaterial.visible = false;
|
|
|
|
|
|
var defaultNormalDepthMaterial = new THREE.ShaderMaterial( {
|
|
|
|
uniforms: THREE.UniformsUtils.clone( normalDepthShader.uniforms ),
|
|
vertexShader: normalDepthShader.vertexShader,
|
|
fragmentShader: normalDepthShader.fragmentShader,
|
|
blending: THREE.NoBlending
|
|
|
|
} );
|
|
|
|
//
|
|
|
|
var initDeferredMaterials = function ( object ) {
|
|
|
|
if ( object.material instanceof THREE.MeshFaceMaterial ) {
|
|
|
|
var colorMaterials = [];
|
|
var normalDepthMaterials = [];
|
|
|
|
var materials = object.material.materials;
|
|
|
|
for ( var i = 0, il = materials.length; i < il; i ++ ) {
|
|
|
|
var deferredMaterials = createDeferredMaterials( materials[ i ] );
|
|
|
|
if ( deferredMaterials.transparent ) {
|
|
|
|
colorMaterials.push( invisibleMaterial );
|
|
normalDepthMaterials.push( invisibleMaterial );
|
|
|
|
} else {
|
|
|
|
colorMaterials.push( deferredMaterials.colorMaterial );
|
|
normalDepthMaterials.push( deferredMaterials.normalDepthMaterial );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
object.userData.colorMaterial = new THREE.MeshFaceMaterial( colorMaterials );
|
|
object.userData.normalDepthMaterial = new THREE.MeshFaceMaterial( normalDepthMaterials );
|
|
|
|
} else {
|
|
|
|
var deferredMaterials = createDeferredMaterials( object.material );
|
|
|
|
object.userData.colorMaterial = deferredMaterials.colorMaterial;
|
|
object.userData.normalDepthMaterial = deferredMaterials.normalDepthMaterial;
|
|
object.userData.transparent = deferredMaterials.transparent;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
var createDeferredMaterials = function ( originalMaterial ) {
|
|
|
|
var deferredMaterials = {};
|
|
|
|
// color material
|
|
// -----------------
|
|
// diffuse color
|
|
// specular color
|
|
// shininess
|
|
// diffuse map
|
|
// vertex colors
|
|
// alphaTest
|
|
// morphs
|
|
|
|
var uniforms = THREE.UniformsUtils.clone( colorShader.uniforms );
|
|
var defines = { "USE_MAP": !! originalMaterial.map, "USE_ENVMAP": !! originalMaterial.envMap, "GAMMA_INPUT": true };
|
|
|
|
var material = new THREE.ShaderMaterial( {
|
|
|
|
fragmentShader: colorShader.fragmentShader,
|
|
vertexShader: colorShader.vertexShader,
|
|
uniforms: uniforms,
|
|
defines: defines,
|
|
shading: originalMaterial.shading
|
|
|
|
} );
|
|
|
|
if ( originalMaterial instanceof THREE.MeshBasicMaterial ) {
|
|
|
|
var diffuse = black;
|
|
var emissive = originalMaterial.color;
|
|
|
|
} else {
|
|
|
|
var diffuse = originalMaterial.color;
|
|
var emissive = originalMaterial.emissive !== undefined ? originalMaterial.emissive : black;
|
|
|
|
}
|
|
|
|
var specular = originalMaterial.specular !== undefined ? originalMaterial.specular : black;
|
|
var shininess = originalMaterial.shininess !== undefined ? originalMaterial.shininess : 1;
|
|
var wrapAround = originalMaterial.wrapAround !== undefined ? ( originalMaterial.wrapAround ? -1 : 1 ) : 1;
|
|
var additiveSpecular = originalMaterial.metal !== undefined ? ( originalMaterial.metal ? 1 : -1 ) : -1;
|
|
|
|
uniforms.emissive.value.copyGammaToLinear( emissive );
|
|
uniforms.diffuse.value.copyGammaToLinear( diffuse );
|
|
uniforms.specular.value.copyGammaToLinear( specular );
|
|
uniforms.shininess.value = shininess;
|
|
uniforms.wrapAround.value = wrapAround;
|
|
uniforms.additiveSpecular.value = additiveSpecular;
|
|
|
|
uniforms.map.value = originalMaterial.map;
|
|
|
|
if ( originalMaterial.envMap ) {
|
|
|
|
uniforms.envMap.value = originalMaterial.envMap;
|
|
uniforms.useRefract.value = originalMaterial.envMap.mapping instanceof THREE.CubeRefractionMapping;
|
|
uniforms.refractionRatio.value = originalMaterial.refractionRatio;
|
|
uniforms.combine.value = originalMaterial.combine;
|
|
uniforms.reflectivity.value = originalMaterial.reflectivity;
|
|
uniforms.flipEnvMap.value = ( originalMaterial.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : -1;
|
|
|
|
uniforms.samplerNormalDepth.value = compNormalDepth.renderTarget2;
|
|
uniforms.viewWidth.value = scaledWidth;
|
|
uniforms.viewHeight.value = scaledHeight;
|
|
|
|
resizableMaterials.push( { "material": material } );
|
|
|
|
}
|
|
|
|
material.vertexColors = originalMaterial.vertexColors;
|
|
material.morphTargets = originalMaterial.morphTargets;
|
|
material.morphNormals = originalMaterial.morphNormals;
|
|
material.skinning = originalMaterial.skinning;
|
|
|
|
material.alphaTest = originalMaterial.alphaTest;
|
|
material.wireframe = originalMaterial.wireframe;
|
|
|
|
// uv repeat and offset setting priorities
|
|
// 1. color map
|
|
// 2. specular map
|
|
// 3. normal map
|
|
// 4. bump map
|
|
|
|
var uvScaleMap;
|
|
|
|
if ( originalMaterial.map ) {
|
|
|
|
uvScaleMap = originalMaterial.map;
|
|
|
|
} else if ( originalMaterial.specularMap ) {
|
|
|
|
uvScaleMap = originalMaterial.specularMap;
|
|
|
|
} else if ( originalMaterial.normalMap ) {
|
|
|
|
uvScaleMap = originalMaterial.normalMap;
|
|
|
|
} else if ( originalMaterial.bumpMap ) {
|
|
|
|
uvScaleMap = originalMaterial.bumpMap;
|
|
|
|
}
|
|
|
|
if ( uvScaleMap !== undefined ) {
|
|
|
|
var offset = uvScaleMap.offset;
|
|
var repeat = uvScaleMap.repeat;
|
|
|
|
uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
|
|
|
|
}
|
|
|
|
deferredMaterials.colorMaterial = material;
|
|
|
|
// normal + depth material
|
|
// -----------------
|
|
// vertex normals
|
|
// morph normals
|
|
// bump map
|
|
// bump scale
|
|
// clip depth
|
|
|
|
if ( originalMaterial.morphTargets || originalMaterial.skinning || originalMaterial.bumpMap ) {
|
|
|
|
var uniforms = THREE.UniformsUtils.clone( normalDepthShader.uniforms );
|
|
var defines = { "USE_BUMPMAP": !!originalMaterial.bumpMap };
|
|
|
|
var normalDepthMaterial = new THREE.ShaderMaterial( {
|
|
|
|
uniforms: uniforms,
|
|
vertexShader: normalDepthShader.vertexShader,
|
|
fragmentShader: normalDepthShader.fragmentShader,
|
|
shading: originalMaterial.shading,
|
|
defines: defines,
|
|
blending: THREE.NoBlending
|
|
|
|
} );
|
|
|
|
normalDepthMaterial.morphTargets = originalMaterial.morphTargets;
|
|
normalDepthMaterial.morphNormals = originalMaterial.morphNormals;
|
|
normalDepthMaterial.skinning = originalMaterial.skinning;
|
|
|
|
if ( originalMaterial.bumpMap ) {
|
|
|
|
uniforms.bumpMap.value = originalMaterial.bumpMap;
|
|
uniforms.bumpScale.value = originalMaterial.bumpScale;
|
|
|
|
var offset = originalMaterial.bumpMap.offset;
|
|
var repeat = originalMaterial.bumpMap.repeat;
|
|
|
|
uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var normalDepthMaterial = defaultNormalDepthMaterial.clone();
|
|
|
|
}
|
|
|
|
normalDepthMaterial.wireframe = originalMaterial.wireframe;
|
|
normalDepthMaterial.vertexColors = originalMaterial.vertexColors;
|
|
|
|
deferredMaterials.normalDepthMaterial = normalDepthMaterial;
|
|
|
|
//
|
|
|
|
deferredMaterials.transparent = originalMaterial.transparent;
|
|
|
|
return deferredMaterials;
|
|
|
|
};
|
|
|
|
var updatePointLightProxy = function ( lightProxy ) {
|
|
|
|
var light = lightProxy.userData.originalLight;
|
|
var uniforms = lightProxy.material.uniforms;
|
|
|
|
// skip infinite pointlights
|
|
// right now you can't switch between infinite and finite pointlights
|
|
// it's just too messy as they use different proxies
|
|
|
|
var distance = light.distance;
|
|
|
|
if ( distance > 0 ) {
|
|
|
|
lightProxy.scale.set( 1, 1, 1 ).multiplyScalar( distance );
|
|
uniforms[ "lightRadius" ].value = distance;
|
|
|
|
positionVS.setFromMatrixPosition( light.matrixWorld );
|
|
positionVS.applyMatrix4( currentCamera.matrixWorldInverse );
|
|
|
|
uniforms[ "lightPositionVS" ].value.copy( positionVS );
|
|
|
|
lightProxy.position.setFromMatrixPosition( light.matrixWorld );
|
|
|
|
} else {
|
|
|
|
uniforms[ "lightRadius" ].value = Infinity;
|
|
|
|
}
|
|
|
|
// linear space colors
|
|
|
|
var intensity = light.intensity * light.intensity;
|
|
|
|
uniforms[ "lightIntensity" ].value = intensity;
|
|
uniforms[ "lightColor" ].value.copyGammaToLinear( light.color );
|
|
|
|
};
|
|
|
|
var createDeferredPointLight = function ( light ) {
|
|
|
|
// setup light material
|
|
|
|
var materialLight = new THREE.ShaderMaterial( {
|
|
|
|
uniforms: THREE.UniformsUtils.clone( pointLightShader.uniforms ),
|
|
vertexShader: pointLightShader.vertexShader,
|
|
fragmentShader: pointLightShader.fragmentShader,
|
|
|
|
blending: THREE.AdditiveBlending,
|
|
depthWrite: false,
|
|
transparent: true,
|
|
|
|
side: THREE.BackSide
|
|
|
|
} );
|
|
|
|
// infinite pointlights use full-screen quad proxy
|
|
// regular pointlights use sphere proxy
|
|
|
|
var geometry;
|
|
|
|
if ( light.distance > 0 ) {
|
|
|
|
geometry = geometryLightSphere;
|
|
|
|
} else {
|
|
|
|
geometry = geometryLightPlane;
|
|
|
|
materialLight.depthTest = false;
|
|
materialLight.side = THREE.FrontSide;
|
|
|
|
}
|
|
|
|
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( geometry, materialLight );
|
|
|
|
// keep reference for color and intensity updates
|
|
|
|
meshLight.userData.originalLight = light;
|
|
|
|
// keep reference for size reset
|
|
|
|
resizableMaterials.push( { "material": materialLight } );
|
|
|
|
// sync proxy uniforms to the original light
|
|
|
|
updatePointLightProxy( meshLight );
|
|
|
|
return meshLight;
|
|
|
|
};
|
|
|
|
var updateSpotLightProxy = function ( lightProxy ) {
|
|
|
|
var light = lightProxy.userData.originalLight;
|
|
var uniforms = lightProxy.material.uniforms;
|
|
|
|
var viewMatrix = currentCamera.matrixWorldInverse;
|
|
var modelMatrix = light.matrixWorld;
|
|
|
|
positionVS.setFromMatrixPosition( modelMatrix );
|
|
positionVS.applyMatrix4( viewMatrix );
|
|
|
|
directionVS.setFromMatrixPosition( modelMatrix );
|
|
tempVS.setFromMatrixPosition( light.target.matrixWorld );
|
|
directionVS.sub( tempVS );
|
|
directionVS.normalize();
|
|
directionVS.transformDirection( viewMatrix );
|
|
|
|
uniforms[ "lightPositionVS" ].value.copy( positionVS );
|
|
uniforms[ "lightDirectionVS" ].value.copy( directionVS );
|
|
|
|
uniforms[ "lightAngle" ].value = light.angle;
|
|
uniforms[ "lightDistance" ].value = light.distance;
|
|
|
|
// linear space colors
|
|
|
|
var intensity = light.intensity * light.intensity;
|
|
|
|
uniforms[ "lightIntensity" ].value = intensity;
|
|
uniforms[ "lightColor" ].value.copyGammaToLinear( light.color );
|
|
|
|
};
|
|
|
|
var createDeferredSpotLight = function ( light ) {
|
|
|
|
// setup light material
|
|
|
|
var uniforms = THREE.UniformsUtils.clone( spotLightShader.uniforms );
|
|
|
|
var materialLight = new THREE.ShaderMaterial( {
|
|
|
|
uniforms: uniforms,
|
|
vertexShader: spotLightShader.vertexShader,
|
|
fragmentShader: spotLightShader.fragmentShader,
|
|
|
|
blending: THREE.AdditiveBlending,
|
|
depthWrite: false,
|
|
depthTest: false,
|
|
transparent: true
|
|
|
|
} );
|
|
|
|
uniforms[ "viewWidth" ].value = scaledWidth;
|
|
uniforms[ "viewHeight" ].value = scaledHeight;
|
|
|
|
uniforms[ 'samplerColor' ].value = compColor.renderTarget2;
|
|
uniforms[ 'samplerNormalDepth' ].value = compNormalDepth.renderTarget2;
|
|
|
|
// create light proxy mesh
|
|
|
|
var meshLight = new THREE.Mesh( geometryLightPlane, materialLight );
|
|
|
|
// keep reference for color and intensity updates
|
|
|
|
meshLight.userData.originalLight = light;
|
|
|
|
// keep reference for size reset
|
|
|
|
resizableMaterials.push( { "material": materialLight } );
|
|
|
|
// sync proxy uniforms to the original light
|
|
|
|
updateSpotLightProxy( meshLight );
|
|
|
|
return meshLight;
|
|
|
|
};
|
|
|
|
var updateDirectionalLightProxy = function ( lightProxy ) {
|
|
|
|
var light = lightProxy.userData.originalLight;
|
|
var uniforms = lightProxy.material.uniforms;
|
|
|
|
directionVS.setFromMatrixPosition( light.matrixWorld );
|
|
tempVS.setFromMatrixPosition( light.target.matrixWorld );
|
|
directionVS.sub( tempVS );
|
|
directionVS.normalize();
|
|
directionVS.transformDirection( currentCamera.matrixWorldInverse );
|
|
|
|
uniforms[ "lightDirectionVS" ].value.copy( directionVS );
|
|
|
|
// linear space colors
|
|
|
|
var intensity = light.intensity * light.intensity;
|
|
|
|
uniforms[ "lightIntensity" ].value = intensity;
|
|
uniforms[ "lightColor" ].value.copyGammaToLinear( light.color );
|
|
|
|
};
|
|
|
|
var createDeferredDirectionalLight = function ( light ) {
|
|
|
|
// setup light material
|
|
|
|
var uniforms = THREE.UniformsUtils.clone( directionalLightShader.uniforms );
|
|
|
|
var materialLight = new THREE.ShaderMaterial( {
|
|
|
|
uniforms: uniforms,
|
|
vertexShader: directionalLightShader.vertexShader,
|
|
fragmentShader: directionalLightShader.fragmentShader,
|
|
|
|
blending: THREE.AdditiveBlending,
|
|
depthWrite: false,
|
|
depthTest: false,
|
|
transparent: true
|
|
|
|
} );
|
|
|
|
uniforms[ "viewWidth" ].value = scaledWidth;
|
|
uniforms[ "viewHeight" ].value = scaledHeight;
|
|
|
|
uniforms[ 'samplerColor' ].value = compColor.renderTarget2;
|
|
uniforms[ 'samplerNormalDepth' ].value = compNormalDepth.renderTarget2;
|
|
|
|
// create light proxy mesh
|
|
|
|
var meshLight = new THREE.Mesh( geometryLightPlane, materialLight );
|
|
|
|
// keep reference for color and intensity updates
|
|
|
|
meshLight.userData.originalLight = light;
|
|
|
|
// keep reference for size reset
|
|
|
|
resizableMaterials.push( { "material": materialLight } );
|
|
|
|
// sync proxy uniforms to the original light
|
|
|
|
updateDirectionalLightProxy( meshLight );
|
|
|
|
return meshLight;
|
|
|
|
};
|
|
|
|
var updateHemisphereLightProxy = function ( lightProxy ) {
|
|
|
|
var light = lightProxy.userData.originalLight;
|
|
var uniforms = lightProxy.material.uniforms;
|
|
|
|
directionVS.setFromMatrixPosition( light.matrixWorld );
|
|
directionVS.normalize();
|
|
directionVS.transformDirection( currentCamera.matrixWorldInverse );
|
|
|
|
uniforms[ "lightDirectionVS" ].value.copy( directionVS );
|
|
|
|
// linear space colors
|
|
|
|
var intensity = light.intensity * light.intensity;
|
|
|
|
uniforms[ "lightIntensity" ].value = intensity;
|
|
uniforms[ "lightColorSky" ].value.copyGammaToLinear( light.color );
|
|
uniforms[ "lightColorGround" ].value.copyGammaToLinear( light.groundColor );
|
|
|
|
};
|
|
|
|
var createDeferredHemisphereLight = function ( light ) {
|
|
|
|
// setup light material
|
|
|
|
var uniforms = THREE.UniformsUtils.clone( hemisphereLightShader.uniforms );
|
|
|
|
var materialLight = new THREE.ShaderMaterial( {
|
|
|
|
uniforms: uniforms,
|
|
vertexShader: hemisphereLightShader.vertexShader,
|
|
fragmentShader: hemisphereLightShader.fragmentShader,
|
|
|
|
blending: THREE.AdditiveBlending,
|
|
depthWrite: false,
|
|
depthTest: false,
|
|
transparent: true
|
|
|
|
} );
|
|
|
|
uniforms[ "viewWidth" ].value = scaledWidth;
|
|
uniforms[ "viewHeight" ].value = scaledHeight;
|
|
|
|
uniforms[ 'samplerColor' ].value = compColor.renderTarget2;
|
|
uniforms[ 'samplerNormalDepth' ].value = compNormalDepth.renderTarget2;
|
|
|
|
// create light proxy mesh
|
|
|
|
var meshLight = new THREE.Mesh( geometryLightPlane, materialLight );
|
|
|
|
// keep reference for color and intensity updates
|
|
|
|
meshLight.userData.originalLight = light;
|
|
|
|
// keep reference for size reset
|
|
|
|
resizableMaterials.push( { "material": materialLight } );
|
|
|
|
// sync proxy uniforms to the original light
|
|
|
|
updateHemisphereLightProxy( meshLight );
|
|
|
|
return meshLight;
|
|
|
|
};
|
|
|
|
var updateAreaLightProxy = function ( lightProxy ) {
|
|
|
|
var light = lightProxy.userData.originalLight;
|
|
var uniforms = lightProxy.material.uniforms;
|
|
|
|
var modelMatrix = light.matrixWorld;
|
|
var viewMatrix = currentCamera.matrixWorldInverse;
|
|
|
|
positionVS.setFromMatrixPosition( modelMatrix );
|
|
positionVS.applyMatrix4( viewMatrix );
|
|
|
|
uniforms[ "lightPositionVS" ].value.copy( positionVS );
|
|
|
|
rightVS.copy( light.right );
|
|
rightVS.transformDirection( modelMatrix );
|
|
rightVS.transformDirection( viewMatrix );
|
|
|
|
normalVS.copy( light.normal );
|
|
normalVS.transformDirection( modelMatrix );
|
|
normalVS.transformDirection( viewMatrix );
|
|
|
|
upVS.crossVectors( rightVS, normalVS );
|
|
upVS.normalize();
|
|
|
|
uniforms[ "lightRightVS" ].value.copy( rightVS );
|
|
uniforms[ "lightNormalVS" ].value.copy( normalVS );
|
|
uniforms[ "lightUpVS" ].value.copy( upVS );
|
|
|
|
uniforms[ "lightWidth" ].value = light.width;
|
|
uniforms[ "lightHeight" ].value = light.height;
|
|
|
|
uniforms[ "constantAttenuation" ].value = light.constantAttenuation;
|
|
uniforms[ "linearAttenuation" ].value = light.linearAttenuation;
|
|
uniforms[ "quadraticAttenuation" ].value = light.quadraticAttenuation;
|
|
|
|
// linear space colors
|
|
|
|
var intensity = light.intensity * light.intensity;
|
|
|
|
uniforms[ "lightIntensity" ].value = intensity;
|
|
uniforms[ "lightColor" ].value.copyGammaToLinear( light.color );
|
|
|
|
};
|
|
|
|
var createDeferredAreaLight = function ( light ) {
|
|
|
|
// setup light material
|
|
|
|
var uniforms = THREE.UniformsUtils.clone( areaLightShader.uniforms );
|
|
|
|
var materialLight = new THREE.ShaderMaterial( {
|
|
|
|
uniforms: uniforms,
|
|
vertexShader: areaLightShader.vertexShader,
|
|
fragmentShader: areaLightShader.fragmentShader,
|
|
|
|
blending: THREE.AdditiveBlending,
|
|
depthWrite: false,
|
|
depthTest: false,
|
|
transparent: true
|
|
|
|
} );
|
|
|
|
uniforms[ "viewWidth" ].value = scaledWidth;
|
|
uniforms[ "viewHeight" ].value = scaledHeight;
|
|
|
|
uniforms[ 'samplerColor' ].value = compColor.renderTarget2;
|
|
uniforms[ 'samplerNormalDepth' ].value = compNormalDepth.renderTarget2;
|
|
|
|
// create light proxy mesh
|
|
|
|
var meshLight = new THREE.Mesh( geometryLightPlane, materialLight );
|
|
|
|
// keep reference for color and intensity updates
|
|
|
|
meshLight.userData.originalLight = light;
|
|
|
|
// keep reference for size reset
|
|
|
|
resizableMaterials.push( { "material": materialLight } );
|
|
|
|
// sync proxy uniforms to the original light
|
|
|
|
updateAreaLightProxy( meshLight );
|
|
|
|
return meshLight;
|
|
|
|
};
|
|
|
|
var createDeferredEmissiveLight = function () {
|
|
|
|
// setup light material
|
|
|
|
var materialLight = new THREE.ShaderMaterial( {
|
|
|
|
uniforms: THREE.UniformsUtils.clone( emissiveLightShader.uniforms ),
|
|
vertexShader: emissiveLightShader.vertexShader,
|
|
fragmentShader: emissiveLightShader.fragmentShader,
|
|
depthTest: false,
|
|
depthWrite: false,
|
|
blending: THREE.NoBlending
|
|
|
|
} );
|
|
|
|
materialLight.uniforms[ "viewWidth" ].value = scaledWidth;
|
|
materialLight.uniforms[ "viewHeight" ].value = scaledHeight;
|
|
|
|
materialLight.uniforms[ 'samplerColor' ].value = compColor.renderTarget2;
|
|
|
|
// create light proxy mesh
|
|
|
|
var meshLight = new THREE.Mesh( geometryLightPlane, materialLight );
|
|
|
|
// keep reference for size reset
|
|
|
|
resizableMaterials.push( { "material": materialLight } );
|
|
|
|
return meshLight;
|
|
|
|
};
|
|
|
|
var initDeferredProperties = function ( object ) {
|
|
|
|
if ( object.userData.deferredInitialized ) return;
|
|
|
|
if ( object.material ) initDeferredMaterials( object );
|
|
|
|
if ( object instanceof THREE.PointLight ) {
|
|
|
|
var proxy = createDeferredPointLight( object );
|
|
|
|
if ( object.distance > 0 ) {
|
|
|
|
lightSceneProxy.add( proxy );
|
|
|
|
} else {
|
|
|
|
lightSceneFullscreen.add( proxy );
|
|
|
|
}
|
|
|
|
} else if ( object instanceof THREE.SpotLight ) {
|
|
|
|
var proxy = createDeferredSpotLight( object );
|
|
lightSceneFullscreen.add( proxy );
|
|
|
|
} else if ( object instanceof THREE.DirectionalLight ) {
|
|
|
|
var proxy = createDeferredDirectionalLight( object );
|
|
lightSceneFullscreen.add( proxy );
|
|
|
|
} else if ( object instanceof THREE.HemisphereLight ) {
|
|
|
|
var proxy = createDeferredHemisphereLight( object );
|
|
lightSceneFullscreen.add( proxy );
|
|
|
|
} else if ( object instanceof THREE.AreaLight ) {
|
|
|
|
var proxy = createDeferredAreaLight( object );
|
|
lightSceneFullscreen.add( proxy );
|
|
|
|
}
|
|
|
|
object.userData.deferredInitialized = true;
|
|
|
|
};
|
|
|
|
//
|
|
|
|
var setMaterialColor = function ( object ) {
|
|
|
|
if ( object.material ) {
|
|
|
|
if ( object.userData.transparent ) {
|
|
|
|
object.material = invisibleMaterial;
|
|
|
|
} else {
|
|
|
|
object.material = object.userData.colorMaterial;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
var setMaterialNormalDepth = function ( object ) {
|
|
|
|
if ( object.material ) {
|
|
|
|
if ( object.userData.transparent ) {
|
|
|
|
object.material = invisibleMaterial;
|
|
|
|
} else {
|
|
|
|
object.material = object.userData.normalDepthMaterial;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// external API
|
|
|
|
this.setAntialias = function ( enabled ) {
|
|
|
|
antialias = enabled;
|
|
|
|
if ( antialias ) {
|
|
|
|
effectFXAA.enabled = true;
|
|
compositePass.renderToScreen = false;
|
|
|
|
} else {
|
|
|
|
effectFXAA.enabled = false;
|
|
compositePass.renderToScreen = true;
|
|
}
|
|
|
|
};
|
|
|
|
this.getAntialias = function () {
|
|
|
|
return antialias;
|
|
|
|
};
|
|
|
|
this.addEffect = function ( effect, normalDepthUniform, colorUniform ) {
|
|
|
|
if ( effect.material && effect.uniforms ) {
|
|
|
|
if ( normalDepthUniform ) effect.uniforms[ normalDepthUniform ].value = compNormalDepth.renderTarget2;
|
|
if ( colorUniform ) effect.uniforms[ colorUniform ].value = compColor.renderTarget2;
|
|
|
|
if ( normalDepthUniform || colorUniform ) {
|
|
|
|
resizableMaterials.push( { "material": effect.material, "normalDepth": normalDepthUniform, "color": colorUniform } );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
compFinal.insertPass( effect, -1 );
|
|
|
|
};
|
|
|
|
this.setScale = function ( scale ) {
|
|
|
|
currentScale = scale;
|
|
|
|
scaledWidth = Math.floor( currentScale * pixelWidth );
|
|
scaledHeight = Math.floor( currentScale * pixelHeight );
|
|
|
|
compNormalDepth.setSize( scaledWidth, scaledHeight );
|
|
compColor.setSize( scaledWidth, scaledHeight );
|
|
compLight.setSize( scaledWidth, scaledHeight );
|
|
compFinal.setSize( scaledWidth, scaledHeight );
|
|
|
|
compColor.renderTarget2.shareDepthFrom = compNormalDepth.renderTarget2;
|
|
compLight.renderTarget2.shareDepthFrom = compNormalDepth.renderTarget2;
|
|
|
|
for ( var i = 0, il = resizableMaterials.length; i < il; i ++ ) {
|
|
|
|
var materialEntry = resizableMaterials[ i ];
|
|
|
|
var material = materialEntry.material;
|
|
var uniforms = material.uniforms;
|
|
|
|
var colorLabel = materialEntry.color !== undefined ? materialEntry.color : 'samplerColor';
|
|
var normalDepthLabel = materialEntry.normalDepth !== undefined ? materialEntry.normalDepth : 'samplerNormalDepth';
|
|
|
|
if ( uniforms[ colorLabel ] ) uniforms[ colorLabel ].value = compColor.renderTarget2;
|
|
if ( uniforms[ normalDepthLabel ] ) uniforms[ normalDepthLabel ].value = compNormalDepth.renderTarget2;
|
|
|
|
if ( uniforms[ 'viewWidth' ] ) uniforms[ "viewWidth" ].value = scaledWidth;
|
|
if ( uniforms[ 'viewHeight' ] ) uniforms[ "viewHeight" ].value = scaledHeight;
|
|
|
|
}
|
|
|
|
compositePass.uniforms[ 'samplerLight' ].value = compLight.renderTarget2;
|
|
|
|
effectFXAA.uniforms[ 'resolution' ].value.set( 1 / pixelWidth, 1 / pixelHeight );
|
|
|
|
};
|
|
|
|
this.setSize = function ( width, height ) {
|
|
|
|
pixelWidth = width;
|
|
pixelHeight = height;
|
|
|
|
this.renderer.setSize( pixelWidth, pixelHeight );
|
|
|
|
this.setScale( currentScale );
|
|
|
|
};
|
|
|
|
//
|
|
|
|
function updateLightProxy ( proxy ) {
|
|
|
|
var uniforms = proxy.material.uniforms;
|
|
|
|
if ( uniforms[ "matProjInverse" ] ) uniforms[ "matProjInverse" ].value = projectionMatrixInverse;
|
|
if ( uniforms[ "matView" ] ) uniforms[ "matView" ].value = currentCamera.matrixWorldInverse;
|
|
|
|
var originalLight = proxy.userData.originalLight;
|
|
|
|
if ( originalLight ) {
|
|
|
|
proxy.visible = originalLight.visible;
|
|
|
|
if ( originalLight instanceof THREE.PointLight ) {
|
|
|
|
updatePointLightProxy( proxy );
|
|
|
|
} else if ( originalLight instanceof THREE.SpotLight ) {
|
|
|
|
updateSpotLightProxy( proxy );
|
|
|
|
} else if ( originalLight instanceof THREE.DirectionalLight ) {
|
|
|
|
updateDirectionalLightProxy( proxy );
|
|
|
|
} else if ( originalLight instanceof THREE.HemisphereLight ) {
|
|
|
|
updateHemisphereLightProxy( proxy );
|
|
|
|
} else if ( originalLight instanceof THREE.AreaLight ) {
|
|
|
|
updateAreaLightProxy( proxy );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
this.render = function ( scene, camera ) {
|
|
|
|
// setup deferred properties
|
|
|
|
if ( ! scene.userData.lightSceneProxy ) {
|
|
|
|
scene.userData.lightSceneProxy = new THREE.Scene();
|
|
scene.userData.lightSceneFullscreen = new THREE.Scene();
|
|
|
|
var meshLight = createDeferredEmissiveLight();
|
|
scene.userData.lightSceneFullscreen.add( meshLight );
|
|
|
|
}
|
|
|
|
currentCamera = camera;
|
|
|
|
lightSceneProxy = scene.userData.lightSceneProxy;
|
|
lightSceneFullscreen = scene.userData.lightSceneFullscreen;
|
|
|
|
passColor.camera = currentCamera;
|
|
passNormalDepth.camera = currentCamera;
|
|
passLightProxy.camera = currentCamera;
|
|
passLightFullscreen.camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 0, 1 );
|
|
|
|
passColor.scene = scene;
|
|
passNormalDepth.scene = scene;
|
|
passLightFullscreen.scene = lightSceneFullscreen;
|
|
passLightProxy.scene = lightSceneProxy;
|
|
|
|
scene.traverse( initDeferredProperties );
|
|
|
|
// update scene graph only once per frame
|
|
// (both color and normalDepth passes use exactly the same scene state)
|
|
|
|
scene.autoUpdate = false;
|
|
scene.updateMatrixWorld();
|
|
|
|
// 1) g-buffer normals + depth pass
|
|
|
|
scene.traverse( setMaterialNormalDepth );
|
|
|
|
// clear shared depth buffer
|
|
|
|
this.renderer.autoClearDepth = true;
|
|
this.renderer.autoClearStencil = true;
|
|
|
|
// write 1 to shared stencil buffer
|
|
// for non-background pixels
|
|
|
|
//gl.enable( gl.STENCIL_TEST );
|
|
gl.stencilOp( gl.REPLACE, gl.REPLACE, gl.REPLACE );
|
|
gl.stencilFunc( gl.ALWAYS, 1, 0xffffffff );
|
|
gl.clearStencil( 0 );
|
|
|
|
compNormalDepth.render();
|
|
|
|
// just touch foreground pixels (stencil == 1)
|
|
// both in color and light passes
|
|
|
|
gl.stencilFunc( gl.EQUAL, 1, 0xffffffff );
|
|
gl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP );
|
|
|
|
// 2) g-buffer color pass
|
|
|
|
scene.traverse( setMaterialColor );
|
|
|
|
// must use clean slate depth buffer
|
|
// otherwise there are z-fighting glitches
|
|
// not enough precision between two geometry passes
|
|
// just to use EQUAL depth test
|
|
|
|
this.renderer.autoClearDepth = true;
|
|
this.renderer.autoClearStencil = false;
|
|
|
|
compColor.render();
|
|
|
|
// 3) light pass
|
|
|
|
// do not clear depth buffer in this pass
|
|
// depth from geometry pass is used for light culling
|
|
// (write light proxy color pixel if behind scene pixel)
|
|
|
|
this.renderer.autoClearDepth = false;
|
|
|
|
scene.autoUpdate = true;
|
|
|
|
gl.depthFunc( gl.GEQUAL );
|
|
|
|
projectionMatrixInverse.getInverse( currentCamera.projectionMatrix );
|
|
|
|
for ( var i = 0, il = lightSceneProxy.children.length; i < il; i ++ ) {
|
|
|
|
var proxy = lightSceneProxy.children[ i ];
|
|
updateLightProxy( proxy );
|
|
|
|
}
|
|
|
|
for ( var i = 0, il = lightSceneFullscreen.children.length; i < il; i ++ ) {
|
|
|
|
var proxy = lightSceneFullscreen.children[ i ];
|
|
updateLightProxy( proxy );
|
|
|
|
}
|
|
|
|
compLight.render();
|
|
|
|
// 4) composite pass
|
|
|
|
// return back to usual depth and stencil handling state
|
|
|
|
this.renderer.autoClearDepth = true;
|
|
this.renderer.autoClearStencil = true;
|
|
gl.depthFunc( gl.LEQUAL );
|
|
gl.disable( gl.STENCIL_TEST );
|
|
|
|
compFinal.render( 0.1 );
|
|
|
|
};
|
|
|
|
//
|
|
|
|
var createRenderTargets = function ( ) {
|
|
|
|
var rtParamsFloatLinear = { minFilter: THREE.NearestFilter, magFilter: THREE.LinearFilter, stencilBuffer: true,
|
|
format: THREE.RGBAFormat, type: THREE.FloatType };
|
|
|
|
var rtParamsFloatNearest = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, stencilBuffer: true,
|
|
format: THREE.RGBAFormat, type: THREE.FloatType };
|
|
|
|
var rtParamsUByte = { minFilter: THREE.NearestFilter, magFilter: THREE.LinearFilter, stencilBuffer: false,
|
|
format: THREE.RGBFormat, type: THREE.UnsignedByteType };
|
|
|
|
// g-buffers
|
|
|
|
var rtColor = new THREE.WebGLRenderTarget( scaledWidth, scaledHeight, rtParamsFloatNearest );
|
|
var rtNormalDepth = new THREE.WebGLRenderTarget( scaledWidth, scaledHeight, rtParamsFloatNearest );
|
|
var rtLight = new THREE.WebGLRenderTarget( scaledWidth, scaledHeight, rtParamsFloatLinear );
|
|
var rtFinal = new THREE.WebGLRenderTarget( scaledWidth, scaledHeight, rtParamsUByte );
|
|
|
|
rtColor.generateMipmaps = false;
|
|
rtNormalDepth.generateMipmaps = false;
|
|
rtLight.generateMipmaps = false;
|
|
rtFinal.generateMipmaps = false;
|
|
|
|
// normal + depth composer
|
|
|
|
passNormalDepth = new THREE.RenderPass();
|
|
passNormalDepth.clear = true;
|
|
|
|
compNormalDepth = new THREE.EffectComposer( _this.renderer, rtNormalDepth );
|
|
compNormalDepth.addPass( passNormalDepth );
|
|
|
|
// color composer
|
|
|
|
passColor = new THREE.RenderPass();
|
|
passColor.clear = true;
|
|
|
|
compColor = new THREE.EffectComposer( _this.renderer, rtColor );
|
|
compColor.addPass( passColor );
|
|
|
|
compColor.renderTarget2.shareDepthFrom = compNormalDepth.renderTarget2;
|
|
|
|
// light composer
|
|
|
|
passLightFullscreen = new THREE.RenderPass();
|
|
passLightFullscreen.clear = true;
|
|
|
|
passLightProxy = new THREE.RenderPass();
|
|
passLightProxy.clear = false;
|
|
|
|
compLight = new THREE.EffectComposer( _this.renderer, rtLight );
|
|
compLight.addPass( passLightFullscreen );
|
|
compLight.addPass( passLightProxy );
|
|
|
|
compLight.renderTarget2.shareDepthFrom = compNormalDepth.renderTarget2;
|
|
|
|
// final composer
|
|
|
|
compositePass = new THREE.ShaderPass( compositeShader );
|
|
compositePass.uniforms[ 'samplerLight' ].value = compLight.renderTarget2;
|
|
compositePass.uniforms[ 'brightness' ].value = brightness;
|
|
compositePass.material.blending = THREE.NoBlending;
|
|
compositePass.clear = true;
|
|
|
|
var defines;
|
|
|
|
switch ( tonemapping ) {
|
|
|
|
case THREE.SimpleOperator: defines = { "TONEMAP_SIMPLE": true }; break;
|
|
case THREE.LinearOperator: defines = { "TONEMAP_LINEAR": true }; break;
|
|
case THREE.ReinhardOperator: defines = { "TONEMAP_REINHARD": true }; break;
|
|
case THREE.FilmicOperator: defines = { "TONEMAP_FILMIC": true }; break;
|
|
case THREE.UnchartedOperator: defines = { "TONEMAP_UNCHARTED": true }; break;
|
|
|
|
}
|
|
|
|
compositePass.material.defines = defines;
|
|
|
|
// FXAA
|
|
|
|
effectFXAA = new THREE.ShaderPass( THREE.FXAAShader );
|
|
effectFXAA.uniforms[ 'resolution' ].value.set( 1 / pixelWidth, 1 / pixelHeight );
|
|
effectFXAA.renderToScreen = true;
|
|
|
|
//
|
|
|
|
compFinal = new THREE.EffectComposer( _this.renderer, rtFinal );
|
|
compFinal.addPass( compositePass );
|
|
compFinal.addPass( effectFXAA );
|
|
|
|
if ( antialias ) {
|
|
|
|
effectFXAA.enabled = true;
|
|
compositePass.renderToScreen = false;
|
|
|
|
} else {
|
|
|
|
effectFXAA.enabled = false;
|
|
compositePass.renderToScreen = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// init
|
|
|
|
createRenderTargets();
|
|
|
|
};
|
|
|
|
// tonemapping operator types
|
|
|
|
THREE.NoOperator = 0;
|
|
THREE.SimpleOperator = 1;
|
|
THREE.LinearOperator = 2;
|
|
THREE.ReinhardOperator = 3;
|
|
THREE.FilmicOperator = 4;
|
|
THREE.UnchartedOperator = 5;
|