[ShaderLab] PhongおよびBlinn-Phong照明モデルの理解

テキスト

        (手作業で写真をコピー)

PhongとBlinn-Phongの両方の照明モデルは、オブジェクトを照らす光の効果を実現するために使用され、オブジェクトのパフォーマンスはハイライト部分を生成します。2つのモデルも非常に似ています。結局のところ、後者は計算上の最適化です。前者。2つの照明モデルには既製のコード関数パッケージがあります。特にshaderforgeプラグインと新しいバージョンのユニティのシェーダーグラフですが、いくつかのグラフィカルコントロールを直接ドラッグアンドドロップして、2つの照明を実現できます。モデルの効果は次のとおりです。単に多すぎます。しかし、原則を理解した後、他の人に尋ねられても私は空っぽになりません。

 

フォン照明モデル

理想的には、光源から放出された光はミラーで反射され、反射光の方向で観察されます。観察者は最も多くの反射光を受け取ることができます。次に、観察者と反射方向の間の角度によって、ハイライトの量が決まります。観察された。角度が大きいほどハイライトは小さくなり、角度が小さいほどハイライトは大きくなります。ハイライトのサイズに影響を与えるもう1つの要素は、表面の滑らかさです。表面が滑らかであるほど、ハイライトは強くなり、表面は粗くなり、ハイライトは弱くなります。Lは光源の方向、Nは頂点の法線方向、Vは観測者の方向、Rは反射光の方向を表します。まず、反射光の方向Rを計算する必要があります。反射光の方向Rは、入射光の方向と法線ベクトルから取得できます。R+ L = 2dot(N、L)N、次にR = 2dot(N、L)NL。(この式の計算方法については、CSDNに、単純なベクトルと三角関数の変換であるメモがあるブログがたくさんあります。理解できない場合は、Baiduでダウンロードするか、ここで直接読むことができます

(別の写真を手でコピーしてください)

 

Blinn-Phong照明モデル

 

フォン照明モデルはハイライト効果を非常にうまく実行できますが、ブリンフォン照明の欠点は計算コストが高いことです。そのため、1977年にジムブリンはフォン照明を改良し、それをブリンフォン照明モデルと呼びました。

Blinn-Phong照明は、Hで表される概念である半角ベクトルを導入します。半角ベクトルは簡単に計算できます。半角ベクトルは、光源方向Lと視線方向Vの加算を正規化することで得られます。Phong照明は、反射方向Rと視線方向Vの間の角度を比較し、Blinn-Phongは、代わりに、半角ベクトルHと法線方向Nの間の角度を比較します。半角ベクトルの計算の複雑さは反射光の計算よりもはるかに単純であるため、Blinn-Phongのパフォーマンスははるかに高く、効果はPhongライト写真の場合と同様であるため、固定パイプラインの照明モデルはOpenGLには、Blinn-Phong照明モデルがあります。

BlinnPhong照明モデルの計算式は次のとおりです。

I(spcular)= I * k * pow(max(0、dot(N、H))、gloss)、ここでIは入射光の色ベクトル、kは鏡面反射係数、光沢は次数です。滑らかさ。

================================================== ================

以下の2つの照明モデルのシェーダーラボコードを貼り付けます

Shader "Custom/Phong" {

Properties{

_Specular("Specular", Color) = (1,1,1,1)

_Diffuse("Diffuse",Color) = (1,1,1,1)

_Gloss("Gloss",Range(1,255)) = 10

}

SubShader{

Pass{

Tags {"LightMode" = "ForwardBase" }

LOD 200



CGPROGRAM

#include "Lighting.cginc"

#pragma vertex vert

#pragma fragment frag



fixed4 _Diffuse;

fixed4 _Specular;

float _Gloss;



struct a2v

{

float4 vertex : POSITION;

float3 normal : NORMAL;

};

struct v2f

{

float4 pos:SV_POSITION;

float3 worldNormal:NORMAL;

float3 worldPos:TEXCOORD1;

};



v2f vert(a2v v)

{

v2f o;

//矩阵变换 获取投影坐标下的顶点

o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

//获取世界坐标下的法线坐标

o.worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));

//o.worldNormal=normalize(mul((float3x3)_Object2World,v.normal));//与上面等价

o.worldPos = mul(_Object2World, v.vertex).xyz;

return o;

}



fixed4 frag(v2f i) :SV_Target

{

//获取环境光

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz*_Diffuse;

//获取灯光方向 并归一化

fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

//归一化世界坐标下的法线

fixed3 worldNormal = normalize(i.worldNormal);

//获取漫反射

fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLight));

//获取反射光的方向 入射光方向为-worldLight,通过reflect函数获取反射光方向

fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));

//获取点在摄像机的观察位置,并归一化 (相机坐标-像素位置)

fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

//phong的高光公式

fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(0, dot(reflectDir, viewDir)), _Gloss);

fixed3 color = diffuse + ambient + specular;

return fixed4(color, 1.0);

}

ENDCG

}

}

FallBack "Specular"

}

 

================================================== ====================

Shader "Custom/Blinn_Phong" {

Properties{

_Specular("Specular",color) = (1,1,1,1)

_Diffuse("Diffuse",color) = (1,1,1,1)

_Gloss("Gloss",Range(1,100)) = 20

}

SubShader{

Pass{

Tags { "RenderType" = "Opaque" }

LOD 200

CGPROGRAM

#include "Lighting.cginc"

#pragma vertex vert

#pragma fragment frag



float3 _Specular;

float3 _Diffuse;

float _Gloss;



struct a2v {

float4 vertex:POSITION;

float3 normal:NORMAL;

};

struct v2f {

float4 pos:SV_POSITION;

float3 worldNormal:NORMAL;

float3 worldPos:TEXCOORD0;

};



v2f vert(a2v a)

{

v2f o;

o.pos = mul(UNITY_MATRIX_MVP, a.vertex);

o.worldNormal = mul(_Object2World, a.normal);

o.worldPos = mul(_Object2World, a.vertex).xyz;

return o;

}



float4 frag(v2f i):SV_Target

{

//获取环境光

float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz*_Diffuse;

//获取灯光 并且归一化

float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

//获取法线并归一化

float3 worldNormal = normalize(i.worldNormal);

//获取视线方向(摄像头位置-像素对应位置) 并归一化

float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

//获取漫反射灯光

float3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(worldNormal, worldLight));

//获取半角向量(光线方向+视线方向) 并归一化

float3 halfDir = normalize(worldLight + viewDir);

//高光公式

float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(halfDir, worldNormal)),_Gloss);

float3 color = ambient + diffuse + specular;

return float4(color,1.0);

}

ENDCG

}

}

FallBack "Diffuse"

}

 

================================================== =====================================

一部のキーコードはすでにコードにコメントされているので、あまり繰り返しません。また、Blinn-Pongは、反射ベクトルを置き換えるために半角ベクトルを導入しているため、シェーダーの計算量ははるかに少なくなります。以下は、Blinn-Phongシェーダーコードの直接の要約です。

ブリンポン照明シェーダー

照明モデル「Lighting.cginc」を参照してください

a2v構造を定義して、モデルの頂点と法線を取得します

v2f構造を定義し、頂点情報を格納します(SV_POSITION)ワールド法線とワールド座標での頂点の位置

 

頂点関数を取得する必要があります

頂点の投影座標

頂点ワールドノーマル

頂点の世界座標

 

フラグメント関数を取得する必要があります

周囲光、照明モデル(UNITY_LIGHTMODEL_AMBIENT.xyz)の周囲光に拡散光を掛ける必要があります

正規化された世界通常

正規化されたライトの位置(_WorldSpaceLightPos0.xyz)

正規化されたポイントの表示角度位置(_WorldSpaceCameraPos.xyz-worldPos)

漫反射(_LightColor0.rgb * Diffuse.rgb * saturate(dot(worldNormal、lightDir)))

正規化された半角ベクトル(viewDir + lightDir)

最終的なハイライト式LightColor0.rbg * _Specular.rbg * pow(dot(halfDir、worldNormal)、_ Gloss)

ハイライトに戻る+周囲光+拡散光

総括する

        これらの2つの照明モデルは実際には中国の名前、すなわちFengGuangzhaoモデルとBrin-FengGuangzhaoモデルを持っていますが、中国語に切り替えた後、それは多くの混乱を感じます。理解の観点から、フォンモデルはより直感的に結論を導き出すことができ、後者の半角ベクトルを計算する方法はわかりませんが、ドット乗算操作なしでコードがはるかに単純であると感じています。後世の気持ち。

        私は1年近く怠惰で、つい最近、この個人的な理解を窒息させる時間がありました。忍耐力はまだ理解しにくい言葉のようです。普段はメモをとる習慣がありますが、メモをとったらそこに置いてあるような気がします。久しぶりに基本的にすべてを忘れてしまい、振り返ってみると気付くのに時間がかかります。こうしてブログを書くそれは私自身の記憶です。それを見直して、これからも頑張っていきたいと思います。

 

おすすめ

転載: blog.csdn.net/ssssssilver/article/details/90232492