/**
 * This shader demonstrates how to create a full-screen quad directly on the GPU using a vertex
 * shader and a "null mesh".
 * A null mesh is a "mesh" that doesn't contain any actual vertex data but instead can be thought 
 * of as a means to invoke the associated vertex shader a certain number of times. The current 
 * invocation of the VS can be determined by requesting a uint value with the SV_VertexId semantic
 * to be provided from the input assembler stage and the vertex shader can then use this information
 * to create the actual mesh data entirely on the GPU. This is mainly of interest for simple geometry
 * such as quads (full screen quads for postprocessing effects as is used here, particle effects as
 * is demonstrated in the «GPGPU Particles» example project and sprites are examples of this). Of course
 * any shape can be created in this manner.
 **/

// The coordinates of the screen quad will never change so we can set these up to be constant.
// We write the values directly as clip space coordinates (what you end up with after transforming 
// from world space by the view and projection matrices); this ranges from (-1 .. +1) for the X and Y axii 
// and (0..1) for the Z axis which maps to the near through the far range of the rendering camera in world space units.
// As such we will want to write our screen quad from (-1, -1) to (1, 1); we'll set the Z coordinate to 0 to draw the 
// quad at the near plane. This means no other possible 3D geometry will be drawn in front of it; if you want this effect 
// you can draw the screen quad at the far plane instead (set the Z coordinate to 1).
// Of course we won't be rendering any other objects to the main camera than this quad so it doesn't really matter what the 
// Z coordinates are set to here.
// Also note that we have to declare constants static const when their values are declared in HLSL like this, otherwise the 
// compiler assumes they are part of the constant buffer bound to register 0.
static const float4x4 Vertex = {
	-1, -1, 0, 1, 
	-1, 1, 0, 1, 
	1, -1, 0, 1, 
	1, 1, 0, 1
};

// Likewise we can set up a constant array of the texture coordinates for the corner vertices of the screen quad
static const float4x2 Texcoord = {
	0, 0, 
	0, 1, 
	1, 0, 
	1, 1
};

// We need information about how large a texel is in UV coordinates; this depends on the screen resolution so it
// makes sense that the DBPro program provides this information through a constant buffer
cbuffer TexelData : register(b1) {
	float TexelWidth;
	float TexelHeight;
};

// We'll need a texture2d and a sampler to sample the source image to the screen quad.
// Because we'll be blurring the output image the sampler should be set to clamp rather than wrap around at the edges.
Texture2D	SCTexture	: register(t0);		
sampler		SCSampler	: register(s0);


// Simple vertex shader output struct
struct vs_out {
	float4 pos		: SV_POSITION;
	float2 texcoord : TEXCOORD;
};

struct gs_in {
	uint   id		: QUADID;
};


/**
 * Since we use a null mesh, there won't be any actual vertex data given to the vertex shader.
 * However it can receive a system value (note the "SV_" prefix of the sematic) that indicates 
 * the vertex id being processed. With a null mesh the vertex shader will be invoked once for each 
 * "vertex" given by the null mesh's vertex count and the current invocation of the vertex shader
 * can then be identified by the SV_VertexId system value. This is enough information to create our 
 * vertex information on the GPU for a screen quad (you can also create other deterministic meshes 
 * such as particles in the same way).
 * Note that for 4 vertices to define a quad (if using indices, SV_VertexId will in fact be set to 
 * the index value and not the vertex it maps to; null meshes doesn't use indices since it makes no
 * sense with no vertex data though) we need to use the TOPOLOGY_TRIANGLESTRIP topology when creating
 * the null mesh. This is also its default topology unless you set it otherwise manually.
 **/
vs_out VS(uint id : SV_VertexId) {
	vs_out OUT;
	
	OUT.pos			= Vertex[id];
	OUT.texcoord	= Texcoord[id];
	
	return OUT;
}


/**
 * The pixel shader will blur the image drawn to the screen quad by sampling blocks of 9x9 texels surrounding 
 * the pixel currently being written from the input render target and then taking the average colour value of those.
 **/
float4 PS(vs_out IN) : SV_TARGET {
	float4 avgCol = float4(0, 0, 0, 0);
	for(int y = -4; y < 5; y++) {
		for(int x = -4; x < 5; x++) {
			avgCol += SCTexture.Sample(SCSampler, IN.texcoord + float2(x * TexelWidth, y * TexelHeight)).rgba;
		}
	}

	return float4(avgCol.rgba / 81.0);	// We sample 9x9 = 81 texels to blend
}
