GPGPU in Unity,

Currently I’m using some GPGPU in my project, to calculate a heightmap.

ZTest Always Cull Off ZWrite Off
      		Fog { Mode off }

			CGPROGRAM
			#include "UnityCG.cginc"
			#include "Perlin.cginc"
			#pragma target 3.0
			#pragma vertex vert
			#pragma fragment frag
			
			struct v2f 
			{
    			float4  pos : SV_POSITION;
    			float2  uv : TEXCOORD0;
			};
			
			v2f vert(appdata_base v)
			{
				v2f OUT;
				OUT.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				OUT.uv = v.texcoord.xy;
				return OUT;
			}
			
			float frag(v2f IN) : COLOR
			{
				return Somefunction(IN.uv,0.5);
			}
			
			ENDCG

Which is working. However, I’m not sure what to do with the vertex shader, as this seems very inefficient.

All I need is an x- and an y-position (which will essentially form a square) for the fragment shader (which is a continuous noise function) , but it’s calculating a position consisting of 4 floats as well, and I can’t freely choose my x- and y-position.

So in fact, I would like to pick the coordinates for the upper left corner and the down right corner and pass all values in between to the fragment shader. It seems trivial, yet I can’t seem to figure it out (I’ve tried getting rid of the “pos”-variable and just go with the UV-texturecoordinates, but then I get errors that Shaderlab is expecting positions as well).

Excuse me if I’m not exactly clear, as I find it difficult to explain. Maybe this C-style code will make it clearer what I’m trying to achieve.

for(x = upperleft.x; x <= downright.y; x += deltaX){
    for(y = upperleft.y; y <= downright.y; y += deltaY){
        RenderTexture(x,y) = SomeFunction(x,y);
    }
}

So, can anyone explain how I could achieve this? Thanks in advance.

You’re rendering it as a quad, right? That means the vertex code gets executed 4 times. That’s negligible overhead, so I say don’t worry about it. The pos argument is needed to position the triangle on the screen/texture for rasterization. You could just pass it through without the matrix multiply, but d3d / gl expect the positions in different ranges and Unity takes care of that for you by adjusting the matrix accordingly.

I did not realise that, I thought it would go through the matrix multiplication for every pixel or something, but now that you mention it this does make a lot more sense (and I feel stupid for not realising that immediately). Thanks for answering.

I’m not exactly sure, this is my code:

float[,] getHeightmap(int posX, int posY) {
		float[,] heightmap = new float[RES_HEIGHTMAP+1,RES_HEIGHTMAP+1];
		RenderTexture dataTex = new RenderTexture(RES_HEIGHTMAP, RES_HEIGHTMAP, 0, RenderTextureFormat.RFloat);
		Texture2D readTex = new Texture2D(RES_HEIGHTMAP, RES_HEIGHTMAP, TextureFormat.ARGB32, false, true);

		LoadTextures(); //uploads the right textures and other variables to the GPU

		//Create the data
		Graphics.Blit(null, dataTex, m_makeData);

		//Store data in normal (2D)texture
		RenderTexture.active = dataTex;
		readTex.ReadPixels(new Rect(0,0,RES_HEIGHTMAP,RES_HEIGHTMAP),0,0);

		RenderTexture.active = null;
		for(int x = 0; x <= RES_HEIGHTMAP; x++)
		{
			for(int y = 0; y <= RES_HEIGHTMAP; y++)
			{
				heightmap[x,y] = readTex.GetPixel(x,y).r;
			}
		}
		dataTex.Release();
		return heightmap;
	}

I’ve just attached the script containing this function to the camera and it worked. I got confused by it this morning, because I’ve never set a plane or anything that should be rendered, it’s just the camera. So I don’t know where Unity is getting the UV-coordinates from, and I have absolutely no idea where I can change them. Could you by any chance tell me where how I could do that?

Yeah Graphics.Blit basically renders a full screen quad for you. You can’t change the UVs though, if you, for example, want to render only a portion of the screen. To do render a quad by yourself, use the GL class and pass in the UVs manually. Google is your best friend here.

Anywho, if you want your code to be any efficient, you shouldn’t move whole textures to CPU with ReadPixels. Thats usually a very bad idea. What do you want to do with the heightmap? I’m pretty sure what you’re doing with it can be done on the GPU as well and much faster.

Thanks a lot, this was exactly what I needed. Currently working out a little bug (seems the outermost pixels are not calculated correctly), but works like a breeze.

I’m aware of the fact that sending the data to the CPU is a very likely bottleneck and inefficient as well. In the future, I might move to a fully GPU rendered terrain , but this helps me work out my algorithms. Not really sure how that will work with collision detection though, because it would be likely that you would still need the data for that on the CPU? (but then again, that could just be a low-resolution version of the heightmap). Still, thanks for the tip.