質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.77%

shaderソースコードの読解について

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,294

shiroshiro_me

score 19

 前提・実現したいこと

unityのshaderで質問です。unityでの開発でぼかしを調節できるすりガラスのようなマテリアルが必要になり、自分自身shaderが全く分からないため、ネット上に上がっていたソースコードを代用しました。しかし、ここで使用しているぼかしの計算式が必要になり、ソースコードの読解を試みましたが、私がよく使っているc#やjavaとは構造が全く違うので読解が難航しています。このソースコードが載っていたサイトにも詳しい実装は書いていませんでした。このソースコードではどのような数式で_Blurの値からぼかしの計算をしているのでしょうか。
回答していただけると幸いです。

 該当のソースコード

Shader "Custom/Frost" 
{
Properties
{
_Color("Color", Color) = (1, 1, 1, 1)

_MainTex("Diffuse", 2D) = "white" {}
_Noise("Noise", 2D) = "black" {}

_Range("Range", Float) = 0.025
_Blur("Blur", Float) = 0.005
}

SubShader
{
Cull Back

GrabPass{ "_Frost" }

CGINCLUDE

include "UnityCG.cginc"

half4 _Color;

sampler2D _MainTex;
float4 _MainTex_ST;

sampler2D _Frost;
sampler2D _Noise;
float4 _Noise_ST;

half _Range;
half _Blur;

ENDCG

Pass
{
CGPROGRAM

pragma target 3.0

pragma vertex vert

pragma fragment frag

struct v2f
{
float4 pos : SV_POSITION;

float3 uv : TEXCOORD;
float4 screenPos : TEXCOORD1;
float3 ray : TEXCOORD2;
};

v2f vert(appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord;
o.screenPos = ComputeScreenPos(o.pos);
o.ray = mul(UNITY_MATRIX_MV, v.vertex).xyz * float3(-1, -1, 1);
return o;
}

half4 frag(v2f i) : SV_Target
{
i.ray = i.ray * (_ProjectionParams.z / i.ray.z);
float2 uv = i.screenPos.xy / i.screenPos.w;

float2 frostUV = tex2D(_Noise, i.uv * _Noise_ST.xy + _Noise_ST.zw).xy;

frostUV -= 0.5;
frostUV *= _Range;
frostUV += uv;

half4 frost = tex2D(_Frost, frostUV);
frost += tex2D(_Frost, frostUV + float2(_Blur, _Blur));
frost += tex2D(_Frost, frostUV + float2(_Blur, -_Blur));
frost += tex2D(_Frost, frostUV + float2(-_Blur, _Blur));
frost += tex2D(_Frost, frostUV + float2(-_Blur, -_Blur));
frost *= 0.2;

half4 diffuse = tex2D(_MainTex, i.uv * _MainTex_ST.xy + _MainTex_ST.zw);

half alpha = _Color.a * diffuse.a;

return half4(frost.xyz + (diffuse.rgb * _Color.rgb * alpha), 1);
}

ENDCG
}
}

Fallback Off
}

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+3

詳細な説明は範囲が広すぎてむずかしいですが、メインの処理は以下の部分ですね。

half4 frost = tex2D(_Frost, frostUV);
frost += tex2D(_Frost, frostUV + float2(_Blur, _Blur));
frost += tex2D(_Frost, frostUV + float2(_Blur, -_Blur));
frost += tex2D(_Frost, frostUV + float2(-_Blur, _Blur));
frost += tex2D(_Frost, frostUV + float2(-_Blur, -_Blur));
frost *= 0.2;

tex2Dはテクスチャから、該当の位置のピクセルを抜き出すビルトインの関数です。
そして「該当の位置」はfrostUV + float2(_Blur, _Blur)として指定しています。
よく見るとプラスマイナスの値を微妙に変えながらピクセルを取り出しているのが分かるかと思います。

つまり、(今回の例では)4つのピクセルの色を取り出し、それを合成して平均値を取ることでぼかしの演出をしている、というわけです。
ボケ自体は光が「滲んだ」ように見えるものなので、複数の色を合成することでこの「滲み」を表現しているんですね。

ブラーの作り方は比較的シンプルなので調べてみると詳細な解説が載っている記事もあると思います。

ちなみにブラーに限らず、こうした画像加工は「コンボリューション(畳み込み)」と呼ばれる方法によって実現しています。(いわゆるフィルター)
過去に、それについての記事を書いたことがあるのでよかったら見てみてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/09/17 11:27

    回答していただきありがとうございます。
    続けて質問させていただくと、要するにこのプログラムは、

    |0.2| 0|0.2|
    | 0|0.2| 0|
    |0.2| 0|0.2|

    のようなフィルタを使った空間フィルタリングにより平滑化を行っているということでしょうか。
    そして_Blurの値によってピクセルを指定ということは、_Blurを大きくすればより遠くのピクセルの値を使って空間フィルタリングを行うということでしょうか。
    また、Rangeはどう扱われているのでしょうか。

    キャンセル

  • 2018/09/19 10:57

    そうですね。その認識です。

    >_Blurを大きくすればより遠くのピクセルの値を使って空間フィルタリングを行うということでしょうか。
    そうです。ただ、今の実装だと_Blurを大きくしても「遠くのピクセルを4つ合成する」だけになるので、大きなボケにはなりません。
    その場合はより広範囲のピクセルを合成する必要があります。(が、認識自体は合っています)

    _Rangeは以下のように使われています。

    ```
    frostUV *= _Range;
    ```

    つまり、算出された(ノイズを適用した)UVに対して、さらに係数倍していることになります。
    仮に数値が`2`だとしたら、UV値が2倍となるため、テクスチャから取得するピクセルが倍の位置になります。

    つまり、テクスチャから取得するピクセルの「範囲(Range)」が広がる(狭まる)わけですね。
    Rangeの使い方はまさに「範囲」の拡縮です。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る