[C++]DirectX 12 3D游戏开发实战—第12章 学习笔记01 2019.5.7

个人学习用,请勿转载

第十二章 几何着色器

词汇

曲面细分:tenssellation 几何着色器:geometry shader 三角形列表:traingle list

内容

如果不启用曲面细分这一环节,几何着色器这个可选阶段会位于顶点着色器与像素着色器之间。顶点着色器以顶点作为输入数据,而几何着色器的输入数据则是完整的图元。如果要绘制三角形列表,那么几何着色器程序实际将对列表中的每个三角形T执行下列操作:

for(UINT i = 0;i<numTriangles;++i)
	OutputPrimitiveList = GeometryShader(T[i].vertexList);

几何着色器以每个三角形的3个顶点作为i输入,且输出的是对应的图元列表。与顶点着色器不能销毁或创建顶点不同,几何着色器可以创建或销毁几何图形。可以借助几何着色器将输入的图元扩展为一个或更多的其它类型图元,或者根据某些条件而选择不输出图元。几何着色器的输出图元类型不一定与输入图元的类型相同。几何着色器常见于将一个点扩展为一个四边形。

学习目标

  1. 学习如何编写几何着色器
  2. 探究如何通过几何着色器来高效的实现公告牌技术(billboard)
  3. 了解自动生成图元ID以及其相关应用
  4. 研究如何创建和使用纹理数组,并认识它们为何如此使用
  5. 理解如何运用alpha-to-coverage技术来辅助解决alpha裁剪的失真问题

12.1编写几何着色器

几何着色器的编写方式比较接近与顶点着色器和像素着色器,也存在若干区别、

[maxvertexcount(N)]
void ShaderName(
	PrimitiveType InputVertexType InputName[NumElements],
	inout StreamOutputObject<OutputVertexType> OutputName)
	{
		//几何着色器的具体实现
	}

必须先指定几何着色器单词调用所输出的顶点数最大值(每个图元都会调用以此几何着色器,走一遍其中的处理流程)。可以使用下列属性语法来设置着色器定义之前的最大顶点数量:

[maxvertexout(N)]

其中N是几何着色器单次调用所输出的顶点的数量最大值,几何着色器与每次输入的顶点个数可能各不相同,但是这个数量却不能超过之前定义的最大值,出于性能考虑,应当零maxvertexout的值尽可能小,在GS每次输出的标量数量在1~20时,它将发挥最佳性能,而GS每次输出的标量在数量保持在27到40时,它的性能会下降到巅峰值的50%,每次调用几何着色器所输出的标量的个数为maxvertexcount与输出顶点类型结构体中标量个数的乘积。,在实践中完全满足这些限制是比较困难的,所以我获取比最佳性能稍差的解决方案,或干脆选择一种不使用几何着色器的实现方法。
几何着色器有输入和输出两个参数(也可以更多)输入参数必须是一个定义有特定图元的顶点数组,点要输入一个顶点,线条要输入两个顶点,三角形需要输入3个顶点,线以及其邻接图元为4个顶点,三角形与其邻接图元为6个顶点,几何着色器的输入顶点类型即为顶点着色器输出的定点类型。输入参数一定要以图元类型作为前缀,用以描述输入到几何着色器的具体图元类型:
1. point:点图元
2. line:线图元
3. triangle:三角形图元
4. lineadj:输入的图元为线列表及其邻接图元,或线条带及其邻接图元
5. triangleadj:输入的图元为三角形列表及其邻接图元,或三角形带及其邻接图元
向几何着色器输入的数据必须是完整图元(如组成线条的两个顶点/构成三角形的3个顶点),因此,几何着色器并不会区分输入的图元究竟是列表结构还说带状结构。据格例子,若绘制的图元实际上是三角形带,但几何着色器仍会把三角形带视作多个三角形并分别进行单独处理,即将每个三角形的3个顶点作为其输入数据。绘制带状结构的过程中会产生额外开销,因为多个图元所共用的顶点在几何着色器中会被处理多次。

输出参数一定要标有inout修饰符。另外它必须是一种流类型(stream type)。流类型存有一些列顶点,它们定义了几何着色器输出的几何图形。几何着色器可以通过内置方法append向输出流列表添加单个顶点:

void StreamOutputObject<OutputVertexType>::Append(OutputVertexType v);

流类型本质上是一种模板类型(template type),其模板参数用以指定输出顶点的具体类型(如GeoOut)。流类型有以下3中

  1. PointStream:一系列顶点所定义的点列表
  2. LineStream:一系列顶点定义的线条带
  3. TriangleStream:一系列顶点定义的三角形带
    几何着色器输出的多个顶点会构成图元,图元的输出类型由流类型指定。对于线条与三角形来说,几何着色器输出的图元必定是线条带与三角形带,而线条列表与三角形列表可以借助内置函数RestartStrip实现:
void StreamOutoutObject<OutputVertexType>::RestartStrip();

如果希望输出三角形列表,则需要在每次向输出流追加3个顶点之后调用RestartStrip。
以下是一些着色器签名的具体示例

//示例1:GS最多输出4个顶点,输入的图元是一个线条,输出的是一个三角形带
[maxvertexcount(4)]
void GS(line VertexOut gin[2],
	inout TriangleStream<GetOut> triStream)
{
//几何着色器的具体实现
//
//
//示例2:GS最多输出32个顶点。输入的图元是一个三角形,输出的是一个三角形带
[maxvertexcount(32)]
void GS(Triangle VertexOut gin[3],
	inout TriangleStream<GeoOut>triStream)
{
//几何着色器具体实现
}
//示例3:GS最多输出4个顶点,输入的图元是一个点,输出的是一个三角形带
[maxvertexvount(4)]
void GS(point VertexOut gin[1],
	inout TriangleStream<GeoOut> triStream)
	{
	//几何着色器的主体
	}
下列几何着色器展示了Append与RestartStrip方法的调用过程,此示例会将输入的三角形进行细分,并输出细分后的4个三角形。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190508002549707.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2MDI4MDcz,size_16,color_FFFFFF,t_70)
struct VertexOut
{
	float3 PosL : POSITION;
	float3 NormalL : NORMAL;
	float2 Tex : TEXCOORD
};
struct GeoOut
{
	float4 PosH : SV_POSITION;
	float3 PosW : POSITION;
	float3 NoemalW : NORMAL;
	float2 Tex : TEXCOORD;
	float FogLerp : FOG;
}
void Subdivide(VertexOut inVerts[3], out VertexOut outVerts[6])
{
VertexOut m[3];
//计算三角形边上的中点
m[0].PosL = 0.5f*(inVerts[0].PosL+inVerts[1].PosL);
m[1].PosL = 0.5f*(inVerts[1].PosL+inVerts[2].PosL);
m[2].PosL = 0.5f*(inVerts[2].PosL+inVerts[0].PosL);

//把点投影到单位球面上
m[0].PosL = normalize(m[0].PosL);
m[0].PosL = normalize(m[1].PosL);
m[0].PosL = normalize(m[2].PosL);

//对纹理坐标进行插值
m[0].Tex = 0.5f*(inVerts[0].Tex+InVerts[1].Tex);
m[1].Tex = 0.5f*(inVerts[1].Tex+InVerts[2].Tex);
m[2].Tex = 0.5f*(inVerts[2].Tex+InVerts[0].Tex);

outVerts[0]=inVerts[0];
outVerts[1]=m[0];
outVerts[1]=m[2];
outVerts[1]=m[1];
outVerts[1]=inVerts[2];
outVerts[1]=inVerts[1];
};

void OutputSubdivision(VertexOut v[6],
	inout TriangleStream<GeoOut> triStream)
{
		GeoOut gout[6];
		[unroll]
		for(int i = 0;i<6;++i)
		{
			//将顶点置换到世界空间
			gout[i].Posw = mul(float4(v[i].PosL,1.0f),gWorld).xyz;
			gout[i].NormalW=mul(v[i].NormalL,(float3x3)gWorldInvTranspose);
			//把顶点变换到齐次裁剪空间
			gout[i].PosH = mul(float4(v[i].PosL,1.0f),gWorldViewProj);
			gout[i].Tex=v[i].Tex;
		}
		//可以将细分的小三角形绘制到两个三角形带中
		//三角形带1:底端的3个三角形
		//三角形带2:顶部的三角形
	[unroll]
	for(int j=0;j<5;++j)
	{
		tiriStream.Append(gout[j]);
	}
	triStream.RestartStrip();
	triStream.Append(gout[1]);
	triStream.Append(gout[5]);
	triStream.Append(gout[3]);

}
[maxvertexcount(8)]
void GS(triangle VertexOut gin[3],inout TriangleStream<GeoOut>)
{
	VertexOut v[6];
	Subdivide(gin,v);
	OutputSubdivision(v,triStream);
}

几何着色器的编译过程也与顶点着色器和像素着色器如出一辙。如果TreeSprite.hlsl文件中有名为GS的几何着色器,则可以用以下防范将其编译为字节码:

mShaders["treeSpriteGS"] = d3dUtil::CompileShader(
	L"Shaders\\TreeSprite.hlsl",nullptr,"GS","gs_5_0");

就像顶点着色器与像素着色器一样,要将指定的几何着色器作为流水线状态对象的一部分,以此将它绑定到渲染流水线上:

D3D12_GRAPHICS_PIPELINE_DESC treeSpritePsoDesc = opaquePsoDesc;
...
treeSpritePsoDesc.GS=
{
	reinterpret_cast<BYTE*>(mShaders["treeSpriteGS"]->GetBufferPointer()),
	mShaders["treeSpriteGS"]->GetBufferSize()
};
  • 若给出一个输入图元,几何着色器也可以根据某些条件选择不输出任何数据,通过这种方式,几何着色器可以销毁几何图形,对很多算法的实现都很有帮助。
  • 如果没有向几何着色器输入组装完整图元所需的足够顶点,将会导致部分图元的遗失。

12.2公告牌技术实现森林效果

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值