structs, semantics, vs_input and vs_output questions

Started by
4 comments, last by digs 8 years, 2 months ago

Hi everyone, I'm new here, and new to HLSL. I'm hoping that some of you may be able to help me understand some of the parts of graphics programming that i don't understand, or can't really seem to find a good explanation on.

I was writing a simple shader last night and realized that I don't understand what's really going on behind the scenes, but instead I'm just memorizing where code is supposed to go.

If we can please start with something as simple as this:


struct VS_INPUT 
{ float4 mPosition : POSITION; };

struct VS_OUTPUT 
{ float4 mPosition : POSITION; };

float4x4 gWorldMatrix;
float4x4 gViewMatrix;
float4x4 gProjectionMatrix;

VS_OUTPUT vs_main ( VS_INPUT Input )
{
VS_OUTPUT Output;

Output.mPosition = mul(Input.mPosition, gWorldMatrix);
Output.mPosition = mul(Output.mPosition, gViewMatrix);
Output.mPosition = mul(Output.mPosition, gProjectionMatrix);

return Output;
}

I realized that I don't even know what the input and output structs are really doing. For example, they appear to be identical (except for their names, which can be called whatever) I guess I don't understand why we need to declare the same variable twice, and why they look identical.

How does my input struct know that it should contain all the semantics for an object?

Is the output struct only used AFTER the object is *returned* from VS_OUTPUT vs_main()?

Advertisement

The input and output structs don't have to be identical. The input semantics specify the fields you want to read from the vertex buffers per vertex. Most of the time, there is a field that represents the positions of a vertex, but this is not required. The input can also be a subset of the fields in your model's vertices or even empty if you only need system-generated values.

The output specifies the fields that are sent to the rasterizer. Here you are actually required to provide a field with the position semantic, so that the rasterizer knows what the triangles you want to draw look like. You can also output additional values from the vertex shader. For example, you might want to calculate the output-vertices' texture coordinates from the input-vertices' position.

Assume that you want to draw a terrain mesh, deform it using a height map and generate a color for each vertex based on its height:


struct VS_INPUT 
{
	float3 positionModelSpace : POSITION;
	float2 textureCoordinate : TEXCOORD0;
};

struct VS_OUTPUT 
{
	float4 positionProjSpace : POSITION;
	float4 color : COLOR0;
};

float4x4 gWorldMatrix;
float4x4 gViewMatrix;
float4x4 gProjectionMatrix;

VS_OUTPUT vs_main ( VS_INPUT Input )
{
	VS_OUTPUT Output;
	
	float4 positionWorldSpace = mul(Input.positionModelSpace, gWorldMatrix);
	
	float height = get_height_from_texture(Input.textureCoordinate);
	positionWorldSpace.y += height;
	
	Output.color = get_color_from_height(height);

	float4 positionViewSpace = mul(positionWorldSpace, gViewMatrix);
	Output.positionProjSpace = mul(positionViewSpace, gProjectionMatrix);

	return Output;
}

current project: Roa

The input should also be aligned with the inputlayout and struct of your input vertices.

You can also say that VS output == PS input (or geometry shader input, if you use one)

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

wow, that clears up so much!

@lwm, changing up the variables names like that helped immensely, beyond words really. I felt like before I had millions of questions, and now not so much.. Thank you! biggrin.png

There is something else I'm trying to understand, and that's the relationship between the VS_OUTPUT struct and the pixel shaders PS_INPUT struct... I should maybe mention that I'm using RenderMonkey to test my code

I modified lwm's example slightly so I could display a texture on my sphere:


struct VS_INPUT 
{
   float4 positionModelSpace : POSITION;
   float2 textureCoordinate : TEXCOORD0;
};

struct VS_OUTPUT 
{
   float4 positionProjSpace : POSITION;
   float2 objectUVs : TEXCOORD0;
};

float4x4 gWorldMatrix;
float4x4 gViewMatrix;
float4x4 gProjectionMatrix;

VS_OUTPUT vs_main ( VS_INPUT Input )
{
   VS_OUTPUT Output;
   
   float4 positionWorldSpace = mul(Input.positionModelSpace, gWorldMatrix);
   float4 positionViewSpace = mul(positionWorldSpace, gViewMatrix);
   Output.positionProjSpace = mul(positionViewSpace, gProjectionMatrix);
   
   Output.objectUVs = Input.textureCoordinate;

   return Output;
}

...

and then for my pixel shader:

...


sampler2D DiffuseSampler;

struct PS_INPUT
{
   float4 mPosition : POSITION;
   float2 mTexCoord : TEXCOORD0;
};


float4 ps_main(PS_INPUT Input) : COLOR0
{   
   float4 albedo = tex2D(DiffuseSampler, Input.mTexCoord);
   return albedo.rgba;
}

which works great

I just don't understand how VS_OUTPUT is sending information to PS_INPUT

typically you have function calls, which I'm not seeing anywhere. Does this have to do with the shaders "entry points"? I Don't know what these are, and will be the next thing I research

From DirectX's point of view, the vertex and pixel shaders are completely independent. The only information DX uses to send data from one pipeline stage to the next are the semantics. Whatever your vertex shader writes to the variable with the TEXCOORD0 semantic will be linearly interpolated across the triangle by the rasterizer and sent to the pixel shader input variable with the same semantic.

Most people [citation needed] will simply reuse the code for the struct for both the VS output and PS input however.

current project: Roa

Amazing, thanks again lwm

This topic is closed to new replies.

Advertisement