You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

264 lines
12 KiB
Plaintext

3 years ago
#pragma kernel UpdateFurMeshKernel
#pragma kernel UpdateClumpsPositionKernel
#pragma multi_compile __ HAS_COLLIDERS
#pragma multi_compile __ VERLET_ENABLED
#pragma multi_compile __ INIT_VERLET
#pragma multi_compile __ HAS_CLUMPS
#pragma multi_compile __ IS_CHILD_CLUMP
#include <HLSLSupport.cginc>
#include "FurMotionInclude.cginc"
#include "Thread.hlsl"
#include "../NormalHelperInclude.cginc"
uniform StructuredBuffer<MeshProperties> sourceMesh;
uniform StructuredBuffer<MeshFullProperties> cardMesh;
int cardMeshVerticesCount;
uint layerVertexStartIndex;
float extraScale = 1;
//START Clumps
uniform StructuredBuffer<HairStrandStruct> clumpsBuffer;
uniform StructuredBuffer<float> clumpAttractionCurve;
uniform RWStructuredBuffer<float4> clumpPointsPosition;
uniform RWStructuredBuffer<float4> parentClumpPointsPosition;
int clumpYCoordinates;
// END Clumps
static float CARD_MESH_HEIGHT = 0.12;
static float MAX_TWIST = 1900;
float3 skinnedMeshScale;
float3 applyClumping(HairStrandStruct hairStrand, float4 colors, float3 currentPosition, float clumpOffset)
{
float3 positionWithClumping = currentPosition;
if (hairStrand.clumpIndices >= 0 && hairStrand.clumpMask > 0)
{
float myScaleY = length(float3(hairStrand.scaleMatrix[0][1], hairStrand.scaleMatrix[1][1], hairStrand.scaleMatrix[2][1]));
int clumpRootIndex = hairStrand.clumpIndices * clumpYCoordinates;
int strandMeshYIndex = colors.y;
float clumpScaleY = clumpPointsPosition[clumpRootIndex + strandMeshYIndex].w;
float scalePercent = myScaleY / clumpScaleY;
float newIndex = scalePercent * (float)strandMeshYIndex;
float newIndexLower = min(floor(newIndex), clumpYCoordinates - 1);
float newIndexUpper = min(newIndexLower + 1, clumpYCoordinates - 1);
float lerpAmount = newIndex - newIndexLower;
float3 clumpPositionLow = clumpPointsPosition[clumpRootIndex + newIndexLower].xyz;
float3 clumpPositionHigh = clumpPointsPosition[clumpRootIndex + newIndexUpper].xyz;
float3 clumpPosition = lerp(clumpPositionLow, clumpPositionHigh, lerpAmount);
clumpPosition.z += clumpOffset; //Not the best solution but ok for now. Prevents the strands from disappearing.
clumpPosition.x += clumpOffset;
float attractionAmountLow = clumpAttractionCurve[(int)newIndexLower];
float attractionAmountHigh = clumpAttractionCurve[(int)newIndexUpper];
float attraction = 1.0 - lerp(attractionAmountLow, attractionAmountHigh, lerpAmount);
positionWithClumping = lerp(currentPosition, clumpPosition, clamp(0, 1, attraction) * hairStrand.clumpMask);
// normal = normalize(currentPosition - clumpPosition);
}
return positionWithClumping;
}
uniform int layerVerticesCount;
MeshProperties getBarycentricMeshData(float3 indices, float3 barycentricCoord)
{
MeshProperties triangle1 = sourceMesh[(int)indices.x];
MeshProperties triangle2 = sourceMesh[(int)indices.y];
MeshProperties triangle3 = sourceMesh[(int)indices.z];
MeshProperties interpolated;
interpolated.sourceVertex = Interpolate3(triangle1.sourceVertex, triangle2.sourceVertex, triangle3.sourceVertex, barycentricCoord);
interpolated.sourceNormal = Interpolate3(triangle1.sourceNormal, triangle2.sourceNormal, triangle3.sourceNormal, barycentricCoord);
interpolated.sourceTangent = Interpolate4(triangle1.sourceTangent, triangle2.sourceTangent, triangle3.sourceTangent, barycentricCoord);
return interpolated;
}
#if defined(VERLET_ENABLED)
RWStructuredBuffer<VerletNode> verletNodes;
RWStructuredBuffer<VerletRestNode> _RestPositions;
void setupVerlet(uint proceduralMeshIndex, float3 currentPosition, float vertexMovability, float windContribution, float3 worldSourceMeshNormal)
{
if(proceduralMeshIndex % 2 == 0)
{
int verletIndex = proceduralMeshIndex / 2;
VerletRestNode restNode = _RestPositions[verletIndex];
restNode.position = currentPosition;
restNode.worldSourceMeshNormal = worldSourceMeshNormal;
#if defined(INIT_VERLET)
VerletNode node;
node.position = currentPosition;
node.prevPosition = currentPosition;
restNode.vertMoveAbility = vertexMovability;
restNode.pinnedAmount = 1 - windContribution;
node.isFixed = restNode.vertMoveAbility == 0 || restNode.pinnedAmount == 1;//This means we can't paint wind during play mode.
node.tangent = 0;
node.rotationDiffFromPrevNode = 0;
node.rotationDiffFromPrevNode2 = 0;
verletNodes[verletIndex] = node;
#endif
_RestPositions[verletIndex] = restNode;
if(vertexMovability == 0)
{
VerletNode fixedNode = verletNodes[verletIndex];
fixedNode.position = currentPosition;
fixedNode.prevPosition = currentPosition;
verletNodes[verletIndex] = fixedNode;
}
}
}
#endif
void setupNormal(const uint proceduralMeshIndex, const float3 worldSourceMeshNormal, float3 currentPosition, uint furMeshIndex)
{
if (proceduralMeshIndex % 2 == 0)
{
uint nextStrandPosIndex = ((uint)proceduralMeshIndex + 2) * furMeshBufferStride;
uint nextMeshIndex = ((uint)proceduralMeshIndex + 1) * furMeshBufferStride;
float3 tangent = normalize(loadSourceVertex(nextStrandPosIndex) - currentPosition);
writeNormal(furMeshIndex, nextMeshIndex, worldSourceMeshNormal, tangent);
}
}
THREAD_SIZE
void UpdateFurMeshKernel(uint3 id : SV_DispatchThreadID)
{
const int i = id.x;
if (i >= layerVerticesCount) return;
const int hairStrandIndex = floor(id.x / cardMeshVerticesCount);
const int cardMeshIndex = i - hairStrandIndex * cardMeshVerticesCount;
const uint proceduralMeshIndex = i + layerVertexStartIndex;
const MeshFullProperties cardMeshData = cardMesh[cardMeshIndex];
float3 cardStaticPosition = cardMeshData.sourceVertex;
float3 vertexBasePosition = cardStaticPosition;
const HairStrandStruct hairStrand = hairStrandsBuffer[hairStrandIndex];
const float3 triangles = hairStrand.triangles;
const float3 barycentricCoord = hairStrand.barycentricCoordinates;
MeshProperties meshProperties = getBarycentricMeshData(triangles, barycentricCoord);
float3 position = meshProperties.sourceVertex;
float3 normal = meshProperties.sourceNormal;
const float4 tangent = meshProperties.sourceTangent;
const float4x4 rotationFromNormalMatrix = quaternionToMatrix(QuaternionLookRotation(tangent, normal));
const float3 worldSourceMeshNormal = mul(objectRotationMatrix, fixed4(normal, 1.0)).xyz;
vertexBasePosition.z *= extraScale;
//Scale
vertexBasePosition = mul(float4(vertexBasePosition, 1), hairStrand.scaleMatrix).xyz;
#if defined(HAS_CLUMPS)
float clumpOffset = vertexBasePosition.z * objectGlobalScale;
#endif
float4 colors = cardMeshData.sourceColor;
const float vertexMoveAbility = colors.r;
//Twist
vertexBasePosition = Unity_RotateAboutAxis_Degrees(vertexBasePosition, float3(0.25 * hairStrand.twist.z, 1, 0),
MAX_TWIST * vertexMoveAbility * hairStrand.twist.x);
//Bend
const float bendAmount = hairStrand.bend.w * 90.0 * vertexMoveAbility;
const float3 bendXZ = float3(hairStrand.bend.x, 0, hairStrand.bend.z);
vertexBasePosition = Unity_RotateAboutAxis_Degrees(vertexBasePosition, bendXZ, bendAmount);
//Root rotation Y and Orientation
vertexBasePosition = mul(hairStrand.rootAndOrientationMatrix, float4(vertexBasePosition, 1)).xyz;
vertexBasePosition = mul(rotationFromNormalMatrix, float4(vertexBasePosition, 1)).xyz;
//Source mesh position
vertexBasePosition += position;
//Apply the localToWorldMatrix
float3 currentPosition = mul(localToWorldMatrix, float4(vertexBasePosition, 1.0)).xyz;
uint furMeshIndex = (uint)proceduralMeshIndex * furMeshBufferStride;
//Add clumping
#if defined(HAS_CLUMPS)
currentPosition = applyClumping(hairStrand, colors, currentPosition, clumpOffset);
#else
#endif
const float3 oldPosition = loadSourceVertex(furMeshIndex);
//Our strand vertex is setup, now let's append the same shenanigans as for the regular move kernels
#if defined(VERLET_ENABLED)
setupVerlet(proceduralMeshIndex, currentPosition, vertexMoveAbility, hairStrand.windContribution, worldSourceMeshNormal);
#else
//NORMAL
setupNormal(proceduralMeshIndex, worldSourceMeshNormal, currentPosition, furMeshIndex);
#endif
//VERTEX
writeVector3(furMeshIndex, 0, currentPosition);
//TANGENT
writeVector4(furMeshIndex, SIZEOF_FLOAT3 * 2, tangent); //Should this use the same logic as the normals?
//OVERRIDE COLOR
writeVector4(furMeshIndex, SIZEOF_FLOAT3 * 2 + SIZEOF_FLOAT4, hairStrand.overrideColor);
//SOURCE MESH UV
writeVector2(furMeshIndex, SIZEOF_FLOAT3 * 2 + SIZEOF_FLOAT4 * 2, hairStrand.uv);
//CARD UV
writeVector2(furMeshIndex, SIZEOF_FLOAT3 * 2 + SIZEOF_FLOAT4 * 2 + SIZEOF_FLOAT2, cardMeshData.sourceUV + hairStrand.uvOffset);
//We store the previous position in uv3-4 for motion vectors
//OLD POS XY Can we use a separate buffer here instead? Maybe use drawPocedural?
writeVector2(furMeshIndex, SIZEOF_FLOAT3 * 2 + SIZEOF_FLOAT4 * 2 + SIZEOF_FLOAT2 * 2, float2(oldPosition.x, oldPosition.y));
//OLD POS Z and HAIR_STRAND_INDEX
writeVector2(furMeshIndex, SIZEOF_FLOAT3 * 2 + SIZEOF_FLOAT4 * 2 + SIZEOF_FLOAT2 * 3, float2(oldPosition.z, (float)hairStrandIndex));
}
uniform float clumpPointsCount;
THREAD_SIZE
void UpdateClumpsPositionKernel(uint3 id : SV_DispatchThreadID)
{
int i = id.x;
if (i >= clumpPointsCount)return;
const int clumpIndex = floor((float)i / (float)clumpYCoordinates);
const int clumpPointIndex = i - clumpIndex * clumpYCoordinates;
const float yPercent = (float)(clumpPointIndex + 1) / (float)clumpYCoordinates;
const HairStrandStruct clump = clumpsBuffer[clumpIndex];
MeshProperties meshProperties = getBarycentricMeshData(clump.triangles, clump.barycentricCoordinates);
float3 clumpPos = meshProperties.sourceVertex;
float3 normal = meshProperties.sourceNormal;
const float4 tangent = meshProperties.sourceTangent;
float3 vertexBasePosition = float3(0, yPercent * CARD_MESH_HEIGHT, 0);
const float4x4 rotationFromNormalMatrix = quaternionToMatrix(QuaternionLookRotation(tangent, normal));
//Scale
vertexBasePosition = mul(float4(vertexBasePosition, 1), clump.scaleMatrix).xyz;
//Bend
#ifndef IS_CHILD_CLUMP
vertexBasePosition = Unity_RotateAboutAxis_Degrees(
vertexBasePosition,
float3(0.25 * clump.twist.w, 1, 0),
MAX_TWIST * (1 - yPercent) * clump.twist.y);
#endif
const float bendAmount = clump.bend.w * 90.0 * yPercent;
const float3 bendXZ = float3(clump.bend.x, 0, clump.bend.z);
vertexBasePosition = Unity_RotateAboutAxis_Degrees(vertexBasePosition, bendXZ, bendAmount);
//Root rotation Y and Orientation TODO: Should look at cam?
vertexBasePosition = mul(clump.rootAndOrientationMatrix, float4(vertexBasePosition, 1)).xyz;
vertexBasePosition = mul(rotationFromNormalMatrix, float4(vertexBasePosition, 1)).xyz;
vertexBasePosition += clumpPos;
float clumpScale = length(float3(clump.scaleMatrix[0][1], clump.scaleMatrix[1][1], clump.scaleMatrix[2][1]));
vertexBasePosition.xyz = mul(localToWorldMatrix, float4(vertexBasePosition, 1.0)).xyz;
#if defined(IS_CHILD_CLUMP)
int clumpRootIndex = clump.clumpIndices * clumpYCoordinates;
float3 parentClumpPointPosition = parentClumpPointsPosition[clumpRootIndex + clumpPointIndex];
float attraction = clumpAttractionCurve[clumpPointIndex];
vertexBasePosition.xyz = lerp(parentClumpPointPosition, vertexBasePosition.xyz, attraction);
#endif
clumpPointsPosition[i] = float4(vertexBasePosition.xyz, clumpScale);
}