64bbc700fa
- This rendering technique reduces the memory required per pipeline by 750MB compared to the PPLL technique!! - Using 3 screen buffers representing the closest 3 hair gragments depths. - Going through second geometry pass that disqualify any fragment further than the third depth, the technique blends the three closes shaders hair fragments. - Supports the Marschner lighting model (big change from the original TressFX 4.1 implementation) - The current technique is almost twice the performance of the PPLL but not quite as advacned when come to final visual quality. Remarks: Unlike the PPLL, this technique is still lacking some of the advanced features added to the PPLL such as 1. Back lobe (TT) conseal by depth comparison 2. Thickness dependency in light transfer (mainly TT) 3. Allowing TT transfer for thin separated hair strands (might be supported by default with no distinction) Signed-off-by: Adi-Amazon <Adi Bar-Lev 82479970+Adi-Amazon@users.noreply.github.com> Signed-off-by: Adi-Amazon <Adi Bar-Lev barlev@amazon.com> Signed-off-by: Adi-Amazon <Adi Bar-Lev 82479970+Adi-Amazon@users.noreply.github.com> Signed-off-by: Adi-Amazon <Adi Bar-Lev barlev@amazon.com> Co-authored-by: Adi-Amazon <Adi Bar-Lev 82479970+Adi-Amazon@users.noreply.github.com>
438 lines
20 KiB
Plaintext
438 lines
20 KiB
Plaintext
/*
|
|
* Modifications Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: (Apache-2.0 OR MIT) AND MIT
|
|
*
|
|
*/
|
|
|
|
//---------------------------------------------------------------------------------------
|
|
// Shader code related to simulating hair strands in compute.
|
|
//-------------------------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
//--------------------------------------------------------------------------------------
|
|
#include <HairSimulationComputeSrgs.azsli>
|
|
#include <HairSimulationCommon.azsli>
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
//
|
|
// IntegrationAndGlobalShapeConstraints
|
|
//
|
|
// Compute shader to simulate the gravitational force with integration and to maintain the
|
|
// global shape constraints.
|
|
//
|
|
// One thread computes one vertex.
|
|
//
|
|
//--------------------------------------------------------------------------------------
|
|
[numthreads(THREAD_GROUP_SIZE, 1, 1)]
|
|
void IntegrationAndGlobalShapeConstraints(
|
|
uint GIndex : SV_GroupIndex,
|
|
uint3 GId : SV_GroupID,
|
|
uint3 DTid : SV_DispatchThreadID)
|
|
{
|
|
uint globalStrandIndex, localStrandIndex, globalVertexIndex, localVertexIndex, numVerticesInTheStrand, indexForSharedMem, strandType;
|
|
CalcIndicesInVertexLevelMaster(GIndex, GId.x, globalStrandIndex, localStrandIndex, globalVertexIndex, localVertexIndex, numVerticesInTheStrand, indexForSharedMem, strandType);
|
|
|
|
// Copy data from init rest data to be used to set updated shared memory
|
|
float4 initialPos = float4(CM_TO_METERS,CM_TO_METERS,CM_TO_METERS,1.0) * g_InitialHairPositions[globalVertexIndex]; // rest position
|
|
|
|
// Apply bone skinning to initial position
|
|
BoneSkinningData skinningData = g_BoneSkinningData[globalStrandIndex];
|
|
float4 bone_quat;
|
|
initialPos.xyz = ApplyVertexBoneSkinning(initialPos.xyz, skinningData, bone_quat);
|
|
|
|
// position when this step starts. In other words, a position from the last step.
|
|
sharedPos[indexForSharedMem] = GetSharedPosition(globalVertexIndex);
|
|
float4 currentPos = sharedPos[indexForSharedMem];
|
|
// float4 currentPos = sharedPos[indexForSharedMem] = g_HairVertexPositions[globalVertexIndex];
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
// Integrate
|
|
float dampingCoeff = GetDamping(strandType);
|
|
float4 oldPos = g_HairVertexPositionsPrev[globalVertexIndex];
|
|
|
|
// reset if we got teleported
|
|
if (g_ResetPositions != 0.0f)
|
|
{ // Originally part of the data here was NaN as the original TressFX code wrote number
|
|
// vertices including the follow hair although the shader accounts for that, hence
|
|
// memory was overwriten. In our implementation the memory resides all within
|
|
// a single buffer and this would actively overwrite the rest of the buffer hence
|
|
// destroying the original contexnt.
|
|
currentPos = initialPos;
|
|
g_HairVertexPositions[globalVertexIndex] = initialPos;
|
|
g_HairVertexPositionsPrev[globalVertexIndex] = initialPos;
|
|
g_HairVertexPositionsPrevPrev[globalVertexIndex] = initialPos;
|
|
oldPos = initialPos;
|
|
}
|
|
|
|
// skipping all the physics simulation in between
|
|
if ( IsMovable(currentPos) )
|
|
sharedPos[indexForSharedMem].xyz = Integrate(currentPos.xyz, oldPos.xyz, initialPos.xyz, dampingCoeff);
|
|
else
|
|
sharedPos[indexForSharedMem] = initialPos;
|
|
|
|
// Global Shape Constraints
|
|
float stiffnessForGlobalShapeMatching = GetGlobalStiffness(strandType);
|
|
float globalShapeMatchingEffectiveRange = GetGlobalRange(strandType);
|
|
|
|
if ( stiffnessForGlobalShapeMatching > 0 && globalShapeMatchingEffectiveRange )
|
|
{
|
|
if ( IsMovable(sharedPos[indexForSharedMem]) )
|
|
{
|
|
if ( (float)localVertexIndex < globalShapeMatchingEffectiveRange * (float)numVerticesInTheStrand )
|
|
{
|
|
float factor = stiffnessForGlobalShapeMatching;
|
|
float3 del = factor * (initialPos - sharedPos[indexForSharedMem]).xyz;
|
|
sharedPos[indexForSharedMem].xyz += del;
|
|
}
|
|
}
|
|
}
|
|
|
|
// update global position buffers
|
|
UpdateFinalVertexPositions(currentPos, sharedPos[indexForSharedMem], globalVertexIndex);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
//
|
|
// Calculate Strand Level Data
|
|
//
|
|
// Propagate velocity shock resulted by attached based mesh
|
|
//
|
|
// One thread computes two vertices within a strand.
|
|
//
|
|
//--------------------------------------------------------------------------------------
|
|
[numthreads(THREAD_GROUP_SIZE, 1, 1)]
|
|
void CalculateStrandLevelData(
|
|
uint GIndex : SV_GroupIndex,
|
|
uint3 GId : SV_GroupID,
|
|
uint3 DTid : SV_DispatchThreadID)
|
|
{
|
|
uint local_id, group_id, globalStrandIndex, numVerticesInTheStrand, globalRootVertexIndex, strandType;
|
|
CalcIndicesInStrandLevelMaster(GIndex, GId.x, globalStrandIndex, numVerticesInTheStrand, globalRootVertexIndex, strandType);
|
|
|
|
// Accounting for the right and left side of the strand.
|
|
float4 pos_old_old[2]; // previous previous positions for vertex 0 (root) and vertex 1.
|
|
float4 pos_old[2]; // previous positions for vertex 0 (root) and vertex 1.
|
|
float4 pos_new[2]; // current positions for vertex 0 (root) and vertex 1.
|
|
|
|
pos_old_old[0] = g_HairVertexPositionsPrevPrev[globalRootVertexIndex];
|
|
pos_old_old[1] = g_HairVertexPositionsPrevPrev[globalRootVertexIndex + 1];
|
|
|
|
pos_old[0] = g_HairVertexPositionsPrev[globalRootVertexIndex];
|
|
pos_old[1] = g_HairVertexPositionsPrev[globalRootVertexIndex + 1];
|
|
|
|
pos_new[0] = g_HairVertexPositions[globalRootVertexIndex];
|
|
pos_new[1] = g_HairVertexPositions[globalRootVertexIndex + 1];
|
|
|
|
float3 u = normalize(pos_old[1].xyz - pos_old[0].xyz);
|
|
float3 v = normalize(pos_new[1].xyz - pos_new[0].xyz);
|
|
|
|
// Compute rotation and translation which transform pos_old to pos_new.
|
|
// Since the first two vertices are immovable, we can assume that there is no scaling during tranform.
|
|
float4 rot = QuatFromTwoUnitVectors(u, v);
|
|
float3 trans = pos_new[0].xyz - MultQuaternionAndVector(rot, pos_old[0].xyz);
|
|
|
|
float vspCoeff = GetVelocityShockPropogation();
|
|
float restLength0 = g_HairRestLengthSRV[globalRootVertexIndex];
|
|
float vspAccelThreshold = GetVSPAccelThreshold();
|
|
|
|
// Increase the VSP coefficient by checking pseudo-acceleration to handle over-stretching when the character moves very fast
|
|
float accel = length(pos_new[1] - 2.0 * pos_old[1] + pos_old_old[1]);
|
|
|
|
if (accel > vspAccelThreshold)
|
|
vspCoeff = 1.0f;
|
|
g_StrandLevelData[globalStrandIndex].vspQuat = rot;
|
|
g_StrandLevelData[globalStrandIndex].vspTranslation = float4(trans, vspCoeff);
|
|
|
|
// Skinning
|
|
|
|
// Copy data from init rest data to be used to set updated shared memory
|
|
float4 initialPos = float4(CM_TO_METERS,CM_TO_METERS,CM_TO_METERS,1.0) * g_InitialHairPositions[globalRootVertexIndex]; // rest position
|
|
|
|
// Apply bone skinning to initial position
|
|
BoneSkinningData skinningData = g_BoneSkinningData[globalStrandIndex];
|
|
float4 bone_quat;
|
|
initialPos.xyz = ApplyVertexBoneSkinning(initialPos.xyz, skinningData, bone_quat);
|
|
g_StrandLevelData[globalStrandIndex].skinningQuat = bone_quat;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
//
|
|
// VelocityShockPropagation
|
|
//
|
|
// Propagate velocity shock resulted by attached based mesh
|
|
//
|
|
// One thread computes a vertex in a strand.
|
|
//
|
|
//--------------------------------------------------------------------------------------
|
|
[numthreads(THREAD_GROUP_SIZE, 1, 1)]
|
|
void VelocityShockPropagation(
|
|
uint GIndex : SV_GroupIndex,
|
|
uint3 GId : SV_GroupID,
|
|
uint3 DTid : SV_DispatchThreadID)
|
|
{
|
|
uint globalStrandIndex, localStrandIndex, globalVertexIndex, localVertexIndex, numVerticesInTheStrand, indexForSharedMem, strandType;
|
|
CalcIndicesInVertexLevelMaster(GIndex, GId.x, globalStrandIndex, localStrandIndex, globalVertexIndex, localVertexIndex, numVerticesInTheStrand, indexForSharedMem, strandType);
|
|
|
|
// The first two vertices are the ones attached to the skin
|
|
if (localVertexIndex < 2)
|
|
return;
|
|
|
|
float4 vspQuat = g_StrandLevelData[globalStrandIndex].vspQuat;
|
|
float4 vspTrans = g_StrandLevelData[globalStrandIndex].vspTranslation;
|
|
float vspCoeff = vspTrans.w;
|
|
|
|
float4 pos_new_n = g_HairVertexPositions[globalVertexIndex];
|
|
float4 pos_old_n = g_HairVertexPositionsPrev[globalVertexIndex];
|
|
|
|
pos_new_n.xyz = (1.f - vspCoeff) * pos_new_n.xyz + vspCoeff * (MultQuaternionAndVector(vspQuat, pos_new_n.xyz) + vspTrans.xyz);
|
|
pos_old_n.xyz = (1.f - vspCoeff) * pos_old_n.xyz + vspCoeff * (MultQuaternionAndVector(vspQuat, pos_old_n.xyz) + vspTrans.xyz);
|
|
|
|
g_HairVertexPositions[globalVertexIndex].xyz = pos_new_n.xyz;
|
|
g_HairVertexPositionsPrev[globalVertexIndex].xyz = pos_old_n.xyz;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
//
|
|
// LocalShapeConstraints
|
|
//
|
|
// Compute shader to maintain the local shape constraints.
|
|
//
|
|
// One thread computes one strand.
|
|
//
|
|
//--------------------------------------------------------------------------------------
|
|
[numthreads(THREAD_GROUP_SIZE, 1, 1)]
|
|
void LocalShapeConstraints(
|
|
uint GIndex : SV_GroupIndex,
|
|
uint3 GId : SV_GroupID,
|
|
uint3 DTid : SV_DispatchThreadID)
|
|
{
|
|
uint local_id, group_id, globalStrandIndex, numVerticesInTheStrand, globalRootVertexIndex, strandType;
|
|
CalcIndicesInStrandLevelMaster(GIndex, GId.x, globalStrandIndex, numVerticesInTheStrand, globalRootVertexIndex, strandType);
|
|
|
|
// stiffness for local shape constraints
|
|
float stiffnessForLocalShapeMatching = GetLocalStiffness(strandType);
|
|
|
|
// Going beyond the TH will create less stability in convergence
|
|
const float stabilityTH = 0.95f;
|
|
stiffnessForLocalShapeMatching = 0.5f * min(stiffnessForLocalShapeMatching, stabilityTH);
|
|
|
|
//--------------------------------------------
|
|
// Local shape constraint for bending/twisting
|
|
//--------------------------------------------
|
|
{
|
|
float4 boneQuat = g_StrandLevelData[globalStrandIndex].skinningQuat;
|
|
|
|
// vertex 1 through n-1
|
|
for (uint localVertexIndex = 1; localVertexIndex < numVerticesInTheStrand - 1; localVertexIndex++)
|
|
{
|
|
uint globalVertexIndex = globalRootVertexIndex + localVertexIndex;
|
|
|
|
float4 pos = g_HairVertexPositions[globalVertexIndex];
|
|
float4 pos_plus_one = g_HairVertexPositions[globalVertexIndex + 1];
|
|
float4 pos_minus_one = g_HairVertexPositions[globalVertexIndex - 1];
|
|
|
|
float3 bindPos = MultQuaternionAndVector(boneQuat, g_InitialHairPositions[globalVertexIndex].xyz * CM_TO_METERS);
|
|
float3 bindPos_plus_one = MultQuaternionAndVector(boneQuat, g_InitialHairPositions[globalVertexIndex + 1].xyz * CM_TO_METERS);
|
|
float3 bindPos_minus_one = MultQuaternionAndVector(boneQuat, g_InitialHairPositions[globalVertexIndex - 1].xyz * CM_TO_METERS);
|
|
|
|
float3 lastVec = pos.xyz - pos_minus_one.xyz;
|
|
|
|
float3 vecBindPose = bindPos_plus_one - bindPos;
|
|
float3 lastVecBindPose = bindPos - bindPos_minus_one;
|
|
float4 rotGlobal = QuatFromTwoUnitVectors(normalize(lastVecBindPose), normalize(lastVec));
|
|
|
|
float3 orgPos_i_plus_1_InGlobalFrame = MultQuaternionAndVector(rotGlobal, vecBindPose) + pos.xyz;
|
|
float3 del = stiffnessForLocalShapeMatching * (orgPos_i_plus_1_InGlobalFrame - pos_plus_one.xyz);
|
|
|
|
if (IsMovable(pos))
|
|
pos.xyz -= del.xyz;
|
|
|
|
if (IsMovable(pos_plus_one))
|
|
pos_plus_one.xyz += del.xyz;
|
|
|
|
|
|
g_HairVertexPositions[globalVertexIndex].xyz = pos.xyz;
|
|
g_HairVertexPositions[globalVertexIndex + 1].xyz = pos_plus_one.xyz;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
//
|
|
// LengthConstriantsWindAndCollision
|
|
//
|
|
// Compute shader to move the vertex position based on wind, maintain the lenght constraints
|
|
// and handles collisions.
|
|
//
|
|
// One thread computes one vertex.
|
|
//
|
|
//--------------------------------------------------------------------------------------
|
|
[numthreads(THREAD_GROUP_SIZE, 1, 1)]
|
|
void LengthConstriantsWindAndCollision(uint GIndex : SV_GroupIndex,
|
|
uint3 GId : SV_GroupID,
|
|
uint3 DTid : SV_DispatchThreadID)
|
|
{
|
|
uint globalStrandIndex, localStrandIndex, globalVertexIndex, localVertexIndex, numVerticesInTheStrand, indexForSharedMem, strandType;
|
|
CalcIndicesInVertexLevelMaster(GIndex, GId.x, globalStrandIndex, localStrandIndex, globalVertexIndex, localVertexIndex, numVerticesInTheStrand, indexForSharedMem, strandType);
|
|
|
|
uint numOfStrandsPerThreadGroup = g_NumOfStrandsPerThreadGroup;
|
|
|
|
//------------------------------
|
|
// Copy data into shared memory
|
|
//------------------------------
|
|
sharedPos[indexForSharedMem] = g_HairVertexPositions[globalVertexIndex];
|
|
sharedLength[indexForSharedMem] = g_HairRestLengthSRV[globalVertexIndex] * CM_TO_METERS;
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
/*
|
|
//------------
|
|
// Wind - does not work yet and requires some LTC
|
|
//------------
|
|
if (any(g_Wind.xyz)) // g_Wind.w is the current frame
|
|
{
|
|
float4 force = float4(0, 0, 0, 0);
|
|
|
|
if ( localVertexIndex >= 2 && localVertexIndex < numVerticesInTheStrand-1 )
|
|
{
|
|
// combining four winds.
|
|
float a = ((float)(globalStrandIndex % 20))/20.0f;
|
|
float3 w = a* g_Wind.xyz + (1.0f - a) * g_Wind1.xyz + a * g_Wind2.xyz + (1.0f - a) * g_Wind3.xyz;
|
|
// float3 w = float3(5.2, 0, 0);
|
|
|
|
uint sharedIndex = localVertexIndex * numOfStrandsPerThreadGroup + localStrandIndex;
|
|
|
|
float3 v = sharedPos[sharedIndex].xyz - sharedPos[sharedIndex+numOfStrandsPerThreadGroup].xyz;
|
|
float3 force = -cross(cross(v, w), v);
|
|
sharedPos[sharedIndex].xyz += force*g_TimeStep*g_TimeStep;
|
|
}
|
|
}
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
*/
|
|
//----------------------------
|
|
// Enforce length constraints
|
|
//----------------------------
|
|
uint a = numVerticesInTheStrand/2.0f;
|
|
uint b = (numVerticesInTheStrand-1)/2.0f;
|
|
|
|
int lengthContraintIterations = GetLengthConstraintIterations();
|
|
|
|
for ( int iterationE=0; iterationE < lengthContraintIterations; iterationE++ )
|
|
{
|
|
uint sharedIndex = 2 * localVertexIndex * numOfStrandsPerThreadGroup + localStrandIndex;
|
|
|
|
// Notice that the following is based on the fact that we are dealing here with two vertices
|
|
// one at each side of the central control point and each should extend towards its side only.
|
|
if( localVertexIndex < a )
|
|
ApplyDistanceConstraint(sharedPos[sharedIndex], sharedPos[sharedIndex+numOfStrandsPerThreadGroup], sharedLength[sharedIndex].x);
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
if( localVertexIndex < b )
|
|
ApplyDistanceConstraint(sharedPos[sharedIndex+numOfStrandsPerThreadGroup], sharedPos[sharedIndex+numOfStrandsPerThreadGroup*2], sharedLength[sharedIndex+numOfStrandsPerThreadGroup].x);
|
|
|
|
GroupMemoryBarrierWithGroupSync();
|
|
}
|
|
|
|
//------------------------------------------
|
|
// Collision handling with capsule objects
|
|
//------------------------------------------
|
|
float4 oldPos = g_HairVertexPositionsPrev[globalVertexIndex];
|
|
bool bAnyColDetected = false; // Adi
|
|
// bool bAnyColDetected = ResolveCapsuleCollisions(sharedPos[indexForSharedMem], oldPos);
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
//-------------------
|
|
// Compute tangent
|
|
//-------------------
|
|
// If this is the last vertex in the strand, we can't get tangent from subtracting from the next vertex, need to use last vertex to current
|
|
uint indexForTangent = (localVertexIndex == numVerticesInTheStrand - 1) ? indexForSharedMem - numOfStrandsPerThreadGroup : indexForSharedMem;
|
|
float3 tangent = sharedPos[indexForTangent + numOfStrandsPerThreadGroup].xyz - sharedPos[indexForTangent].xyz;
|
|
g_HairVertexTangents[globalVertexIndex].xyz = normalize(tangent);
|
|
|
|
//---------------------------------------
|
|
// clamp velocities, rewrite history
|
|
//---------------------------------------
|
|
float3 positionDelta = sharedPos[indexForSharedMem].xyz - oldPos;
|
|
float speedSqr = dot(positionDelta, positionDelta);
|
|
if (speedSqr > g_ClampPositionDelta * g_ClampPositionDelta) {
|
|
positionDelta *= g_ClampPositionDelta * g_ClampPositionDelta / speedSqr;
|
|
g_HairVertexPositionsPrev[globalVertexIndex].xyz = sharedPos[indexForSharedMem].xyz - positionDelta;
|
|
}
|
|
|
|
//---------------------------------------
|
|
// update global position buffers
|
|
//---------------------------------------
|
|
g_HairVertexPositions[globalVertexIndex] = sharedPos[indexForSharedMem];
|
|
|
|
if (bAnyColDetected)
|
|
g_HairVertexPositionsPrev[globalVertexIndex] = sharedPos[indexForSharedMem];
|
|
|
|
return;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
//
|
|
// UpdateFollowHairVertices
|
|
//
|
|
// Last stage update of the follow hair to follow their guide hair
|
|
//
|
|
// One thread computes one vertex.
|
|
//
|
|
//--------------------------------------------------------------------------------------
|
|
[numthreads(THREAD_GROUP_SIZE, 1, 1)]
|
|
void UpdateFollowHairVertices(
|
|
uint GIndex : SV_GroupIndex,
|
|
uint3 GId : SV_GroupID,
|
|
uint3 DTid : SV_DispatchThreadID)
|
|
{
|
|
uint globalStrandIndex, localStrandIndex, globalVertexIndex, localVertexIndex, numVerticesInTheStrand, indexForSharedMem, strandType;
|
|
CalcIndicesInVertexLevelMaster(GIndex, GId.x, globalStrandIndex, localStrandIndex, globalVertexIndex, localVertexIndex, numVerticesInTheStrand, indexForSharedMem, strandType);
|
|
|
|
sharedPos[indexForSharedMem] = GetSharedPosition(globalVertexIndex); // g_HairVertexPositions[globalVertexIndex];
|
|
sharedTangent[indexForSharedMem].xyz = GetSharedTangent(globalVertexIndex); // g_HairVertexTangents[globalVertexIndex];
|
|
GroupMemoryBarrierWithGroupSync();
|
|
|
|
for ( uint i = 0; i < g_NumFollowHairsPerGuideHair; i++ )
|
|
{
|
|
int globalFollowVertexIndex = globalVertexIndex + numVerticesInTheStrand * (i + 1);
|
|
int globalFollowStrandIndex = globalStrandIndex + i + 1;
|
|
float factor = g_TipSeparationFactor*((float)localVertexIndex / (float)numVerticesInTheStrand) + 1.0f;
|
|
float3 followPos = sharedPos[indexForSharedMem].xyz + factor * CM_TO_METERS * g_FollowHairRootOffset[globalFollowStrandIndex].xyz;
|
|
|
|
SetSharedPosition3(globalFollowVertexIndex, followPos);
|
|
// g_HairVertexPositions[globalFollowVertexIndex].xyz = followPos;
|
|
//-----------------------
|
|
// SetSharedTangent(globalFollowVertexIndex, sharedTangent[indexForSharedMem]);
|
|
g_HairVertexTangents[globalFollowVertexIndex] = sharedTangent[indexForSharedMem];
|
|
}
|
|
|
|
return;
|
|
}
|