一、背景
1.1应用
- 曲面细分着色器
- 海浪(曲面细分+顶点位移动画)
- (交互)雪地(曲面细分+顶点位移)
- 置换贴图(曲面细分+顶点位移)
- 几何着色器
- 几何动画(几何图元)
- 草地等(与曲面细分着色器结合)
1.2渲染管线
- 顶点着色器
- 曲面细分着色器
- 细分控制着色器Hull Shader(TCS)
- Tessellation Primitive Generator(不可编程)
- 细分计算着色器Domain Shader(TES)
- 几何着色器
- 片元着色器
1.3曲面细分着色器
1.3.1TESS的输入与输出
1.3.2HULL shader参数
- Tessellation Factor
- 决定将一条边分成几部分
- equal_Spacing
- fractional_even_spacing
- fractional_odd_spacing

- Inner Tessellation Factor
- (与上面的参数是同一等级)将边等分后,向内延伸相交,直至内部没有交点。

1.4几何着色器
1.4.1几何着色器的输入与输出
- 输入为图元(三角形、矩形、边)
- 根据图元不同,shader中会出现对应不同数量的顶点
- 输出同样为图元(一个或多个),需要自己从顶点构建,顺序很重要,同时定义最大输出的顶点数
二、曲面细分
- 将一个Quad细分
- 与置换贴图结合
- 注意使用置换贴图不在fs中,也是在domain shader中的vert函数进行的,GPU无法获取mipmap信息,需要使用tex2Dlod等来读取图片。
- 法线也需要重新计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| #pragma hull hullProgram #pragma domain ds
#pragma vertex tessvert #pragma fragment frag #include "UnityCG.cginc" #include "Tessellation.cginc"
... struct VertexInput; struct VertexOutput{ float4 vertex: SV_POSITION; float3 normal; float3 tangent; float2 uv; }; VertexOutput vert(VertexInput v) { VertexOutput o; o.uv = TRANSFORM_TEX(v.uv,_MainTex); o.vertex = UnityObjectToClipPos(v.vertex); o.normal = v.normal; o.tangent = v.tangent; }
#ifdef UNITY_CAN_COMPIE_TESSELLATION struct TessVertex { float4 vertex: INTERNALTESSPOS; float3 normal: NORMAL; float4 tangent: TANGENT; float2 uv: TEXCOORD0; }; struct OutputPatchConstant { float edge[3]: SV_TESSFACTOR; float inside: SV_INSIDETESSFACTOR; }; TessVertex tessvert(VertexInput v){ TessVertex o; o.vertex = v.vertex; o.normal = v.normal; o.tangent = v.tangent; o.uv = v.uv; return o; }
float _TessellationUniform; OutputPatchConstant hsconst(InputPatch<TessVertex, 3> patch) { OutputPatchConstant o; o.edge[0] = _TessellationUniform; o.edge[1] = _TessellationUniform; o.edge[2] = _TessellationUniform; o.inside = _TessellationUniform; }
[UNITY_domian("tri")] [UNITY_partitioning("fractional_odd")] [UNITY_outputtopology("triangl_cw")] [UNITY_patchconstantfunc("hsconst")] [UNITY_outputcontrolpoints(3)] TessVertex hullProgram(InputPatch<TessVertex, 3> patch, uint id : SV_OutputControlPointID){ return patch[id]; }
[UNITY_domain("tri")] VertexOutput ds (OutputPatchConstant tessFactors, const OutputPatch<TessVertex, 3> patch, float3 bary : SV_DomainLocation) {
VertexInput v; v.vertex = patch[0].vertex*bary.x + patch[1].vertex*bary.y + patch[2].vertex*bary.z; v.tangent = patch[0].tangent*bary.x + patch[1].tangent*bary.y + patch[2].tangent*bary.z; v.normal = patch[0].normal*bary.x + patch[1].normal*bary.y + patch[2].normal*bary.z; v.uv = patch[0].uv*bary.x + patch[1].uv*bary.y + patch[2].uv*bary.z; VertexOutput o = vert(v); return o; }
#ENDIF
float4 frag...
|
三、几何着色器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #pragma vertex vert #pragma fragment frag #pragma geometry geo ... struct VertexInput; struct VertexOutput{ float4 vertex: SV_POSITION; float3 normal; float3 tangent; };
VertexOutput vert; struct geometryOutput{ float4 pos: SV_POSITION; float2 uv: TEXCOORD0; } geopetryOutput CreateGeoOutput(float3 pos, float2 uv){ geometryOutput o; o.pos = UnityObjectToClipPos; o.uv = uv; return o; }
[maxvertexcount(3)] void geo(triangle vertexOutput IN[3]: SV_POSITION, inout TriangleStream<geometryOutput> triStream){ float pos = IN[0].vertex; float vNormal = IN[0].normal; float vTangent = IN[0].tangent; float vBinormal = cross(vNormal,vTangent)*vTangent.w; float height = ... float width = ... ... geometryOutput o; triStream.Append(CreatGeoOutput(dosomething(pos),dosomething(uv)); triStream.Append(CreatGeoOutput(dosomething(pos),dosomething(uv)); triStream.Append(CreatGeoOutput(dosomething(pos),dosomething(uv)); }
|
也可以添加更多图元,如:

三角形的构建是triStream自动完成的。(uv是自己计算的。)
作业
使用曲面细分、几何着色器做一些有意思的Demo

看了一圈,基本上大家都是参考那两个教程,就不多写什么了。(做到这个效果还挺想复刻一下Clannad的片头的,说不定挺适合)
这里主要完成了基本的曲面细分-几何着色器多pass渲染带阴影和光照的草地-风的扰动。
事实上,几何着色器构建的图元会覆盖原来的三角形图元,原来的表面其实消失了。这里可以再加一个pass渲染。
并且地面pass可以不做细分来节省开销
还可以进一步处理的部分:
继续往后做这两个方向感觉就需要考虑偏向may佬说的在项目中整体方案的思路了。
暂时就放一放
参考资料
[1] https://www.bilibili.com/video/BV1XX4y1A7Ns
【技术美术百人计划】图形 3.3 曲面细分与几何着色器 大规模草渲染
[2] https://catlikecoding.com/unity/tutorials/advanced-rendering/tessellation/
[3] https://roystan.net/articles/grass-shader.html