本文已参与[新人创作礼]活动,一起开启掘金创作之路
1、渲染过程概要
首先还是这张图!要明白他们的渲染顺序!这很重要
顶点着色器→曲面细分着色器→几何着色器→片元着色器
其中顶点着色器、片元着色器是完全可编程着色器
曲面细分着色器、几何着色器是可选可选着色器
什么是几何着色器:在顶点和片段着色器之间有一个可选的着色器,叫做几何着色器(Geometry Shader)。几何着色器以一个或多个表示为一个单独基本图形(primitive)的顶点作为输入,比如可以是一个点或者三角形。几何着色器在将这些顶点发送到下一个着色阶段之前,可以将这些顶点转变为它认为合适的内容。几何着色器有意思的地方在于它可以把(一个或多个)顶点转变为完全不同的基本图形(primitive),从而生成比原来多得多的顶点。
2、从我们熟悉的顶点片元着色器组合使用引出几何着色器
1、我们回忆一下之前如何简单的将一张图片渲染到物体表面? 主要shader部分如下
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos: SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.pos= UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb = col.rgb;
return col;
}
具体代码详看我之前写的博客一个简易Shader,图片Shader 2、从渲染过程我们可以看出“几何着色器”是处于“顶点着色器”和“片元着色器”之间的,那我们如何在不改变上面shader效果的情况下插入“几何着色器”呢? 直接上代码
Shader "Geometry/GeometryTexture"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
SubShader
{
Cull Back ZWrite On ZTest LEqual
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2g
{
float2 uv : TEXCOORD0;
float4 pos: SV_POSITION;
};
struct g2f
{
float2 uv : TEXCOORD0;
float4 pos: SV_POSITION;
};
v2g vert(appdata v)
{
v2g g;
g.pos = UnityObjectToClipPos(v.vertex);
g.uv = v.uv;
return g;
}
//静态制定单个调用的最大顶点个数
[maxvertexcount(3)]
void geom(triangle v2g input[3], inout TriangleStream<g2f> outStream) {
for (int i = 0; i < 3; i++) {
g2f o = (g2f)0;
o.pos = input[i].pos;
o.uv = input[i].uv;
//将一个顶点添加到输出流列表
outStream.Append(o);
}
//RestartStrip可以模拟一个primitives list
outStream.RestartStrip();
}
fixed4 frag(g2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb = col.rgb;
return col;
}
ENDCG
}
}
}
效果如下图,左边为第一个shader的效果,右边为第二个shader的效果,可以看到是一样的。
3、剖析几何着色器
①声明函数
#pragma geometry geom
//这个没什么好说的,跟之前的声明一样
②定义结构体
struct v2g
{
float2 uv : TEXCOORD0;
float4 pos: SV_POSITION;
};
//跟顶点片元结构体的定义一样
③声明代码块
//静态制定单个调用的最大顶点个数
[maxvertexcount(3)]
void geom(triangle v2g input[3], inout TriangleStream<g2f> outStream) {
for (int i = 0; i < 3; i++)
{
g2f o = (g2f)0;
o.pos = input[i].pos;
o.uv = input[i].uv;
//将一个顶点添加到输出流列表
outStream.Append(o);
}
//RestartStrip可以模拟一个primitives list
outStream.RestartStrip();
}
下面主要针对“声明代码块”部分进行详解
几何着色器 输入的基本图形 (primitive)类型有如下几种
分别对应下面几幅图
我们在代码块中使用的便是 triangle
流输出对象类型有
我们在代码中使用的便是 TriangleStream 根据上述说明我们可以有以下几种组合
void geom(triangle v2g input[3],inout TriangleStream<g2f> outStream)
void geom(triangle v2g input[3],inout PointStream<g2f> outStream)
void geom(triangle v2g input[3],inout LineStream<g2f> outStream)
void geom(point v2g input[1],inout PointStream<g2f> outStream)
void geom(line v2g input[2],inout LineStream<g2f> outStream)
.
.
.
.
//此处就不一一列举
再来说明一下 [maxvertexcount(N)]的作用 maxvertexcount属性值指定了geometry shader能够输出的vertices数量的最大值,N的值应该尽量小,[NVIDIA08]指出,当GS输出在1到20个标量之间时,可以实现GS的性能峰值,如果GS输出在27-40个标量之间,则性能下降50%。每次调用的标量输出数是最大顶点输出数和输出顶点类型结构中的标量数的乘积。
最后说一下代码内几个特殊函数的意义 1、Append(o),将一个顶点追加到输出流列表 2、RestartStrip(),结束当前原始strip并启动一个新strip。如果当前strip没有足够的顶点来填充原语拓扑,则结束处的不完全原语将被丢弃。(大概就是确保每次输出的都是“三角形图元”)
PS.后面会陆陆续续带来几篇关于几何着色器的研究的文章,本篇中的几何着色器并没有做任何处理
努力真的是有用的,你要坚信努力一定会给你带来收获,每天坚持多学一点,多学一点,一天天积累,你会追赶上前面的人