1
votes

Okay, i'm about stuck. I've been trying to convert the Nvidia WireFrame Effect to vs, gs and ps HLSL Files and use in my DirectX11.1 application. The conversion see simple enough. here is the Nvida Sample

here is my take on the vs:

cbuffer WorldViewProjectionType : register(b0)
{
matrix World;
matrix View;
matrix Projection;
};

struct VS_INPUT
{
    float3 Pos : POSITION;
    float3 Norm : NORMAL;
    float2 Tex : TEXCOORD0;
};

struct GS_INPUT
{
    float4 Pos  : POSITION;
    float4 PosV : TEXCOORD0;
};

GS_INPUT main(VS_INPUT input)
{
GS_INPUT output;

matrix WorldView = mul(World, View);
matrix WorldViewProjection = mul(WorldView, Projection);

output.Pos = mul(float4(input.Pos, 1), WorldViewProjection);
output.PosV = mul(float4(input.Pos, 1), WorldView);


return output;
}

The gs:

float4 LightVector = float4(0, 0, 1, 0);
float4 FillColor = float4(0.0f, 0.125f, 0.3f, 1.0f);

struct GS_INPUT
{
float4 Pos  : POSITION;
float4 PosV : TEXCOORD0;
};

struct PS_INPUT_WIRE
{
float4 Pos : SV_POSITION;
float4 Col : TEXCOORD0;
noperspective float3 Heights : TEXCOORD1;
};

// Compute the triangle face normal from 3 points
float3 faceNormal(in float3 posA, in float3 posB, in float3 posC)
{
return normalize(cross(normalize(posB - posA), normalize(posC - posA)));
}

// Compute the final color of a face depending on its facing of the light
float4 shadeFace(in float4 verA, in float4 verB, in float4 verC)
{
// Compute the triangle face normal in view frame
float3 normal = faceNormal(verA, verB, verC);

    // Then the color of the face.
    float shade = 0.5*abs(dot(normal, LightVector));

return float4(FillColor.xyz*shade, 1);
}

[maxvertexcount(3)]
void main(triangle GS_INPUT input[3], inout TriangleStream<PS_INPUT_WIRE> outStream)
{
PS_INPUT_WIRE output;

// Shade and colour face.
output.Col = shadeFace(input[0].PosV, input[1].PosV, input[2].PosV);

// Emit the 3 vertices
// The Height attribute is based on the constant
output.Pos = input[0].Pos;
output.Heights = float3(1, 0, 0);
outStream.Append(output);

output.Pos = input[1].Pos;
output.Heights = float3(0, 1, 0);
outStream.Append(output);

output.Pos = input[2].Pos;
output.Heights = float3(0, 0, 1);
outStream.Append(output);

outStream.RestartStrip();
}

and the ps:

float4 WireColor = float4(1, 1, 1, 1);
float LineWidth = 0.15;
float PatternPeriod = 0.15;
float4 PatternColor = float4(1, 1, 0.5, 1);

struct PS_INPUT_WIRE
{
float4 Pos : SV_POSITION;
float4 Col : TEXCOORD0;
noperspective float3 Heights : TEXCOORD1;
};

float det(float2 a, float2 b)
{
return (a.x*b.y - a.y*b.x);
}

float4 main(PS_INPUT_WIRE input) : SV_Target
{
// Compute the shortest square distance between the fragment and the edges.
float3 eDists;
float3 vDists;
uint3 order = uint3(0, 1, 2);

    float dist;

float3 ddxHeights = ddx(input.Heights);
    float3 ddyHeights = ddy(input.Heights);
    float3 invddHeights = 1.0 / sqrt(ddxHeights*ddxHeights + ddyHeights*ddyHeights);

    eDists = input.Heights * invddHeights;
vDists = (1.0 - input.Heights) * invddHeights;

if (eDists[1] < eDists[0])
{
    order.xy = order.yx;
}
if (eDists[2] < eDists[order.y])
{
    order.yz = order.zy;
}
if (eDists[2] < eDists[order.x])
{
    order.xy = order.yx;
}

// Now compute the coordinate of the fragment along each edges

float2 hDirs[3];
hDirs[0] = float2(ddxHeights[0], ddyHeights[0]) * invddHeights[0];
hDirs[1] = float2(ddxHeights[1], ddyHeights[1]) * invddHeights[1];
hDirs[2] = float2(ddxHeights[2], ddyHeights[2]) * invddHeights[2];

float2 hTans[3];
hTans[0] = float2(-hDirs[0].y, hDirs[0].x);
hTans[1] = float2(-hDirs[1].y, hDirs[1].x);
hTans[2] = float2(-hDirs[2].y, hDirs[2].x);

float2 ePoints[3];
ePoints[0] = input.Pos.xy - hDirs[0] * eDists[0];
ePoints[1] = input.Pos.xy - hDirs[1] * eDists[1];
ePoints[2] = input.Pos.xy - hDirs[2] * eDists[2];

float2 eCoords[3];
eCoords[0].x = det(hTans[1], ePoints[0] - ePoints[1]) / det(hTans[0], hTans[1]);
eCoords[0].y = det(hTans[2], ePoints[0] - ePoints[2]) / det(hTans[0], hTans[2]);

eCoords[1].x = det(hTans[2], ePoints[1] - ePoints[2]) / det(hTans[1], hTans[2]);
eCoords[1].y = det(hTans[0], ePoints[1] - ePoints[0]) / det(hTans[1], hTans[0]);

eCoords[2].x = det(hTans[0], ePoints[2] - ePoints[0]) / det(hTans[2], hTans[0]);
eCoords[2].y = det(hTans[1], ePoints[2] - ePoints[1]) / det(hTans[2], hTans[1]);


float2 edgeCoord;

// Current coordinate along closest edge in pixels
edgeCoord.x = abs(eCoords[order.x].x);
// Length of the closest edge in pixels
edgeCoord.y = abs(eCoords[order.x].y - eCoords[order.x].x);

dist = eDists[order.x];

// Standard wire color
float4 color = WireColor;
    float realLineWidth = 0.5*LineWidth;

// if on the diagonal edge apply pattern
if (2 == order.x)
{
    if (dist > LineWidth + 1) discard;

    float patternPos = (abs(edgeCoord.x - 0.5 * edgeCoord.y)) % (PatternPeriod * 2 * LineWidth) - PatternPeriod * LineWidth;
    dist = sqrt(patternPos*patternPos + dist*dist);

    color = PatternColor;
    realLineWidth = LineWidth;

    // Filling the corners near the vertices with the WireColor
    if (eDists[order.y] < (0.5*LineWidth + 1))
    {
        dist = eDists[order.y];
        color = WireColor;
        realLineWidth = 0.5*LineWidth;
    }
}
// Cull fragments too far from the edge.
else if (dist > 0.5*LineWidth + 1) discard;

// Map the computed distance to the [0,2] range on the border of the line.
dist = clamp((dist - (realLineWidth - 1)), 0, 2);

// Alpha is computed from the function exp2(-2(x)^2).
dist *= dist;
float alpha = exp2(-2 * dist);

color.a *= alpha;
return color;
}

pretty much a direct copy of there implementation. In vs2013 i set the HLSL compiler to version 5.0 and output a Header file containing the bytecode etc.

In my application i have defined a new class {SolidWireFrame.cpp} that creates a vs, gs and ps using the bytecode in the header files.

these build vs, gs and ps shaders without any exceptions/errors that i know of.

The effect also sets the DepthStencilState, RasterizerState and BlendStates. So I created methods in my SolidWireFrame class to replicate these States.

bool SolidWireFrame::InitializeBlendState(ID3D11Device* device)
{
    D3D11_BLEND_DESC blendStateDescription;
    // Clear the blend state description.
    ZeroMemory(&blendStateDescription, sizeof(D3D11_BLEND_DESC));
    blendStateDescription.RenderTarget[0].BlendEnable = TRUE;
    blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
    blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
    blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
    blendStateDescription.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
    blendStateDescription.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_DEST_ALPHA;
    blendStateDescription.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
    blendStateDescription.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;

    // Create the blend state using the description.
    HRESULT result = device->CreateBlendState(&blendStateDescription, &m_pBlendingState);
    if (FAILED(result))
    {
        return false;
    }
    return true;
}

bool SolidWireFrame::InitializeDepthStencilState(ID3D11Device* device)
{
    D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
    // Initialize the description of the stencil state.
    ZeroMemory(&depthStencilDesc, sizeof(depthStencilDesc));
    // Set up the description of the stencil state.
    depthStencilDesc.DepthEnable = TRUE;
    depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
    depthStencilDesc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;

    depthStencilDesc.StencilEnable = FALSE;
    depthStencilDesc.StencilReadMask = 255;
    depthStencilDesc.StencilWriteMask = 255;
    depthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
    depthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
    depthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

    // Create the depth stencil state.
    HRESULT result = device->CreateDepthStencilState(&depthStencilDesc, &m_pdepthLessEqualStencilState);
    if (FAILED(result))
    {
        return false;
    }
    return true;
}

bool SolidWireFrame::InitializeRaterizerState(ID3D11Device* device)
{
    D3D11_RASTERIZER_DESC rasterizerDescription;
    ZeroMemory(&rasterizerDescription, sizeof(D3D11_RASTERIZER_DESC));
    rasterizerDescription.FillMode = D3D11_FILL_SOLID;
    rasterizerDescription.CullMode = D3D11_CULL_NONE;
    rasterizerDescription.DepthBias = FALSE;
    rasterizerDescription.MultisampleEnable = TRUE;

    rasterizerDescription.FrontCounterClockwise = FALSE;
    rasterizerDescription.DepthBiasClamp = 0.000000000;
    rasterizerDescription.SlopeScaledDepthBias = 0.000000000;
    rasterizerDescription.DepthClipEnable = TRUE;
    rasterizerDescription.ScissorEnable = FALSE;
    rasterizerDescription.AntialiasedLineEnable = FALSE;

    HRESULT hr = device->CreateRasterizerState(&rasterizerDescription, &m_pfillRasterizerState);
    if (FAILED(hr))
        return false;
    return true;
}

Now in my rendering loop i simply call solidWireFrame->ApplySolidWirePatter(devicecontext) which effectively sets the InputLayout, three states and the vs, gs and ps.

void SolidWireFrame::ApplySolidWirePattern(ID3D11DeviceContext* deviceContext)
{   
    deviceContext->IASetInputLayout(m_vertexLayout);
    SetDepthStencilState(deviceContext, m_pdepthLessEqualStencilState);
    SetRasterizerState(deviceContext, m_pfillRasterizerState);
    SetBlendState(deviceContext, m_pBlendingState);
    deviceContext->VSSetShader(m_vertexShader, NULL, 0);
    deviceContext->GSSetShader(m_geometrySolidWireShader, NULL, 0);
    deviceContext->PSSetShader(m_pixelSolidWireShader, NULL, 0);
}

but all i see is a cleared screen. what could be wrong?

please help.

oh, the rendering work if wireframe is not set.

Normal No WireFrame

1

1 Answers

0
votes

Seems i had a little bug in my code. when i was rendering with a texture i was using different vs, ps. so when i flipped to the wireframe vs, gs and ps i failed to update the vs Constantbuffers, which contained the world, view, projection.

so now i have wireframe, although i guess my windings are incorrect, since it hides the incorrect edge. but i'll fix that.

void SolidWireFrame::ApplySolidWirePattern(ID3D11DeviceContext* deviceContext, ID3D11Buffer* worldViewProjectionType)
{   


    deviceContext->IASetInputLayout(m_vertexLayout);
    deviceContext->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    SetDepthStencilState(deviceContext, m_pdepthLessEqualStencilState);
    SetRasterizerState(deviceContext, m_pfillRasterizerState);
    SetBlendState(deviceContext, m_pBlendingState);
    deviceContext->VSSetShader(m_vertexShader, NULL, 0);
    deviceContext->VSSetConstantBuffers(0, 1, &worldViewProjectionType);
    deviceContext->GSSetShader(m_geometrySolidWireShader, NULL, 0);
    deviceContext->PSSetShader(m_pixelSolidWireShader, NULL, 0);
}