Unity 几何着色器
Unity 几何着色器
如果学习不能带来价值,那将毫无意义
简介
在顶点和片段着色器之间有一个可选的着色器,叫做几何着色器(Geometry Shader)。几何着色器以一个或多个表示为一个单独基本图形(primitive)即图元的顶点作为输入,比如可以是一个点或者三角形。几何着色器在将这些顶点发送到下一个着色阶段之前,可以将这些顶点转变为它认为合适的内容。几何着色器有意思的地方在于它可以把(一个或多个)顶点转变为完全不同的基本图形(primitive),从而生成比原来多得多的顶点。
输入
几何着色器阶段可将整个图元作为输入,并能够在输出上生成顶点。
必须首先指定单次调用几何着色器输出的顶点的最大数量(每个图元调用几何着色器)。这可以通过使用以下属性语法在着色器定义之前设置最大顶点数:
[maxvertexcount(N)]
其中N是几何着色器为单个调用输出的顶点的最大数量。几何着色器可以在每次调用时输出的顶点数量是可变的,但不能超过定义的最大值。出于性能考虑,最大顶点数应尽可能小; [NVIDIA08]指出,当GS输出在1到20个标量之间时,可以实现GS的性能峰值,如果GS输出在27-40个标量之间,则性能下降50%。每次调用的标量输出数是最大顶点输出数和输出顶点类型结构中的标量数的乘积。
基本图形 | 描述 |
---|---|
point | 绘制GL_POINTS基本图形的时候(1) |
line | 当绘制GL_LINES或GL_LINE_STRIP(2)时 |
lineadj | 输入图元具有邻接线段(4) |
triangle | GL_TRIANGLES, GL_TRIANGLE_STRIP或GL_TRIANGLE_FAN(3) |
triangleadj | 输入图元具有邻接三角形(6) |
输出
几何着色器通过将顶点附加到输出流对象来一次输出一个顶点。 流的拓扑由固定声明确定,选择 TriangleStream、LineStream 和 PointStream 作为 GS 阶段的输出。
声明
- [maxvertexcount(3)]
- void geometry_shader(point VS_OUTPUT IN[1], inout TriangleStream<GS_
- OUTPUT> triStream) { /*shader body*/ }
示例
不做处理
几何着色器位于顶点着色器和片元着色器之间,下面示例中几何着色器没做多余的效果,仅仅相当于默认的数据传递。
- Shader "ShaderCookbook/几何着色器/SimplePoint"
- {
- Properties
- {
- _MainTex ("Texture", 2D) = "white" {}
- }
- SubShader
- {
- Tags { "RenderType"="Opaque" }
- LOD 100
- Pass
- {
- CGPROGRAM
- #pragma vertex vert
- //-------声明几何着色器
- #pragma geometry geom
- #pragma fragment frag
- #include "UnityCG.cginc"
- struct appdata
- {
- float4 vertex : POSITION;
- float2 uv : TEXCOORD0;
- };
- //-------顶点向几何阶段传递数据
- struct v2g{
- float4 vertex:SV_POSITION;
- float2 uv:TEXCOORD0;
- };
- //-------几何阶段向片元阶段传递数据
- struct g2f
- {
- float2 uv : TEXCOORD0;
- float4 vertex : SV_POSITION;
- };
- sampler2D _MainTex;
- float4 _MainTex_ST;
- v2g vert (appdata v)
- {
- v2g o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.uv = TRANSFORM_TEX(v.uv, _MainTex);
- return o;
- }
- //-------静态制定单个调用的最大顶点个数
- [maxvertexcount(3)]
- void geom(triangle v2g input[3],inout TriangleStream<g2f> outStream){
- for(int i=0;i<3;i++){
- g2f o=(g2f)0;
- o.vertex=input[i].vertex;
- o.uv=input[i].uv;
- //-----将一个顶点添加到输出流列表
- outStream.Append(o);
- }
- //-------restart strip可以模拟一个primitives list
- outStream.RestartStrip();
- }
- fixed4 frag (g2f i) : SV_Target
- {
- // sample the texture
- fixed4 col = tex2D(_MainTex, i.uv);
- return col;
- }
- ENDCG
- }
- }
- }
点
示例一:单纯的点
- Shader "ShaderCookbook/几何着色器/OnlyPoint"
- {
- Properties
- {
- _MainTex ("Texture", 2D) = "white" {}
- }
- SubShader
- {
- Tags { "RenderType"="Opaque" }
- LOD 100
- Pass
- {
- CGPROGRAM
- #pragma vertex vert
- //-------声明几何着色器
- #pragma geometry geom
- #pragma fragment frag
- #include "UnityCG.cginc"
- struct appdata
- {
- float4 vertex : POSITION;
- float2 uv : TEXCOORD0;
- };
- //-------顶点向几何阶段传递数据
- struct v2g{
- float4 vertex:SV_POSITION;
- float2 uv:TEXCOORD0;
- };
- //-------几何阶段向片元阶段传递数据
- struct g2f
- {
- float2 uv : TEXCOORD0;
- float4 vertex : SV_POSITION;
- };
- sampler2D _MainTex;
- float4 _MainTex_ST;
- v2g vert (appdata v)
- {
- v2g o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.uv = TRANSFORM_TEX(v.uv, _MainTex);
- return o;
- }
- //-------静态制定单个调用的最大顶点个数
- [maxvertexcount(4)]
- void geom(point v2g input[1],inout PointStream<g2f> outStream){
- g2f o=(g2f)0;
- o.vertex=input[0].vertex;
- o.uv=input[0].uv;
- outStream.Append(o);
- }
- fixed4 frag (g2f i) : SV_Target
- {
- // sample the texture
- fixed4 col = tex2D(_MainTex, i.uv);
- return col;
- }
- ENDCG
- }
- }
- }
示例二:散裂
这里需要注意的是我们通过以v[0]为原点构建两个向量,通过这两个向量我们可以自定义点。
- Shader "ShaderCookbook/GeometryShader/分解" {
- Properties {
- _MainTex("Texture", 2 D) = "white" {}
- _Height("Length", float) = 0.5
- _Offset("Offset", float) = 0.1
- _StripColor("StripColor", Color) = (1, 1, 1, 1)
- _OutColor("OutColor", Color) = (1, 1, 1, 1)
- _InColor("InColor", Color) = (1, 1, 1, 1)
- }
- SubShader {
- Cull off
- Pass {
- Tags {
- "RenderType" = "Opaque"
- }
- CGPROGRAM# pragma vertex vert# pragma fragment frag#include "UnityCG.cginc"
- struct appdata {
- float4 vertex: POSITION;
- float2 uv: TEXCOORD0;
- };
- struct v2f {
- float4 vertex: SV_POSITION;
- float4 objPos: TEXCOORD1;
- float2 uv: TEXCOORD0;
- };
- sampler2D _MainTex;
- float4 _MainTex_ST;
- float _Height;
- float _Offset;
- fixed4 _StripColor;
- v2f vert(appdata v) {
- v2f o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.objPos = v.vertex;
- o.uv = v.uv;
- return o;
- }
- fixed4 frag(v2f i): SV_Target {
- fixed4 col = tex2D(_MainTex, i.uv);
- clip(_Height + _Offset - i.objPos.y);
- if (i.objPos.y > _Height)
- col = _StripColor;
- return col;
- }
- ENDCG
- }
- pass {
- Tags {
- "RenderType" = "Opaque"
- }
- CGPROGRAM# pragma vertex vert# pragma geometry geome# pragma fragment frag#include "UnityCG.cginc"
- fixed4 _OutColor;
- fixed4 _InColor;
- float _Height;
- float _Offset;
- struct appdata {
- float4 vertex: POSITION;
- float2 uv: TEXCOORD0;
- float3 normal: NORMAL;
- };
- struct v2g {
- float4 objPos: TEXCOORD0;
- float3 normal: NORMAL;
- };
- struct g2f {
- float4 vertex: SV_POSITION;
- fixed4 col: TEXCOORD0;
- };
- void ADD_VERT(float4 v, g2f o, inout PointStream < g2f > outstream) {
- o.vertex = v;
- outstream.Append(o);
- }
- v2g vert(appdata v) {
- v2g o;
- o.objPos = v.vertex;
- o.normal = v.normal;
- return o;
- }
- [maxvertexcount(6)]
- void geome(triangle v2g input[3], inout PointStream < g2f > outStream) {
- g2f o;
- //--------将一个三角面三个顶点的平均位置作为均值
- float4 vertex = (input[0].objPos + input[1].objPos + input[2].objPos) / 3.0;
- float3 normal = (input[0].normal + input[1].normal + input[2].normal) / 3.0;
- if (vertex.y < _Height + _Offset)
- return;
- //-------以v[0]为原点构建两个向量,用来在后续过程中通过这两个向量来构建三角面中自定义的点
- float4 s = input[1].objPos - input[0].objPos;
- float4 t = input[2].objPos - input[0].objPos;
- o.col = _OutColor * 2;
- for (int i = 0; i < 3; i++) {
- input[i].objPos.xyz += input[i].normal * (vertex.y - _Height);
- input[i].objPos = UnityObjectToClipPos(input[i].objPos);
- ADD_VERT(input[i].objPos, o, outStream);
- }
- o.col = _InColor * 2;
- //-------通过s,t两个向量构建自定义点
- float4 v[3];
- v[0] = 0.2 * s + 0.2 * t;
- v[1] = 0.4 * s + 0.6 * t;
- v[2] = 0.6 * s + 0.4 * t;
- for (int i = 0; i < 3; i++) {
- v[i].xyz += normal * (vertex.y - _Height);
- v[i] = UnityObjectToClipPos(v[i]);
- ADD_VERT(v[i], o, outStream);
- }
- }
- fixed4 frag(g2f i): SV_Target {
- fixed4 col = i.col;
- return col;
- }
- ENDCG
- }
- }
- }
线
示例:wire frame
- Shader "ShaderCookbook/几何着色器/TriangleLine"
- {
- Properties
- {
- _MainTex ("Texture", 2D) = "white" {}
- }
- SubShader
- {
- Tags { "RenderType"="Opaque" }
- LOD 100
- Pass
- {
- CGPROGRAM
- #pragma vertex vert
- //-------声明几何着色器
- #pragma geometry geom
- #pragma fragment frag
- #include "UnityCG.cginc"
- struct appdata
- {
- float4 vertex : POSITION;
- float2 uv : TEXCOORD0;
- };
- //-------顶点向几何阶段传递数据
- struct v2g{
- float4 vertex:SV_POSITION;
- float2 uv:TEXCOORD0;
- };
- //-------几何阶段向片元阶段传递数据
- struct g2f
- {
- float2 uv : TEXCOORD0;
- float4 vertex : SV_POSITION;
- };
- sampler2D _MainTex;
- float4 _MainTex_ST;
- v2g vert (appdata v)
- {
- v2g o;
- o.vertex = UnityObjectToClipPos(v.vertex);
- o.uv = TRANSFORM_TEX(v.uv, _MainTex);
- return o;
- }
- //-------静态制定单个调用的最大顶点个数
- [maxvertexcount(3)]
- //使用三角面作为输入,线段作为输出我们得到完整的线框
- void geom(triangle v2g input[3],inout LineStream<g2f> outStream){
- g2f o=(g2f)0;
- o.vertex=input[0].vertex;
- o.uv=input[0].uv;
- outStream.Append(o);
- o.vertex=input[1].vertex;
- o.uv=input[1].uv;
- outStream.Append(o);
- o.vertex=input[2].vertex;
- o.uv=input[2].uv;
- outStream.Append(o);
- }
- fixed4 frag (g2f i) : SV_Target
- {
- // sample the texture
- fixed4 col = tex2D(_MainTex, i.uv);
- return col;
- }
- ENDCG
- }
- }
- }
面
示例:尖刺
- Shader "ShaderCookbook/GeometryShader/尖刺" {
- Properties {
- _MainTex("Texture", 2D) = "white" {}
- _Length("Length", float) = 0.5
- }
- SubShader {
- Tags {
- "RenderType" = "Opaque"
- }
- LOD 100
- Pass {
- CGPROGRAM
- #pragma vertex vert
- #pragma geometry geom
- #pragma fragment frag
- // make fog work
- #pragma multi_compile_fog
- #include "UnityCG.cginc"
- struct appdata {
- float4 vertex: POSITION;
- float2 uv: TEXCOORD0;
- };
- struct v2g {
- float4 pos: SV_POSITION;
- float2 uv: TEXCOORD0;
- };
- struct g2f {
- float2 uv: TEXCOORD0;
- float4 vertex: SV_POSITION;
- };
- sampler2D _MainTex;
- float4 _MainTex_ST;
- float _Length;
- void ADD_VERT(float3 v, g2f o, inout TriangleStream < g2f > tristream) {
- o.vertex = UnityObjectToClipPos(v);
- tristream.Append(o);
- }
- void ADD_TRI(float3 p0, float3 p1, float3 p2, g2f o, inout TriangleStream < g2f > tristream) {
- ADD_VERT(p0, o, tristream);
- ADD_VERT(p1, o, tristream);
- ADD_VERT(p2, o, tristream);
- tristream.RestartStrip();
- }
- v2g vert(appdata v) {
- v2g o = (v2g) 0;
- o.pos = v.vertex;
- o.uv = TRANSFORM_TEX(v.uv, _MainTex);
- return o;
- }
- [maxvertexcount(9)]
- void geom(triangle v2g input[3], inout TriangleStream < g2f > outStream) {
- g2f o;
- //-----------这里通过三角面的两个边叉乘来获得垂直于三角面的法线方向
- float3 s = (input[1].pos - input[0].pos).xyz;
- float3 t = (input[2].pos - input[0].pos).xyz;
- float3 normal = normalize(cross(s, t));
- float3 centerPos = (input[0].pos.xyz + input[1].pos.xyz + input[2].pos.xyz) / 3.0;
- float2 centerUv = (input[0].uv + input[1].uv + input[2].uv) / 3.0;
- o.uv = centerUv;
- centerPos += normal * _Length * abs(sin(_Time.y * 5));
- ADD_TRI(input[0].pos, centerPos, input[2].pos, o, outStream);
- ADD_TRI(input[2].pos, centerPos, input[1].pos, o, outStream);
- ADD_TRI(input[1].pos, centerPos, input[0].pos, o, outStream);
- }
- fixed4 frag(g2f i): SV_Target {
- fixed4 col = tex2D(_MainTex, i.uv);
- return col;
- }
- ENDCG
- }
- }
- }
Other
项目工程:链接: https://pan.baidu.com/s/1eGk6GHIfWzIFcAX6pxTGRA 提取码: fv75
解压密码:https://fgk.pw/i/eqm2Bj23236
这里推荐一款可视化shader编程工具,对美术同学非常友好,就像建模工具中的材质编辑器一样