UnityShader——初探Compute Shader
来源:互联网 发布:linux c执行shell命令 编辑:程序博客网 时间:2024/05/18 04:45
Compute Shader是基于DX11(SM4.5+)的在GPU上运行的程序,通过Compute Shader我们可以将大量可以并行的计算放到GPU中计算从而节省CPU资源,Unity 5.6版本提供的 Graphics.DrawMeshInstancedIndirect 接口可以非常方便的配合ComputeShader做大规模渲染。
先将一些Compute Shader中不同于普通Shader的概念梳理下:
numthreads(MSDN)
个人理解:
numthreads 定义了一个三维的线程结构,
如果我们在程序的Dispatch接口发送了(5,3,2)这样的结构,就会生成5x3x2个线程组,其中每个组的线程结构由ComputeShader中的numthreads定义,图中numthreads定义了10x8x3的三维结构,由此,我们可以分析4个HLSL关键词的定义。
SV_GroupThreadID 表示该线程在该组内的位置
SV_GroupID 表示整个组所分配的位置
SV_DispatchThreadID 表示该线程在所有组的线程中的位置
SV_GroupIndex 表示该线程在该组内的索引
通过这些关键词,我们可以在并行计算时获取其他线程的输入数据
如果是计算4X4的矩阵加法,可以定义为4X4X1的numthreads结构,这样线程的索引会自动匹配输入的矩阵,同样,我们可以定义16X1X1的结构,但这样只能基于当前线程数去计算输入矩阵(原文是 however it would then have to calculate the current matrix entry based on the current thread number. 没太理解)
SM4.5 允许numthreads最多768条线程
SM5.0 允许numthreads最多1024条线程
Sampler
sampler在ComputeShader中的定义与普通Shader略有不同,常用的DX9的声明方法在ComputeShader中不再适用,贴图采样需使用DX10/11中的方法
又因为贴图的Mip level在compute shader中没有定义,因此无法将线程数匹配到具体像素,必须自己定义Mip level,所以使用Texture.SampleLevel 或者 Texture.Load 来采样,几何着色器和顶点着色器同理。
Example
我们首先在C#脚本中和Shader中定义同样的结构体
public struct MyInstance{ public Vector3 color; public Vector3 position; public Vector3 velocity; public Vector3 scale;}
struct _myIns{ float3 color; float3 position; float3 velocity; float3 scale;};
在C#脚本中初始化ComputeBuffer并赋值到Compute Shader和渲染用的普通Shader中
void InitBuffer() { argsBuffer = new ComputeBuffer(1, args.Length * sizeof(uint), ComputeBufferType.IndirectArguments); uint numIndices = meshInstance.GetIndexCount(0); args[0] = numIndices; args[1] = (uint)num; argsBuffer.SetData(args); instanceBuffer = new ComputeBuffer(num, MySize.SizeOfFloat3*4); _instance = new MyInstance[num]; for (int i = 0; i < num; i++) { MyInstance mi = new MyInstance(); mi.color = new Vector3(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f)); mi.position = Random.insideUnitSphere * Radius; mi.velocity = Random.insideUnitSphere; mi.scale = Vector3.one; _instance[i] = mi; } instanceBuffer.SetData(_instance); matinstance.SetBuffer("positionBuffer", instanceBuffer); //compute shader init _kernel = insCompute.FindKernel("CSMain"); if (_kernel == -1) { Debug.LogError("Failed to find kernel"); return; } insCompute.SetBuffer(_kernel, "inss", instanceBuffer); insCompute.SetFloat("deltaTime", Time.fixedDeltaTime); insCompute.SetFloat("radiu", Radius); insCompute.SetTexture(_kernel, "noiseTex", noiseTex); }
ComputeShader我们简单的使用了128x1x1的线程结构
float deltaTime;float radiu;RWStructuredBuffer<_myIns> inss;Texture3D<float4> noiseTex;SamplerState samplernoiseTex{ Filter = MIN_MAG_MIP_LINEAR; AddressU = Wrap; AddressV = Wrap;};[numthreads(BLOCKSIZE,1,1)]void CSMain (uint3 id : SV_DispatchThreadID){ // TODO: insert actual code here! uint i = id.x; uint num, stride; inss.GetDimensions(num, stride); float3 position = inss[i].position; float3 velocity = inss[i].velocity; float3 ns = inss[i].scale; float3 uv = float3(abs(position.x),abs(position.y),abs(position.z))/radiu; ns = noiseTex.SampleLevel(samplernoiseTex,uv,0); //caculate position += 5 * velocity * deltaTime; if(i < num) { inss[i].position = position; inss[i].velocity = velocity; inss[i].scale = ns*ns; }}
普通Shader中通过SV_InstanceID获取GPU Instance索引
v2f vert (appdata_full v, uint instanceID : SV_InstanceID) { #if SHADER_TARGET >= 45 _myIns data = positionBuffer[instanceID]; #else _myIns data = 0; #endif float3 localPosition = v.vertex.xyz * data.scale; float3 worldPosition = data.position + localPosition; float3 worldNormal = v.normal; float3 ndotl = saturate(dot(worldNormal, _WorldSpaceLightPos0.xyz)); float3 ambient = ShadeSH9(float4(worldNormal, 1.0f)); float3 diffuse = (ndotl * _LightColor0.rgb); float3 color = data.color; v2f o; o.pos = mul(UNITY_MATRIX_VP, float4(worldPosition, 1.0f)); o.uv_MainTex = v.texcoord; o.ambient = ambient; o.diffuse = diffuse; o.color = color; TRANSFER_SHADOW(o) return o; }
最后在Update中通过DrawMeshInstancedIndirect进行绘制
private void Update() { var numOfGroups = Mathf.CeilToInt((float)num / GroupSize); insCompute.Dispatch(_kernel, numOfGroups, 1, 1); Bounds bs = new Bounds(transform.position, Vector3.one * Radius); Graphics.DrawMeshInstancedIndirect(meshInstance, 0, matinstance, bs, argsBuffer); }
最终运行结果如下:
- UnityShader——初探Compute Shader
- UnityShader入门 积雪Shader
- 【Unity Shader】UnityShader基础
- UnityShader学习-纹理shader
- UnityShader——Wireframe
- Compute Shader (DX11)
- DirectX 11 - Compute Shader
- unity3d 从零开始compute shader
- Unity5 Compute && Geometry Shader
- GLSL-Compute Shader
- [UnityShader]Shader上的优化
- [Shader] UnityShader系列---灰白效果
- [UnityShader基础]简单Shader本体
- UnityShader——玩弄GlobalFog
- DirectX 11 Compute Shader tutorial
- Direct Compute Shader 处理图像
- 安卓 opengles compute shader
- GLSL(5)-compute shader小结
- 认识PNG
- Python列表教程
- Servlet实例
- php匹配年月日的正则
- Linux(CentOS 6)下设置nginx开机自动启动和chkconfig管理
- UnityShader——初探Compute Shader
- 1028. 人口普查(20)
- JAVA使用ROME读取百度RSS的编码错误问题
- Springmvc映射请求(二)
- Maven依赖的scope去除部署不需要的jar包(打包)
- 【iOS】标签视图与分组静态表布局使用、利用通知和全局变量传值
- 简谈JVM
- centos 安装 php7
- Hough变换非常详细讲解