渲染管線概念:也被稱為可編程流水線,渲染管線指的是在給定的3D場景中,根據一架給定的攝像機的視角生成2D圖像的一系列步奏。之所以是2D圖像,是因為我們的屏幕是2D的,因此我們需要通過2D屏幕來呈現3D場景。在了解渲染管線之前需要了解人體眼睛看到的物體會有以下特性:1、平行線匯集成一個點(向遠處望去,目光會在一個點聚集);2、物體的大小會隨著距離增加而減小,越遠的物體看起來就越小;3、物體會有重疊(景深).光照和陰影是能體現立體感和體積感的因素.三角形是構建物體模型的最基本單位,三角形越多,密度越大,效果越好,但是越耗性能.
寫shader的目的就是告訴GPU往屏幕哪里畫顏色、怎么畫顏色;Shader的作用就是計算物體最終顯示的顏色是什么.Shader在渲染管線中可以由程序員控制的部分是Vertex Shader(頂點著色:作用是頂點變換,輪廓信息,把模型正確的、有層次的畫在屏幕上)和 Fragment Shader(片元著色:作用是修正每個像素的顏色);
3d坐標轉換(矩陣變換,逐頂點霧化、材質屬性和光照屬性)-->面的組裝,面截取,面剔除-->光柵化-->對每一個像素區域進行著色,貼圖.光柵化的解釋:是把頂點數據轉換為片元的過程,具有將圖轉化為一個個柵格組成的圖象的作用.決定窗口坐標中的哪些整型柵格區域被基本圖元占用;分配一個顏色值和一個深度值到各個區域,把物體的數學描述以及與物體相關的顏色信息轉換為屏幕上用于對應位置的像素及用于填充像素的顏色,使用插值計算方式,這個過程稱為光柵化.
簡潔來說即是CPU進行模型數據/貼圖/色彩的準備,然后將其發送給GPU進行使用,GPU將其一一對應,比如模型數據中每個頂點/每個面上的點對應的每一個像素每一個貼圖的像素等等需要大量計算,并且GPU適合于這種計算(CPU不適合).
另外一種GPU渲染管線的流程是:①Vertex Shader(頂點著色器,對每個頂點相關的屬性進行創建/修改/忽略,程序員參與編寫);②The Geometry Shader(幾何著色器,可選階段,從多邊形網格中增刪頂點);③Clipping(裁剪,將沒有在屏幕中顯示出來的部分進行裁剪);④Screen Mapping(屏幕映射,將之前步驟得到的坐標映射到對應的屏幕坐標系上);⑤Triangle Steup(三角形設定,計算三角形表面的差異以及三角形其他相關數據);⑥Triangle? Traversal(三角形遍歷,找到那些采樣點或像素在三角形中);⑦Pixel?Shader(像素著色,對每一個像素進行處理);⑧Merger(合并,顏色修改(Color Modifying),Z緩沖(Z-buffer),混合(Blend),模板(Stencil)和相關緩存)等.
RGBA(red,green,blue,Alpha)的計算方式是按照每個值都在0-1(包含0-1)的范圍內進行(純標量)加減乘運算的;為什么白光會被吸收掉一部分,并反射出去,利用運算公式來表示:C0(1,1,1)這是白色,C1(0.75,0.25,0.5)這是粉紅色,2個顏色分量相乘即C=(1*1,1*0,1*0)=(0.75,0.25,0.5)得到粉紅色,紅色吸收25%反射75%,綠色吸收25%反射75%,藍色吸收50%反射50%;這就是為什么夏天穿黑色的衣服更容易發熱的原因,黑色(0,0,0),白色-黑色表示吸收率為100%.
頂點數據:傳遞給GPU的數據是頂點數據,為了能獲取到法線數據,將法線數據存儲到頂點數據里面,在GPU處理的階段,直接獲取到法線的數據或者其他數據.將頂點數據進行封裝,一般包括①空間坐標信息②法線信息③顏色信息④其他.將這些數據放入 頂點數據/頂點緩沖區 容器等待GPU進行加載.
索引緩存:等待GPU加載之前還有 索引/索引緩沖區? 這個概念,是因為如果給GPU 4個頂點,GPU 不知道怎么去繪制圖形,繪制成什么樣子的圖形,就需要索引幫忙完成繪制的樣子.①索引順序不同,構成的三角形也不同,法向量也不同;②DX默認順時針為正方向,OpenGL默認逆時針為正方向;③背面剔除需要用到索引順序(關閉三角形背面的渲染,減少模型的面數);④索引可以減小內存(不會把模型的2面都會渲染出來);⑤索引是簡單整數數據;
優化:將多個模型的頂點數據/索引數據合并成一個頂點緩沖區/索引緩沖區,然后通知GPU進行渲染,而不是進行多次通知,因為通知是有成本的.這叫做合并緩沖區.模型的頂點/索引數據經常變化,一般情況下不加入合并緩沖區.
頂點數據變換測試:頂點著色,需要我們自己編寫函數,傳入頂點數據,并且交由GPU執行.
投影:即將3D物體投影到2D窗口上面的過程.
GPU有可能處理的非常快速有可能處理的非常慢,顯示器的顯示速度就無法跟GPU的繪制速度保持一致.因此就通過雙緩沖區來解決這個問題。一個是前臺緩沖區一個是后臺緩沖區。顯卡將繪制好的圖像放置于這個兩個緩沖區中,然后切換顯示就可以避免這個問題。比如:緩沖區1和緩沖區2。假設最初緩沖區1為前臺緩沖區,前臺緩沖區顯示的已經繪制完成的畫面。緩沖區2就為后臺緩沖區,我們將數據持續寫入到后臺緩沖區2。當緩沖區2寫入完成之后,就交換這兩個緩沖區。把緩沖區1當成后臺緩沖區,緩沖區2當成前臺緩沖區,然后我們繼續將數據寫入到后臺緩沖區1。那么這個緩沖區就保存我們所有的圖像信息。GPU會假定平臺都會完成NDC規范化設備坐標,即后臺緩沖區的屏幕大小在[-1,1]這個區間中,也是3D映射到2D上面算出的比值規范,不然,則不會正確顯示模型.
片元著色器:像素著色,頂點插值會被當做像素著色器的輸入數據,傳入到像素著色器里面。與頂點著色器一樣,像素著色器也是一個函數,它處理得是像素片段。因此它也叫片段著色器。像素著色器的任務就是為每個像素片段計算一個顏色值。1、盡量使用常量值 2、盡量在頂點著色器里面計算好需要的數據? 3、避免在像素著色器里面做復雜的計算;? ? ? ? ? ? ? ? ? ? ? 輸出階段: 像素片段由像素著色器生成之后,被傳送到輸出合并器(output merger),在該階段某些像素被丟棄,未被丟棄的像素被寫入到后臺緩沖區。混合工作就是在這個階段完成的,在后面我們會專門講到混合。
OBJ解析?
OBJ文件是一種文本文件,可以直接用記事本打開進行查看和編輯修改。OBJ文件格式支持直線(Line)、多邊形(Polygon)、表面(Surface)和自由形態曲線(Free-form Curve)。直線和多邊形通過它們的點來描述,曲線和表面則根據它們的控制點和依附于曲線類型的額外信息來定義,這些信息支持規則和不規則的曲線。 (1)OBJ文件是一種3D模型文件。不包含動畫、材質特性、粒子等信息。(2)OBJ文件主要支持多邊形(Polygons)模型。(3)OBJ文件支持法線和貼圖(UV)坐標。關鍵字
頂點數據(Vertex data):v 幾何體頂點(Geometric vertices)? vt? 貼圖坐標點(Texture vertices)? vn 頂點法線(Vertex normals)?
元素(Elements):? p 點(Point)? l 線(Line)? f? 面(Face)? curv? 曲線(Curve)? ?curv2 2D? 曲線(2D curve)? surf? 表面(Surface)
組(Grouping):? g? 組名稱(Group name)? s 光滑組(Smoothing group)? mg? 合并組(Merging group)? o 對象名稱(Object name)
顯示(Display)/渲染屬性(render attributes): usemtl 材質名稱(Material name)? mtllib 材質庫(Material library)
例子:一個模型有頂點,UV坐標,法線,最后定義模型.例子,我們手工編寫一個三角模型, 新建一個txt文件然后輸入:
將后綴的txt去掉,在unity3d編輯器中obj是可以被識別的,如下變成這種模式:
最后導入unity3d編輯器中:
攝像機照射m模型,然后Scene中從背面看這個模型是透明的,Game中是正面,這個就是plane的特性.這些模型是由美術人員建模并構建,不需要游戲開發人員操心.本章只探究原理.ps:UV映射的意思是將3D物體拆成2D的一個面片,再將2D的圖片,根據拆成2D面片的UV坐標進行映射,像素一一對應.
Shader
著色器語言/類型:?1標量:??bool #true | false????int? ?# 32位有符號整數????half/fixed # 16位浮點數????float # 32位浮點數????double # 64位浮點數
2:向量:?????float2 # 2D向量????float3 # 3D向量????float4 #4D向量????復合分量:????float4 u = {1.0f, 2.0f, 3.0f, 4.0f}????u.xyzw? ?u.wyyz????u.zzxy? ?u.rgba
3、矩陣類型? ? float2x2 # 2x2矩陣 mat2???????????????????? float3x3 # 3x3矩陣 mat3???????????????? float4x4 # 4x4矩陣 mat4
4、數組? ? float m[4]???????????? half????a[2]???????????????? float3 v[12]
5、結構體? ? ?????結構體的定義方法跟c/c++完全一樣。但是不能包含函數。 struct Input { float3 pos;???? float3 normal;????float2 uv0;????float2 uv1; }
6、typedef關鍵字?typedef與c/c++功能一樣。即給某個類型指定一個別名typedef float2 point;???? float2 pos = point pos
7、變量修飾符? ? ?static????????#靜態類型,程序無法訪問這個變量????uniform????#常量,程序指定一個值,在著色器中不會在發生變化,也無法改變 const????????#常量
8、類型轉換? ? ?轉換機制非常靈活????float3 n = float3(1, 0, 0)???????? float3 v = 2.0f * n - 1.0f
9、關鍵字+保留字????asm bool compile double else extern half if in matrix out pass shared static string true typedef uniform volatile while auto const_cast explicit new reinterpret_cast template unsigned break case continue default friend goto operator private short signed this throw using virtual catch char delete dynamic_cast long mutable protected public sizeof static_cast Try typename class enum namespace register switch union
10、運算符?[]????.????>????<????<=???? >= != == ! && || ?: + += - -= * *= / /= % %= ++ -- = () ,% #取摸,也可能是mod函數。可以用于整數和浮點數
?float4 u = float4(1.0f, 2.0f, 3.0f, 4.0f)
? float4 v = float4(2.0f, 3.0f, 3.0f, 4.0f)
?float4 sum = u + v
sum++
sum = sum * sum
?注意:如果是對于矩陣,乘法運算可能在不同的平臺有不同意義。
11、程序流程控制?return if (condition) {? } else if (condition) {? } else {? }? for (initial;condition;increment) {} while(condition) {} do {} while();
12、函數?函數語法跟c/c++一樣。? 1、參數為值傳遞? 2、不支持遞歸????3、都是內聯函數
bool foo(in a, int b) { bool ret = false; if (a > b) { ret = true;} else {? ret = false; } return ret;}
float square(float x) {? return x * x;}
13、內置函數
????abs(x)????????????????????????# x絕對值
????ceil(x)????????????????????????#返回x最小整數
????clamp(x, a, b)????????????#截取x到[a,b]
????clip(x)????????????????????????#丟棄當前像素。有些平臺是discard指令
????cos(x)????????????????????????# x余弦值
????cross(u, v)????????????????#返回u和v的叉積
????degrees(x)????????????????#弧度轉角度
????distance(u,v)????????????# u和v之間的距離
????dot(u,v)????????????????????# u和v的點積
????floor(x)????????????????????????#返回x的最大整數
????frac(x)????????????????????????#返回小數部分
????length(x)????????????????????#返回x的長度
????lerp(u, v, t)????????????????#根據參數t在u和v之間線性插值[0,1]
????log(x)????????????????????????# ln(x)
????log10(x)????????????????????# log10(x)
????max(x,y)????????????????????#最大
????min(x,y)????????????????????#最小
????mul(m,n)????????????????????#矩陣乘積
????normalize(v)????????????#規范化
????pow(b,n)????????????????????#返回b的n次方
????radians(x)????????????????#角度轉弧度
????saturate(x)????????????????# clamp(x, 0.0, 1.0)
????sin(x)????????????????????????# x的正弦值
????sqrt(x)????????????????????????#開放
????reflect(v,n)????????????????#根據入射向量v和法線n計算反射向量
????refract(v,n,eta)????????#根據入射向量v,法線n和折射指數比計算反射向量
????tan(x)????????????????????????#正切
????transpose????????????????#轉置矩陣
????texture1d|sample1d?# 1d紋理采樣
????texture2d|sample2d????# 2d紋理采樣
????texture3d|sample3d????# 3d紋理采樣
編寫shader前的準備
我在選擇的編輯器是VS,需要從github上面下載?ShaderlabVS插件(https://github.com/wudixiaop/ShaderlabVS/releases)下載?ShaderlabVS-2017-0.8.zip?雙擊添加即可.下面是基本格式,都需要我們去給這些屬性賦值之后,我們在下面代碼編程中才可以使用這些"材料":
shader里面的屬性類型包括:
Range(0,1) 定義浮點數屬性,在檢視器中可通過一個標注最大最小的滑動條來修改.? ? Color 定義顏色屬性? ? ? ? 2D 定義2d紋理類型? ? ? ? ????????????????????Rect 定義長方形(非2次方)紋理屬性? ? Cube 定義立方貼圖紋理屬性? ? Float 定義浮點數屬性? ? Vector定義一個四元素容器(vector4)屬性? ? 備注:對于Range和Float的類型只能是單精度值,對于Color和Vector類型的屬性將包含4個由括號圍住的數描述.對于紋理(2D,Cube,Rect)缺省值皆可以是一個空字符串也可以是某個內置的缺省紋理:"white" ,"block","gray","bump"? ? ;? ?
?紋理后面的大括號{}為紋理屬性選項,為可選項,①Texgen texgenmode紋理生成模式,為對應貼圖的自動紋理坐標生成模式,為objectLinear,EyeLinear,SphereMap,CubeReflect,CubeNormal之一,這些模式和OpenGL紋理生成模式相對應,如果使用自定義頂點程序,紋理生成將被忽略.②LightmapMode光照貼圖模式,紋理將被渲染器的光線貼圖屬性所影響,不能被使用在材質中,而是取自渲染器的設定.
必須具有一個(或者一個以上的)SubShader{Pass{}} 這個shader文件才可以被使用.FallBack "Diffuse"是指所有的SubShader都不能滿足(硬件性能太低),會調用這個Diffuse shader 使用.FallBack Off 明確表示沒有后援的shader?FallBack "name"制動后援名字.
這里只做個例子,具體的標簽值需要去網站查找 https://docs.unity3d.com/Manual/SL-SubShaderTags.html ;
RenderState通用狀態:????Cull 設置多邊形剔除模式? Back 背面剔除 Front 前面剔除 Off都不剔除 ;ZTest 深度測試,默認LEqual ;? ColorMask ;ZTest;ZWrite ;AlphaTest ;Blend ;FOG;? ? ? ? ? ? ? ? ? ? 查詢網址:https://docs.unity3d.com/Manual/SL-CullAndDepth.html? ?
pragma 的格式 https://docs.unity3d.com/Manual/ShaderTut2.html??
例子:#pragma vertex vert??????#pragma? 頂點著色器? 頂點著色器方法,將數據(系統操作)傳入此方法中進行(程序員)加工.
在Properties定義的屬性需要在pass通道里面重新定義一樣的名字,不然無法使用.文檔 https://docs.unity3d.com/Manual/SL-PropertiesInPrograms.html
_NameTex_ST? ? ?具有重復貼圖的shader
3d建立大型場景常用的shader
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Custom/TilingShader"{Properties{
_Color("Base Color", Color) = (1,1,1,1)
_MainTex("Base(RGB)", 2D) = "white" {}
_ColorU("ColorU", float) = 1.0
_ColorV("ColorV", float) = 1.0
_DetailTex("DetailTex", 2D) = "white" {}
_DetailU("DetailU", float) = 1.0
_DetailV("DetailV", float) = 1.0}
SubShader{
tags{"Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True"}
Blend SrcAlpha OneMinusSrcAlpha
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 _Color;
sampler2D _MainTex;
float _ColorU;
float _ColorV;
sampler2D _DetailTex;
float _DetailU;
float _DetailV;
struct v2f{float4 pos:POSITION;float2 uv:TEXCOORD0;};
float4 _DetailTex_ST;
v2f vert(appdata_base v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
//o.uv = TRANSFORM_TEX(v.texcoord, _DetailTex);
//o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
return o;}
half4 frag(v2f i):COLOR
{half4 c = tex2D(_MainTex , i.uv * float2(_ColorU, _ColorV)) * _Color;
half4 d = tex2D(_DetailTex, i.uv * float2(_DetailU, _DetailV));
return c * d;}ENDCG}}}
LightMap:模擬燈光對物體的影響,將這個場景變成一張圖片,然后貼在真實場景上面進行模擬.一般對靜態物體進行模擬.燈光照射的明暗信息記錄下來的過程,叫做烘焙.優點是省去復雜的光照計算,可以對貼圖進行二次處理,缺點是多了一層紋理,通常需要額外的UV,靜態貼圖無法動態改變光的方向(比如游戲場景中的電燈泡,風一吹需要來回晃動,此時使用靜態貼圖比較假)等.
unity3d shader中RenderType的所有類型:https://blog.csdn.net/u013477973/article/details/80607989
Opaque: 用于大多數著色器(法線著色器、自發光著色器、反射著色器以及地形的著色器)。不透明,圖片什么樣,就在游戲中展示出什么樣
Transparent:用于半透明/全透明著色器(透明著色器、粒子著色器、字體著色器、地形額外通道的著色器)。
TransparentCutout: 蒙皮透明著色器(Transparent Cutout,兩個通道的植被著色器)。
Background: Skybox shaders. 天空盒著色器。
Overlay: GUITexture, Halo, Flare shaders. 光暈著色器、閃光著色器。
TreeOpaque: terrain engine tree bark. 地形引擎中的樹皮。
TreeTransparentCutout: terrain engine tree leaves. 地形引擎中的樹葉。
TreeBillboard: terrain engine billboarded trees. 地形引擎中的廣告牌樹。
Grass: terrain engine grass. 地形引擎中的草。
GrassBillboard: terrain engine billboarded grass. 地形引擎何中的廣告牌草。
Fade:淡入淡出
ps:Draw Call :每次引擎準備數據并通知GPU的過程,通俗講,就是每幀調用顯卡渲染物體的次數.