HLSL中的计算着色器的基础语法

现在的GPU已经非常的厉害了,数以亿计的计算可以用来处理非常多的数据,这么强大的计算能力不止能处理与数学图形有关的运算,还可以帮助CPU去处理海量的数据,比如粒子系统的运动管理,如果用CPU来处理的话,就非常影响游戏的体验,可能会造成游戏卡顿所以可以交给GPU去运算,算好后就可以进行渲染了,这就是计算着色器,计算着色器不属于GPU渲染管线的中一部分,而是一个独立的Compute Shader 计算流水线,它能够使用GPU内的资源并且将计算好的资源传回CPU。

了解CPU与GPU之间通信的人可能会反驳,CPlU和GPU之间的通信相对于它们各自的运算效率来说非常慢,会造成游戏的卡顿,实际上它们之间通信速率的确相对较慢但相对于GPGPU通用GPU程序设计的海量的运算来说,GPU节省的时间就已经大于数据在CPU和GPU中的传输时间,所以还是非常提倡使用计算着色器(渲染流水线与计算流水线不是同一条)。

渲染流水线

渲染流水线

 

GPU 拥有一个由数以千计的更小、更高效的核心(专为同时处理多重任务而设计)组成的大规模并行计算架构,GPU编程中可以根据程序需求将线程分为线程组,将将线程组成一张三维网格,可以通过命令列表来进行线程的调度comList->Dispatch(3,2,1);这条语句中给接下来GPU操作分配线程组,分配的线程组为横向三列,纵向两行垂直一层,所以分配了3✖2✖1=6个线程组,每个线程组的中线程的个数应该设置为32的倍数(英伟达),其他种类GPU可能是64的倍数,所以在进行GPU编程时,最好分配的线程数为64的倍数,这样就可以通过是兼容两种GPU了,线程组的大小为自己设置的。

[numthreads(16, 16, 1)]
void SobelCS(int3 dispatchThreadID : SV_DispatchThreadID)
{
    // Sample the pixels in the neighborhood of this pixel.
	float4 c[3][3];
	for(int i = 0; i < 3; ++i)
	{
		for(int j = 0; j < 3; ++j)
		{
			int2 xy = dispatchThreadID.xy + int2(-1 + j, -1 + i);
			c[i][j] = gInput[xy]; 
		}
	}

	// For each color channel, estimate partial x derivative using Sobel scheme.
	float4 Gx = -1.0f*c[0][0] - 2.0f*c[1][0] - 1.0f*c[2][0] + 1.0f*c[0][2] + 2.0f*c[1][2] + 1.0f*c[2][2];

	// For each color channel, estimate partial y derivative using Sobel scheme.
	float4 Gy = -1.0f*c[2][0] - 2.0f*c[2][1] - 1.0f*c[2][1] + 1.0f*c[0][0] + 2.0f*c[0][1] + 1.0f*c[0][2];

	// Gradient is (Gx, Gy).  For each color channel, compute magnitude to get maximum rate of change.
	float4 mag = sqrt(Gx*Gx + Gy*Gy);

	// Make edges black, and nonedges white.
	mag = 1.0f - saturate(CalcLuminance(mag.rgb));

	gOutput[dispatchThreadID.xy] = mag;
}

这是一段计算着色器程序,先只关注一下它的格式:

[numthreads(16, 16, 1)]
void SobelCS(int3 dispatchThreadID : SV_DispatchThreadID)
{
	//省略
}

其中需要关注几个东西:

  • [numthreads(16, 16, 1)]这个在每个计算着色器的开头,设置了线程组中线程的数量,它的三个数值和上面分配线程组的含义类似只是,变成了三维的线程网格,一共分配了16✖16✖1个线程。
  • int3 dispatchThreadID : SV_DispatchThreadID 其中冒号后面的为语义这个语义的意思是当前为此计算着色器对应的线程ID,语义是微软自己定义的,我们使用它们能更方便的编程。比如给1024✖1024像素的纹理分配1024✖1024✖1的线程,然后Dispatch(1,1,1)只分配一个线程组这个线程组就专门处理这个纹理,每个像素都有一个专属的线程,然后我们就可以通过线程ID进行索引这个纹理。
  • 计算流水线和渲染流水线是两个不同的管线,上上图(渲染流水线)中可以看到计算着色器处于GPU中,可以直接操作GPU内的数据,所以可以给计算着色器设置于渲染流水线一样的根签名,并且绑上描述器,计算着色器就可以读取数据了。
  • 计算着色器可以读取GPU里的数据也可以将处理好的数据CopyResource(命令列表的一个函数)回CPU,CPU想要使用的话就必须通过映射函数去映射之后才能读取,计算着色器不能进行图像的渲染,只能将计算好的数据传回给CPU,让CPU通过这且数据,命令GPU渲染流水线去绘制。

下面是一些计算着色器常用的语义:

  • SV_DispatchThreadID 线程ID
  • SV_GroupThreadID 线程组的ID

picture

未经允许不得转载:他日重逢 » HLSL中的计算着色器的基础语法

赞 (1) 打赏

评论 1

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. 绿软吧感谢分享回复

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏

隐藏
变装