DirectCompute tutorial for Unity 4: Buffers | Cheney Shen

Technology blog

DirectCompute tutorial for Unity 4: Buffers

In DirectCompute there are two types of data structures you will be using, textures and buffers. Last tutorial covered textures and today I will be covering buffers. While textures are good for data that needs filtering or mipmaping like color information, buffers or more suited to representing information for vertices like position or normals. Buffers can also easily send or retrieve data from the GPU which is a feature that’s rather lacking in Unity.

(你可选的数据结构除了texture,还有buffer。texture在你的数据需要filtering/mipmapping的时候特别好用,buffer适用于存储点信息比如position/normals)

 

There are 5 types of buffers you can have, structured, append, consume, counter and raw. Today I will only be covering structured buffers because this is the most commonly used type. I will try and cover the others in separate tutorials as they are a little more advanced and I need to cover some of the basics first.

(有五种类型的buffer你可以用:structured, append, consume, counter and raw,这一篇只讲structured buffer)

 

So let’s start of by creating a C# script, name it BufferExample and paste in the following code.

t003 - 01

 

We will also need a material to draw our buffer with so create a normal Cg shader, paste in the follow code and create a material out of it.

(创建一个material赋予下面的shader)

t003 - 02

 

Now to run the scene attach the script to the main camera node and bind the material to the material attribute on the script. This script has to be attached to a camera because to render a buffer we need to use the “OnPostRender” function which will only be called if the script is on a camera (EDIT – turns out you can use “OnRenderObject” if you don’t want the script attached to a camera). Run the scene and you should see a number of random red points.

(把上面的c#代码赋给主相机,代码的material挂上上面建的cameraMaterial)

t003 - 03

 

(下面讲解一下代码)

Now notice the creation of the buffer from this line.

 

The three parameters passed to the constructor are the count, stride and type. The count is simply the number of elements in the buffer. In this case 1024 points. The stride is the size in bytes of each element in the buffer. Since each element in the buffer represents a position in 3D space we need a 3 component vector of floats. A float is 4 bytes so we need a stride of 4 * 3. I like to use the sizeof operator as I think it makes the code more clear. The last parameter is the buffer type and it is optional. If left out it creates a buffer of default type which is a structured buffer.

(三个参数:elements数量,每个elements大小,类型默认就是structured buffer)

 

Now we need to pass the positions we have made to the buffer like so.

(传入数据)

 

This passes the data to the GPU. Just note that passing data to and from the GPU can be a slow processes and in general it is faster to send data than retrieve data.

 

Now we need to actually draw the data. Drawing buffers has to be done in the “OnPostRender” function which will only be called if the script is attached to the camera. The draw call is made using the “DrawProcedural” function like so…

(在OnPostRender函数里绘制数据)

 

There are a few key points here.

The first is that the materials pass must be set before the DrawProcedural call. Fail to do this and nothing will be drawn. You must also bind the buffer to the material but you only have to do this once, not every frame like I am here. Now have a look at the “DrawProcedural” function. The first parameter is the topology type and in this case I am just rendering points but you can render the data as lines, line strips, quads or triangles. You must however order them to match the topology. For example if you render lines every two points in the buffer will make a line segment and for triangles every three points will make a triangle. The next two parameters are the vertex count and the instance count. The vertex count is just the number vertices you will be drawing, in this case the number of elements in the buffer. The instance count is how many times you want to draw the same data. Here we are just rendering the points once but you could render them many times and have each instance in a different location.

(材质设置必须在call DrawProcedural 函数之前,不然绘不出来。)

(DrawProcedural函数参数:拓扑结构类型;vertex count;instance count)

 

(渲染线的效果如下:)

t003 - 04

Now for the material. This is pretty straight forward. You just need to declare your buffer as a uniform like so…

(对于material就直接声明structurebuffer来使用吧)

 

Since buffers are only in DirectCompute you must also set the shader target to SM5 like so…

(shader版本要求)

 

The vertex shader must also have the argument “uint id : SV_VertexID“. This allows you to access the correct element in the buffer like so…

(vertex shader存在SV_VertexID这个参数可以让你很容易的进入目标element)

 

Buffers are a generic data structure and don’t have to be floats like in this example. We could use integers instead. Change the scripts start function to this…

(buffer支持多种数据结构,但是比较推荐使用int,下面就是float转int后使用的例子)

 

 

and the shaders uniform declaration to this…

You could even use a double but just be aware that double precision in shaders is still not widely supported on GPU’s although its common on newer cards.

(buffer你可以使用double,但目前GPU对此支持还不够好,建议不要用)

 

You are not limited to using primitives like float or int. You can also use Unity’s Vectors like so…

(你还可以使用unity vectors数据结构!!!)

 

 

With the uniform as…

You can also create you own structs to use.  Change your scripts start function to this with the struct declaration above…

(还可以使用自定义数据结构)

 

 

and the shader to this…

 

This will draw each point like before but now they will also be random colors. Just be aware that you need to use a struct not a class.

(shader中要注意要使用struct关键字而不是class来建结构)

 

When using buffers you will often find you need to copy one into another. This can be easily done with a compute shader like so…

(buffer拷贝)

 

 

You can see that buffer1 is copied into buffer2. Since buffers are always 1 dimensional it is best done from a 1 dimension thread group.

(拷贝的时候注意维度要一样)

 

Just like textures are objects so to are buffers and they have some functions that can be called from them. You can use the load function to access a buffers element just like with the subscript operator.

(buffer使用和texture一样有其他方法使用:load)

 

Buffers also have a “GetDimension” function. This returns the number of elements in the buffer and their stride.

(buffer使用和texture一样有其他方法使用:getdimensions)

 

 

 

 

 


Post a Comment

Your email address will not be published. Required fields are marked *

  • Categories

  • Tags