// rd_LightWrap.jsx // Copyright (c) 2008-2013 Jeffrey R. Almasol. All rights reserved. // portfolio: www.redefinery.com // // Name: rd_LightWrap // Version: 1.2 // // Description: // This script creates the light wrap effect using the technique described in // the Light chapter of the book. In addition, it adds a Contamination and // Wrap Offset controls to the precomp layer for easier adjustment, and // ties the position, scale, and rotation properties from the foreground and // background layers to those in the precomp to synchronize any // transformations you decide to do. // // Prerequisites: // -- This script requires After Effects CS4 or later. // // Usage: // 1. Select a layer (foreground) that has at least one layer below it (background). // Note: Both layers must be 2D and not have their Position properties separated. // 2. Run this script. // 3. Use the Contamination and Wrap Offset effect controls that are created on the // Light Wrap precomp layer to adjust the light wrap effect. // // Legal Notices: // This script is provided "as is," without warranty of any kind, expressed or implied. // In no event shall the script's author be held liable for any damages arising in any // way from the use of this script. // // This script is excerpted from Adobe After Effects CC Visual Effects and Compositing Studio Techniques by Mark Christiansen. // (c) 2013. Published by Adobe Press. All rights reserved. A complete chapter on scripting // by Jeff Almasol is included with the book. Additional scripts are available at // http://aescripts.com/rd-studio-techniques/ (function rd_LightWrap() { // Globals // Store all constants in a global object, for consolidated organization in ExtendScript Toolkit's Data Browser var rd_LightWrapData = new Object(); rd_LightWrapData.scriptName = "rd: Light Wrap"; rd_LightWrapData.scriptTitle = rd_LightWrapData.scriptName + " v1.2"; // Various text strings are defined as associative arrays (dictionaries) to support localizability via rd_localize() function rd_LightWrapData.strLightWrapCompNamePrefix = {en: "Light Wrap: "}; rd_LightWrapData.strAdjLayerName = {en: "Adjustment Layer"}; rd_LightWrapData.strContamination = {en: "Contamination"}; rd_LightWrapData.strWrapOffset = {en: "Wrap Offset"}; rd_LightWrapData.strMarkerText = {en: "Adjust Contamination and Wrap Offset to change the light wrap"}; rd_LightWrapData.strErrNoCompSel = {en: "Cannot perform operation. Please select or open a single composition in the Project panel, and try again."}; rd_LightWrapData.strErrMoreThan1Selection = {en: "Select only one layer as the foreground, make sure there is a background layer below it, and try again."}; rd_LightWrapData.strErrNeed2DLayers = {en: "Make sure both the foreground and background layers are 2D, and do not have separated Position properties."}; rd_LightWrapData.strMinAE90 = {en: "This script requires Adobe After Effects CS4 or later."}; // rd_localize() // // Description: // This function localizes the given string variable based on the current locale. // // Parameters: // strVar - The string variable's name. // // Returns: // String. // function rd_localize(strVar) { return strVar["en"]; } // rd_LightWrap_main() // // Description: // This function performs the main operation of the script. // // Parameters: // None. // // Returns: // Nothing. // function rd_LightWrap_main() { // Check that a project exists if (app.project === null) return; // Get the current (active/frontmost) comp var comp = app.project.activeItem; if ((comp === null) || !(comp instanceof CompItem)) { alert(rd_localize(rd_LightWrapData.strErrNoCompSel), rd_LightWrapData.scriptName); return; } // Make sure only one layer is selected, and there is at least one layer below it if ((comp.selectedLayers.length !== 1) || (comp.selectedLayers[0].index >= comp.numLayers)) { alert(rd_localize(rd_LightWrapData.strErrMoreThan1Selection), rd_LightWrapData.scriptName); return; } var fgLayer = comp.selectedLayers[0]; var bgLayer = comp.layer(comp.selectedLayers[0].index+1); // .layer() is 1-based, selectedLayers is 0-based, so need to do +1 to get next layer // Do a pre-flight check to make sure both layers are 2D and their Position properties are not separated // (to make it easier to deal with expressions later on). if (fgLayer.threeDLayer || bgLayer.threeDLayer || fgLayer.position.dimensionsSeparated || bgLayer.position.dimensionsSeparated) { alert(rd_localize(rd_LightWrapData.strErrNeed2DLayers), rd_LightWrapData.scriptName); return; } app.beginUndoGroup(rd_LightWrapData.scriptName); // Step 1: Create a new composition that contains the background and foreground layers. var lightWrapComp = app.project.items.addComp(rd_localize(rd_LightWrapData.strLightWrapCompNamePrefix) + fgLayer.name, comp.width, comp.height, comp.pixelAspect, comp.duration, comp.frameRate); bgLayer.copyToComp(lightWrapComp); fgLayer.copyToComp(lightWrapComp); var lWfgLayer = lightWrapComp.layer(1); var lWbgLayer = lightWrapComp.layer(2); // Step 2: Set Silhouette Alpha blending mode for the foreground layer. lWfgLayer.blendingMode = BlendingMode.SILHOUETE_ALPHA; // yes, it's spelled incorrectly :-/ // Step 3: Add an adjustment layer at the top, and apply Fast Blur. var adjLayer = lightWrapComp.layers.addSolid([1,1,1], rd_localize(rd_LightWrapData.strAdjLayerName), lightWrapComp.width, lightWrapComp.height, lightWrapComp.pixelAspect, lightWrapComp.duration); adjLayer.adjustmentLayer = true; adjLayer.moveBefore(lWfgLayer); var fastBlurFX = adjLayer.property("ADBE Effect Parade").addProperty("ADBE Fast Blur"); // Step 4: In Fast Blur, check the Repeat Edge Pixels toggle on and crank up the blurriness. fastBlurFX.property("ADBE Fast Blur-0003").setValue(true); fastBlurFX.property("ADBE Fast Blur-0001").setValue(50); // Step 5: Duplicate the foreground layer, move the copy to the top, and set its blending mode // to Stencil Alpha. var stencilAlphaLayer = lWfgLayer.duplicate(); stencilAlphaLayer.moveToBeginning(); stencilAlphaLayer.blendingMode = BlendingMode.STENCIL_ALPHA; // Step 6: Place the resulting comp in the master comp (above the original foreground layer), and set blending mode to Add. var lightWrapPrecompLayer = comp.layers.add(lightWrapComp); lightWrapPrecompLayer.moveBefore(fgLayer); lightWrapPrecompLayer.startTime = 0; lightWrapPrecompLayer.blendingMode = BlendingMode.ADD; // Bonus Step: Add some expression sliders to the precomp to expose the Fast Blur effect's Blurriness, // a Wrap Offset for the silhouette alpha layer, and basic transforms for the foreground and background // layers. Add a layer marker to the prcomp layer // as a reminder. var slider = lightWrapPrecompLayer.property("ADBE Effect Parade").addProperty("ADBE Slider Control"); slider.name = rd_localize(rd_LightWrapData.strContamination); slider.property("ADBE Slider Control-0001").setValue(50); adjLayer.property("ADBE Effect Parade").property("ADBE Fast Blur").property("ADBE Fast Blur-0001").expression = "comp(\"" + comp.name + "\").layer(\"" + lightWrapPrecompLayer.name + "\").effect(\"" + rd_localize(rd_LightWrapData.strContamination) + "\")(\"ADBE Slider Control-0001\");"; var offset = lightWrapPrecompLayer.property("ADBE Effect Parade").addProperty("ADBE Point Control"); offset.name = rd_localize(rd_LightWrapData.strWrapOffset); offset.property("ADBE Point Control-0001").setValue([0,0]); stencilAlphaLayer.position.expression = "comp(\"" + comp.name + "\").layer(\"" + fgLayer.name + "\").transform.position;"; lWfgLayer.position.expression = "comp(\"" + comp.name + "\").layer(\"" + fgLayer.name + "\").transform.position + comp(\"" + comp.name + "\").layer(\"" + lightWrapPrecompLayer.name + "\").effect(\"" + rd_localize(rd_LightWrapData.strWrapOffset) + "\")(\"ADBE Point Control-0001\");"; stencilAlphaLayer.scale.expression = lWfgLayer.scale.expression = "comp(\"" + comp.name + "\").layer(\"" + fgLayer.name + "\").transform.scale;"; stencilAlphaLayer.rotation.expression = lWfgLayer.rotation.expression = "comp(\"" + comp.name + "\").layer(\"" + fgLayer.name + "\").transform.rotation;"; lWbgLayer.position.expression = "comp(\"" + comp.name + "\").layer(\"" + bgLayer.name + "\").transform.position;"; lWbgLayer.scale.expression = "comp(\"" + comp.name + "\").layer(\"" + bgLayer.name + "\").transform.scale;"; lWbgLayer.rotation.expression = "comp(\"" + comp.name + "\").layer(\"" + bgLayer.name + "\").transform.rotation;"; lightWrapPrecompLayer.property("Marker").setValueAtTime(lightWrapPrecompLayer.inPoint, new MarkerValue(rd_localize(rd_LightWrapData.strMarkerText))); app.endUndoGroup(); } // main code: // // Prerequisite check for After Effects CS4 or later if (parseFloat(app.version) < 9.0) alert(rd_localize(rd_LightWrapData.strMinAE90), rd_LightWrapData.scriptName); else rd_LightWrap_main(); })();