StreamOut RenderTarget to fully obtain all hair vertices for NVIDIA HairWorks?


I’ve successfully modified the pixel shader used in HsShadowDx11HairSample.cpp to have 8 lights instead of one, which works perfectly.

Now I wonder, whether its possible to render the hair vertices to a DirectX 11 Stream Out (SO) render target by using a Geometry-Shader Object with Stream Output

I found struct NvHair_PixelShaderInput, which would be the output of a geometry shader, if one exists. In its description it says: […]input to this pixel shader (output from eariler geometry/vertex stages)[…], so this inidcates there could be already a geometry shader.
In “Dx11ApiInstance::preRender” in NvHairDx11ApiInstance.cpp I found context->GSSetShaderResources and a call to context->SOSetTargetswith “positionSoBuffer” which I want as result.

positionSoBuffer = nextTessellatedMasterStrandBuffer();
seems to set this butter to produce then the tesselated positions through the SO, right? So when I use staging with the “positionSoBuffer” from the “preRender”, do I get the full hair positions? So does the “preRender” function do already what I want?
Or are these only the master strands, which later then again get tesselated?
How do I get the fully tesselated vertices ?
Is there an indication where the strands start/end ?

I found \src\Nv\HairWorks\Internal\Shader\Geometry\NvHairSplineGeometry.hlsl
which seems to be the Geometry Shader I was searching for, its producing vertex, tangent normal outputs.
And I found NvHairInterpolateGeometry.hlsl, which outputs NvHair_PixelShaderInput: this seems to be for the full hair rendering.
I found numVertsPerSegments and numMasterSegments in Dx11ApiInstance::_prepareTesselateConstantBuffer,
and I obtained these from the “HairWorks::Asset” class:

auto numGuideHairs = asset->m_rootIndices.getSize();
auto numVertices = asset->m_masterStrandControlVertices.getSize();
auto numGuideHairsControlVertexOffsets = asset->m_masterStrandControlVertexOffsets.getSize();

so it seems that gives me the topology of all hair strands

It turned out that:
numTessellatedPoints = strandPointCounts * numGuideHairs

Thank you !

My System:
HairWorks 1.3
DirectX 11
Windows10PRO 64bit (21H1)

Turned out, that Dx11ApiInstance::preRender gave me the guide hairs only; I was able to obtain all the vertex and normal data through Strean Out (SO) and passed it to my pathtracer module.
So next I need to also export all the final hairs with a derivative of NvHairInterpolateGeometry.hlsl
struct DSOut seems to give me all the required data.

here some screenshots from what I got so far:

Hi there @m001,

nice to see what you can still get out of this tool and thank you for sharing!

It is a legacy SDK without any support, so you might be on your own on this one. I just wonder where you found version 1.3 of the SDK? Even I am not able to find that anymore on our servers…

Hi @MarkusHoHo,

thank you for your answer.
I downloaded it in 2017 and (the file has timestamp June 7th 2017, so
it seems that I unzipped it on that day).

At that time I had this version information:
NVIDIA HairWorks v1.3.1 Jul 24 2017
NVIDIA HairWorks v1.3 Feb 27 2017
NVIDIA HairWorks v1.2.1 Nov 29 2016
NVIDIA HairWorks v1.2 June 17 2016
NVIDIA HairWorks v1.1.1 Dec 06 2015

From my notes I had a problem with v1.4 beta (GitHub):
“FurViewer cannot read its previous projects/APX files (unverified)”
So I did never upgrade to 1.4
Were there later versions?
I cannot find any repo of HairWorks on Github anymore in,
although Flex, Flow and WaveWorks are still there.

Is there any newer replacement API for HairWorks from NVIDIA?

Thank you.

Thanks for the reply.

No, there were no newer versions anymore, Hairworks was discontinued.

There is also no dedicated replacement since there are now all kinds of other third-party tools available for hair and fur. For example Houdini Groom or MetaHuman with UnrealEngine.

The closest you get in terms of NVIDIA SDKs is still Omniverse and PhysX. But I don’t think that will allow you to use SO render targets.

Thank you for the information.

So I will continue. Maybe someone else here on the forums still is using this SDK.
However, I think I already got, what I needed to know.

From the numFaces * 64 (batch instance size) = instances I get number of final tesellated hairs.

#define NHAIRS_PER_PATCH 64  // in NvHairInternalShaderCommon.h
const unsigned int numFaces = (unsigned int)apiInst->m_numFaces;
auto strandPointCounts = inst->getStrandPointCount();
const unsigned int numSegmentVertex_ids = strandPointCounts - 1;  // = "numSegments" in HS
context->Draw(apiInst->m_numFaces, 0);   // HS, DS, GS =>  SO

On the example I used (the 2nd post in this thread) here:
strandPointCounts=9 => numSegmentVertex_ids=8
330 faces
168960 primitives generated (found through D3D11_QUERY_DATA_SO_STATISTICS.PrimitivesStorageNeeded)

168960 = 330 * 8 * 64
330 faces * (8 vertices per hair instance) * (64 batch instance size)
330 faces * 64 (batch instance size) = 21120 instances => 21120 final hairs
each final hair has up to 8 vertices

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.