遊戲圖像學習入門到精通-Shader總結篇

流水線
1.應用階段:(CPU)輸出渲染圖元,粗粒度剔除等 比如完全不在相機範圍內的需要剔除,文件系統的粒子系統實現就用到粗粒度剔除。
2.幾何階段:(GPU)把頂點座標轉換到屏幕空間,包含了模型空間 到世界空間 到觀察空間(相機視角view) 到齊次裁剪空間(投影project空間,四維矩陣,通過-w<x<w判斷是否在裁剪空間,xyz值都在-w,w之間)
到歸一化設備座標NDC(四維矩陣通過齊次除法,齊次座標的xyz除以w實現歸一化(x/w,y/w,z/w,w/w),xy座標到在-1,1上,z座標一般是0,1) 到屏幕空間(通過屏幕寬高和歸一化座標計算)。
a.頂點着色器:座標變換和逐頂點光照,將頂點空間轉換到齊次裁剪空間。
b.曲面細分着色器:可選
c.幾何着色器:可選
d.裁剪:通過齊次裁剪座標的-w<x<w判斷不在視野範圍內的部分或者全部裁剪,歸一化。
e.屏幕映射:把NDC座標轉換爲屏幕座標
3.光柵化階段:(GPU)把幾何階段傳來的數據來產生屏幕上的像素,計算每個圖元覆蓋了哪些像素,計算他們的顏色、
a.三角形設置:計算網格的三角形表達式
b.三角形遍歷:檢查每個像素是否被網格覆蓋,被覆蓋就生成一個片元。
c.片元着色器:對片元進行渲染操作
d.逐片元操作:模板測試,深度測試 混合等
e.屏幕圖像


矩陣:
MA=AM的轉置(M是矩陣,A是向量,該公式不適合矩陣與矩陣)

座標轉換:
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);頂點位置模型空間到齊次空間
o.worldNormal = mul((float3x3)_Object2World,v.normal);//在正交變化下可以得到近似模型空間轉換到世界空間的法向量,但是非正交情況會得到錯誤的情況。也不能只是逆矩陣(如果用_World2Object 會出錯)
o.ViewNormal = mul(v.normal, (float3x3)UNITY_MATRIX_IT_MV);//公式上的正確轉換方式,法線模型空間到view空間,用該矩陣的逆轉置矩陣去轉換。但是Unity裏邊也許考慮了這個 可能不需要再做一遍。
//但是這裏要注意下在Unity裏邊也許 o.view_pos_nor.xyz = mul(UNITY_MATRIX_MV, float4(v.normal,0)).xyz;是正確的,有可能Unity的這個矩陣幫我們考慮法線問題。‘

normal或者頂點轉換到View空間參與計算,記得考慮座標系變化。可以i.view_pos_nor.z=-i.view_pos_nor.z;把z反向,因爲Unity物體是左手座標系,camera space是右手座標系。
當然也可以直接在變換矩陣裏邊考慮好 座標系的變化。Camera.worldToCameraMatrix 是右手座標系計算,需要反轉z,transform.worldToLocalMatrix和unity一樣的左手座標系計算。
在Opengl中,本地座標系和世界座標系都是右手座標系,直到投影空間中才變換爲左手。而unity中的本地座標系和世界座標系是左手系統,但是View空間是右手座標。


API:
UNITY_MATRIX_MVP 將頂點方向矢量從模型空間變換到裁剪空間
UNITY_MATRIX_MV 將頂點方向矢量從模型空間變換到觀察空間
UNITY_MATRIX_V 將頂點方向矢量從世界空間變換到觀察空間
UNITY_MATRIX_P 將頂點方向矢量從觀察空間變換到裁剪空間
UNITY_MATRIX_VP 將頂點方向矢量從世界空間變換到裁剪空間
UNITY_MATRIX_T_MV UNITY_MATRIX_MV的轉置矩陣
UNITY_MATRIX_IT_MV UNITY_MATRIX_MV的逆轉置矩陣,用於將法線從模型空間轉換到觀察空間
_Object2World 將頂點方向矢量從模型空間變換到世界空間,矩陣。
_World2Object 將頂點方向矢量從世界空間變換到模型空間,矩陣。
模型空間到世界空間的矩陣簡稱M矩陣,世界空間到View空間的矩陣簡稱V矩陣,View到Project空間的矩陣簡稱P矩陣。

這些矩陣需要先聲明瞭才能用


_WorldSpaceCameraPos 該攝像機在世界空間中的座標
_ProjectionParams x = 1,如果投影翻轉則x = -1 y是camera近裁剪平面 z是camera遠裁剪平面 w是1/遠裁剪平面
_ScreenParams x = 屏幕寬度,y = 屏幕高度,z = 1 + 1.0/屏幕寬度, w = 1 + 1.0/height屏幕高度(指像素數)
_ZBufferParams x = 1-far/near y = far/near z = x/far w = y/far
unity_OrthoParams
unity_Cameraprojection
unity_CameraInvProjection
unity_CameraWorldClipPlanes[6] 攝像機在世界座標下的6個裁剪面,分別是左右上下近遠、


1.表面着色器
void surf (Input IN, inout SurfaceOutput o) {}表面着色器,unity特殊封裝的着色器
Input IN:可以引用外部定義輸入參數
inout SurfaceOutput o:輸出參數
struct SurfaceOutput//普通光照
{
half3 Albedo;//紋理,反射率,是漫反射的顏色值
half3 Normal;//法線座標
half3 Emission;//自發光顏色
half Specular;//高光,鏡面反射係數
half Gloss;//光澤度
half Alpha;//alpha通道
}
基於物理的光照模型:金屬工作流SurfaceOutputStandard 高光工作流SurfaceOutputStandardSpecular
half3,half4代表rgba或者xyz,可以分開用 Albedo.xy=1.或Albedo.ga=1


#pragma surface surfname lightModel op - 指出函數surfname 表面着色器。lightModel的光照模型和可選op操作,還可以添加頂點修改函數vertex和顏色修改函數finalcolor。lightModel的輸入結構有定義的固定幾種
#pragma surface surf CustomLambert vertex:myvert finalcolor:mycolor addshadow exclude_path:deferred exclude_path:prepass nometa
#pragma vertex name - 指出函數name 是頂點着色器。
#pragma fragment name - 指出函數name 是片段着色器。
#pragma fragmentoption option - 添加option 到編輯的OpenGL片段程序。參看ARB fragment program說明書瞭解被允許的選項列表。這個指示在頂點程序或者編輯到非OpenGL targets的程序沒有影響。
#pragma multi_compile_builtin - 爲了pixel-lit shaders;;這個將告知Unity去編輯大量的這個着色器程序數列以支持所有的照明種類,和所有的陰影選項。
#pragma multi_compile_builtin_noshadows - 對於pixel-lit 着色器,不接受陰影。這將告知Unity去編輯幾個該着色器程序的數列來支持所有的照明種類。這比multi_compile_builtin pragma可以更快的編輯,而且結果着色器也更小。
#pragma target name - 那個着色器target 去編輯。細節參看shader targets。
#pragma only_renderers space separated names - 只爲給定的渲染器編輯着色器。默認情況下,着色器爲所有的渲染器被編輯。細節參看 renderers。
#pragma exclude_renderers space separated names - 不爲給定的渲染器編輯着色器。默認情況下,着色器爲所有的渲染器被編輯。細節參看 renderers。

#pragma multi_compile TEST1 TEST2 告訴Unity編譯兩個不同的
shader版本 分別定義了宏定義TEST1和TEST2.在C#中用
material.shaderKeywords = [“TEST1”]或者Shader.EnableKeyword
(“TEST1”)去區分調用不同宏的內容;;


2.頂點着色器
struct appdata_full {//vertex輸入
float4 vertex : POSITION;//must
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;//該頂點的紋理座標,第一組紋理座標uv 也就是第一張貼圖的座標、爲了實現多重紋理貼圖,比如子彈打在牆上的彈痕等
float4 texcoord1 : TEXCOORD1;//n的數量和shadermodel版本有關, Mesh.uv2
float4 texcoord2 : TEXCOORD2;//Mesh.uv3 可以通過外部設置該uv座標
float4 texcoord3 : TEXCOORD3;
fixed4 color : COLOR;//頂點顏色
};@上次買的

3.片段着色器
struct v2f{//vertec的輸出和frag的輸入
float4 vertex :SV_POSITION;//must
float3 color0:COLOR0;
float3 color1:COLOR1;
float4:texcoord:TEXCOORD0;//TEXCOORD0-TEXCOORD7自定義紋理座標
}
SV_Tatget //frag的輸出,輸出float4的顏色 也可以是COLOR0各個平臺會自動轉換標誌


光照:
1.逐頂點光照:在頂點着色器階段計算光照,效率高但是效果不好,在邊緣像素映射的時候插值可能會產生鋸齒。
2.逐像素光照:在片元着色器階段計算光照,計算量大,但是邊緣表現效果好。
3.半蘭伯特模型:處理無光照的地方,也讓其有光,不然可能是全黑。經驗模型。
4.Blinn-Phong模型:高光反射模型的經驗模型,高光部分看起來會更大更亮寫。更符合實際些。

#include “Lighting.cginc”
Tags { “LightMode”=“ForwardBase” }

WorldSpaceViewDir(float4 v) 輸入模型空間中的頂點座標,返回世界空間中從該點到攝像機的觀察方向
UnityWorldSpaceViewDir(float4 v) 輸入世界空間中的頂點座標,返回世界空間中從該點到攝像機的觀察方向
ObjSpaceViewDir(float4 v) 輸入模型空間中的頂點座標,返回模型空間中從該點到攝像機的觀察方向
WorldSpaceLightDir() 僅用於前向渲染,輸入模型空間中的頂點座標,返回世界空間中從該點到光源光照方向,沒有歸一化。
UnityWorldSpaceLightDir() 僅用於前向渲染,輸入世界空間中的頂點座標,返回世界空間中從該點到光源光照方向,沒有歸一化。
ObjSpaceLightDir() 僅用於前向渲染,輸入模型空間中的頂點座標,返回模型空間中從該點到光源光照方向,沒有歸一化。
UnityObjectToWorldNormal(float3 v)把法線從模型空間轉換到世界空間
UnityObjectToWorldDir(float3 v)把方向矢量從模型空間轉換到世界空間
UnityWorldToObjectDir(float3 v)把方向矢量從世界空間轉換到模型空間
_WorldSpaceLightPos0.xyz 獲取平行光光源方向,或者點光源的光源位置
_LightColor0.rgb 獲取當前pass的光源顏色和強度
UNITY_LIGHTMODEL_AMBIENT.xyz; 環境光
normalize(_WorldSpaceCameraPos.xyz - worldPos.xyz); 世界空間下的view視覺方向 UnityWorldSpaceViewDir

a.漫反射公式:diff=Cmax(0,dot<L,N>);//C是顏色和強度_LightColor0.rgb
代碼: diff=max(0,dot(i.normal,i.lightDir))//i的單位向量and單位法向量
c=tex2D(tex,i.uv)
_LightColor0*diff//_LightColor0表示的是場景中平行光的顏色和強度

b.高光反射公式:Spec=pow(max(0,dot(R,V),gloss))//R 單位反射向量reflect(ray,normal)函數獲取,V視線單位方向向量 ,gloss光色度
代碼: Spec=pow(max(0,dot(reflect(-i.lightDir,i.normal),viewdir),32))
c=c**_LightColor0*(diff+Spec)

顏色相加和相乘的物理意義,一般的各種光源都是顏色疊加,相乘會變淡。

在PS中 tex2D 自動計算應該使用的紋理層。
tex2Dbias需要在t.w中指定一個偏移量來把自動計算出的紋理層全部偏移指定的值。
tex2Dgrad需要提供屏幕座標x和y方向上的梯度來確定應該使用的紋理層。
tex2Dlod需要在t.w中明確指定要使用的紋理層。
texCUBElod(_Cube,float4(dir.xyz,lod))方向和Lod係數


紋理 uv座標是頂點存儲了圖片上的座標
_MainTex (“Main Tex”, 2D) = “white” {}
sampler2D _MainTex;
float4 _MainTex_ST;//Unity中 紋理_ST來默認聲明該紋理的屬性_MainTex_ST.xy表示Scale, Till縮放,_MainTex_ST.zw表示Transform 偏移
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);//vs輸入紋理座標和紋理值輸出UV,ps對uv進行紋理採樣和計算。UV通常在0-1範圍,等於o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;//反射率

法線貼圖:xyz映射存成rgb值。一般存在切線空間,z軸法線方向,x軸切線方向,y軸副(法)切線方向,我們ds的法線貼圖xy存法線z存粗糙度。
TANGENT_SPACE_ROTATION;//Unity來獲取rotation矩陣,從模型空間到切線空間變換的矩陣。僅存在旋轉和平移時,一個矩陣的轉置矩陣等於他的逆矩陣。
自己實現:
float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w; //切線空間的w分量用來存儲負法線向內還是向外
float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);//float3x3是按行存儲

float3 tangentNormal = UnpackNormal(packedNormal);Unity將法線貼圖紋理座標0,1映射到正常法線座標-1,1,返回切線空間下的法線方向。法線貼圖要設置成Normal格式。該設置unity有優化 rgb值不再是法線xyz的映射了,如果不設置的話要自己算 該公式不能用。
自己實現:
tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;//座標反映射,自己計算的方法 因爲packedNormal.z分量一般被壓縮去掉,比如我們ds的z分量存儲的是粗糙度,xy存法線。
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));//通過xy計算z 法線貼圖一般會採用壓縮的方式存儲 那麼只有xy分量是正確的,z分量需要經過xy計算出來。
如果法線貼圖是藍色:代表存在切線空間,而且xyz可能沒有被壓縮。如果顯示了其他顏色比如ds的綠色,就代表z通道被用做其他用途了。
雖然在切線空間下計算光照更省,但爲同一建議一般在世界空間下計算。

遮罩紋理


1.透明度測試AlphaTest:只要有一個片元的透明度不、、滿足條件就被裁剪,用來優化顯示。
AlphaTest Greater AlphaValue//僅渲染 alpha 值大於 AlphaValue 的像素。AlphaValue :0-1
AlphaTest GEqual AlphaValue //僅渲染 alpha 值大於或等於 AlphaValue 的像素。
AlphaTest Less AlphaValue//僅渲染 alpha 值小於 AlphaValue 的像素。
AlphaTest LEqual AlphaValue //僅渲染 alpha 值小於或等於 AlphaValue 的像素。
AlphaTest Equal AlphaValue //僅渲染 alpha 值等於 AlphaValue 的像素。
AlphaTest NotEqual AlphaValue //僅渲染 alpha 值不等於 AlphaValue 的像素。
AlphaTest Always //渲染所有像素。這在功能上相當於 Alpha 測試關 (AlphaTest Off)。
AlphaTest Never //不渲染任何像素。
第一步透明度測試不通過的放棄該片元
2.模板測試:Stencil如果開啓了模板測試,GPU會首先會讀取模板緩衝區的值,然後把讀取的參考值ref和該緩存值進行比較,比較方式由Comp指定,比如大於Greater就表示通過模板測試,
然後由Pass Fail ZFail去指定通過和不通過模板和深度測試後對緩衝區的值進行的Operation處理。
Stencil
{ Ref 2 //設置模板參考值爲2
Comp equal //比較方式,有8種比較方式。 Greater指ref值大於緩存值。
Pass Operation //這個是當stencil測試和深度測試都通過的時候,進行的stencilOperation操作方法
Fail Operation //這個是在模版測試和深度測試都失敗時執行的stencilOperation方法
ZFail Operation //這個是在stencil測試通過,但是深度測試沒有通過的時候執行的stencilOperation方法。
ReadMask readMask //readMask默認是255,ds按位進行對比的時候常用,設置隱碼後 讀取ref和buff值都需要與該碼進行與操作後再比較Comp。(0-255)
WriteMask writeMask//寫操作進行與操作 ,設置該掩碼後先進行按位與操作後再寫入
}
Comp 的參數包括Greater/GEqual/Less/LEqual/Equal/NotEqual/Always/Never
Operation的參數包括:
Keep 保持
Zero 歸零
Replace 拿比較的參考值替代原來buffer的值
IncrSat 值增加1,但是不溢出,如果是255,就不再加
DecrSat 值減少1,不溢出,到0就不再減
Invert 翻轉所有的位,所以1會變成254
IncrWrap 值增加1,會溢出,所以255會變成0
DecrWrap 值減少1,會溢出,所以0會變成255

第二步模板測試不通過的放棄該片元

clip(x) //x的任何分量小於0 被裁剪掉
discard//捨棄當前片元
ZWrite Off//關閉深入寫入 默認開啓
ColorMask RGB|A|0 //設置顏色通道的寫掩碼,爲0表示該pass不進行顏色輸出。
3.深度測試ZTEST:一個片元離攝像機的遠近,渲染後會進行深度寫入,通常會判斷緩存深度和當前片元深度 可知前後關係。
ZTest Always //指的是直接將當前像素顏色(不是深度)寫進顏色緩衝區中 相當於ZTest Off
ZTest Never //而Never指的是不要將當前像素顏色寫進顏色緩衝區中,相當於消失。
ZTest Greater/GEqual/Less/LEqual/Equal/NotEqual/Always/Never/Off,默認值爲LEqual 即當物體深度小於或等於緩存深度值時(越遠深度越大),該物體渲染,就是默認的先後順序渲染。
第三步深度測試不通過的放棄該片元

透明度混合AlphaBlending:該片元需要關閉深度寫入,不關閉深度測試。會導致片元之間深度穿插。可以採用2個pass,第一個pass只用來做深度寫入ZWrite On,第二個pass只用來輸出顏色ZWrite Off,這樣深度和顏色效果纔會正確
Blend Off//關閉混合,只有blend打開後ps輸出a通道纔有意義
Blend SrcFactor DstFactor//用同樣的因子對rgba進行混合(默認都開啓混合)第一個參數對源顏色(當前片元顏色,ps輸出的顏色)*SrcFactor混合,
第二個參數對目標顏色(當前讀到的緩衝區顏色)*DstFactor混合,混合後默認相加後會重新寫入緩衝區(相加後超過1的自動截斷到1)。混合包括RABG值。結果都是源顏色和目標顏色與各自因子相乘後再加起來作爲輸出顏色。
shader裏邊的向量相乘不同於點乘叉乘,相當於各項分別相乘。

Blend SrcFactor DstFactor,SrcFactorA DstFactorA//把rgb和a的混合因子分開。
混合因子
One //因子是1
Zero //因子是0
SrcColor //因子爲源顏色值,當前片元顏色,對應rgba分量分別與SrcColor分量相乘
SrcCAlpha //因子爲源顏色透明值值,對應rgba分別與SrcCAlpha相乘。
DstColor //因子爲目標顏色值,當前讀到的緩衝區顏色
DstAlpha //因子爲目標顏色透明值值
OneMinusSrcColor//因子爲1-源顏色
OneMinusSrcAlpha//因子爲1-源alpha
OneMinusDstColor//因子爲1-目標顏色
OneMinusDstAlpha//因子爲1-目標alpha
例子:
Blend SrcAlpha OneMinusSrcAlpha// Alpha混合,正常的透明度混合
Blend OneMinusDstColor One //柔和相加Soft Additive
Blend One One // Additive相加 線性減淡
Blend One OneMinusDstColor // Soft Additive比較柔和的相加
Blend DstColor Zero // Multiplicative乘法
Blend DstColor SrcColor // 2x Multiplicative2倍乘法

BlendOp OP//對源和目標顏色進行其他操作,而不是默認的相加,op操作包括:
Add //相加
Sub //源顏色減目標顏色
RevSub//目標顏色減源顏色
Min //使用2者較小的值
Min //使用2者較大的值
chen
BlendOp Min
Blend One One //組合變暗

雙面渲染:一般採用多個pass分別渲染正面和背面
Cull Back|Front|Off
Cull Back默認的背對相機圖元不渲染
Cull Front朝向相機圖元不渲染,只顯示背面
Cull Off關閉剔除功能 全部渲染 性能低,但是可以實現比如看見物體內部結構。
不透明物體有深度測試,先渲前後沒有關係,但是先渲染近的效率會更高,因爲遠的會被深度測試自動剔除不用渲染。沒辦法保證遠近?
透明物體一般要先渲遠的,再渲近的才能保證視覺順序正確。


SubShader的Tag{}標籤類型:
Queue:渲染順序,保證渲染順序小的先渲 大的後渲,在quene一樣大的時候無法保證渲染順序。同一批renderQueue值的材質一起渲,渲完後統一進行深度填入,第二批再取新值進行渲染。
RenderType:Unity可以運行時替換符合特定RenderType的所有Shader,着色器分類 也可以自定義。
ForceNoShadowCasting:值爲”true”時,表示不接受陰影。
IgnoreProjector:值爲”true”時,表示不接受Projector組件的投影。常用語半透明物體
DisableBatching:是否對subshader進行批處理,當shader中需要對頂點進行偏移的時候,該項設置爲true
CanUseSpriteAtlas:當該subshader用於sprite時 該標籤設爲false
PreviewType:指明材質麪包怎麼預覽材質 比如 “PreviewType”=“Plane”
LightMode : 渲染路徑 ,pass的標籤類型


渲染隊列:“Queue”=“Transparent”
Background:1000 //該聲明的物體最先被渲染
Geometry:2000 //默認的不透明物體使用的渲染隊列
AlphaTest:2450 //透明度測試,默認不透明物體渲染完後就渲染該物體
Transparent:3000//透明物體,在Geometry和AlphaTest後渲染,保證不透明物體渲染完了再渲染透明的。
Overlay:4000 //該隊列用來實現疊加效果,該物體會在最後被渲染。


RenderType:
Opaque:絕大部分不透明的物體都使用這個;
Transparent:絕大部分透明的物體、包括粒子特效都使用這個;
Background:天空盒都使用這個;
Overlay:GUI、鏡頭光暈都使用這個


渲染路徑
Tag{ “LightMode” = “ForwardBase”}//爲每個pass指定渲染路徑
LightMode包括:
Always:所有渲染路徑該pass都會渲染,但不計算光照
ForwardBase:前向渲染,該pass會計算環境光,最重要的平行光,逐頂點光和 Lightmaps
ForwardAdd:前向渲染,該pass會計算額外的逐像素光源,每個pass對應一個光源。光源多該pass會被多次調用 效率變低。
Deferred:延時渲染,該Pass會渲染G-buffer
ShadowCaster:把物體的深度信息渲染到陰影映射紋理或深度紋理中
PrepassBase:遺留的延遲渲染,該pass會渲染法線和高光反射的指數部分、
PrepassFinal:遺留的延遲渲染,該pass通過合併紋理 光照 自發光來渲染得到最後的顏色
Vertex:遺留的頂點照明渲染

1.前向渲染:包括ForwardBase類型渲染常用光照和ForwardAdd額外光照
#pragma multicompile_fwdbase //ForwardBase中用來保證光照衰減等參數正確賦值。
#pragma multicompile_fwdadd //ForwardAdd中用來保證可以訪問到正確的光照變量.
#pragma multicompile_fwdadd_fullshadows //ForwardAdd中用來計算陰影效果
USING_DIRECTIONAL_LIGHT//平行光的宏定義
_LightColor0 //該pass的桌像素光照顏色
_WorldSpaceLightPos0 //獲取平行光光源方向,或者點光源的光源位置
_LightMatrix0 //世界空間到光源空間(光源位置爲座標原點的座標系)的變換矩陣
_LightTexture0 //光照衰減紋理

tips:光源的RendeMode參數設置爲Important unity會自動採用像素光源,如果不重要就是頂點光源。還有qualitysetting裏邊的PixelLIghtCount,超過這個數也會用頂點光照
光照衰減
float3 lightCoord = mul(_LightMatrix0, float4(i.worldPos, 1)).xyz;
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;//UNITY_ATTEN_CHANNEL獲得衰減值所在的分量
float shadow=SHADOW_ATTENUATION(i);//負值使用SHADOW_COORDS對相關紋理進行採樣,返回值爲陰影。關閉陰影的狀態是等於1
return fixed4(ambient + (diffuse + specular) * atten * shadow, 1.0);//atten爲衰減,shadow爲陰影
------計算別人投到自己身上的陰影和衰減
SHADOW_COORDS(n)//聲明一個_ShadowCoord的陰影紋理座標 ps輸入座標,n的值是聲明TEXCOORD0-7座標使用的個數
TRANSFER_SHADOW(o);//用於在頂點着色器中計算上一步聲明中的陰影紋理座標 並傳向ps階段。
float shadow=SHADOW_ATTENUATION(i);//負值使用SHADOW_COORDS對相關紋理進行採樣,返回值爲陰影。關閉陰影的狀態是等於1
UNITY_LIGHT_ATTENUATION(atten,v2f i, i.worldPos);//計算別人投影到身上的陰影#include “AutoLight.cginc” Unity會將光照衰減和陰影相乘後存在第一個參數中,並自動聲明atten變量。第二個參數結構體包含SHADOW_COORDS,第三個參數世界空間座標
return fixed4((diffuse + specular) * atten, 1.0);//UNITY_LIGHT_ATTENUATION出的atten爲衰減和陰影
-----計算陰影投影到別人身上,自己的陰影
V2F_SHADOW_CASTER//unity裏邊定義陰影投射需要定義的變量
TRANSFER_SHADOW_CASTER_NORMALOFFSET(0)//unity對頂點進行自動處理
SHADOW_CASTER_FRAGMENT(i)//unity自動完成陰影投射部分,把結果輸出到深度圖和陰影映射紋理中
–ds2的陰影採用的是屏幕後處理的方式去計算陰影,延遲渲染
2.頂點照明渲染:過時的渲染方式。效果差。

3.延遲渲染:通常2個pass,第一個pass計算哪些片元可見,第二個pass計算真實光照。


1.反射方向:float3 reflect = reflect(o.lightDir, o.worldNormal);//入射光線,表面法線
2.折射: float3 refract = refract(normalize(o.lightDir), normalize(o.worldNormal), _RefractRatio);//入射光線單位向量,表面法線單位向量,介質比

3.鏡子效果:使用相機的RenderTexture來設置渲染紋理。o.uv.x = 1 - o.uv.x;座標需要翻轉一下。
4.玻璃效果:反射和折射使用cubemap進行採樣 是天空盒的cubemap,然後反射需要採樣的是周圍環境的光照和紋理。
GrabPass { “_RefractionTex” }//會把屏幕輸出到_RefractionTex的texture中, _RefractionTex_TexelSize ,要先聲明該變量 就可以可以得到該紋理紋素大小,例如255*255的紋素是(1/255,1/255) 值是: Vector4(1 / width, 1 / height, width, height);
GrabPass{} //然後用_GrabTexture直接訪問屏幕圖像,但是這樣效率比較低,推薦要上面需要聲明的方法。
o.scrPos = ComputeGrabScreenPos(o.pos);//得到對應被抓取的屏幕圖像的採樣座標
反射和折射需要顯示環境的效果,所以需要對環境的cubemap進行採樣。先用反射和折射的公式計算出光線,然後對環境貼圖進行採樣texCUBE(_Cubemap, i.worldRefl).rgb就可以得到具體效果了。
反射skybox 3d採樣,折射屏幕抓取圖像2d採樣。


時間變量
_Time:float4 //t是自該場景加載開始所經過的時間,4個分量是(t/20,t,2t,3t)
_SinTime:float4 //t是時間的正玄弦值,四個分量的值分別是(t/8,t/4,t/2,t)
_CosTime:float4 //t是時間的餘玄弦值,四個分量的值分別是(t/8,t/4,t/2,t)
unity_DeltaTime:float4// dt是時間增量,4個分量分別是(dt,1/dt,smoothDt,1/smoothDt)
序列幀動畫:時間去控制uv座標映射轉換。uv座標的xy是頂點座標,映射到小格子裏邊,和UItexture的xy和寬高不一樣。
背景偏移動畫:時間控制uv座標偏移。
水流動畫:通過時間和正弦函數去控制頂點偏移,通過時間控制uv移動。設置DisableBatching=true
廣告牌BillBoarding:根據視覺方向來旋轉被紋理着色的多邊形。頂點動畫


屏幕後處理
void OnRenderImage(RenderTexture src, RenderTexture dest){}//全部渲染完後將調用,屏幕紋理存在src上,用來計算後通過dest返回,添加[ImageEffectOpaque]屬性,可使不透明物體(AlphaTest)被渲染完後立即調用.
Graphics.Blit(src, dest);//直接copy紋理。src是屏幕當前或上一步渲染的紋理,dest是目標紋理
Graphics.Blit(src, dest, material,pass=-1);//將把src傳到shader的material的_MainTex紋理。經過material(shader)的處理後輸出到dest渲染到屏幕.pass默認是-1會調用所有pass,否則只調用給定順序的pass。指定pass渲染很重要。

基於顏色變化的邊緣檢測:Sobel卷積算法,對邊緣點進行採樣計算 和特定矩陣卷積相乘。
高斯模糊:多次採樣紋理混合 消耗較大
Bloom效果:把較亮區域提取出來進行高斯模糊 模擬擴散效果,然後再與原紋理混合。
運動模糊:將上一幀的屏幕圖像存到renderTexture中,然後執行Graphics.Blit(src, renderTexture, material),shader將開啓混合Blend SrcAlpha OneMinusSrcAlpha把src紋理和目標緩衝紋理renderTexture進行混合,然後再Blit輸出到dst進行渲染。就得到了運動模糊效果。


深度和法線紋理
float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
_CameraDepthNormalsTexture //unity中調用camera.depthTextureMode=DepthTextureMode.Depth/DepthNormal;這句話後可以通過該變量訪問深度紋理或者深度和法線紋理,project空間
float _depth=SAMPLE_DEPTH_TEXTURE(tex,uv)//對深度紋理進行採樣,返回project空間下非線性深度值。和tex2D類似 只是封裝了平臺。自動將NDC座標下的深度映射(0,1)需要轉換到(-1,1)veiw空間去計算
_depth 是非線性深度值
LinearEyeDepth(_depth)負責把深度紋理的採樣結果轉換到視角view空間下的線性深度值 轉換到nearclip到farclip範圍
Linear01Depth(_depth)則會返回一個範圍在0,1的線性深度值,轉換到0-1的範圍。

half4 sample1 = tex2D(_CameraDepthNormalsTexture, i.uv);
half2 centerNormal = DecodeViewNormalStereo(sample1);//center.xy存的法線的映射值,EncodeViewNormalStereo
float centerDepth = DecodeFloatRG(center.zw);//zw深度,EncodeFloatRG
DecodeDepthNormal(sample1,out centerNormal,out centerDepth)//獲取採樣的法線和深度

Camera.worldToCameraMatrix //世界轉相機矩陣 world2view
Camera.cameraToWorldMatrix //相機轉世界矩陣
Camera.projectionMatrix //get投影矩陣viewToproject 視角空間到2維投影空間矩陣,set自定義的投影矩陣。如果你改變這個矩陣,相機的渲染不再基於它的fieldOfView更新,直到調用ResetProjectionMatrix
默認把view2project矩陣叫成project矩陣,默認把World2view矩陣叫做view矩陣。比如ViewProject就是world 2 project矩陣
全局霧效
深度霧效:通過每個頂點的深度值計算出該點到攝像機的距離d,然後把距離d進行參與公式計算得到霧效圖(遠的霧濃 rgb值大,近的霧淡 rgb值小),再把原圖和霧效圖進行混合。一般用線性,指數,指數平方公式,ds採用指數平方。
地面霧效:通過深度值和攝像機的方向向量計算該點到攝像機的偏移量,再加上攝像機的位置得到該頂點在世界空間中的座標,然後把該座標的y值參與霧效計算。如果用座標z參與計算和深度霧類似。
#pragma multi_compile_fog

基於法線的邊緣檢測:防止陰影等信息干擾檢測,判斷臨近的4個點的法線和深度值是否是近似,如果差距過大則是邊緣roberts算法。(屏幕後處理)


渲染輪廓線:基於法線方向擴散的邊緣檢測,第一個pass對頂點進行法線方向擴散渲染,第二個pass用真實渲染實際光照,覆蓋第一次,對擴散的頂點未被覆蓋的像素就產生了輪廓效果。(模型輪廓)
主角遮擋輪廓線渲染:第一個pass 渲染Ztest greater不寫深度,第二個pass ztest less正常渲染顏色。這裏要注意:要想主角材質之間不渲染相互遮擋,所有RenderQueue必須設成相同,才能保證在同一批裏邊渲
染 深度值是一樣的,如果先後渲染順序不同,深度值已經被寫入過,就會出現內部互相遮擋渲染。


噪聲
消融效果:怪物消失漸散的效果,把某個像素值小於閾值的裁剪掉,閾值附近的值用burncolor進行混合。陰影的pass裏邊算陰影時也把該項給clip掉,這樣陰影就動態變化了//clip(burn.r - _BurnAmount);
水面擾動效果:用時間去控制偏移距離,然後對該頂點的uv偏移兩點的法線平均值來代替該點的法線值。水面=反射+折射+繞動,波浪效果要用正弦函數偏移頂點、
ds水內的擾動UV=uv+0.03*offset, offset通過時間和噪聲圖計算,這樣會產生頂點和旁邊點的擾動0.03倍距離左右。
float2 speed = _Time.y * float2(_WaveXSpeed, _WaveYSpeed);
/ Get the normal in tangent space
fixed3 bump1 = UnpackNormal(tex2D(_WaveMap, i.uv.zw + speed)).rgb;
fixed3 bump2 = UnpackNormal(tex2D(_WaveMap, i.uv.zw - speed)).rgb;
fixed3 bump = normalize(bump1 + bump2);
全局(動態)霧效:通過時間控制噪聲紋理的偏移距離,然後根據噪聲顏色值來參與計算霧效濃度,然後計算霧效,就有了流動和淡濃的效果。

線性空間:顏色的正常編碼比如0,0.5,1編碼到0,0.5,1.
伽馬空間:顏色編碼存儲在伽馬空間,對顏色值一般進行Power(col,0.45)計算,比如0,0.22,1編碼到0,0.5,1.把更多的顏色值用來存儲眼睛容易辨別的較暗局域。該轉換會使顏色看起來變亮了、我們的大多數圖片顏色值存儲在該空間。
伽馬矯正:指將線性空間衍射到伽馬空間。2.2*0.45=1
CRT顯示伽馬:由於歷史和巧合,屏幕對顏色進行輸出的時候會進行顯示伽馬操作,計算Power(col,2.2),所以輸出會比原圖變暗。power有很多好處,比如可以使線性硬邊緣軟化,看起來更柔和。
Unity ColorSpace Setting:不支持移動平臺,所以我們一般都選擇Gamma Space
Gamma Space:實際上就是“放任模式”,不會對shader的輸入進行任何處理,即使輸入可能是非線性的;也不會對輸出像素進行任何處理,這意味着輸出的像素會經過顯示器的display gamma轉換後得到非預期的亮度,通常表現爲整個場景會比較昏暗。
Linear Space:Unity會背地裏把輸入紋理設置爲sRGB模式,這種模式下硬件在對紋理進行採樣時會自動將其轉換到線性空間中.也會設置一個sRGB格式的buffer,此時GPU會在shader寫入color buffer前自動進行伽馬校正再次轉換到伽馬空間。
流程Gamma Space:原光照-伽馬編碼(圖片存儲的格式編碼,變亮)-Unity不處理-CRT顯示伽馬(變暗)-輸出原光照顏色(相比編碼後的原圖會變暗)
流程Linear Space:Unity光照-Unity自動轉換線性空間計算 然後再伽馬矯正轉換到伽馬空間存儲在緩衝中-CRT顯示伽馬-輸出顏色正確,如果用gamma的話 計算完光照不會進行伽馬矯正 所以經過CRT會變暗。linear的線性轉換指圖片採樣 ,光照計算兩者一樣。
ds2:圖片格式在伽馬空間,選擇ColorSpace爲Gamma,進行顏色採樣時先進行float3 diffuseCol = pow(tex2D( diffTex, texCoord ), 2.2 ); 伽馬矯正,在線性空間計算光照屏幕後處理,最後在matcopy輸出的時候再return fragColor.rgb = pow(fragColor.rgb, 1.0/2.2);還原回去。最後經過CRT的伽馬矯正輸出。

HDR高動態範圍渲染:正常光照圖像範圍是0-1,但是實際光照可能超過1 ,HDR使用16位圖進行渲染 使顏色值包括在更大的範圍內計算 最後再通過Tonemapping衍射到0-1範圍 ,使顏色更加真實。
當camera上的hdr被激活時,OnRenderImage的source圖會存儲成16位的ARGBHalf格式,然後計算Bloom和Tonemapping生成的採樣圖也可以定義成16位格式,那麼效果會更好。HDR對美術和硬件性能要求高,所以手遊一般很少用,難達到那麼好的效果。
Tonemapping 色調映射:HDR使效果看起來更柔和,防止過亮和過暗, tonemapping根據當前的場景推算出場景的平均亮度,再根據這個平均亮度選取一個合適的亮度域,再將整個場景映射到這個亮度域得到正確的結果。用該公式把採用的hdr顏色轉換到ldr範圍值內。
如果沒有HDR也能簡單的柔和。只有在相機啓用HDR時才更有意義。這個特效需要顯卡擁有像素着色器(2.0)或者OpenGL ES 2.0,但是我們採用的是簡單的伽馬參數控制的色調映射。同時在該算法裏邊計算亮度,飽和度和銳化修改。簡單的tonemapping算法有很多種實現方式。
算法千差萬別,但是目的是一樣的,就是通過公式,把輸入值HDR顏色或正常顏色映射到0-1範圍內去顯示,可以畫出公式曲線理解都在0-1範圍內,並且根據曝光係數調整曲線 使大部分顏色輸出到看起來更加接近該亮度的區域。
Bloom :可通過HDR實現效果更佳,bloom本身可通過模糊實現,開啓HDR後高斯採樣圖我們可以使用RenderTextureFormat.ARGBHalf格式的紋理 採用了更高精度的紋理後,再通過算法效果會更好。
數據點雲模型的作用:有時候求比如高斯模糊的時候,可以用工具按規則生成周圍隨機的一些偏移點,數據點雲的模型效果有時候比直接計算效果好,如果提前知道模型和計算方式的話。

延遲渲染:所謂延遲渲染只是預先把所有燈光的顏色算好後,疊加到主光上參與光照計算,比如ds會把預先把點光源光照計算出來,通過在光源處Camera.RenderToCubemap(rt)渲染周圍全景圖,用該全景圖去計算光照,全部點光源計算完了 再去疊加計算主光的光照。而不用像延遲渲染那樣每個燈光要渲染計算所有場景光照。

全局光照GI:計算的是光的二次反射,有兩種算法,漫反射爲主:一般從該點法線方向按一定規則隨機散射幾條射線出去一定距離,只要計算到該射線點和該採樣點相交的(該種射線方向可只採樣一次對比),就把交點算入該點的顏色影響範圍內,把幾條射線的交點顏色按照距離衰減疊加到源顏色 就可以了。
另一種是高光爲主(ds)方法,從視線的反射方向方向射出幾條射線,射出去計算交點和SSR一樣,分8段計算深度對比,每段都有一定隨機偏移,計算交點後出uv採樣疊加到源顏色上。如果所有射線採樣點深度都比實際採樣點深度大,說明該方向沒有對該點顏色有貢獻。

環境遮蔽AO:傳統的AO實現方法通常是使用光線跟蹤的來完成。通過從每個可視點發出若干條光線 ,並與全局的場景進行相交測試來得到相應點的AO值。用於計算某點除了光源以爲被其他的物體遮擋導致的軟陰影。所以開啓會變暗。
屏幕空間環境光遮蔽SSAO:屏幕後處理實現,根據點雲模型(或者random採樣),取周圍規則隨機點進行深度採樣,然後將該點深度和周圍採樣點的深度進行對比,正常模型是對周圍法線求半球積分 半球積分需要更多的採樣次數效果纔會比球積分好,爲效率我們求得是對視角方向整球積分,半球積分需要對cos積分後進行二次積分,不然會導致半球面的同一個角度的圓圈上的點算出來的值是一樣的,結果不正確。
我們簡化爲向量P(當前點到周圍採樣點uv偏移對應的viewpos的方向向量)與viewdir的點乘(cos是sin的積分,全球積分),該SSAO各個角度看到的效果強弱不一樣,當該點爲凸點 周圍的採樣深度大於該點,當該點爲凹點時周圍深度小於該點,點乘後大於0爲凹,小於0爲凸,根據深度差與ViewDir的點乘就知道周圍點對該點的AO貢獻值。由於我們blend one one,而且是整球積分點乘後的AO值-1,1,大於0表示凹處被遮擋,小於0表示凸出不遮擋,轉換到0,1後,減去0.5可只顯示遮擋處,但是會導致夾角處半圓覆蓋面積不夠採樣失真,所以我們用原值,整體場景會變亮很多,再通過其他方式把暗度調下
SSAOPro採用的是向量P(當前點到周圍採樣點uv偏移對應的wordpos的方向向量)與當前點normal的點乘的半球積分max(0.0, dot(norm, v))(1/(1+d)),d是該點到採樣點的距離,該SSAO強弱與視角無關,採樣次數少的時候全球積分效果好,採樣很多或者模型精度高的話半球積分效果好,目前手機上只能用全球積分可以達到效果。SSAO優化一個採用UIMASK裁剪被UI遮擋的地方,另外一個用深度裁剪 遠處的地方不進行SSAO可以提高效率。另外對SSAO計算後的圖進行Blur後再疊加,效果會更好但是指令數會增加。
SSAO消耗=blur指令數
blur像素+SSAO指令數*SSAO像素數量,把shader編譯到彙編指令,數一下有多少條指令 可做參考,只選單平臺可方便看。SSAOPro140個指令非常高了,像我們SSAO才50個。
常用優化方法:降低分辨率,減少採樣數,UIMask,隔像素計算。
高斯模糊Blur:高斯公式計算了周圍所有點對該點的疊加的影響值,越遠影響越小,採樣範圍越大效果越正確。我們一般取周圍2圈的附近代表點進行模擬,因爲實際方式去採樣一圈8個點效率太低、
取該點周圍1個像素和2個像素距離的四個點,再乘以一個倍數,然後再將多張圖按高斯規律疊加,也就是根據高斯算法離中心點越近的所佔該點像素值比率越大,因爲高斯公式是2維的,所以正常採用2個pass分別對水平豎直方向一維採樣模擬降維,但是效果要求不高的時候我們也可以用一個pass取模擬近似效果,9個點:float weight[3] = {0.4026, 0.2442/2, 0.0545/2}保證所有采樣點疊加在一起的權重和爲1。如果不按高斯權重進行疊加顏色會得到其他類似模糊效果
另外cpu階段傳過來採樣的圖最好先進行降分辨率(參數控制分辨率降低倍數)處理再採樣提高效率。然後CPU還要多次採樣混合效果纔好,但效率低。

Bloom:把較亮區域提取出來進行高斯模糊 模擬擴散效果,然後再與原紋理混合。bloom的高斯模糊需要用精確的二維採樣效果才能好。
運動模糊Blur:指的的是物體運動的拖影模糊,但賽車拖影模糊一般用環境徑向模糊,物體運動模糊可計算上一幀和當前幀進行混合採樣 和 相機運動模糊可求取相機運動方向在uv裏邊進行偏移後混合採樣。

Dof景深DepthOfFeild:指目標渲染附近的一定深度範圍內顯示清晰圖片,其他範圍呈現模糊效果。遊戲中一般用一個Dis,深度大於Dis的點經模糊處理 越遠越模糊 形成景深效果。ds的方式是用DrawMesh 第一個pass把深度大於Dis的點模糊渲染到tex中,第二個pass去把上幀繪製的tex直接加到currentColor中。其實我們也可以採用一個pass 把深度大於DIs的直接模糊返回,小於的返回原值就好了。
自發光:一般是用做邊緣發光,正常就是在shader內部自定義光照參數,比如UI上顯示的模型不經過光照可以自定義光照。
抗鋸齒AA:FXAA快速近似抗鋸齒是屏幕後處理常用方法,取uv當前頂點和左上,左下,右上,右下的半個uv像素偏移點,求取5個點的亮度值(dot(col,float3(0.22,0.707,0.071))或其他公式Luminance()),然後分別求出5個點的最小和最大亮度值,如果最大亮度與最小亮度值的差小於某個閥值(比如0.5或者計算)判定爲非邊緣點直接返回當前顏色,
然後對uv加減半個像素偏移(*Dir)採樣出col0,col1,再對uv加減2個像素偏移(*Dir)採樣出col2,col3(其中像素偏移值要和兩對角線亮度之差的差and和組成的向量通過公式計算單位向量Dir後相乘取近似),然後把前2個顏色合併(col0/2+col1/2)得ColA如果該值亮度不在之前計算的最小值與最大值之間返回該值,否則把四個顏色進行合併來返回、
求像素偏移倍數Dir是FXAA關鍵,該向量相乘後得到的uv點可以更加趨向邊緣,比如四個採樣點,左邊和右邊色差大,乘以Dir後取樣的左上右下點會趨向上下方向,使得邊緣更加真實。抗鋸齒採樣其實就是輕微的模糊處理,只是採樣範圍小點,亮度變化不大的不模糊就好了。
多重採樣抗鋸齒MSAA:只對畫面中物體的邊緣進行放大、混合的抗鋸操作,因爲邊緣是鋸齒最明顯的地方(注意不是所有的邊緣)。提取邊緣,主要是結合深度技術。MSAA是種硬件AA。我們一般說的4x、8x,就是放大倍數,放得越大,供混合的採樣越充份,效果越好,但是處理速度也就越慢
草隨風動:
水的泡沫:
邊緣光RimLight:有兩種方法實現,傳統的算法是求視角方向和頂點法向量,如果兩者垂直說明該頂點邊緣光,加上邊緣光顏色就好,該種效果所有輪廓線邊緣都會渲出。ds的辦法是屏幕後處理用法線貼圖的xy(節省效率可不用decodenormal)對一張masktexture進行採樣,採樣後的顏色乘以邊緣光顏色返回即可。
原理在於mask貼圖左下角爲原點,向上y變大,向右x變大(0-1),對於模型頂點的法向量也是向上y爲正 向下y爲負,向右x變大(-1,1),其未經decode的法線貼圖xy值(0-1)方向剛好對應了mask採樣方向,那麼對應maxk的上半部分賦一個凹圓顏色值,其他地方爲0值,那麼採樣的疊加的結果就是所有模型的上邊緣會有邊緣光因爲normal.y>0採樣、
ds裏邊還採用了stencil開關開材質是否開啓邊緣光。然後相機離主角位置遠近來動態設置邊緣光顏色倍數,越遠設置越大看起來邊緣光更多。

橡膠皮膚材質SSS次表面散射:光線進入材質後進入材質內部經反射折射影響周圍點的亮度呈現橡膠透明效果。ds的ssss優化過用模擬點來找到周圍的點的採樣的亮度值再來計算這些點對該點的亮度影響值,然後疊加,影響值一般與距離平方成反比,越近影響越大。

體積光:有三種實現方式,第一種是對頂點光源反方向進行拉絲偏移,第二種是對亮度較大的頂點uv進行光源反方向模糊偏移徑向模糊處理。第三種是光線追蹤、
徑向模糊:可實現賽車的運動周圍環境模糊和太陽光光線射出的效果,太陽光將亮度高的圖分離出來降分辨率進行模糊然後和原圖疊加。從屏幕中心點或者太陽位置向四周各自方向進行uv偏移實現太陽光的體積光。先求中心點到當前uv點的方向dir(包含距離和方向信息),最後對該點進行多次偏移採樣疊加,每次偏移uvoffset=diri_Dis就可以實現該效果。爲了優化效率可以把採樣模糊的圖分辨率降低,模糊後和原圖混合。

水下:繪製Quad,通過blend 混合,判斷世界空間該點的高度是否小於水面高度,小於水面高度部分進行擾動效果加霧效處理,當攝像機高於水面不渲染,低於渲染,處於水面之間的和水進行lerp操作。
更加逼真的水下效果需要繪製水面的反射水滴和折射天空,水中更加體現距離差的霧效和水底的焦散效果。還有最基本的擾動。
水下渲染最生硬的地方是當相機處於水之間的時候,那麼水面上下可能都只在屏幕內,水下側可以計算玩家位置上方水的屏幕y座標向下插值blend減弱水效果,但是水上側和場景融合就比較生硬 只能做相機碰撞解決 邏輯保證相機只能在水下或者水上。

下雨:用數張雨滴的法線貼圖,輪流採樣作爲紋理的法線方向就有了下雨效果,法線貼圖內含有數個雨滴各種效果。通過drawmesh繪製quad。雨周圍的反射可以用cubemap。一般小場景的反射用cubemap採樣,大場景只能再計算一次反射效果了。
float2 rainuv=frac(worldpos.xz*0.25);//用世界座標的每隔四米採樣一次rain圖片。frac取小數部分。然後blend one one
屏幕反射:繪製Quad,通過blend one one,從視角方向計算頂點的反射光線,首先對當前點頂點座標通過噪聲圖隨機偏移一點使之產生模糊效果(因爲該算法得不到精確的鏡面反射效果),然後從改點的反射光線方向射出一條射線比如for循環10次 每次在該反射方向加上一個偏移值進行對比計算,
如果加偏移後的點的深度大於該點對應屏幕(實際背景)的深度值,說明射線已經射到模型內部了,那麼取出該uv給原頂點用,同事對skytexture進行反射採樣和射線反射進行疊加。其中射線的每次偏移量根據攝像機角度計算,遠處偏移大,垂直角度偏移小。由於射線採樣深度次數多,所以效率不高。
垂直同步:顯示器上的所有圖像都是一線一線的掃描上去的,垂直同步就是等上一幀的線掃完了再畫下一幀 ·防止畫面撕裂,,高配置硬件開啓垂直同步能使得遊戲進程和顯示器刷新率同步,使得畫面平滑,使得畫面穩定,低配置關閉垂直同步可以一定程度上減小顯卡的負擔,讓畫面更加流暢一些。

噪音的作用:

自陰影HorizonMapping:1.通過法線方向與光源方向是否一致可以判斷該點是否是陰影點,2爲解決自身遮擋的問題,計算每一個點的仰角(當前點向某一個方向射出的射線,使其恰好遮擋該點,沒有遮擋則是0,用遞歸偏移去計算)然後把信息存在貼圖裏邊,光照的時候就只計算該點的仰角和光源角度,光源仰角大於該點我們就照亮該點。3.該方法只適合光源在某一個平面運動,如果光源在360空間運動,需要更麻煩的插值計算或者實時計算。


OnPreCull () // 把這個賦給相機.每幀在相機裁剪場景前調用,裁剪決定哪些物體可見 會被渲染。
OnPreRender ()// 把這個賦給相機.每幀在相機渲染場景之前被調用
OnPostRender () // 把這個賦給相機.每幀在相機完成場景渲染之後被調用。
void OnRenderImage(RenderTexture src, RenderTexture dest){}//屏幕後處理函數
Graphics.Blit (src, dest,mat,pass);//屏幕後處理 src當前屏幕紋理
Graphics.DrawMesh(aMesh, Vector3.zero, Quaternion.identity, aMaterial, 0);//給定材質繪製網格模型 只能在Update裏邊調用生效。

mat.SetPass(0);//給當前渲染幀設置shader渲染,爲渲染激活給定的pass 設置後如果blit沒有使用材質也會使用該pass,Blit and DrawMeshNow.
Graphics.SetRenderTarget(rtex)//設置當前的渲染紋理,如果設置該項後,DrawMeshNow再繪製的圖像將保存在該紋理中,然後blit到dst就可以單獨渲染顯示了。這是渲染總的圖形目標不同於相機視角的渲染目標。blit完後也需要重新設置SetRenderTarget。
Graphics.SetRenderTarget(colorbuff,depthbuff)//把渲染好的紋理和深度設置好 再繪製DrawMeshNow 纔會有疊加效果。一般情況下DrawMeshNow的rendertarget設置成相同的一個。
Graphics.DrawMeshNow(aMesh, Vector3.zero, Quaternion.identity);//只可渲染靜態物體,在相機上,用上一次(最近一次)處理的材質進行繪製,一般前邊加mat.SetPass(1);只用該pass進行渲染。matrix 可以對mesh進行矩陣變換。一定要SetRenderTarget纔會繪製在對應的buffer上
Graphics 是unity的底層API,Camera是Unity的上層API
手動繪製動態物體可用:CommandBuffer渲染,buff.DrawRender(render,mat);Graphics.ExcuteCommandBuffer(buff);該API會消耗2個drawcall,所以靜態物體儘量用DrawMeshNow.

Rendertexture rt=new Rendertexture(…)//w.h.depthbuffer,對於要渲染深度的rt,depthbuffer要設置至少16位。
camera.targetTexture=rt //設置相機的rt 相機會把渲染內容設置到rt上,然後該相機game窗口不可見
camera.clearFlags=CameraFlags.SolidColor;//記得設置的默認顏色
camera.SetTargetBuffers(rt.colorBuffer,rt.depthBuffer)//設置當前相機的渲染目標RenderTarget設置後要麼EncodeDepth
camera.RenderWithShader(shader,“RenderType”)//找物體shader有"RenderType"字段 和shader有"RenderType"字段相等的shader的subshader進行替換渲染。每幀調用纔會生效。也可以自定義如"Tag"=“MainChar”
繪製到rt後再和其他相機rt混合然後blit輸出,在該功能下的相機深度和顏色都是新的只滿足條件的物體的。如果在場景中,多個camera需要把深度和顏色存起來,然後設進去。
用camera渲染和其他camera混合渲染的時候camera.clearFlags=CameraFlags.Nothing不然如果設置SolidColor會把以前的color 給Clear掉

ds渲染colortexture和depthtexture,colortexture的colorbuffer用來存顏色和粗糙度,depthbuffer用來存系統深度,depthtexture的colorbuffer用來存深度和法線。
RenderTexture的colorbuffer和depthbuffer的理解:colorbuffer用來存camera.SetTargetBuffers後的顏色值,其中的值可以自己定義 可以是顏色也可以是深度和法向量 然後自己用來計算 對應RenderTexture的rgba值,depthbuffer是系統用來寫depth用的,還包含了stencil等信息,只決定前後關係,不會給我們參與計算。
所以在SetTargetBuffers爲多個RT時,shader裏邊的frag輸出也是輸出多個color值。
Camera.SetReplacementShader(shader,tag)//調用這個函數之後,相機將使用替換的shader來渲染它的視圖,一直替換渲染。當replacementTag爲空時會替換視口中所有物體的shader。
Camera.ResetReplacementShader()//從相機上移除shader替換。

要分清楚camera的SetTargetBuffers和Graphics的SetRenderTarget。如果是相機渲染到某個紋理用camera的,但是如果要設置圖形輸出的紋理設置Graphics,不要搞混。

ds渲染流程:shader寫好後在colorbuff.cg裏邊運算和輸出,屏幕後處理會根據平臺和設置使用替換的shader如MRT.shader去渲染,然後MRT.cg去輸出顏色。
模型材質上使用的shader並不是最終渲染使用的shader,根據設置在precull階段 會使用替換shader去渲染,比如流程用前向渲染和支持MRT的Shader渲染和低版本的DiffuseRoughness渲染。然後屏幕後處理階段用替換shader去實現一些特殊效果。
同一個shader的pass越多那麼提交GPU繪製的批次就越多 也就是drawcall越多。所以主角輪廓線渲染怪物深度時要當心。
屏幕水波紋擾動效果:1.把上一幀的屏幕圖像傳給水波紋和maintex參與計算擾動,然後縮放水波紋大小就形成了水波紋大小。技能特效的擾動也用該方法。


陰影:1.先用陰影相機變換到光源的位置渲染深度,將深度存儲在自定義深度紋理中。由於深度z範圍從nearclip到farclip,將z除以遠裁剪值,以把深度轉換到0-1空間進行存儲。
2.用正常相機渲染頂點 將頂點轉換到陰影相機空間 求得深度值z,再除以farclip轉換到0-1範圍內 再和上面採樣的depth做比較 判斷該點是否是陰影。陰影相機的targetTexture=rendertexture一定要設置,保證獲取陰影相機proj矩陣是按照陰影相機寬高渲染,不然project矩陣寬高會不對。
陰影鋸齒解決:
1.由於場景陰影分辨率不足,陰影計算鋸齒會比較嚴重,爲了使陰影效果更逼真 沒有鋸齒,可以採用周圍四次採樣深度值進行插值計算。普通場景可採用該方式提高效果。
5.對於主角或一些較高要求的物件,可以採用另外的陰影相機去渲染一張高分辨率的深度圖計算簡單計算陰影,這樣就不會有鋸齒了,最多可能會顯得比較自然硬。
6.可以在normal方向或者光照方向偏移一點點的bias來減少鋸齒,不能直接對z進行偏移。另外在畫陰影的時候參與光照計算效果會更好,如果直接畫陰影可能會
4.渲染RT的new Rendertexture(…)的時候 一定要設置深度至少16位纔有效。第三位參數,不然深度可能沒寫進去。
5.一般採用計算光照渲染的時候乘以陰影係數的效果會比直接畫陰影效果好
6.在設置相機的buffers和RenderWithShader後必須調用Graphics.SetRenderTarget,否則GPU上後續pass會失去原rt的信息。因爲camera.SetTargetBuffers會自動設置gpu的SetRenderTarget,但是調用完後會清除,所以必須重設。
7.normal等信息最好來自depthTex採樣,而不是vertex傳過來,因爲depthTex已經處理過了是否有bump等情況,會方便點。
8.RenderWithShader計算的參數來自於原材質的shader,需要聲明原變量就可以用,但是在計算主角的時候 我們直接採樣RT 就是用pro_uv去採樣參與計算、DiffuseRT經ColorBuffer.cg計算裏邊已經包含了粗糙度等基本信息 方便後續計算。
9.關於陰影係數,正常就是0和1,對於多次採樣有過度的可能有插值,對於單次採樣的0-1表現可能會生硬,一般採用該係數乘以一個值轉換到0-0.n或者0.n到1
10.軟陰影判斷陰影處的強度,可以通過判定該點和周圍採樣的深度差值,如果完全處於陰影點,周圍的深度和該點一樣,如果處於陰影邊緣點 周圍點的深度和該點就會有較大差值。
場景普通陰影(低分辨率)等級分級:
0級:沒有陰影處理
1-級:採樣1次深度進行簡單的深度對比。(當相機分辨率足夠的時候 用這個最精確,主角陰影繪製)
1級:y方向採樣2次深度進行對比插值,會有一點點邊緣軟陰影。
2級:2個方向進行4次採樣,再進行2次lerp,軟陰影效果會更好一點。
3級:周圍9個點進行採樣和多次插值,軟陰影效果可以很棒了。
4級:周圍8或12個點按一定規則去採樣,再和rot隨機圖進行矩陣乘法組合隨機轉向,保證相連點不採樣到同一點,最後疊加的效果邊緣模糊感更強。rot採樣圖很重要。ds暫時沒用
float depth = DecodeDepth(XY_Depth.xy)-newdepth>0;// depth=0 or 1


常用矩陣變換
Camera.worldToCameraMatrix //世界轉相機矩陣 world2view 右手座標系
Camera.cameraToWorldMatrix //相機轉世界矩陣
Camera.projectionMatrix //get投影矩陣viewToproject 視角空間到2維投影空間矩陣
transform.TransformDirection (Vector3.forward);//從自身座標到世界座標變換方向。返回的向量與direction有同樣的長度。
transform.InverseTransformDirection(0, 0, 1);//變換方向從世界座標到自身座標 important
transform.TransformPoint(2, 0, 0);//頂點變換位置從自身座標到世界座標
transform.InverseTransformPoint(0, 0, 0);//頂點變換位置從世界座標到自身座標,c#裏相機的世界空間轉view空間用這個,不需要用矩陣計算。
transform.worldToLocalMatrix//Unity的左手座標系,相機可用這個從world轉換到view空間,這個與Camera.worldToCameraMatrix(右手座標系)的區別在於z軸方向反向。Camera.worldToCameraMatrix與shader的UNITY_MATRIX_MV相同,用這個計算需要反轉z值。transform是unity左手系,camera是右手系。
GL.GetGPUProjectionMatrix(camera.projectionMatrix,true)//從Camera的投影矩陣得到GPU(圖形處理器)的投影矩陣(該函數在跨平臺是有用)。view2project
vec3=Matrix4x4.MultiplyPoint(vec3) //通過該矩陣把vec3點變換位置,從某一空間投影到另一個空間。
vec3=Matrix4x4.MultiplyVector(vec3) //通過該矩陣把vec3向量變換方向
Camera.RenderToCubemap(rt)//用相機渲染360全景圖,其中rt要設置isCubeMap=true,isPowerOfTwo=true; 這時候修改camera的旋轉是沒用的,一般用RenderTexture去渲染比CubeMap消耗少、
Camera.WorldToScreenPoint§//wolrd to 屏幕空間 左下角0,0 右上角width,height。 z就是深度了
Camera.WorldToViewportPoint§//world to view空間 左下角0,0 右上角1,1


平臺和宏定義
Direct3D裏面是左上角爲原點;
OpenGL和OpenGL ES是左下角爲原點。
三維座標轉換爲齊次座標 頂點把w分量設爲1,向量把w分量設爲0
SHADER_API_OPENGL - desktop OpenGL
SHADER_API_D3D9 - Direct3D 9
SHADER_API_D3D11 - desktop Direct3D 11
SHADER_API_GLES - OpenGL ES 2.0 (desktop or mobile), use presence of SHADER_API_MOBILE to determine.
SHADER_API_GLES3

頂點轉換:
#if UNITY_UV_STARTS_AT_TOP//dx 這個不管用了 無法識別Shadow_API_Metal 蘋果的新設備支持的平臺
proj.y*=-1;
#endif
不完全等價:
#if SHADER_API_GLES||SHADER_API_GLES3
//大部分安卓手機和部分ios手機,一般用該宏定義
#else
proj.y*=-1; //一般用這個
#endif
uv轉換:
#if SHADER_API_MOBILE
#if UNITY_UV_STARTS_AT_TOP
#else
uv.y = 1-uv.y;
#endif
#else
#if UNITY_UV_STARTS_AT_TOP
uv.y = 1-uv.y;
#endif
#endif
-------Unity預編譯宏--------
1.在pass裏邊#define USE_Dynamic_Fog 1 然後寫多個pass分級, 然後在cg裏邊 #if USE_Dynamic_Fog==1 … #endif,也可以只定義不取值。

2.#pragma multi_compile LOW Mid High,編譯了3個宏,該方式宏只能多選一,不能選多個,用 material.shaderKeywords = [“LOW”];或者material.EnableKeyword(“LOW”),那麼Shader裏邊只能判斷三個狀態,三個中的其中一個生效,如果沒有Enable一個宏或者Enable了多個宏,則默認第一個宏打開。
注意的是如果編譯單個宏就是打開和關閉狀態則用:該方法就可以在代碼裏邊通過打開關閉開關分級。
#pragma multi_compile R_Off R_On 也需要分兩個宏來區分,與#define情況不同。或者
#pragma multi_compile __ R_Off 與上等效(注意空格)。 等同於#pragma shader_feature R_Off //#if R_Off … #endif 這個和我們自己定義#define AO_QUILITY 3效果差不多


矩陣乘法和投影矩陣:
MNA:代表A先經過N矩陣變換再經過M矩陣變換。從右往左看。
矩陣變換的順序一般是:先縮放,再旋轉再平移,MtMrMsP
縮放矩陣:左對角線值
旋轉矩陣:左3x3矩陣
平移矩陣:第四列
Matrix4x4 cameraviewToshadowview=shadowcamera.worldToCameraMatrix
camera.cameraToWorldMatrix//從一個相機的view空間轉換到另一個shadow相機的view空間
一般情況下矩陣乘法結合在CPU裏邊做好,在GPU階段一般不這麼做,一是效率不好,另外一個是shader的矩陣直接相乘意思和上面的不一樣。
world和View之間的矩陣變換是線性的,但是一旦轉到project空間就是非線性的,所以要除以w進行歸一化。
常用變量計算方法:
float4 projectpos/=projectpos.w;//歸一化 歸一化後的NDC座標範圍在-1,1內,projectpos先用MVP矩陣把頂點轉換到project空間。
float2 uv=projectpos.xy*0.5f+0.5f;//project(需要歸一化)座標的xy就是uv座標的衍射 屏幕圖像的uv,uv座標也可以在頂點階段用unity的接口ComputeScreenPos()計算,然後在frag裏邊去歸一化就可以用了。
projectpos.z存的是深度緩存0-1 但是unity將其進行了矯正 是非線性的,會導致近處精度高 遠處精度低,那麼遠處效果就不會好,我們ds的深度值用的是view空間的z值0-far。
float depth=-viewpos.z/_ProjectionParams.w;//view座標的z值存的就是深度值,除以farclip衍射到0-1範圍存儲,z(<0表示在前方)座標需要乘以-1衍射深度。但是使用我們自定義的viewmatrix不需要,採用不同座標系已經定好了方向。projet的z是深度緩存。
渲染深度的時候要記得先把背景圖設置成白色,或者把相機clearFlag設置SolidColor然後顏色設置成白色,保證沒有被渲染的地方深度爲1

關於像素深度值還原座標:(因爲每個點都要矩陣轉換,所以效率不高)
float3 viewpos=mul(project_Inverse,float3(projectpos.x,projectpos.y,depth));//若depth=projectpos.z 表示把該模型的頂點還原view空間,取depth代表屏幕處於同一點顯示的前後座標點將project空間轉換到view空間、一般用來背景圖的深度值還原其具體座標。
我們的計算方式:(project空間還原知道深度的像素view 座標點 )
float3 farpos=float3(projectpos.xy,1);//該值代表project空間遠裁決上和當前屏幕xy重合的最遠座標點。ps裏邊計算projectpos的時候如果用系統提供的矩陣一定要在dx下projectpos.y=1-projectpos.y我們自己的矩陣好像是有轉換過的。
Vector3 _FarCorner;//c#裏邊計算的相機的farclip右上方座標點(x,y,z),其對應的左下方就是(-x,-y,z);這兩個座標的衍射剛好對應於project空間xy的(-1,1)之間。
float3 viewfarpos=farpos*_FarCorner;//該式直接將farpos映射到view空間的遠裁決上和當前屏幕xy重合的最遠座標點。
float3 viewpos=viewfarpos*depth;//利用相似三角形原理,在屏幕上重合的兩個點,在相機perspective視角下呈現相似三角形,遠裁剪上的點(proj.z=1)和求得點(z=depth)是一條線,直接相乘可得座標點。

ViewPos的理解,以相機爲座標系原點的座標,顯示結果是屏幕座標中間是(0,0),向上y變大 向下y變小,右邊x變大,座標x變小爲負、不要直接與世界座標空間的一些位置參與計算。normalize(ViewPos)爲相機到點方向與點到相機視角方向反向相反。
float4 wpos=mul(View2World,float4(vpos,1));//矩陣轉換空間的時候一定要帶上四維座標,如果只是三維的mul(View2World,vpos)代表值進行了三維運算的旋轉和縮放,沒有計算平移。如果要用w參與轉換一定要先保證矩陣轉換信息裏邊和原始值的w正確,像我們的farcorner的w是沒用的,所以轉換後w是無效的。

關於我們屏幕後處理的DrawMesh:
Graphics.DrawMesh(Quad,Vector3.zero,Quaternion.identity,0)//默認情況如果MVP轉換該mesh位置在相機原點。視錐範圍外 被剔除。
o.pos=float4(v.verter.x2,v.vertex.y2,0,1);//簡化直接轉換到project空間的近裁剪位置。ZTest Less
o.pos=float4(v.verter.x2,v.vertex.y2,1,1);//簡化直接轉換到project空間的遠裁剪位置。ZTest Greater
所以根據以上兩個pos轉換的位置不同,ZTest 也要不同。Quad的座標是-0.5,0.5 乘2直接到proj空間的-1,1.然後proj.z值0-1代表近裁剪到遠裁剪,proj.w=1 代表頂點。

float3 dir=mul((float3x3)Project,vdir);//vdir是float3的時候 矩陣必須轉換成三維矩陣,否則可能有些平臺會報錯。或者把vdir轉換爲float4也行,點w=1,向量w=0、
float4 dir=mul(Project,float4(vdir,0));//or

在給shader設置參數的時候:
mat.SetTexture("",_tex);//儘量用這個,只給該材質設置參數
Shader.SetGlobalTexture("",_tex)//全局變量,在cpu階段查找所有材質,有該變量就設置該參數。效率影響在cpu 應該不是gpu

環境光貼圖
Diffuse Environment CubeMap :使用網格表面的法線去採樣
specular environment CubeMap :使用反射矢量去採樣


常用光照模型和向量計算方法:
1.漫反射Lambert:float3 diff=max(0,dot(i.normal,i.lightDir));//lightDir是光源方向的單位向量,normal是法向量。C是光源顏色。反射光線的強度與表面法線和光源方向之間的夾角的餘弦值成正比。取max防止顏色爲負
2.Half-Lambert:float3 diff=dot(i.normal,i.lightDir)0.5f+0.5f;//將顏色空間映射到0-1而不是採用lambert的截斷,lamber模型會使沒有光照的地方全黑,可使用環境光避免,HalfLambert會使沒有光照的地方顯示微光。大部分情況用該模型。
2.高光反射Phong:float3 spec=pow(saturate(dot(reflectdir,viewdir)),_Gross);//視角方向與反射方向點乘截取後的gloss次方。gloss光澤度越大 亮點越小,求反射光用reflect注意光源方向和入射方向相反。
4.Blinn-Phong:float3= pow(max(0,dot(worldNormal,halfDir)),_Gross); float3 halfDir=normalize(worldlightdir,ViewDir); //對視角方向和光照方向相加再歸一化得到halfDir作爲參數,再來和法向量點乘 再power。高光看起來更亮更大一些,大部分情況用這個模型
3.輸出顏色:float4 col=(diff+spec+ambient)C
3.光源方向:常說的光源(光照)方向一般指點到光源的方向,而不是入射光線的方向。_WorldSpaceLightPos0.xyz,.w=0表示平行光,.w=1表示點光源或聚光燈,當然不同引擎可能存的方向也不一樣,但是unity是這樣定義的。
4.視角方向:float worldViewDir=normalize(_WorldSpaceCameraPos.xyz - worldPos.xyz); //unity API: UnityWorldSpaceViewDir(worldPos.xyz)一般指點到相機的視角方向,該公式在世界空間下。
5.View空間視角放向:float3 viewdir=viewpos;//view空間的座標值等於相機到該點的視角方向。
5.反射方向:float3 reflect = reflect(o.lightDir, o.worldNormal);//入射光線(不是光源方向),表面法線,一般用來求採樣cubemap或者天空盒採樣的反射信息。場景中的實時反射一般採用單獨渲染反射紋理, 把座標進行變換後合併。
6.視角光線的反射方向:float3 o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);//用視角方向的反向量 反求入射光線(反向),視角方向求視角光線的反射方向,用該反射方向去採樣紋理。
7.折射方向: float3 refract = refract(normalize(o.lightDir), normalize(o.worldNormal), _RefractRatio);//入射光線單位向量,表面法線單位向量,介質比
8.視角光線的折射方向:float3 worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);//用視角方向的反向量 反求折射光線(反向)
9.菲涅爾反射:float fresnel=pow(1-saturate(dot(worldViewDir,normal)),4)//視角方向和法線方向,他們之間的夾角越小,fresnel越弱 反射越越小 折射越強。另外也有反射和漫反射之間fresnel插值公式。爲了效率可不執行power操作。
float3 finalcol=fresnel
reflect+(1-fresnel)refract;//fresnel插值反射和折射,用來計算反射和折射之間根據視角方向各種佔的比重
10:光照衰減公式:float atten= 1/(_WorldSpaceLightPos0.xyz,wpos.xyz);//衰減物理公式與距離的平方成反比,有時候也用距離反比函數做衰減。
10.高度霧:float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);然後把該值和顏色進行插值。有線性霧,指數霧,指數平方霧。霧對噪聲圖進行動態採樣,可以實現動態霧效的效果。
11.PBR基於物理的着色:輸入參數 粗糙度和金屬性,ds的完美模式下采用PBR渲染,PBR()接口輸入各種方向,返回diff和spec,內部實現了金屬材質曲線的方式。但是快速的前向模式下,只是計算了簡單的漫反射。
金屬度大,漫反射變小,高光反射變大,金屬材質高光反射率RGB分量可能不同
金屬度小,漫反射變大,高光反射變小,非金屬材質高光反射率RGB分量是一樣的(大多數很接近<0.05, 0.05, 0.05>)
粗糙度大:光滑變暗,漫反射增多,鏡面反射變小
粗糙度小:越光滑,鏡面反射效果增多 漫反射變小
複雜的PBR效果是疊加了環境高光貼圖形成的,並不是單一的PBR公式。疊加的貼圖可以是金屬也可以是其他材質。
傳統光照模型:I=普通漫反射+高光
Cook-Torrance光照模型:I=普通漫反射L+鏡面反射光強R,光滑度控制鏡面反射,金屬度控制高光。
L=Lambert
saturate(1-f0
f0);//float f0 = (1-RR)/(1+RR);
鏡面反射光強R=FDG/(VN)或R=FDG/((VN)(NL)),也可以省略成R=FDG,其中F是Frenel係數,D是微平面法線分佈函數,G是幾何遮擋係數
F=f0+(1-f0)(1-vh)`5//f0代表入射角接近0 靠近法向量時的fresnel反射係數。該公式精確度1%。
D複雜公式
G幾何遮擋公式,也可以簡略爲固定值比如0.25
ds公式:float R=saturate(1-_SmoothBase)
float3 diff=1/PI max(0,dot(lightdir,normal))saturate(1-power((1-RR)/(1+RR),2));
float h=normalize(worldlightdir,ViewDir);
float nh=saturate(dot(normal,h));
float3 spec=R
RRR/power((nhnh(RRRR-1)+1),2)(1+meatal*3)/4PI
Ps.normal轉換到View空間參與計算,記得考慮座標系變化,可以i.view_pos_nor.z=-i.view_pos_nor.z把z反向,因爲Unity物體是左手座標系,view space是右手座標系。ds的EncodeNormal和DecodeNormal會自動轉換座標系,但是Unity的EncodeViewNormalStereo是不會轉換座標系的。頂點轉換也一樣要考慮座標系。

12.次表面散射其中一種實現方式環繞光照:diff = ( dot(normal, lightDir) + wrap )/( 1+wrap ) //wrap爲環繞參數,最後再計算該點模型的厚度作爲參數相乘、輸出顏色=coldiffdepthMinus;//diff乘以顏色和厚度(前後深度差)

發佈了19 篇原創文章 · 獲贊 17 · 訪問量 2396
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章