エフェクト欄だけで完結する線ワイプのエクスプレッションです。
STEP
まずは画像にエフェクトを適用する。
エクスプレッション制御/スライダー制御
- 30%(線の太さ用。掛かってるか確認のため、ちょっとだけ数値を上げておく)
トランジション/リニアワイプ
- 変換終了:50%(掛かってるかの確認用)
描画/レーザー
- 長さ:100
- 柔らかさ:0
- 内側のカラー:白
- 外側のカラー:白
- 3D遠近法:チェックなし
- 元を合成:チェックあり
STEP
レーザー『開始点』『終了点』に以下のエクスプレッションをコピペ。
■開始点
//基準点と目標点と角度から、回転した目標点の位置を出す関数。
function mGetRotatedPt( aStdPt , aTgtPt , aRtn ){
var mLgtRX = aTgtPt[0] - aStdPt[0];
var mLgtRY = aTgtPt[1] - aStdPt[1];
var mLgtR = Math.sqrt( Math.pow( mLgtRX , 2 ) +Math.pow( mLgtRY , 2 ) );
var mROgl = Math.atan2 ( ( mLgtRY ) , ( mLgtRX ) );
var mRd = aRtn * ( Math.PI / 180 ) ;
var mPtR = [ Math.cos( mROgl + mRd ) * mLgtR , Math.sin( mROgl + mRd ) * mLgtR ];
return mPtR + aStdPt;
}
//点は4つ必要なので処理をまとめた関数。
function mMakeRotPt( aRtn ){
var mRtn = aRtn;
//0~360未満までを繰り返す回転。
var mRtnRoop = mRtn % 360;
//マイナス対策をする。
if( mRtnRoop < 0 ){ mRtnRoop = 360 + mRtnRoop; };
//90度ごとに増えるカウンター。
var mCnt = Math.floor( mRtnRoop / 90 );
//下記回転位置はそれぞれ180度区切りで2つに分かれるものが2つある状態で、
//カウンターを使って90度ごとに横軸上円(0~180)、縦軸右円(0~180)、
//横軸下円(180~360)、縦軸左円(180~360)用へと移り変わる角度を作る。
if( mCnt === 0 ){
var mNewRtn = linear(mRtnRoop , 0 , 90 , 0 , 180 );
}else if( mCnt === 2 ){
var mNewRtn = linear(mRtnRoop , 180 , 270 , 180 , 360 );
}else if( mCnt === 1 ){
var mNewRtn = linear(mRtnRoop , 90 , 180 , 0 , 180 );
}else if( mCnt === 3 ){
var mNewRtn = linear(mRtnRoop , 270 , 360 , 180 , 360 );
}
//カウンターを使って、角度が0~90と180~270の場合は横軸上円&下円の値を使い、
//90~180と270~360の場合は縦軸右円&左円を使うようにする。
if( mCnt === 0 || mCnt === 2 ){
var mStdPt = [thisLayer.width / 2 ,0 ];
var mTgtPt = [0 , 0];
var mRttPt = mGetRotatedPt( mStdPt , mTgtPt , mNewRtn );
//ターゲットが原点の横軸円を、180度区切りで下に移行するよう分ける。
if( mNewRtn >= 180 ){
mRttPt = mRttPt+[0,thisLayer.height];
}
}else{
var mStdPt = [0 , thisLayer.height / 2];
var mTgtPt = [0 , 0];
var mRttPt = mGetRotatedPt( mStdPt , mTgtPt , mNewRtn );
//ターゲットが原点の縦軸円を、180度までは右円になるよう分ける。
if( mNewRtn < 180 ){
mRttPt = mRttPt + [thisLayer.width,0];
}
}
return mRttPt;
}
var mRtn = effect("リニアワイプ")("ワイプ角度");
var mRotPt1 = mMakeRotPt( mRtn );
var mRotPt2 = mMakeRotPt( mRtn +90 );
var mRotPt3 = mMakeRotPt( mRtn +180 );
var mRotPt4 = mMakeRotPt( mRtn +270 );
var mPct = effect("リニアワイプ")("変換終了");
linear( mPct , 0 , 100 , mRotPt4 , mRotPt1 );
■終了点
//基準点と目標点と角度から、回転した目標点の位置を出す関数。
function mGetRotatedPt( aStdPt , aTgtPt , aRtn ){
var mLgtRX = aTgtPt[0] - aStdPt[0];
var mLgtRY = aTgtPt[1] - aStdPt[1];
var mLgtR = Math.sqrt( Math.pow( mLgtRX , 2 ) +Math.pow( mLgtRY , 2 ) );
var mROgl = Math.atan2 ( ( mLgtRY ) , ( mLgtRX ) );
var mRd = aRtn * ( Math.PI / 180 ) ;
var mPtR = [ Math.cos( mROgl + mRd ) * mLgtR , Math.sin( mROgl + mRd ) * mLgtR ];
return mPtR + aStdPt;
}
//点は4つ必要なので処理をまとめた関数。
function mMakeRotPt( aRtn ){
var mRtn = aRtn;
//0~360未満までを繰り返す回転。
var mRtnRoop = mRtn % 360;
//マイナス対策をする。
if( mRtnRoop < 0 ){ mRtnRoop = 360 + mRtnRoop; };
//90度ごとに増えるカウンター。
var mCnt = Math.floor( mRtnRoop / 90 );
//下記回転位置はそれぞれ180度区切りで2つに分かれるものが2つある状態で、
//カウンターを使って90度ごとに横軸上円(0~180)、縦軸右円(0~180)、
//横軸下円(180~360)、縦軸左円(180~360)用へと移り変わる角度を作る。
if( mCnt === 0 ){
var mNewRtn = linear(mRtnRoop , 0 , 90 , 0 , 180 );
}else if( mCnt === 2 ){
var mNewRtn = linear(mRtnRoop , 180 , 270 , 180 , 360 );
}else if( mCnt === 1 ){
var mNewRtn = linear(mRtnRoop , 90 , 180 , 0 , 180 );
}else if( mCnt === 3 ){
var mNewRtn = linear(mRtnRoop , 270 , 360 , 180 , 360 );
}
//カウンターを使って、角度が0~90と180~270の場合は横軸上円&下円の値を使い、
//90~180と270~360の場合は縦軸右円&左円を使うようにする。
if( mCnt === 0 || mCnt === 2 ){
var mStdPt = [thisLayer.width / 2 ,0 ];
var mTgtPt = [0 , 0];
var mRttPt = mGetRotatedPt( mStdPt , mTgtPt , mNewRtn );
//ターゲットが原点の横軸円を、180度区切りで下に移行するよう分ける。
if( mNewRtn >= 180 ){
mRttPt = mRttPt+[0,thisLayer.height];
}
}else{
var mStdPt = [0 , thisLayer.height / 2];
var mTgtPt = [0 , 0];
var mRttPt = mGetRotatedPt( mStdPt , mTgtPt , mNewRtn );
//ターゲットが原点の縦軸円を、180度までは右円になるよう分ける。
if( mNewRtn < 180 ){
mRttPt = mRttPt + [thisLayer.width,0];
}
}
return mRttPt;
}
var mRtn = effect("リニアワイプ")("ワイプ角度");
var mRotPt1 = mMakeRotPt( mRtn );
var mRotPt2 = mMakeRotPt( mRtn +90 );
var mRotPt3 = mMakeRotPt( mRtn +180 );
var mRotPt4 = mMakeRotPt( mRtn +270 );
var mPct = effect("リニアワイプ")("変換終了");
linear( mPct , 0 , 100 , mRotPt3 , mRotPt2 );
*開始点と終了点の違いは最後の行だけです。
STEP
レーザー『開始点の太さ』『終了点の太さ』両方に以下のエクスプレッションをコピペ。
■『開始点の太さ』と『終了点の太さ』の両方に適用。
var mStop = effect("リニアワイプ")("変換終了");
if( mStop <= 0 || 100 <= mStop ){
var mRst = 0;
}else{
var mRst = effect("スライダー制御")("スライダー");
}
mRst;
以上です!
『スライダー制御』で線の太さが変えられます。調整するには『変換の終了』『ワイプ角度』です。リニアワイプとまったく一緒ですね。
使いどころ
「線付きのワイプで映像がどんどん移り変わる」みたいな映像は、オシャレな映画のOPとかで案外使われています。しかしAEって線付きのワイプが無いですよね?(もしかしてあります…?)作例がなんとも地味なTipとなりますがご参考下さい。エフェクトのみで完結しており、レイヤーが増えないのがいいですね。
解説
以下、かんたんな作成経緯&解説です。
- おしゃれ映画(『マイレージマイライフ』だったか…)を見て、作りたいなあと思う。
- エフェクトのワイプを観察すると、始点と終点は必ず四隅のどこかに当たっている。
- もしかして、画像外で回りつつぴったり貼りつく『逆バウンディングボックス』を作ればよいのでは?
- 三角関数で最後まで完成。それを見てあることに気付く。
- 『逆バウンディングボックス』の四隅は、幅や高さの半分を中心に半円が回っている。ならば…
- 『画像の大きさ+角度から位置を割り出す関数+リニア関数』を使って直感的なバージョンができる!
という具合です。
一応、三角関数版も載せておきます。コピペする文が違うだけで、使い方は一緒です。どちらが重いのか…?体感上はそんなに違いが無く、行数もあんまり変わってないです。お好きなほうをお使い下さい。
三角関数版も開始点と終了点の違いは最後だけです。
■三角関数版:開始点
function mMakeRotPtTri( aRot ){
//逆バウンディングボックスは四隅が必ず90度のため、
//回転方向と合わせれば3角度全てが出る、かつ直角三角形、かつ斜辺(画像のWorH)もわかる。
//なので三角比を使って四隅を出す。
//シータの元となる任意の角度。UIは上方向がゼロで始まる角度であることに注意する。
var mRot = aRot;
var mW = thisComp.width;
var mH = thisComp.height;
//四隅それぞれの基準原点(シータがある点、扇の持ち手)からの長さ。斜辺(画像WorH)*cosシータ。
//シータは斜辺(画像のWorH)の向きと任意角度を鑑みて出す。
var mLgt1 = mW * Math.cos(degreesToRadians( 90 - mRot));
var mLgt2 = mH * Math.cos(degreesToRadians( 180 - mRot));
var mLgt3 = mW * Math.cos(degreesToRadians( 270 - mRot));
var mLgt4 = mH * Math.cos(degreesToRadians( 0 - mRot));
//aStdPt位置からaLgtの距離の、aRot回転した点を出す関数。
//-90して開始角度を上方向にしてある。
function mGetRotPt( aLgt , aRot , aStdPt ){
var mRotTmp = aRot - 90;
var mX = (aLgt * Math.cos(degreesToRadians(mRotTmp ))) + aStdPt[0];
var mY = (aLgt * Math.sin(degreesToRadians(mRotTmp ))) + aStdPt[1];
return [mX , mY];
}
//0~360未満までを繰り返す回転。
var mRotRoop = mRot % 360;
//マイナス対策をする。
if( mRotRoop < 0 ){ mRotRoop = 360 + mRotRoop; };
//90度ごとに増えるカウンター。
var mCnt = Math.floor( mRotRoop / 90 );
if( mCnt === 0 ){
var mNewRot = mGetRotPt( mLgt1 , mRot , [0,0] );
}else if( mCnt === 1 ){
var mNewRot = mGetRotPt( mLgt2 , mRot , [mW,0] );
}else if( mCnt === 2 ){
var mNewRot = mGetRotPt( mLgt3 , mRot , [mW,mH] );
}else if( mCnt === 3 ){
var mNewRot = mGetRotPt( mLgt4 , mRot , [0,mH] );
}
return mNewRot;
}
var mRot = effect("リニアワイプ")("ワイプ角度");
var mRotPt1 = mMakeRotPtTri( mRot );
var mRotPt2 = mMakeRotPtTri( mRot +90 );
var mRotPt3 = mMakeRotPtTri( mRot +180 );
var mRotPt4 = mMakeRotPtTri( mRot +270 );
var mPct = effect("リニアワイプ")("変換終了")
linear( mPct , 0 , 100 , mRotPt4 , mRotPt1 );
■三角関数版:終了点
function mMakeRotPtTri( aRot ){
//逆バウンディングボックスは四隅が必ず90度のため、
//回転方向と合わせれば3角度全てが出る、かつ直角三角形、かつ斜辺(画像のWorH)もわかる。
//なので三角比を使って四隅を出す。
//シータの元となる任意の角度。UIは上方向がゼロで始まる角度であることに注意する。
var mRot = aRot;
var mW = thisComp.width;
var mH = thisComp.height;
//四隅それぞれの基準原点(シータがある点、扇の持ち手)からの長さ。斜辺(画像WorH)*cosシータ。
//シータは斜辺(画像のWorH)の向きと任意角度を鑑みて出す。
var mLgt1 = mW * Math.cos(degreesToRadians( 90 - mRot));
var mLgt2 = mH * Math.cos(degreesToRadians( 180 - mRot));
var mLgt3 = mW * Math.cos(degreesToRadians( 270 - mRot));
var mLgt4 = mH * Math.cos(degreesToRadians( 0 - mRot));
//aStdPt位置からaLgtの距離の、aRot回転した点を出す関数。
//-90して開始角度を上方向にしてある。
function mGetRotPt( aLgt , aRot , aStdPt ){
var mRotTmp = aRot - 90;
var mX = (aLgt * Math.cos(degreesToRadians(mRotTmp ))) + aStdPt[0];
var mY = (aLgt * Math.sin(degreesToRadians(mRotTmp ))) + aStdPt[1];
return [mX , mY];
}
//0~360未満までを繰り返す回転。
var mRotRoop = mRot % 360;
//マイナス対策をする。
if( mRotRoop < 0 ){ mRotRoop = 360 + mRotRoop; };
//90度ごとに増えるカウンター。
var mCnt = Math.floor( mRotRoop / 90 );
if( mCnt === 0 ){
var mNewRot = mGetRotPt( mLgt1 , mRot , [0,0] );
}else if( mCnt === 1 ){
var mNewRot = mGetRotPt( mLgt2 , mRot , [mW,0] );
}else if( mCnt === 2 ){
var mNewRot = mGetRotPt( mLgt3 , mRot , [mW,mH] );
}else if( mCnt === 3 ){
var mNewRot = mGetRotPt( mLgt4 , mRot , [0,mH] );
}
return mNewRot;
}
var mRot = effect("リニアワイプ")("ワイプ角度");
var mRotPt1 = mMakeRotPtTri( mRot );
var mRotPt2 = mMakeRotPtTri( mRot +90 );
var mRotPt3 = mMakeRotPtTri( mRot +180 );
var mRotPt4 = mMakeRotPtTri( mRot +270 );
var mPct = effect("リニアワイプ")("変換終了")
linear( mPct , 0 , 100 , mRotPt3 , mRotPt2 );
以上です!
FFXとかプロジェクトを作っておいてご活用ください。