unity輕量管線LWRP中實現tessellation功能
阿新 • • 發佈:2019-01-13
記錄使用unity 輕量管線中遇到的一些坑
在LWRP中使用表面細分功能和傳統buildin裡使用方式基本一致(使用hull domain shader的方式,而不是surface shader ,LWRP不支援surface shader)。唯一問題,這篇文章的發出時的LWRP版本 4.6之前,如果開啟了MSAA,會導致表面細分功能出現問題,考慮可能是unity bug,期待後續修復。
儘管這篇文章是講在LWRP下實現tessellation的方法,同樣適用於傳統渲染管線和HDRP高清渲染光線,只需做些許調整。
Shader "LWRP/Custom/TessellationSimple"
{
Properties
{
// 這裡填入需要在inspector面板顯示的屬性
// 例如tessellation factor就可以設定成可調節的,而不是直接在程式碼裡定義
}
SubShader
{
// 輕量管線需要宣告 RenderPipeline 為 LightweightPipeline,否則不會顯示
Tags { "RenderType"="Opaque" "RenderPipeline"="LightweightPipeline" }
//LOD 100
Pass
{
// 聲明當前pass的為輕量管線中的LightweightForward,這是輕量管線中的著色pass
// 還有其他pass,可以直接使用 Fallback 裡標準內建材質的定義
// 如果有頂點位移動畫,可能需要重寫DepthOnly pass 和shadowcaster pass。
Tags {"LightMode"="LightweightForward"}
HLSLPROGRAM
// tessellatio 需要GPU支援shader target 4.6 也就是 OpenGLES 3.2 ,其他平臺metal, D3D,vulkan 可以參看unity文件
#pragma target 4.6
// LWRP 4.x版本 修改了引用package裡hlsl檔案的搜尋路徑,需要填完整路徑"Package/xxxx/yyyy/zzzz.hlsl"
#include "LWRP/ShaderLibrary/Core.hlsl"
// 頂點著色器vert
#pragma vertex vert
// 片元著色器frag
#pragma fragment frag
// hull shader,Hull 這裡計算表面細分因子
#pragma hull Hull
// domain shader,Domain 這裡執行表面細分邏輯
#pragma domain Domain
// 定義巨集,用於後邊設定最大tessellation 因子,可以設定最大值64,但是在主機平臺上最大隻能為15,建議移動端也設定為15
#define MAX_TESSELLATION_FACTORS 15.0
// 頂點shader的輸入結構
struct CustomVertexInput {
float4 vertex :POSITION; // 模型空間下頂點座標
float3 normal :NORMAL; // 模型空間下法線
float4 tangent :TANGENT; // 模型空間下切線
float2 texcoord :TEXCOORD0; // 頂點的UV座標
};
// 頂點shader 的輸出結構,以及 tessellation 的輸入結構
struct CustomVaryingsMeshToDS{
float3 posWS :INTERNALTESSPOS; // 世界空間下頂點座標
float3 normal:NORMAL; // 法線,這裡為模型空間下法線
float4 tangent:TANGENT; // 切線,這裡為模型空間下切線
float2 uv:TEXCOORD0; // uv
};
// tessellation domain shader的輸出結構和片元shader的輸入結構
struct CustomVertexOutput {
float4 clipPos :SV_POSITION; // 齊次裁剪空間下座標
float4 uv :TEXCOORD0; // UV 座標 ,xy主紋理UV,zw,法線紋理UV。
float3 posWS :TEXCOORD2; // 世界空間下座標
float4 normal :TEXCOORD3; // 法線
float4 tangent :TEXCOORD4; // 切線
};
// 表面細分因子的結構,通用結構,和unity buildIn shader 裡定義的UnityTessellationFactor結構一樣
struct TessellationFactors{
float edge[3]:SV_TessFactor; //三角面的三個邊的細分因子
float inside:SV_InsideTessFactor; // 三角形內部的細分因子
};
TessellationFactors HullConstant(InputPatch<CustomVaryingsMeshToDS,3> input){
// 這裡細分因子需要根據不同需求計算,這裡直接寫裸數,可以參照Buildin shader 裡的表面細分的幾種方式。
float4 tf=float4(2,2,2,1);// GetTessellationFactors(p0,p1,p2,n0,n1,n2);
TessellationFactors output;
output.edge[0]=min(tf.x,MAX_TESSELLATION_FACTORS);
output.edge[1]=min(tf.y,MAX_TESSELLATION_FACTORS);
output.edge[2]=min(tf.z,MAX_TESSELLATION_FACTORS);
output.inside=min(tf.w,MAX_TESSELLATION_FACTORS);
return output;
}
// 頂點著色器
CustomVaryingsMeshToDS vert (CustomVertexInput v)
{
CustomVaryingsMeshToDS o=(CustomVaryingsMeshToDS)0;
o.uv.xy = v.texcoord;
// 這裡需要注意的是,只計算世界空間下頂點座標即可,齊次空間下的計算會在Domain shader裡處理
float3 posWS = mul(UNITY_MATRIX_M, float4(v.vertex.xyz,1.0)).xyz;
o.normal.xyz =v.normal;
o.tangent =v.tangent;
o.posWS = posWS;
return o;
}
//[maxtessfactor(MAX_TESSELLATION_FACTORS)]
[domain("tri")] // 處理三角面
[partitioning("integer")] // 細分的因子的引數型別,這裡用integer表示整數,可以為浮點數 "fractional_odd"
[outputtopology("triangle_cw")] // 順時針頂點排列作為三角面的正面
[patchconstantfunc("HullConstant")] // 計算三角面細分的因子的函式,這裡並不是常量,不同三角面可以有不同的值,常量可以理解為對一個三角面內部的三個頂點來講是統一的值。
[outputcontrolpoints(3)] // 明確指出每個patch處理三個頂點資料
CustomVaryingsMeshToDS Hull(InputPatch<CustomVaryingsMeshToDS,3> input ,uint id:SV_OutputControlPointID){
return input[id]; // 這裡直接輸出,計算細分因子是在指定的HullConstant函式裡
// 這裡可以進行其他計算,例如頂點位移等等
}
// 把細分的結果資料轉入光柵化階段這裡只把頂點的世界座標轉為了齊次裁剪空間的座標,這裡往往會有其他計算
CustomVertexOutput VertTesselation(CustomVaryingsMeshToDS input)
{
CustomVertexOutput output=(CustomVertexOutput)0;
output.clipPos=mul(UNITY_MATRIX_VP,float4(input.posWS,1.0)); //TransformWorldToHClip(input.posWS);
output.posWS=input.posWS;
output.uv.xy=input.uv;
output.normal=float4(input.normal,1);
output.tangent=input.tangent;
return output;
}
[domain("tri")] // 指定處理的是三角面 triangle
CustomVertexOutput Domain(TessellationFactors tessFactors,const OutputPatch<CustomVaryingsMeshToDS,3> input ,float3 baryCoords:SV_DomainLocation){
CustomVaryingsMeshToDS data;
// 這裡使用了一個巨集定義來減少重複程式碼的書寫
#define MY_DOMAIN_PROGRAM_INTERPOLATE(fieldName) data.fieldName = \
input[0].fieldName * baryCoords.x + \
input[1].fieldName * baryCoords.y + \
input[2].fieldName * baryCoords.z;
MY_DOMAIN_PROGRAM_INTERPOLATE(posWS) // 插值計算頂點座標
MY_DOMAIN_PROGRAM_INTERPOLATE(normal) // 插值計演算法線
MY_DOMAIN_PROGRAM_INTERPOLATE(tangent) // 插值計算切線
MY_DOMAIN_PROGRAM_INTERPOLATE(uv) // 插值計算UV
return VertTesselation(data); // 處理插值結果,準備光柵化階段需要的資料
}
// 片元著色器
half4 frag (CustomVertexOutput IN) : SV_Target
{
// 簡單的輸出白色
float4 col = float4(1,1,1,1);
return col;
}
ENDHLSL
}
}
// 使用標準內建shader裡的其他pass
// 需要注意的是,LWRP4.x版本修改了標準內建shader的名稱,這裡需要對應的修改
FallBack "LightweightPipeline/Standard (Physically Based)"
}