/* * File: VolumeLight_1_1.jsx * Author: Peter Torpey * www.petertorpey.com * Version: 1.1 12-28-05, 07-22-06 * Original code Copyright 2005-2006 Peter Torpey. * This code may be used, distributed, and modified for personal * use provided the name and URL of the original author, above, * remain intact. * * If this code is used in the creation of a notable production, * please notify the author. The author's e-mail address may be * found at http://www.petertorpey.com/contact.html . * * Host: Adobe After Effects Professional 6.5 and 7.0 * * * Dependencies: * None. * * * Installation: * Place this file in After Effects' "Script" folder or a subfolder. * On Windows, the default location is: * C:\Program Files\Adobe\After Effects 6.5\Support Files\Scripts\ * * * Description: * Creates and rigs effects to simulate volumetric lighting for any * selected Lights. * * * Usage: * Create a composition with a camera and one or more lights. * Select the lights to which you would like to apply the volumetric effect. * Run this script. * * Parameters: * None. * * * Notes: * This script and any scripts upon which it depends have been tested under * AE6.5 Pro on WinXP. * * Version 1.1 updates LightLayer detection for 7.0. * * * Known Issues: * None. * * * For the Future: * * */ /*** APPLICATION GLOBALS ***/ { /*** DEFAULTS ***/ var NAME_STR = "Volume Light"; /*** SCRIPT GLOBALS ***/ var myReadyState = true; var myBlackSolid = null; if (parseFloat(app.version) < 7.0) { function isLightLayer(light) { return (light && (light instanceof Layer) && light.property("coneAngle")); } } else { function isLightLayer(light) { return (light && (light instanceof LightLayer)); } } function addBlackSolid(ccomp) { var layer; if (!myBlackSolid) { for (i = app.project.numItems; i >= 1; i--) { var curItem = app.project.item(i); if (curItem.typeName == "Footage" && curItem.mainSource instanceof SolidSource && curItem.mainSource.color == [0, 0, 0] && curItem.width == ccomp.width && curItem.height == ccomp.height && curItem.pixelAspec == ccomp.pixelAspect) { myBlackSolid = curItem; break; } } } if (!myBlackSolid) { layer = ccomp.layers.addSolid([0, 0, 0], "Black Solid (Lights)", ccomp.width, ccomp.height, ccomp.pixelAspect, ccomp.duration); myBlackSolid = layer.source; layer.startTime = 0; layer.moveToBeginning(); } else { layer = ccomp.layers.add(myBlackSolid, ccomp.duration); layer.startTime = 0; layer.moveToBeginning(); } return layer; } function getLightPOINull(ccomp, light) { if (light.pointOfInterest.expressionEnabled) { // If the POI already has an expression, look for a layer that might be linked var exprLayer = light.pointOfInterest.expression.match("[(]\"(.*?)\"[)]"); if (exprLayer && ccomp.layer(exprLayer[1])) { // Assume that the found layer name is the position source (null) return ccomp.layer(exprLayer[1]); } else { // There's some sort of expression that we don't want to overwrite, so we'll wire our null backwards var poiNull = ccomp.layers.addNull(ccomp.duration); if (poiNull) { poiNull.moveBefore(light); poiNull.name = light.name + " POI"; poiNull.threeDLayer = true; poiNull.position.expression = "thisComp.layer(\"" + light.name + "\").pointOfInterest;"; poiNull.position.expressionEnabled = true; } return poiNull; } } else { // Add a null that controls the POI var poiNull = ccomp.layers.addNull(ccomp.duration); if (poiNull) { poiNull.moveBefore(light); poiNull.name = light.name + " POI"; poiNull.threeDLayer = true; poiNull.position.setValue(light.pointOfInterest.value); light.pointOfInterest.expression = "thisComp.layer(\"" + poiNull.name + "\").position;"; light.pointOfInterest.expressionEnabled = true; } return poiNull; } } function createFlare(light, poi, solid) { if (!isLightLayer(light)) throw "Invalid parameter 'light'"; if (!(poi && (poi instanceof AVLayer) && poi.threeDLayer)) throw "Invalid parameter 'poi'"; if (!(solid && (solid instanceof AVLayer))) throw "Invalid parameter 'solid'"; solid.name = light.name + " Flare"; solid.blendingMode = BlendingMode.ADD; solid.property("opacity").expression = "(thisComp.layer(\"" + light.name + "\").active) ? 100 : 0;"; solid.property("opacity").expressionEnabled = true; solid.shy = true; var effects = solid.property("ADBE Effect Parade"); if (effects.canAddProperty("ADBE Lens Flare")) { var cameraError = false; var flare = effects.addProperty("ADBE Lens Flare"); flare.property("ADBE Lens Flare-0004").setValue(3); flare.property("ADBE Lens Flare-0001").expression = "thisComp.layer(\"" + light.name + "\").toComp([0,0,0]);"; flare.property("ADBE Lens Flare-0001").expressionEnabled = true; try { flare.property("ADBE Lens Flare-0002").expression = "var cp = thisComp.activeCamera.position;\nvar lp = thisComp.layer(\"" + light.name + "\").position;\nvar pp = thisComp.layer(\"" + poi.name + "\").position;\nvar cv = normalize(sub(cp, lp));\nvar pv = normalize(sub(pp, lp));\nvar theta = Math.acos(dot(cv, pv));\nthisComp.layer(\"" + light.name + "\").intensity * (Math.PI / 2 - theta);"; } catch (e) { cameraError = true; } finally { flare.property("ADBE Lens Flare-0002").expressionEnabled = (!cameraError); } } } function createBeam(light, poi, solid) { if (!isLightLayer(light)) throw "Invalid parameter 'light'"; if (!(poi && (poi instanceof AVLayer) && poi.threeDLayer)) throw "Invalid parameter 'poi'"; if (!(solid && solid instanceof AVLayer)) throw "Invalid parameter 'solid'"; solid.name = light.name + " Beam"; solid.blendingMode = BlendingMode.SCREEN; solid.property("opacity").expression = "(thisComp.layer(\"" + light.name + "\").active) ? 100 : 0;"; solid.property("opacity").expressionEnabled = true; solid.shy = true; var effects = solid.property("ADBE Effect Parade"); if (effects.canAddProperty("ADBE Laser")) { var cameraError = false; var beam = effects.addProperty("ADBE Laser"); beam.property("ADBE Laser-0003").setValue(1); beam.property("ADBE Laser-0004").setValue(0.5); beam.property("ADBE Laser-0005").setValue(4); beam.property("ADBE Laser-0008").setValue([1.0, 1.0, 1.0, 1.0]); beam.property("ADBE Laser-0009").setValue([1.0, 1.0, 1.0, 1.0]); beam.property("ADBE Laser-0010").setValue(0); beam.property("ADBE Laser-0011").setValue(0); beam.property("ADBE Laser-0001").expression = "thisComp.layer(\"" + light.name + "\").toComp([0,0,0]);"; beam.property("ADBE Laser-0001").expressionEnabled = true; beam.property("ADBE Laser-0002").expression = "thisComp.layer(\"" + poi.name + "\").toComp([0,0,0]);"; beam.property("ADBE Laser-0002").expressionEnabled = true; try { beam.property("ADBE Laser-0006").expression = "var l = length(thisComp.layer(\"" + poi.name + "\").position, thisComp.layer(\"" + light.name + "\").position);\nvar r = l * Math.tan(degreesToRadians(thisComp.layer(\"" + light.name + "\").coneAngle) / 1.1);\nvar d = thisComp.activeCamera.zoom / length(thisComp.activeCamera.position, thisComp.layer(\"" + poi.name + "\").position);\nd * r * (1.0 - effect(\"" + beam.name + "\")(\"" + beam.property("ADBE Laser-0007").name + "\") / 200);"; } catch (e) { cameraError = true; } finally { beam.property("ADBE Laser-0006").expressionEnabled = (!cameraError); } beam.property("ADBE Laser-0007").expression = "thisComp.layer(\"" + light.name + "\").coneFeather;"; beam.property("ADBE Laser-0007").expressionEnabled = true; } if (effects.canAddProperty("ADBE Ramp")) { var ramp = effects.addProperty("ADBE Ramp"); ramp.property("ADBE Ramp-0004").setValue([0.0, 0.0, 0.0, 0.0]); ramp.property("ADBE Ramp-0005").setValue(2); ramp.property("ADBE Ramp-0006").setValue(0); ramp.property("ADBE Ramp-0007").setValue(0); ramp.property("ADBE Ramp-0001").expression = "thisComp.layer(\"" + light.name + "\").toComp([0,0,0]);"; ramp.property("ADBE Ramp-0001").expressionEnabled = true; ramp.property("ADBE Ramp-0002").expression = "thisComp.layer(\"" + light.name + "\").color * thisComp.layer(\"" + light.name + "\").intensity / 100;"; ramp.property("ADBE Ramp-0002").expressionEnabled = true; ramp.property("ADBE Ramp-0003").expression = "thisComp.layer(\"" + poi.name + "\").toComp([0,0,0]);"; ramp.property("ADBE Ramp-0003").expressionEnabled = true; } if (effects.canAddProperty("ADBE Fractal Noise")) { var noise = effects.addProperty("ADBE Fractal Noise"); noise.property("ADBE Fractal Noise-0001").setValue(1); noise.property("ADBE Fractal Noise-0002").setValue(3); noise.property("ADBE Fractal Noise-0003").setValue(0); noise.property("ADBE Fractal Noise-0004").setValue(100); noise.property("ADBE Fractal Noise-0005").setValue(0); noise.property("ADBE Fractal Noise-0006").setValue(1); noise.property("ADBE Fractal Noise-0008").setValue(0); noise.property("ADBE Fractal Noise-0009").setValue(1); noise.property("ADBE Fractal Noise-0010").setValue(100); noise.property("ADBE Fractal Noise-0011").setValue(100); noise.property("ADBE Fractal Noise-0012").setValue(100); noise.property("ADBE Fractal Noise-0031").setValue(0); noise.property("ADBE Fractal Noise-0015").setValue(6); noise.property("ADBE Fractal Noise-0017").setValue(70); noise.property("ADBE Fractal Noise-0018").setValue(56); noise.property("ADBE Fractal Noise-0019").setValue(0); noise.property("ADBE Fractal Noise-0020").setValue([0, 0]); noise.property("ADBE Fractal Noise-0021").setValue(0); noise.property("ADBE Fractal Noise-0023").setValue(0); noise.property("ADBE Fractal Noise-0025").setValue(0); noise.property("ADBE Fractal Noise-0026").setValue(1); noise.property("ADBE Fractal Noise-0027").setValue(0); noise.property("ADBE Fractal Noise-0029").setValue(15); noise.property("ADBE Fractal Noise-0030").setValue(7); noise.property("ADBE Fractal Noise-0013").expression = "thisComp.layer(\"" + light.name + "\").toComp([0,0,0]);"; noise.property("ADBE Fractal Noise-0013").expressionEnabled = true; } } function makeLightVolumetric(ccomp, light) { var nullPOI; var solid; app.beginUndoGroup(NAME_STR); nullPOI = getLightPOINull(ccomp, light); solid = addBlackSolid(ccomp); createBeam(light, nullPOI, solid); solid = addBlackSolid(ccomp); createFlare(light, nullPOI, solid); app.endUndoGroup(); } function main() { if (!myReadyState) { return -1; } var curItem = app.project.activeItem; var lightFound = false; if (curItem && (curItem instanceof CompItem)) { var curLayers = curItem.selectedLayers; if (curLayers.length > 0) { var curLayer; for (var i = 0; i < curLayers.length; i++) { curLayer = curLayers[i]; if (isLightLayer(curLayer)) { lightFound = true; try { makeLightVolumetric(curItem, curLayer); } catch (e) { alert(e); } } } if (lightFound && !curItem.activeCamera) { alert("The composition does not have an active camera.\nExpressions requiring a camera will be disabled.\nPlease add a camera before enabling these expressions."); } } } if (!lightFound) { alert("No Spot Light Layers are selected.\nPlease select a Spot Light Layer."); return 1; } return 0; } main(); }