HairWorks SDK provides various API functions to manage assets and GPU resources, render and simulate hairs. Above diagram summarizes how each API calls relates to user inputs (left) and internal resources (right). Main header file is located in include\GFSDK_HairWorks.h and must be included to use HairWorks data structures and runtime APIs. All the API functions are encapsuled by an abstract API class GFSDK_HairSDK. HairWorks runs in any DirectX11 capable GPU devices and require dx11 runtime.
The first step is to locate and load the HairWorks dll (located in bin\win32 and bin\win64). Make sure your runtime path points to the directory or copy the dll to your bin path.
To load the library:
#include "GFSDK_HairWorks.h" // hairworks main header file
GFSDK_HairSDK* g_pHairSDK = GFSDK_LoadHairSDK("GFSDK_HairWorks.win64.dll",
GFSDK_HAIRWORKS_VERSION);
A custom memory allocator may be supplied as an optional argument. See the header file.
HairWorks defines two different types of hair data - asset (GFSDK_HairAssetDescriptor) and instance (GFSDK_HairInstanceDescriptor).
A hair asset stores all the geometry data such as hair curves, skinning weights, etc., which are authored outside of HairWorks runtime and do not change during runtime. All the asset data reside in CPU memory once loaded and do not consume any GPU memory, thus do not require DirectX runtime to exist.
From each asset, users can instantiate multiple hair instances and its runtime behavior is controlled by the instance descriptor. Creation of each instance requires GPU buffers to be allocated and managed, thus requires DirectX runtime to be initialized beforehand. Separation of assets from instances allows users to share the same hair asset to create multiple characters, and efficiently manage GPU resources when hairs are not needed in certain parts of game.
HairWorks provides multiple ways to load hair asset data.
You can load a hair asset from the .apx/.apb file (generated by DCC tool or FurViewer) using the API:
g_pHairSDK->LoadHairAssetFromFile(
"test.apx", // file path to the .apx/.apb file
&hairAssetID); // output hair asset ID from HairWorks
, or you can directly load it from memory buffer (e.g. when custom file system is used) using the API:
g_pHairSDK->LoadHairAssetFromMemory(
pMemoryBuffer, // user pointer that stores the entire apx file in memory
memoryBufferSizeBytes, // size of the user memory buffer
&hairAssetID); // output hair asset ID from HairWorks
, or you can manually fill in the asset descriptor (GFSDK_HairAssetDescriptor) and create a hair asset from it:
GFSDK_HairAssetDescriptor hairAssetDescriptor;
hairAssetDescriptor.m_NumGuideHairs = 4; // number of guide hairs
hairAssetDescriptor.m_NumVertices = 20; // number of total hair cvs
hairAssetDescriptor.m_pVertices = vertices; // cv position data
hairAssetDescriptor.m_pEndIndices = endIndices; // index to last cv for each guide curve
...
g_pHairSDK->CreateHairAsset(hairAssetDescriptor, &hairAssetID);
All above APIs output a unique hair asset ID (GFSDK_HairAssetID) that can be later used to identify the hair asset for further modification and instance creation, so save this ID somewhere.
When D3D device is created and intialized, initialize HairWorks runtime using the API:
g_pHairSDK->InitRenderResources(pd3dDevice);
This will initialize all the global GPU resources used by HairWorks runtime such as shaders, etc.
Once the DirectX runtime was initialized, we can start creating hair instances using the API:
GFSDK_HairInstanceID hairInstanceID;
g_pHairSDK->CreateHairInstance(hairAssetID, &hairInstanceID);
When hair instances are no longer needed, delete the resource using the API:
g_pHairSDK->FreeHairInstance(hairInstanceID);
To simulate and render hairs, use the API functions described in this section. In many situations, users may want to separate code path for animation/simulation from rendering control flow. For each such code path, one needs to set the followings.
set render context for HairWorks:
g_pHairSDK->SetCurrentContext(pd3dContext);
set view matrix and projection matrix:
D3DXMATRIX projection; // ... get projection matrix from your camera definition
D3DXMATRIX view; // // ... get model view matrix from your camera definition
g_pHairSDK->SetViewProjection((const gfsdk_float4x4*)&view,(const gfsdk_float4x4*)&projection);
A hint about handedness of the camera may be supplied (used for backface culling feature). It is important to provide correct camera information as many features such as LOD rely on camera info. If your application has multiple windows with different camera settings, make sure to provide each camera info properly before calling the APIs below.
By default, HairWorks use control parameters stored in the asset. By updating instance descriptor, users can change all the control parameters anytime during runtime:
GFSDK_HairInstanceDescriptor hairInstanceDescriptor;
hairInstanceDescriptor.m_width = 0.2;
hairInstanceDescriptor.m_density = 1.0;
hairInstanceDescriptor.m_lengthNoise = 0.0f;
hairInstanceDescriptor.m_simulate = true;
...
g_pHairSDK->UpdateInstanceDescriptor(hairInstanceID, hairInstanceDescriptor);
To move and animate hairs, a typical process is to give it an animation first, by updating bones used to skin animated hair shapes. Once skinning data are set, we call StepSimulation() to simulate motion of hairs for each frame.
A skinning matrix is defined as inverse(bind pose) * world space bone matrix. This is exactly the same matrix you would use to skin underlying character mesh vertices.
Note that number of bones and bone orders must match the ones defined in the asset descriptor. (see the SetBoneRemapping() API to deal with bone number/order mismatch issue).
HairWorks provide the following options to update skinning.
Updating the skinning with linear matrix:
g_pHairSDK->UpdateSkinningMatrices(hairInstanceID, numBones, pSkinningMatrices);
Updating the skinning with dual quaternion:
g_pHairSDK->UpdateSkinningDQs(hairInstanceID, numBones, pDQs);
Note: If skinning update APIs are not used, HairWorks uses identity transform for all the skinning data.
To activate simulation, m_simulate member of GFSDK_HairInstanceDescriptor must be set to true:
// store hairInstanceDescriptor somewhere
hairInstanceDescriptor.m_simulate = true;
g_pHairSDK->UpdateInstanceDescriptor(hairInstanceID, hairInstanceDescriptor);
To simulate hairs, use the following API:
g_pHairSDK->StepSimulation();
This function simulates all active hair instances in a batch call. Note that if m_simulate is set to false, hairs will be animated due to the skinning data. So it is important to use this function even if you only want skinning based animation.
To render hairs, users need to set device context and camera info as above. Then users would set pixel shader and other rendering resources if needed. Then calling RenderHairs() API will draw hairs onto current render target buffers.
To provide maximum flexibility in hair rendering, HairWorks doesn’t use any hair shaders. Instead, we provide various example hair shaders in the included sample codes. Users can use such hair shaders or modified version of them which suit their needs the best. It is user’s responsibility to create and manage such shaders.
Set your hair pixel shader before rendering hairs:
pd3dContext->PSSetShader(myCustomHairWorksShader, NULL, 0);
To render a hair instance, use the API:
g_pHairSDK->RenderHairs(hairInstanceID);
HairWorks provide the following APIs for customized shader resource managment.
In RenderHairs() call, users can provide additional shader settings to indicate what part of shader pipeline will be customized by the users.
HairWorks shaders need some global info such as camera projection, hair materials to be defined in a constant buffer. This API facilitate preparation of such constant buffers.
See sample codes for more details of how these APIs and hair shaders work.
To release the instance, asset, render resource and library, call the following APIs:
g_pHairSDK->FreeHairInstance(hairInstanceID); // deletes a hair instance
g_pHairSDK->FreeHairAsset(hairAssetID); // deletes a hair asset
g_pHairSDK->FreeRenderResources(); // shut down global GPU resources
g_pHairSDK->Release(); // deletes the SDK