日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

shader 获取法线_Unity Shader 入门到改行5——法线贴图

發布時間:2024/9/27 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 shader 获取法线_Unity Shader 入门到改行5——法线贴图 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

the best of blur

1. 法線貼圖理論

1.1 什么是法線貼圖

一般的貼圖中存儲的是表面顏色值(RGBA),而法線貼圖存放的則是法線信息(xyzw),假設某頂點處的 uv 坐標為 (u,v), 那么在法線貼圖 (u,v)處紋素的值表示該頂點的“法線”方向。通常法線貼圖中存儲的并不是這個頂點的真實法線信息。

1.2 法線貼圖的作用

想象一下,如果我們想要表現一個凹凸不平的模型表面(想象一個橙子的表面),有哪些辦法呢?

直接把模型做成凹凸不平。這種方法最理想,效果也最好。但是模型需要太多頂點了,例如橙子表面的一個“坑”,需要增加額外的若干個頂點。

做一個一定精度的平滑模型(例如把橙子做成一個球體模型),把表面的”坑“或”凸點“信息,也就是某一點的”海拔“記錄下來,渲染的時候根據這些信息動態生成頂點信息,得到凹凸不平的模型。不用說,這種方法需要單獨的存儲空間來記錄凹凸信息,而且頂點動態生成將會非常消耗。

和第二種方法一樣,做一個平滑模型,同樣記錄表面的“海拔”,渲染時不是動態生成頂點,而是根據“海拔”信息反推頂點的法線信息,通過光照效果來表現表面的”凹凸“。這種方法在計算光照時需要先進行表面法線的計算,比較消耗。

同樣做一個光滑模型,不是記錄表面的凹凸信息本身,而是記錄”假定的凹凸情形下的法線信息“,渲染時根據“有偏差”的法線信息來進行光照計算,使得渲染出來的畫面看起來凹凸不平。

上面第三種方法稱為基于“高度紋理”的凹凸表現。而第四種方法就是基于“法線紋理”的凹凸表現。

注意:高度貼圖和法線貼圖用來表現“凹凸”,在模型輪廓的邊緣會穿幫。比如你可以用這兩種方法使一個平滑的橙子模型表面看起來凹凸不平,但是在橙子的邊緣總是平滑的。

1.3 法線貼圖紋素取值范圍

通常貼圖紋素用來表示 RGBA,那么每個分量的取值范圍是[0,1],而法線的每個分量取值范圍為[-1,1],所以用貼圖紋素表示一個法線時,需要針對每一個分量做映射

pixel = (normal + 1) / 2;

在針對法線貼圖采樣后,進行逆運算

normal = 2 * pixel - 1;

得到實際的法線分量值。

1.4 法線貼圖基于什么坐標系

法線貼圖儲存了表面法線,而法線是一個方向,那么這個方向是基于什么坐標系?通常跟隨頂點數據一起傳輸到 頂點著色器中的法線,由 NORMAL 語義指定,是基于模型坐標系的。所以我們可以將法線在模型坐標中的值存儲到法線貼圖中,得到模型空間的法線貼圖,而在實際制作中,應用更多的是頂點切線空間的法線貼圖。

對于每個頂點,以頂點自身作為原點,頂點切線方向為x軸,法線方向為z軸,切線和法線方向叉乘得到 y 軸(副法線方向),得到這個頂點的 切線坐標空間,基于這個空間的法線記錄下來得到 頂點切線空間的法線貼圖。

左:模型空間的法線貼圖 右:切線空間的法線貼圖

模型空間法線貼圖的優點

(1)實現簡單,直觀

(2)更平滑的縫合和邊界處的表現。

切線空間法線貼圖的優點

(1)可重用,記錄的是“相對法線信息”,而模型空間的法線貼圖記錄的是“絕對法線信息”。

(2)可以做 UV 動畫來實現凹凸移動效果。

(3)可壓縮。z分量永遠是正方向,可以只存儲xy分量。

1.5 為什么切線空間的法線貼圖看起來都是偏藍色的?

切線空間的法線貼圖保存的是基于頂點的切線空間中的法線數值,而在頂點的切線空間中,真實法線的反向永遠是(0,0,1),經過上述的計算公式得到法線貼圖中存儲的值為 (0.5,0.5, 1),偏藍色。而修改后的法線通常也是 z 值最大,因為你不太可能有90度以上的法線修改,整體還是偏藍。

通常使用頂點切線空間的法線貼圖,而頂點空間中的修改后的法線值,z分量最大,換算成顏色就是 b 分量最大,所以法線貼圖通常看起來偏藍色。

2. 如何在 Shader 中應用法線貼圖

我們使用在切線空間下的法線貼圖,先上完整 shader 代碼,然后逐步分析,代碼如下:

Shader "Shader_Examples/04_NormalTexture_TangentSpace"

{

Properties

{

_MainTex ("Texture", 2D) = "white" {}

_SpecularColor ("SpecularColor", Color) = (1,1,1,1)

_Gloss ("Gloss", Range(8, 256)) = 20

_BumpTex ("BumpTex", 2D) = "bump" {}

_BumpScale ("BumpScale", Float) = 1.0

}

SubShader

{

Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase" }

Pass

{

CGPROGRAM

#pragma vertex vert

#pragma fragment frag

#include "UnityCG.cginc"

#include "Lighting.cginc"

sampler2D _MainTex;

float4 _MainTex_ST;

fixed4 _SpecularColor;

float _BumpScale;

sampler2D _BumpTex;

float4 _BumpTex_ST;

float _Gloss;

struct appdata

{

float4 vertex : POSITION;

float2 uv : TEXCOORD0;

float4 tangent : TANGENT;

float3 normal : NORMAL;

};

struct v2f

{

float2 uv : TEXCOORD0;

float4 vertex : SV_POSITION;

float3 lightDir : TEXCOORD1;

float3 viewDir : TEXCOORD2;

};

v2f vert (appdata v)

{

v2f o;

o.vertex = UnityObjectToClipPos(v.vertex);

// 模型空間副法線

fixed3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;

float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);

float3 lightDir = ObjSpaceLightDir(v.vertex);

float3 viewDir = ObjSpaceViewDir(v.vertex);

o.lightDir = mul(rotation, lightDir);

o.viewDir = mul(rotation, viewDir);

o.uv = v.uv;

return o;

}

fixed4 frag (v2f i) : SV_Target

{

float3 lightDir = normalize(i.lightDir);

float3 viewDir = normalize(i.viewDir);

float3 halfDir = normalize(lightDir + viewDir);

float4 packedNormal = tex2D(_BumpTex, i.uv);

float3 tangentNormal = UnpackNormal(packedNormal);

tangentNormal.xy *= _BumpScale;

tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

fixed3 albedo = tex2D(_MainTex, i.uv).rgb;

fixed3 diffuse = _LightColor0.rgb * albedo.rgb * saturate(dot(tangentNormal, lightDir));

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

fixed3 specular = _SpecularColor * _LightColor0 * pow(saturate(dot(halfDir, tangentNormal)), _Gloss);

return fixed4(diffuse + ambient + specular, 1.0);

}

ENDCG

}

}

}

渲染效果如圖:

法線貼圖效果

2.1 shader 屬性與對應的變量

Properties

{

_MainTex ("Texture", 2D) = "white" {}

_SpecularColor ("SpecularColor", Color) = (1,1,1,1)

_Gloss ("Gloss", Range(8, 256)) = 20

_BumpTex ("BumpTex", 2D) = "bump" {}

_BumpScale ("BumpScale", Float) = 1.0

}

漫反射紋理 _MainTex, 高光顏色 _SpecularColor 和高光系數 _Gloss 沒什么好說的,新增的紋理 _BumpTex 為法線貼圖,默認值為 unity 內置法線貼圖 "bump",_BumpScale 用來控制表面的“凹凸”程度,后面會分析它是怎么起作用的。對應的變量聲明:

sampler2D _MainTex;

float4 _MainTex_ST;

fixed4 _SpecularColor;

float _BumpScale;

sampler2D _BumpTex;

float4 _BumpTex_ST;

float _Gloss;

2.2 著色器輸入結構

struct appdata

{

float4 vertex : POSITION;

float2 uv : TEXCOORD0;

float4 tangent : TANGENT;

float3 normal : NORMAL;

};

struct v2f

{

float2 uv : TEXCOORD0;

float4 vertex : SV_POSITION;

float3 lightDir : TEXCOORD1;

float3 viewDir : TEXCOORD2;

};

語義TANGENT指定的切線是一個 float4 類型的變量,而語義NORMAL指定的法線是 float3 類型,因為 TANGENT 的z分量需要用來確定 副法線 的方向,下一個段落會介紹如何計算副法線

因為使用了頂點切線空間下的法線貼圖,我們需要把所有的光照計算都變換到頂點切線空間下,在頂點著色器中將光線方向lightDir和視線方向viewDir變換到頂點切線空間,再輸入到片元著色器中。

因為我們這里沒有涉及到紋理的 ST 變化,所以 _MainTex 和 _BumpTex 功用紋理坐標

v2f 中并沒有定義法線,因為我們這里使用的是發現貼圖中的法線,而不直接使用頂點法線了

2.3 頂點著色器

v2f vert (appdata v)

{

v2f o;

o.vertex = UnityObjectToClipPos(v.vertex);

// 模型空間副法線

fixed3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;

// 模型空間到頂點切線空間的變換矩陣

float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);

// 光線方向和視線防線變換到頂點切線空間

float3 lightDir = ObjSpaceLightDir(v.vertex);

float3 viewDir = ObjSpaceViewDir(v.vertex);

o.lightDir = mul(rotation, lightDir);

o.viewDir = mul(rotation, viewDir);

o.uv = v.uv;

return o;

}

頂點的法線:頂點所在的所有平面的法線加權平均,得到頂點法線

頂點的切線:我們都知道頂點切線與頂點法線垂直、但與頂點法線垂直的方向有很多?哪一條是頂點切線呢?約定俗成 切線最終規定為頂點 uv 坐標中的 u 方向,可以參考文末的參考文章1。

頂點的副法線:由法線和切線叉乘得到,方向性由頂點切線的z分量確定。

如何計算模型空間到頂點切線空間的變換矩陣:參考我的推導過程模型空間到頂點切線空間變換矩陣的推導。結論就是:將模型空間下的切線、副法線、法線按行排列得到變換矩陣。

在頂點著色器中將光線方向和視線方向變換到頂點的切線空間并傳遞給片元著色器。

2.4 片元著色器

fixed4 frag (v2f i) : SV_Target

{

float3 lightDir = normalize(i.lightDir);

float3 viewDir = normalize(i.viewDir);

float3 halfDir = normalize(lightDir + viewDir);

float4 packedNormal = tex2D(_BumpTex, i.uv);

float3 tangentNormal = UnpackNormal(packedNormal);

tangentNormal.xy *= _BumpScale;

tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));

fixed3 albedo = tex2D(_MainTex, i.uv).rgb;

fixed3 diffuse = _LightColor0.rgb * albedo.rgb * saturate(dot(tangentNormal, lightDir));

fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;

fixed3 specular = _SpecularColor * _LightColor0 * pow(saturate(dot(halfDir, tangentNormal)), _Gloss);

return fixed4(diffuse + ambient + specular, 1.0);

}

如何從法線貼圖中得到法線:tex2D采樣 _BumpTex 得到該點的法線像素值,需要計算出對應的xyz值,因我們已經在 Unity 編輯器中將 _BumpTex 設置為 "Normal Map" ,所以內置方法 UnpackNormal 已經執行了這個計算

albedo,diffuse,ambient,specular 的計算不用多說了

_BumpScale 的作用:用來控制“凹凸程度”,當 _BumpScale 為0時,表示該點的頂點法線和法線貼圖中采樣出的法線重合,說明該點沒有“凹凸”,_BumpScale 絕對值越大,表示該點的頂點法線和貼圖中的法線偏差越遠,說明“凹凸感”越明顯。

下面5個膠囊體的 _BumpScale 取值分別為 2/1/0/-1/-2

不同的_BumpScale凹凸效果

3. Unity中的法線貼圖類型設置

在上面的片元著色器中,我們從法線貼圖中采樣出紋素后,使用了 Unity 內置函數 UnpackNormal 來計算最終的法線值。只有正確的設置圖片的類型為 "Normal Map" 時,使用這個內置函數才能得到正確結果,在 Unity 中的設置面板如下:

法線貼圖設置

Create from Grayscale 表示是否“高度圖”生成的紋理貼圖。當我們在貼圖中記錄的是相對高度(黑色表示更低,白色表示更高)時,除了要設置類型為“Normal Map”之外,還要勾選這個選項,這個貼圖就會被當成紋理貼圖使用了。

勾選了 Create from Grayscale 之后,有兩個選項:bumpness表示凹凸程度,filtering 決定了如何生成紋理貼圖,smooth 表示生成的法線過渡比較平滑,而sharp 則表示法線過渡比較鋒利。

總結

以上是生活随笔為你收集整理的shader 获取法线_Unity Shader 入门到改行5——法线贴图的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲成av | 亚洲精品人妻av | 在线免费观看高清视频 | aaa亚洲精品 | 一久久 | 37p粉嫩大胆色噜噜噜 | 毛片福利 | 亚洲xx站| 91高清在线免费观看 | 亚洲石原莉奈一区二区在线观看 | 天天伊人网| 五月av在线 | 欧美日韩一区二区不卡 | 麻豆av一区二区三区久久 | 欧美日韩激情一区 | 激情五月婷婷小说 | www.毛片com| 国产女女做受ⅹxx高潮 | 污视频免费在线观看网站 | 亚洲男人av| 欧美a级在线观看 | a√天堂资源 | 亚洲国产tv | 亚洲特级毛片 | 精品国产乱码久久久久久久软件 | 在线久草 | 午夜天堂精品久久久久 | 欧美国产精品一区二区 | 麻豆亚洲一区 | 美女毛片在线 | 一级空姐毛片 | 老女人丨91丨九色 | 五月婷婷一区二区三区 | 国产中文字幕在线 | 久久久久久日产精品 | 一区二区三区视频 | 欧美在线播放视频 | 99热6这里只有精品 三级av在线免费观看 | aaaaaaa毛片 | 久久看av | 欧美精品一区二区三区三州 | 99视频精品 | 欧美成人三级 | 亚洲www. | 成人午夜免费福利视频 | 大胸美女吻戏 | 少妇愉情理伦片bd | 五月婷婷社区 | 日本黄色大片网站 | 在线国产小视频 | 人妻少妇偷人精品久久性色 | 69精品久久 | 伊人影院中文字幕 | 欧美超碰在线观看 | 亚洲丁香婷婷 | 国产精品 色 | 亚洲天堂中文字幕在线观看 | 少妇姐姐| 国产精品99久久久久久宅男 | 国产精品日韩在线 | 亚洲精品资源在线 | 精品无码一区二区三区电影桃花 | 亚洲天堂av影院 | 中国精品一区二区 | 亚洲一区影视 | 亚洲人无码成www久久 | 91免费片 | 在线观看av国产一区二区 | 美女日批网站 | 国产精品久久国产精品 | 亚洲色成人www永久在线观看 | 日本色综合 | 欧美熟妇乱码在线一区 | 日韩一区二区高清视频 | 欧美激情图区 | 久久婷婷亚洲 | 99伊人| 精品动漫3d一区二区三区免费版 | 青青草香蕉 | 日韩二区三区四区 | 欧美成人乱码一二三四区免费 | 97黄色片 | 特黄在线 | 少妇一级淫片免费观看 | 日韩草逼 | 五级毛片 | 丁香啪啪综合成人亚洲 | a v在线视频| 天天综合天天综合 | 亚洲欧美999 | 免费a级大片 | www日本视频 | 五月色婷| 97久久久| 天堂va蜜桃一区二区三区 | 黄色在线免费观看网站 | 久九九| 一级片福利 | 亚洲最大的成人网站 |