Home

Awesome

TressFX OpenGL

Direct port of AMD's TressFX hair/fur rendering and simulation technology to OpenGL.

https://github.com/Scthe/TressFX-OpenGL/assets/9325337/0e6c5b6f-c7b3-43f6-8f1e-bf5137a9c87a

360 view of Sintel's model. Unfortunately, Sintel is not the greatest example to demonstrate TressFX on. Read more in FAQ part of this readme!

What is this?

TressFX is AMD's library used for simulation and rendering of hair. It has been used in commercial games like newest Tomb Raider titles and Deus Ex: Mankind Divided. The library itself is open source under GPUOpen initiative. Please visit provided links to get more details.

AMD's version of TressFX uses HLSL (DirectX's shading language) and the Sushi framework. It also provides fill-the-gaps library that is independent of the rest of the source code. The main focus of my project was to provide the implementation for various methods that are needed to render and simulate hair using OpenGL and GLSL. I tried to stick to AMD's version as closely as possible, even if it would have been easier to just write everything from grounds up.

Due to time constraints following techniques are not yet implemented:

There might be still a few bugs that I will hopefully get rid off when I have a little bit more spare time.

https://github.com/Scthe/TressFX-OpenGL/assets/9325337/45fae226-db84-4baf-899e-4656bd4c2b09

Usage

Each build step has been automated with scripts (found in bin directory).

  1. Use cmake to generate project. You will need to provide paths for GLM, SDL2 and GLAD. Script: generate_makefile.clang.bat. PS. I've used clang, not tested on other compilers.

  2. Generate shaders. I've written simple python script that processes .glsl files for #pragma include "xxx.glsl" and textually replaces this line with content of the file. You can even do this by hand. Script: build_glsl.py. At the end of this step You should have following files:

    • src\shaders\generated\gl-tfx\ppll_build.frag.glsl
    • src\shaders\generated\gl-tfx\ppll_build.vert.glsl
    • src\shaders\generated\gl-tfx\ppll_resolve.frag.glsl
    • src\shaders\generated\gl-tfx\ppll_resolve.vert.glsl
    • src\shaders\generated\gl-tfx\sim0_IntegrationAndGlobalShapeConstraints.comp.glsl
    • src\shaders\generated\gl-tfx\sim1_VelocityShockPropagation.comp.glsl
    • src\shaders\generated\gl-tfx\sim2_LocalShapeConstraints.comp.glsl
    • src\shaders\generated\gl-tfx\sim3_LengthConstraintsWindAndCollision.comp.glsl
    • src\shaders\generated\gl-tfx\sim3_TMP_LengthConstraintsWindAndCollision.comp.glsl
    • src\shaders\generated\gl-tfx\sim4_UpdateFollowHairVertices.comp.glsl

    PS. build_glsl.py also calls bin\glslcompiler\GLSLCompiler.exe, which is small shader compiler. It takes .glsl path as first argument and does test compile. This tool is useful for development and is not included in this repo. Just comment this line, You should not need it (TressFx.exe will fail anyway if shader does not compile).

  3. Build: make. Script: build_and_run.bat. PS. this script also invokes build_glsl.py and runs bin\TressFx.exe upon successful compile. All-in-one.

  4. Run bin\TressFx.exe

TL;DR: It's just 2 scripts to run project from scratch:

  1. generate_makefile.clang.bat
  2. build_and_run.bat that calls:
    1. build_glsl.py
    2. make on cmake-generated makefile
    3. bin\TressFx.exe - to run executable

FAQ

Q: Required OpenGL version?

A: Use 4.5+. Technically direct state access(DSA), shader storage buffer object(SSBO), compute shaders, image load/store, atomic ops are required. They could be used through extensions, but You would probably have to write support for them by hand. Just use 4.5+ and it should work.

Q: Some warnings during compilation?

A: They are from AMD's code that is included. AMD had used visual studio, so there were a few things I had to change to make it compile under clang. See next question.

Q: What changes were needed compared to AMD's library?

A: Complete list. I tried to keep the changes to minimum, even if it included having warnings during compilation. My own API is also modeled to be as close as possible.

Q: Is this a Blender plugin?

A: Sure. It is very bare-bones ATM, I will probably make a separate github repo for it. It does not support .tfxbone (skinning) and per-strand uv, but it is enough for simulation and basic rendering. Should work with AMD's viewer, although I did not test it.

Q: How to load new models?

A: Authoring new models for TressFX is actually quite complicated. First, there is the scale. There are certain simulation steps that require models to have roughly comparable scale. Good example is wind: displacing hair strand that has length of 3 units is vastly different to hair strand 300 units long. Sintel's model is about 50x50x50 blender units.

Another thing is tweaking all simulation and rendering parameters. E.g. AMD's library calculates random vectors around the guide hair where follow hair will be placed. Since You do not have any input in this (unless You modify AMD's library, which I tried not to do), it may be required to tweak it later.

There are also collision capsules that are really hard to get right. When hair near root intersects with collision capsule it is automatically 'pushed away'. Collision resolve has the highest priority and that results in colliding part of the hair strand just ignoring any other simulation forces. One could make the capsules smaller, but that leads to penetration of the object.

Fortunately, just getting it to display requires only to import the model from Blender / Maya.

Q: Available models?

A: Found in assets:

Q: Sintel? How cool!

A: Well, I made a rule to not modify original sintel lite hair model. Absolutely no content authoring, even small fixes. As a result, there are a few issues, which can probably be noticed when watching animations above. The biggest issue is wind that blows from one side and pushes half of the hair strands 'into' the head model. This is resolved as a collision with collision capsule and fixed by overriding strand position. Which in turn results in half of the hair just ignoring wind altogether. I think one could solve this by using multiple wind sources, instead of averaging? Or just properly author the model.

We can compare this to models from commercial games that utilize TressFX:

Q: What is glUtils?

A: VERY thin wrapper around OpenGL. Thin as in 'You still have to write most of gl- calls anyway' thin. It mostly deals with resource creation. It was made to make OpenGL api more functional. Good example is glUtils::apply_draw_parameters that takes DrawParameters structure as an argument. Feel free to look around, there are some comments about how does OpenGL work out there that You may find interesting.

Q: What to do if I have a problem?

A: In main() set log level to trace: logger::Log::ReportingLevel = logger::Trace;. It will generate A LOT of text into console. Also, the code is CRAMMED with assertions. And if the assertion is not clear enough, just go to the code line (printed with assertion message) and there is usually a comment about what could have gone wrong.

Q: Your PC?

A: GTX 1050 Ti, Driver 398.36

Honorable mentions and other 3rd party stuff

Licencing

Certain parts of this project are under AMD's licence (which is MIT) - noted usually as licence file in directory or in file header. Rest is under licence of this project (which is also MIT).