フーノページ



CSSアニメーション

SVG、feComponentTransferのgamma、discrete、table関数について。










syntax。

<filter id = "「「5idName」」">
<feComponentTransfer">
    <feFuncR type = "「「1identity/linear/gamma/discrete/table」」" ../>
    <feFuncG ../>
    <feFuncB ../>
    <feFuncA ../>
</feComponentTransfer>  
</filter>
「「3<!--gamma-->」」
<feFuncX type = "gamma"
    exponent = "num" 「「3 //指数」」
    amplitude = "num"「「3 //増幅率」」
    offset = "num"「「3 //原点からのY成分の距離」」
/>

「「3<!--discrete-->」」
<feFuncX type = "discrete"
    tableValues = "num num .." 「「3 //階段関数のY値」」
/>

「「3<!--table-->」」
<feFuncX type = "gamma"
    tableValues = "num num .." 「「3 //始点/終点を含む折れ線関数の頂点」」
/>




gamma/discrete/table。


こんにちは、「ふ」です。
SVGのfeComponentTransferフィルター、後編をお届けします。
今回は少しばかり複雑な、

・gamma関数
・discrete関数
・table関数

〜について紹介していきます。頑張って学習していきましょう 🎶


feComponentTransferの基本については前編⬇︎を参考にしてください。

feComponentTransfer

SVG、feComponentTransferフィルターで色変換。

〽️ RGBAごとに操作が可能。





gamma関数。



gamma関数は、入力と出力の関係をガンマ曲線をつかって指定します。

ガ.ン.マ ?

ガンマ曲線はそもそも、映像の明るさを補正するために使われるものです。
詳しくお話しすると長くなるので、別途記事⬇︎を設けました。

「ガンマ..?」の方は参考にしてください。

ガンマ補正

ガンマ特性・ガンマ補正について知っておく。

〽️ web屋向けガンマ講座。



ガンマ関数には3つの属性があります。

<feFunc「「2X」」 type = "gamma"
    exponent = "数値"(指数。初期値:1)
    offset = "数値"(Y方向の原点からの距離。初期値:0)
    amplitude = "数値"(増幅率。初期値:1)
/>

それぞれを詳しくみていきましょう。



exponent(指数)。


グラフ⬆︎はexponentを「2」としたときの、inとoutの関係を表したものです。

ガンマ関数の基本形は out = in^γ(inのγ乗)で表される、べき乗関数です。
exponentはそのγ(ガンマ)の部分、つまり指数を指定します。初期値は1で、ただのin = outの直線となります。

◾️ sleep.png



前回に続き、就寝中のこの子にがんばってもらいましょう。

<svg style = "position:fixed;">

<filter id = "「「5gamma01」」">
<feComponentTransfer>
    <feFuncR type = "「「1gamma」」" exponent = "「「12」」"/>
    <feFuncG type = "「「1gamma」」" exponent = "「「12」」"/>
    <feFuncB type = "「「1gamma」」" exponent = "「「12」」"/>
</feComponentTransfer>    
</filter>

</svg>

filterのid名を「gamma01」とし、RGBともにexponent(指数)を「2」にしました。画像に施してみます。

<img src = "sleep.png" style = "filter:url(「「5#gamma01」」);">



全体が暗くなっています。


in = outのグラフと比較してみましょう。γ>1のとき、中間部分が本来の入力値より小さく変換されて出力されます。
RGB値は、値が小さいほど黒に近づきます。全体が暗くなってしまったのはそのためです。


こんどはexponent(γ)を1より小さい「0.5」で試してみます。こうすると、中間部分が入力値より大きい値に変換されて出力されるようになります。

<feComponentTransfer>
    <feFuncR type = "gamma" exponent = "「「10.5」」"/>
    <feFuncG type = "gamma" exponent = "「「10.5」」"/>
    <feFuncB type = "gamma" exponent = "「「10.5」」"/>
</feComponentTransfer>    



全体が白に近づきました。
また、暗い部分の陰影がはっきりするようになりました。


グラフを観察します。
inが小さい部分において、変化の度合いが急激になっています。ただ単に白に近づくのではなくて、入力値が暗い部分のメリハリが大きくなる傾向にあるんですね。

これまでRGBともに同じガンマカーブを指定してきましたが、もちろんチャンネルごとに入出力の関係を変化させることもできます。

<feComponentTransfer>
    <feFuncR type = "gamma" exponent = "「「11」」"/>
    <feFuncG type = "gamma" exponent = "「「110」」"/>
    <feFuncB type = "gamma" exponent = "「「10.1」」"/>
</feComponentTransfer>    


R,G,Bのexponent属性値を極端に変えてみました。

R:恒等変換(in = outの状態)
G:inがよほど大きくない限り、outは0またはそれに近い状態
B:inがよほど小さくない限り、outは1またはそれに近い状態



結果⬆︎です。
このような奇抜な効果を施すこともできます。



offset(Y方向の距離)。

ガンマ関数は out = in^γ で表される指数関数なので、exponentだけの指定では必ず原点を通る直線になります。

offset属性は原点からのY位置をずらす働きをします。これを使うと曲線全体が底上げ/底下げという形となります。ただしoffsetにより、0〜1の範囲をこえた部分は0または1に丸められます。

これまではoffset指定なしの初期値「0」で試してきたので、ガンマ曲線は原点が始点となっていました。少しいじってみましょう。

<feComponentTransfer>
    <feFuncR type = "gamma" exponent = "2" offset = "「「10.3」」"/>
    <feFuncG type = "gamma" exponent = "2" offset = "「「10.3」」"/>
    <feFuncB type = "gamma" exponent = "2" offset = "「「10.3」」"/>
</feComponentTransfer>    


γ値「2」の曲線に対し、offset「0.3」で上に押し上げています。RGBすべて同じ条件にしました。



全体がかなり白っぽくなりました。
高い入力値の部分は「1」に丸められ、結果#fffの占める割合が増えたためです。

offsetは負の値をとることもできます。

<feComponentTransfer>
    <feFuncR type = "gamma" exponent = "0.5" offset = "「「1-0.3」」"/>
    <feFuncG type = "gamma" exponent = "0.5" offset = "「「1-0.3」」"/>
    <feFuncB type = "gamma" exponent = "0.5" offset = "「「1-0.3」」"/>
</feComponentTransfer>    


out = in^0.5のグラフを、Y方向に0.3ぶん押し下げた状態。



暗い部分のコントラストがほとんどなくなってしまいました。
小さい入力値の部分は0に丸められ、#000の占める割合が増えたのです。



amplitude(増幅率)。

amplitude属性は、in^γを実数倍させたものをoutとして出力させます。初期値は「1」で、in^γの状態がそのまま出力となります。


たとえば⬆︎は、exponentを「2」としてout = in^2としている状態。


amplitudeを「2」とすると、2*(in^2)の状態となります。
ここでも0〜1の範囲を越えたものは、0または1に丸められます。


もちろんoffset属性を加えて、底上げ/底下げの処理も可能です。
3つの属性を式で表すと、

out = amplitude( in^exponent ) + offset

ということになります。

amplitude属性の指定なし/あり、双方をくらべてみましょう。

<feComponentTransfer>
<feFuncR type = "gamma" exponent = "0.5"/> 
<feFuncG type = "gamma" exponent = "0.5"/> 
<feFuncB type = "gamma" exponent = "0.5"/> 
</feComponentTransfer>   


こちらは先に試した、exponent「0.5」の曲線です。
amplitudeは指定しておらず、初期値の「1」のままです。



中間部分の変化率が大きくなっているので、白に近い部分が増えました。が、明るくなっている..というよりは「白っぽくなった」という印象です。

amplitudeを「2」とし、増幅させてみましょう。


こうすると入力値の低い部分において、変化率が激しくなります。

<feComponentTransfer>
<feFuncR type = "gamma" exponent = "0.5" 「「1amplitude = "2"」」/> 
<feFuncG type = "gamma" exponent = "0.5" 「「1amplitude = "2"」」/> 
<feFuncB type = "gamma" exponent = "0.5" 「「1amplitude = "2"」」/> 
</feComponentTransfer>   



白っぽくなるだけexponent0.5だけのやつに比べ、ちゃんと「明るくなった」という印象です。
入力値の小さい部分の変化率が激しくなったため、色彩豊かになったのですね。




discrete関数。



discrete関数はいわゆる「階段関数」を定義します。
入力値の指定した範囲を同じ出力値に変換するため、全体の色数を少なくする効果があります。

<feFunc「「2X」」 type = "discrete"
    tableValues = "数値 数値 ...."
/>

属性値はtableValuesの1つで、複数の数値を半角スペースで区切って指定します。tableValues属性を指定しない場合、恒等変換( in = out )が渡されます。

<filter id = "「「5discrete01」」">
<feComponentTransfer>
    <feFuncR type = "discrete" tableValues = "「「10 0.5 1」」"/> 
    <feFuncG type = "discrete" tableValues = "「「10 0.5 1」」"/> 
    <feFuncB type = "discrete" tableValues = "「「10 0.5 1」」"/>  
</feComponentTransfer>   
</filter>

R/G/Bの各チャンネルのtableValues属性に「0 0.5 1」と3つの数値を記述しました。


すると入力値の0〜1の範囲が、指定した値の数で均等に分割されます。
今回数値は3つなので、3等分されました。


そして各範囲の出力値は、指定した数値が割り当てられます。

<img src = "sleep.png" style = "filter:url(「「5#discrete01」」)">

元画像に対し、discrete関数でフィルターを掛けてみます。



結果はこの⬆︎ようになりました。
R/G/Bそれぞれの出力値が「 0 0.5 1 」のいずれかになるので、多くとも27色でしか描画されません。

ほかにも試してみましょう。

<feComponentTransfer>
<feFuncG type = "discrete" tableValues = "「「10」」"/> 
<feFuncB type = "discrete" tableValues = "「「10」」"/> 
</feComponentTransfer>   


Rは指定なしなのでin = outの恒等変換で出力されます。
GとBは常に0が出力されるようにしました。



結果。
赤と黒だけの画像になりました。

もっと遊んでみます。

<feComponentTransfer>
<feFuncR type = "discrete" tableValues = "1 0"/> 
<feFuncG type = "discrete" tableValues = "0.5 0"/> 
</feComponentTransfer>   



discrete関数は、下→上の順に指定しなくても構いません。逆方向や上下ランダムに指定することもできるのです。



そして、結果⬆︎⬆︎。
前衛的アートっぽく?なってしまいましたね。ふふふ。




table関数。



table関数は、折れ線グラフによる変換を指定します。

<feFunc「「2X」」 type = "table"
    tableValues = "数値 数値 数値 ...."
/>

table関数においても、具体的な内容はtableValues属性が使用されます。が、discrete関数とはその扱われ方が異なっています。

<filter id = "table01">
<feComponentTransfer>
<feFuncR type = "table" tableValues = "「「10.5 0 1」」"/> 
</feComponentTransfer>    
</filter>

いまfeFuncRにおいて、tableValue属性に「 0.5 0 1 」と3つの数値を指定しました。


discrete関数のtableValues属性は、数値の数だけ0〜1の範囲を分割したのに対し、table関数のtableValuesの内容は、始点と終点を含めた折れ線の頂点に割り当てられます。
つまり3つの数値を記述した場合は、区間は2つ、ということになります。


今回はfeFuncRだけにtable関数とtableValues属性を指定しました。
tableValues属性を指定しない場合にはdiscrete関数のときと同様、恒等変換が返されます。

<img src = "sleep.png" style = "filter:url(「「5#table01」」)">

指定した値で、table関数を使ってみます。



⬆︎のような結果になりました。
discreteがいきなり出力値をジャンプさせたのに対し、table関数の各区間は線形変換です。そのため、discreteに比べてスムーズな色変化となります。

table関数でも遊んでみましょう。

<feComponentTransfer>
<feFuncR type = "table" tableValues = "1 0.5"/> 
<feFuncG type = "table" tableValues = "1 0.5"/> 
<feFuncB type = "table" tableValues = "1 0.5"/> 
</feComponentTransfer>


R/G/Bすべて逆向きの変化にしてみました。



明るい部分と暗い部分が反転した感じです。
ショックを受けたときに使えそうですね。 

組み合わせてみてね。


最後までお読みくださり、ありがとうございました。

前回-今回にわたり、feComponentTransferフィルターの各関数について紹介してきました。記事内ではそれぞれの特性を解説するためにR/G/Bともに同じtype属性を使ってきました。が、

各関数の特徴がわかったら、「Rはlinear、Gはtable..」などと組み合わせて使うのもおもしろそうですね。
いろいろと実験してみてください。

ではまた〜 🎵




「ふ」です。

ふ

ベクターグラフィック、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。