In a Pixel Shader, how to get uninterpolated pixel coordinates near some vertex

Started by
25 comments, last by AleksBak 5 years, 2 months ago

Hello! Thanks for the forum and possible answers to my stupid questions.

In Vertex Shader (only this stage before Pixel Shader!) i give the vertex coordinates so:


struct STATIC_VS_INPUT
{
	float3	position        : POSITION0;
	float3	normal          : NORMAL0;
	float2	texcoord        : TEXCOORD0;
	uint	VertexId        : SV_VERTEXID0;
};

struct STATIC_VS_OUT
{
	float4	Pos             : SV_POSITION0;	
	float4	Color           : COLOR0;
	float2	Tex             : TEXCOORD;
	uint	VertexId        : VERTEXID0;
	float4	VertexPos       : CURRENT0;
};

STATIC_VS_OUT main(STATIC_VS_INPUT vin)
{
    STATIC_VS_OUT output;
    output.Pos = mul(float4(vin.position, 1.0f), cbVS.wvp);
    output.VertexPos = float4(output.Pos.xyzw);

    output.Color = float4(saturate(dot(g_LightDir, (output.Pos * 0.5f).xyz) * g_LightColor), 1.0f);
    output.Tex = vin.texcoord;

    output.VertexId = vin.VertexId;
    return output;
}

In Pixel Shader, I do the following:


cbuffer ConstantBufferPS : register(b0)
{
    /*	__declspec(align(16)) struct ConstantBuffer_StaticPS
     *	{
     *		uint WndBufWidth;
     *		uint WndBufHeight;
     *		uint SelectedVertexID;
     *		uint SelectedPrimitiveID;
     *	}; */
    ConstantBuffer_StaticPS cbPS;
};

struct STATIC_PS_OUT
{
    float4	Color	: SV_TARGET;
    float	Depth	: SV_DEPTH;
};

STATIC_PS_OUT main(STATIC_VS_OUT input, in uint PrimitiveID : SV_PrimitiveID)
{
    // 1. after RS input.Pos 'x' and 'y' are in units of pixels;
    // 2. Our input.VertexPos (the dublicate of input.Pos in VS) still is in "View Space'

    STATIC_PS_OUT output;

    output.Depth = input.Pos.z;

    // if current 'Vertex' must be selected we draw it white:
    if (input.VertexId == cbPS.SelectedVertexID)
    {        
        output.Color = float4(1.0f, 1.0f, 1.0f, 1.0f);  // white
    }
    else
    {
        output.Color = meshTex2D.Sample(samplerState, input.Tex) * input.Color;
    }

    return output;
}

In theory, I should only get white pixels next to the "selected" vertex. But I get not only these white pixels, but also completely white triangles (see pic.):

In the picture, these white triangles on the bus buffer (on front). My task is to separate only the 'Selected Vertex'. But for some reason all the fragments in the triangles next to this Vertex turned white. How do I get what I want? I also tried to get separately the position of the vertex in another field of the structure (see above ). Then it translated to 'screen space coordinate system (in PS)':


    // float screen_x = (vertex.x / vertex.w + 1) * 0.5 * screenwidth;
    float screen_x = (input.VertexPos.x / input.VertexPos.w + 1.f) * .5f * cbPS.WndBufWidth;

But again, I got the position (may be interpolated  in Rasterizer Stage - 'RS') of the current pixel (not the Vertex!) in Pixel Shader stage. Tell me how to solve this problem. Thank.

Desktop Screenshot 2019.02.13 - 13.41.26.42.png

Advertisement

Your approach won't work because the Interpolation between uints is clamped to integer values by definition. Depending on the uints you are interpolating between this may produce a single value across the whole triangle.

One option would be to change to having a 'selected' float set to 0.0 or 1.0 in the vertex shader and colouring by threshold in the pixel shader (e.g. "selected > 0.7" or something) or using it to blend values for a smoother effect. This does not strike me as an optimal solution but it should work, I think.

13 minutes ago, Irusan, son of Arusan said:

Your approach won't work because the Interpolation between uints is clamped to integer values by definition. Depending on the uints you are interpolating between this may produce a single value across the whole triangle...

I didn't quite understand what you wanted to write (sorry if I'm dumb). I need to find only the Vertex coordinates. And not the adjacent pixels in triangles. I.e. when the pixel shader is executed many times I want to separate Vertex in some of these parallel threads only. And I decided to separate this 'Vertex 'by coordinates. Those only it is needed, and not adjacent pixels.

Those. in the Pixel Shader (in one of the many it's parallel streams), let's say only react to the vertex V1 (see fig.)

1*iXasM6kAXxMEO1adrJedtQ.jpeg

 

 

25 minutes ago, Irusan, son of Arusan said:

...One option would be to change to having a 'selected' float set to 0.0 or 1.0 in the vertex shader and colouring by threshold in the pixel shader (e.g. "selected > 0.7" or something) or using it to blend values for a smoother effect. This does not strike me as an optimal solution but it should work, I think.

Can you explain a little more exactly what to do? I still do not quite understand. Thank.

This bit of code:


    // if current 'Vertex' must be selected we draw it white:
    if (input.VertexId == cbPS.SelectedVertexID)
    {        
        output.Color = float4(1.0f, 1.0f, 1.0f, 1.0f);  // white
    }

Compares the "interpolated" uint input.VertexId to the constant uint cbPS.SelectedVertexID. But unlike in your example with the coloured triangle the values you are interpolating between to get input.VertexId are integers and therefore can only hold whole number values. I don't know exactly how the graphics card will implement interpolation for integers, but I do know that it can have only a very limited number of values, if you're interpolating between two adjacent integers just two, and - worse - many possible implementations will produce a single value across the entire triangle in this case.I suspect this is why you are seeing the whole white triangle.

To fix it, change your vertex shader code to pass a `selected` value through instead of a VertexID, and make this value a float, something like this, although it's packed poorly and you can likely do better


struct STATIC_VS_OUT
{
	float4	Pos             : SV_POSITION0;	
	float4	Color           : COLOR0;
	float2	Tex             : TEXCOORD;
	float	Selected        : SELECTED;
	float4	VertexPos       : CURRENT0;
};

This means you will need to pass the selected vertex id as a constant to the vertex shader instead of the pixel shader, and then do a comparison in there, replacing your current "output.VertexId = vin.VertexId" line with something like:


if (vin.VertexId == cbSp.SelectedVertexId) {
  output.Selected = 1.0;
} else {
  output.Selected = 0.0;
}

Finally in your pixel shader, you can do this (or, for a better effect, some kind of blend to white), instead of comparing the vertexId to the selectedID:


//0.7 is an arbitrary threshold, you can vary to taste
if (input.selected > 0.7) {
  output.colour = float4(1.0, 1.0, 1.0, 1.0); // white
} else {
  // etc...
}

Is that clearer?

6 minutes ago, Irusan, son of Arusan said:

......

To fix it, change your vertex shader code to pass a `selected` value through instead of a VertexID, and make this value a float, something like this, although it's packed poorly and you can likely do better



struct STATIC_VS_OUT
{
	float4	Pos             : SV_POSITION0;	
	float4	Color           : COLOR0;
	float2	Tex             : TEXCOORD;
	float	Selected        : SELECTED;
	float4	VertexPos       : CURRENT0;
};

.....

Thank you very much for the answer! But you are mistaken in my opinion. Of course, I will now try to do as you advise (to do as float), but nothing will work out in my opinion.

7 minutes ago, Irusan, son of Arusan said:

This bit of code:



    // if current 'Vertex' must be selected we draw it white:
    if (input.VertexId == cbPS.SelectedVertexID)
    {        
        output.Color = float4(1.0f, 1.0f, 1.0f, 1.0f);  // white
    }

Compares the "interpolated" uint input.VertexId to the constant uint cbPS.SelectedVertexID.

No! This value 'input.VertexId' does not interpolate! If you look at this, the Vertex_ID is just passing uint value from the Vertex Shader to the Pixel Shader (see the code - SV_Vertex_ID). In the Constant Buffer for the Pixel Shader, I also pass in which some uint value - cbPS.SelectedVertexID for the "selection" Vertex. There are no problems with this and I can select it every time in the pixel shader. All adjacent pixels with it are also highlighted in white (see screenshot). But I want to select just the Vertex. Therefore, in parallel, I transfer its individual coordinates to the Pixel Shader - the input.VertexPos field in the structure. However, I see (when debugging) that this field input. VertexPos also interpolates. I translate this field into screen coordinates, and then compare it with the coordinates of the current pixel (which is currently being drawn from this stream) and I see that they are the same:


float screen_x = (input.VertexPos.x / input.VertexPos.w + 1.f) * .5f * cbPS.WndBufWidth;

// Now, screen_x == input.Pos.x for current primitive (triangle) !!! But why?
// I gave this input.VertexPos field separately from VS, but it turns out again the same.

 

Pixel shader values are always interpolated. This is the point. The vertices set values for the vertices, and then the values for each pixel within the triangle are determined by interpolation. Anything you output from the vertex shader will get interpolated before they reach the pixel shader stage. If you think about it, it pretty much has to be this way: a pixel lies between three vertices in a triangle, if it's not going to interpolate then how could it decide which of the three vertices to take data from if they are different?

Those. If we say a pixel in some kind of triangle has coordinates (800.5, 450.5) on the screen - field


input.Pos

. And again I get this value (800.5, 450.5) for


input.VertexPos

Although in theory it should not change inside the triangle. Should not interpolate. But for some reason this is not the case. And because of this, I cannot determine when exactly a 'Vertex Pixel' is drawn at a given point in time. And where are just the pixels inside the triangle. I clearly explained? Linus wrote that the ability to correctly write a question is half the solution to the problem. I probably poorly explained initially what I need.

2 minutes ago, Irusan, son of Arusan said:

... If you think about it, it pretty much has to be this way: a pixel lies between three vertices in a triangle, if it's not going to interpolate then how could it decide which of the three vertices to take data from if they are different?

But I separately transmit the duplicate coordinates of Vertex (see the field input.VertexPos - it's not 'SV_POSITION' semantic!) which I just do the same in the Vertex Shader. In theory, it should not be interpolated in the Rasterizer (in stage before of the Pixel Shader). But it goes out with me, too, is interpolated. Because of what I can’t find out when a 'Vertex' is drawn from me, and when it’s not (adjacent pixels in a triangle).

14 minutes ago, AleksBak said:

But I separately transmit the duplicate coordinates of Vertex (see the field input.VertexPos - it's not 'SV_POSITION' semantic!) which I just do the same in the Vertex Shader. In theory, it should not be interpolated in the Rasterizer (in stage before of the Pixel Shader). But it goes out with me, too, is interpolated. Because of what I can’t find out when a 'Vertex' is drawn from me, and when it’s not (adjacent pixels in a triangle).

All values that are output from the VertexShader are interpolated during the raster stage. All of them. It doesn't matter whether you use a named semantic or not. Colour, texture position, day-of-the-week, hat-tippness, whatever. It doesn't matter. The pipeline takes the values from the vertex shader and then interpolates them onto pixels as it rasterises. Setting a different output field makes no difference, it still gets interpolated.

From the Microsoft documentation (you don't say which graphics API you are using, this is for DirectX11 but it's the same on others), emphasis is mine:

Quote

Pixel shader input data includes vertex attributes (that can be interpolated with or without perspective correction) or can be treated as per-primitive constants. Pixel shader inputs are interpolated from the vertex attributes of the primitive being rasterized, based on the interpolation mode declared. If a primitive gets clipped before rasterization, the interpolation mode is honored during the clipping process as well.

 

I will explain once more, otherwise I see I can explain something very badly (sorry). I need in the Pixel Shader to respond only to the 'Selected Vertex', and not the adjacent pixels in the triangle. Those. Only the pixels (fragments) of this Vertex is painted white. The code of the Pixel Shader that I gave at first topic is not quite complete (I can fix it - sorry, but then I explained there, what else am I doing). I compare it further along the coordinate input.Pos and input.VertexPos) and only if they are equal (i.e. now we have a 'Selected Vertex' pixel drawn, and not adjacent pixels in a triangle), then it should be selected.

2 minutes ago, Irusan, son of Arusan said:

All values that are output from the VertexShader are interpolated during the raster stage. All of them. It doesn't matter whether you use a named semantic or not. Colour, texture position, day-of-the-week, hat-tippness, whatever. It doesn't matter. ...

This is very bad. Why do these vipers do this? This driver does. But why SV_VertexID not interpolated? So it goes. Then it turns out I can try to separately transmit the 3 float coordinates of the vertex - in x, y and z from VS --> PS? Then this harmful driver will not be able to interpolate this coordinate. I need some time to check it in my program (if it makes sense at all) and then I will write what I will have in the end.

This topic is closed to new replies.

Advertisement