wip(standalone): GPU step 6 -- near field on GPU, exact; cold start still slower than CPU
Near LOD0/1 (detail band, relief, root refinement, normals) now evaluated and meshed on the GPU. 960 near tiles: zero sign/topology differences vs CPU and WARP, vertex max 0.0049 mm (was 15 cm); far survival audit passes; CPU hashes 44/44 unchanged. Far connector-panel outlier (64.56 cm, LOD8) remains a tessellation difference, and remote captures show missing far geometry on GPU. Cold readiness (viewer clock, my interleaved runs): CPU 2.51/2.49/2.40 s, GPU 5.05/2.68/2.80 s. The ~2.1 s Present stall hit only the first GPU launch. Viewer spends ~1.13 s before shape compile starts (startup checks), which is the gap between the 0.95 s benchmark and ~2.4 s on-screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
0e730cdbba
commit
b0a14262b1
50 files changed
+1681
-236
No files matched your search
@@ -27,9 +27,32 @@ cbuffer TileConstants : register(b0)
|
||||
float VoxelSizeCm;
|
||||
uint OutputBaseVertex;
|
||||
uint OutputArgsByteOffset;
|
||||
uint ConstantPad;
|
||||
uint NearFieldMode;
|
||||
uint OutputVertexCapacity;
|
||||
uint3 TileConstantsPadding;
|
||||
};
|
||||
|
||||
cbuffer FarFieldConstants : register(b1)
|
||||
{
|
||||
float FarStrataLo;
|
||||
float FarStrataHi;
|
||||
float FarStrataBlend;
|
||||
uint FarSampleCount;
|
||||
uint FarEvalCounterEnabled;
|
||||
uint FarSpatialBinMask;
|
||||
uint FarSpatialBinSize;
|
||||
uint FarSpatialBinCapacity;
|
||||
uint FarNearDetailEnabled;
|
||||
uint FarFieldSeed;
|
||||
uint FarDetailLayerCount;
|
||||
uint FarFieldConstantPad;
|
||||
int4 FarDetailMinVoxel;
|
||||
int4 FarDetailMaxVoxel;
|
||||
};
|
||||
|
||||
float NearFieldDensityAt(float3 position, uint clampNearZero,out uint distanceEvaluations);
|
||||
StructuredBuffer<float> FarNearNormalDensityInput : register(t18);
|
||||
|
||||
cbuffer FarConnectorConstants : register(b2)
|
||||
{
|
||||
uint FarConnectorCount;
|
||||
@@ -73,58 +96,74 @@ float LatticeValue(StructuredBuffer<float> density, int3 p)
|
||||
return density[SampleIndex(p)];
|
||||
}
|
||||
|
||||
float GradientLatticeValue(StructuredBuffer<float> density,int3 p)
|
||||
{
|
||||
const float cached=LatticeValue(density,p);
|
||||
if(NearFieldMode!=0&&StepVoxels==1&&abs(abs(cached)-4.125f)<1.0e-4f)
|
||||
{
|
||||
// The CPU mesher replaces the fixed proxy-band fallback with the
|
||||
// unclamped analytic field when it samples gradients. Match that
|
||||
// normal path while retaining packed lattice values for classification
|
||||
// and edge positions.
|
||||
return FarNearNormalDensityInput[SampleIndex(p)];
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
float3 LatticeGradient(StructuredBuffer<float> density, int3 p)
|
||||
{
|
||||
return float3(
|
||||
LatticeValue(density, p + int3(1,0,0)) - LatticeValue(density, p - int3(1,0,0)),
|
||||
LatticeValue(density, p + int3(0,1,0)) - LatticeValue(density, p - int3(0,1,0)),
|
||||
LatticeValue(density, p + int3(0,0,1)) - LatticeValue(density, p - int3(0,0,1)));
|
||||
GradientLatticeValue(density, p + int3(1,0,0)) - GradientLatticeValue(density, p - int3(1,0,0)),
|
||||
GradientLatticeValue(density, p + int3(0,1,0)) - GradientLatticeValue(density, p - int3(0,1,0)),
|
||||
GradientLatticeValue(density, p + int3(0,0,1)) - GradientLatticeValue(density, p - int3(0,0,1)));
|
||||
}
|
||||
|
||||
float3 EdgePosition(int3 cell, uint edge, StructuredBuffer<float> density,
|
||||
StructuredBuffer<uint> midpointIds,
|
||||
StructuredBuffer<MidpointRecord> midpointRecords)
|
||||
float EdgeRootParameter(int3 cell,uint edge,StructuredBuffer<float> density,
|
||||
StructuredBuffer<uint> midpointIds,StructuredBuffer<MidpointRecord> midpointRecords)
|
||||
{
|
||||
const int2 corners=EdgeCorners[edge];
|
||||
const int3 pa=cell+CornerOffset[corners.x];
|
||||
const int3 pb=cell+CornerOffset[corners.y];
|
||||
const float da=LatticeValue(density,pa),db=LatticeValue(density,pb);
|
||||
const uint midId=midpointIds[CellIndex(uint3(cell))];
|
||||
float mid=0.0f;uint hasMid=0;
|
||||
if(midId!=0xffffffffu)
|
||||
{
|
||||
const MidpointRecord record=midpointRecords[midId];
|
||||
hasMid=(record.metadata.x>>edge)&1;
|
||||
mid=record.values[edge>>2][edge&3];
|
||||
}
|
||||
else if(NearFieldMode!=0&&StepVoxels==1)
|
||||
{
|
||||
const float linearT=VF_FieldRefineRoot(da,db,0.0f,0);
|
||||
if(linearT>=0.375f&&linearT<=0.625f)
|
||||
{
|
||||
const float3 midpoint=(float3(OriginX,OriginY,OriginZ)
|
||||
+float3((pa+pb)*StepVoxels)*0.5f);
|
||||
uint ignoredEvaluations=0;
|
||||
mid=NearFieldDensityAt(midpoint,0,ignoredEvaluations);
|
||||
hasMid=1;
|
||||
}
|
||||
}
|
||||
return VF_FieldRefineRoot(da,db,mid,hasMid);
|
||||
}
|
||||
|
||||
float3 EdgePosition(int3 cell,uint edge,float t)
|
||||
{
|
||||
const int2 corners = EdgeCorners[edge];
|
||||
const int3 pa = cell + CornerOffset[corners.x];
|
||||
const int3 pb = cell + CornerOffset[corners.y];
|
||||
const float da = LatticeValue(density, pa);
|
||||
const float db = LatticeValue(density, pb);
|
||||
const uint midId = midpointIds[CellIndex(uint3(cell))];
|
||||
float mid = 0.0f;
|
||||
uint hasMid = 0;
|
||||
if (midId != 0xffffffffu)
|
||||
{
|
||||
const MidpointRecord record = midpointRecords[midId];
|
||||
hasMid = (record.metadata.x >> edge) & 1;
|
||||
mid = record.values[edge >> 2][edge & 3];
|
||||
}
|
||||
const float t = VF_FieldRefineRoot(da, db, mid, hasMid);
|
||||
const float3 originCm = float3(OriginX, OriginY, OriginZ) * VoxelSizeCm;
|
||||
const float3 a = originCm + float3(pa * StepVoxels) * VoxelSizeCm;
|
||||
const float3 b = originCm + float3(pb * StepVoxels) * VoxelSizeCm;
|
||||
return lerp(a, b, t);
|
||||
}
|
||||
|
||||
float3 EdgeNormal(int3 cell, uint edge, StructuredBuffer<float> density,
|
||||
StructuredBuffer<uint> midpointIds,
|
||||
StructuredBuffer<MidpointRecord> midpointRecords)
|
||||
float3 EdgeNormal(int3 cell,uint edge,float t,StructuredBuffer<float> density)
|
||||
{
|
||||
const int2 corners = EdgeCorners[edge];
|
||||
const int3 pa = cell + CornerOffset[corners.x];
|
||||
const int3 pb = cell + CornerOffset[corners.y];
|
||||
const float da = LatticeValue(density, pa);
|
||||
const float db = LatticeValue(density, pb);
|
||||
const uint midId = midpointIds[CellIndex(uint3(cell))];
|
||||
float mid = 0.0f;
|
||||
uint hasMid = 0;
|
||||
if (midId != 0xffffffffu)
|
||||
{
|
||||
const MidpointRecord record = midpointRecords[midId];
|
||||
hasMid = (record.metadata.x >> edge) & 1;
|
||||
mid = record.values[edge >> 2][edge & 3];
|
||||
}
|
||||
const float t = VF_FieldRefineRoot(da, db, mid, hasMid);
|
||||
const float3 ga = LatticeGradient(density, pa);
|
||||
const float3 gb = LatticeGradient(density, pb);
|
||||
const float3 g = lerp(ga, gb, t);
|
||||
@@ -145,6 +184,17 @@ struct FarFieldSpatialBin
|
||||
uint count;
|
||||
uint padding;
|
||||
};
|
||||
struct FarFieldDetailLayer
|
||||
{
|
||||
int lo;
|
||||
int hi;
|
||||
float surfaceRoughness;
|
||||
float roughnessFrequency;
|
||||
float verticalScale;
|
||||
float domainWarpStrength;
|
||||
float domainWarpFrequency;
|
||||
uint roughnessNoiseType;
|
||||
};
|
||||
struct FarFieldConnectorPanel
|
||||
{
|
||||
float3 start; float radius;
|
||||
@@ -171,6 +221,9 @@ StructuredBuffer<uint> FarProtectedAir : register(t13);
|
||||
StructuredBuffer<uint> FarProtectedSolid : register(t14);
|
||||
RWStructuredBuffer<float> FarFieldDensity : register(u7);
|
||||
StructuredBuffer<float> GpuSignDensity : register(t15);
|
||||
StructuredBuffer<FarFieldDetailLayer> FarDetailLayers : register(t16);
|
||||
StructuredBuffer<uint> FarNearPackedProxy : register(t17);
|
||||
RWStructuredBuffer<float> FarNearNormalDensityOutput : register(u5);
|
||||
StructuredBuffer<FarFieldConnectorPanel> FarConnectorPanels : register(t8);
|
||||
StructuredBuffer<FarConnectorRing> FarConnectorRingInput : register(t9);
|
||||
RWStructuredBuffer<FarConnectorRing> FarConnectorRingOutput : register(u7);
|
||||
@@ -178,18 +231,6 @@ RWByteAddressBuffer FarConnectorDrawArgs : register(u4);
|
||||
RWByteAddressBuffer FarConnectorVertexBytes : register(u5);
|
||||
RWByteAddressBuffer FarConnectorIndexBytes : register(u6);
|
||||
|
||||
cbuffer FarFieldConstants : register(b1)
|
||||
{
|
||||
float FarStrataLo;
|
||||
float FarStrataHi;
|
||||
float FarStrataBlend;
|
||||
uint FarSampleCount;
|
||||
uint FarEvalCounterEnabled;
|
||||
uint FarSpatialBinMask;
|
||||
uint FarSpatialBinSize;
|
||||
uint FarSpatialBinCapacity;
|
||||
};
|
||||
|
||||
RWStructuredBuffer<uint> FarFieldEvalCounters : register(u6);
|
||||
|
||||
uint FarFieldBinHash(int3 coord)
|
||||
@@ -223,6 +264,169 @@ FarFieldSpatialBin FarFieldFindBin(float3 position)
|
||||
return emptyBin;
|
||||
}
|
||||
|
||||
float NearOwnerRoughnessAtStep(float3 position,float surfaceSdf,float caveInfluence,
|
||||
FarFieldDetailLayer layer,uint anchorStep)
|
||||
{
|
||||
const float depth=layer.surfaceRoughness*2.0f;
|
||||
if(!(depth>0.0f)||abs(surfaceSdf)>=depth||!(layer.roughnessFrequency>0.0f))return 0.0f;
|
||||
const int step=max(1,(int)anchorStep);
|
||||
const int gridZ=(int)floor(position.z/(float)step)*step;
|
||||
const float sampleZ0=max((float)gridZ,(float)layer.lo);
|
||||
const float sampleZ1=min((float)(gridZ+step),(float)layer.hi);
|
||||
float total;
|
||||
if(sampleZ1>sampleZ0)
|
||||
{
|
||||
const float lower=VF_FieldOwnerRoughnessCore(position.x,position.y,sampleZ0,
|
||||
FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale,
|
||||
layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType);
|
||||
const float upper=VF_FieldOwnerRoughnessCore(position.x,position.y,sampleZ1,
|
||||
FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale,
|
||||
layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType);
|
||||
const float alpha=(position.z-sampleZ0)/(sampleZ1-sampleZ0);
|
||||
total=lower+(upper-lower)*alpha;
|
||||
}
|
||||
else
|
||||
total=VF_FieldOwnerRoughnessCore(position.x,position.y,position.z,
|
||||
FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale,
|
||||
layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType);
|
||||
if(surfaceSdf<0.0f)
|
||||
total=lerp(total,min(total,0.0f),clamp(caveInfluence,0.0f,1.0f));
|
||||
const float fade=1.0f-abs(surfaceSdf)/depth;
|
||||
return total*fade*fade;
|
||||
}
|
||||
|
||||
float NearOwnerRoughness(float3 position,float surfaceSdf,float caveInfluence,
|
||||
float passageInfluence,FarFieldDetailLayer layer)
|
||||
{
|
||||
const float passage=saturate(passageInfluence);
|
||||
const float anchored=NearOwnerRoughnessAtStep(position,surfaceSdf,caveInfluence,layer,8);
|
||||
if(passage<=0.40f)return anchored;
|
||||
const float direct=NearOwnerRoughnessAtStep(position,surfaceSdf,caveInfluence,layer,2);
|
||||
if(passage>=0.60f)return direct;
|
||||
const float directWeight=VF_FieldSmoothUnit((passage-0.40f)/0.20f);
|
||||
const float anchoredWeight=1.0f-directWeight;
|
||||
const float normalization=1.0f/sqrt(anchoredWeight*anchoredWeight+directWeight*directWeight);
|
||||
return (anchoredWeight*anchored+directWeight*direct)*normalization;
|
||||
}
|
||||
|
||||
float NearFieldDensityAt(float3 position,uint clampNearZero,out uint distanceEvaluations)
|
||||
{
|
||||
const FarFieldSpatialBin range=FarFieldFindBin(position);
|
||||
float cave=100000.0f,overlay=-100000.0f;
|
||||
float caveFloorInfluence=0.0f,cavePassageInfluence=0.0f;
|
||||
distanceEvaluations=0;
|
||||
[loop]
|
||||
for(uint item=0;item<range.count;++item)
|
||||
{
|
||||
const VF_FieldPrimitive shape=FarShapes[FarSpatialBinShapeIds[range.offset+item]];
|
||||
if((shape.flags&VF_FIELD_PRIMITIVE_CARVE)!=0)
|
||||
{
|
||||
if(position.x<shape.minVoxel[0]-6||position.x>shape.maxVoxel[0]+6
|
||||
||position.y<shape.minVoxel[1]-6||position.y>shape.maxVoxel[1]+6
|
||||
||position.z<shape.minVoxel[2]-6||position.z>shape.maxVoxel[2]+6)continue;
|
||||
float distance=VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z);
|
||||
++distanceEvaluations;
|
||||
float primitiveFloorInfluence=0.0f;
|
||||
if((shape.flags&VF_FIELD_PRIMITIVE_HAS_FLOOR)!=0)
|
||||
{
|
||||
const float floor=VF_FieldPrimitiveFloorAt(shape,position.x,position.y);
|
||||
const float floorT=(abs(position.z-floor)-shape.floorBlendRadius)/12.0f;
|
||||
primitiveFloorInfluence=1.0f-VF_FieldSmoothUnit(floorT);
|
||||
distance=VF_FieldSmoothMax(distance,floor-position.z,shape.floorBlendRadius);
|
||||
}
|
||||
const float metadataWidth=max(shape.blendRadius,6.0f);
|
||||
const float primitiveWeight=VF_FieldSmoothUnit(0.5f+(cave-distance)/(2.0f*metadataWidth));
|
||||
caveFloorInfluence=lerp(caveFloorInfluence,primitiveFloorInfluence,primitiveWeight);
|
||||
if(cavePassageInfluence>0.0f||(shape.flags&VF_FIELD_PRIMITIVE_PASSAGE)!=0)
|
||||
cavePassageInfluence=lerp(cavePassageInfluence,
|
||||
(shape.flags&VF_FIELD_PRIMITIVE_PASSAGE)!=0?1.0f:0.0f,primitiveWeight);
|
||||
cave=VF_FieldSmoothMin(cave,distance,shape.blendRadius);
|
||||
}
|
||||
else if(position.x>=shape.minVoxel[0]&&position.x<=shape.maxVoxel[0]
|
||||
&&position.y>=shape.minVoxel[1]&&position.y<=shape.maxVoxel[1]
|
||||
&&position.z>=shape.minVoxel[2]&&position.z<=shape.maxVoxel[2])
|
||||
{
|
||||
overlay=max(overlay,-VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z));
|
||||
++distanceEvaluations;
|
||||
}
|
||||
}
|
||||
const float base=min(position.z-FarStrataLo,FarStrataHi-position.z);
|
||||
const float carveOrOverlay=max(cave,overlay);
|
||||
const float caveVsOverlay=VF_FieldSmoothUnit(0.5f+(cave-overlay)/12.0f);
|
||||
const float carveInfluence=VF_FieldSmoothUnit(0.5f+(base-carveOrOverlay)/12.0f);
|
||||
const float caveInfluence=carveInfluence*caveVsOverlay;
|
||||
const float floorInfluence=caveInfluence*caveFloorInfluence;
|
||||
const float passageInfluence=caveInfluence*cavePassageInfluence;
|
||||
const float surfaceSdf=base<=carveOrOverlay?base:(cave>=overlay?cave:overlay);
|
||||
float rockDistance=VF_FieldSmoothMin(base,carveOrOverlay,FarStrataBlend);
|
||||
if(NearFieldMode!=0
|
||||
&&all(position>=float3(FarDetailMinVoxel.xyz))
|
||||
&&all(position<=float3(FarDetailMaxVoxel.xyz))
|
||||
&&abs(rockDistance)<=4.125f)
|
||||
{
|
||||
const float macro=VF_FieldValueNoise(position.x,position.y,position.z,32,
|
||||
FarFieldSeed^0xB5297A4Du);
|
||||
const float mid=VF_FieldValueNoise(position.x,position.y,position.z,16,
|
||||
FarFieldSeed^0x63D83595u);
|
||||
const float fine=VF_FieldValueNoise(position.x,position.y,position.z,4,
|
||||
FarFieldSeed^0xA511E9B3u);
|
||||
const float relief=1.20f*macro+(0.80f+0.25f*macro)*(0.40f*mid+0.60f*fine);
|
||||
const float passageBandRelief=1.20f*macro+(0.80f+0.25f*macro)*(0.40f*mid);
|
||||
float ownerRelief=0.0f;
|
||||
[loop]
|
||||
for(uint layerIndex=0;layerIndex<FarDetailLayerCount;++layerIndex)
|
||||
{
|
||||
const FarFieldDetailLayer layer=FarDetailLayers[layerIndex];
|
||||
if(position.z>=layer.lo&&position.z<=layer.hi)
|
||||
{ ownerRelief=NearOwnerRoughness(position,surfaceSdf,caveInfluence,
|
||||
passageInfluence,layer);break; }
|
||||
}
|
||||
const float caveFactor=saturate(caveInfluence);
|
||||
const float floorFactor=min(caveFactor,saturate(floorInfluence));
|
||||
const float passageFactor=saturate(passageInfluence);
|
||||
const float nonPassage=1.0f-passageFactor;
|
||||
const float strataWeight=nonPassage*(1.0f-caveFactor);
|
||||
const float caveWeight=nonPassage*(caveFactor-floorFactor);
|
||||
const float floorWeight=nonPassage*floorFactor;
|
||||
const float detail=strataWeight*(1.35f*(relief+0.5f*ownerRelief))
|
||||
+caveWeight*relief+floorWeight*(0.55f*relief)
|
||||
+passageFactor*(0.75f*passageBandRelief);
|
||||
|
||||
float ownerBound=0.0f;
|
||||
[loop]
|
||||
for(uint layerIndex=0;layerIndex<FarDetailLayerCount;++layerIndex)
|
||||
{
|
||||
const FarFieldDetailLayer layer=FarDetailLayers[layerIndex];
|
||||
if(position.z>=layer.lo&&position.z<=layer.hi)
|
||||
{
|
||||
const float depth=layer.surfaceRoughness*2.0f;
|
||||
if(depth>0.0f&&abs(surfaceSdf)<depth&&layer.roughnessFrequency>0.0f)
|
||||
{
|
||||
const float fade=1.0f-abs(surfaceSdf)/depth;
|
||||
ownerBound=3.15f*1.25f*layer.surfaceRoughness*fade*fade;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
const float reliefBound=1.20f*abs(macro)+(0.80f+0.25f*macro);
|
||||
const float contrast=1.35f*(1.0f-caveFactor)+1.00f*(caveFactor-floorFactor)+0.55f*floorFactor;
|
||||
const float localSupremum=min(3.9f,(reliefBound+0.5f*ownerBound)*contrast);
|
||||
const float edge=clamp(localSupremum+0.125f,1.5f,4.125f);
|
||||
const float distance=abs(rockDistance);
|
||||
float fade=1.0f;
|
||||
if(distance>1.5f)
|
||||
{
|
||||
const float width=edge-1.5f;
|
||||
fade=width>0.0f?VF_FieldSmoothUnit((edge-distance)/width):0.0f;
|
||||
}
|
||||
rockDistance+=clamp(detail,-3.9f,3.9f)*fade;
|
||||
}
|
||||
float density=-rockDistance;
|
||||
if(clampNearZero!=0&&abs(density)<0.05f)
|
||||
density=rockDistance>=0.0f?-0.05f:0.05f;
|
||||
return density;
|
||||
}
|
||||
|
||||
[numthreads(64,1,1)]
|
||||
void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
{
|
||||
@@ -233,50 +437,72 @@ void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
const uint z = index / (SampleDim * SampleDim);
|
||||
const float3 position = float3(OriginX, OriginY, OriginZ)
|
||||
+ float3(int(x) - 1, int(y) - 1, int(z) - 1) * StepVoxels;
|
||||
const FarFieldSpatialBin range = FarFieldFindBin(position);
|
||||
float cave = 100000.0f;
|
||||
float overlay = -100000.0f;
|
||||
uint distanceEvaluations = 0;
|
||||
[loop]
|
||||
for (uint item = 0; item < range.count; ++item)
|
||||
float density;
|
||||
uint distanceEvaluations=0;
|
||||
float nearNormalDensity=0.0f;
|
||||
if(NearFieldMode!=0)
|
||||
{
|
||||
const VF_FieldPrimitive shape = FarShapes[FarSpatialBinShapeIds[range.offset + item]];
|
||||
if ((shape.flags & VF_FIELD_PRIMITIVE_CARVE) != 0)
|
||||
nearNormalDensity=NearFieldDensityAt(position,0,distanceEvaluations);
|
||||
density=nearNormalDensity;
|
||||
if(abs(density)<0.05f)
|
||||
density=density<0.0f?-0.05f:(density>0.0f?0.05f:-0.05f);
|
||||
const uint packedProxy=FarNearPackedProxy[index];
|
||||
if(packedProxy!=0xffffffffu)
|
||||
{
|
||||
if (position.x < shape.minVoxel[0] - 6 || position.x > shape.maxVoxel[0] + 6
|
||||
|| position.y < shape.minVoxel[1] - 6 || position.y > shape.maxVoxel[1] + 6
|
||||
|| position.z < shape.minVoxel[2] - 6 || position.z > shape.maxVoxel[2] + 6)
|
||||
continue;
|
||||
float distance = VF_FieldPrimitiveDistance(shape,
|
||||
position.x, position.y, position.z);
|
||||
++distanceEvaluations;
|
||||
if ((shape.flags & VF_FIELD_PRIMITIVE_HAS_FLOOR) != 0)
|
||||
{
|
||||
const float floorZ = VF_FieldPrimitiveFloorAt(shape, position.x, position.y);
|
||||
distance = VF_FieldSmoothMax(distance, floorZ - position.z,
|
||||
shape.floorBlendRadius);
|
||||
}
|
||||
cave = VF_FieldSmoothMin(cave, distance, shape.blendRadius);
|
||||
}
|
||||
else if (position.x >= shape.minVoxel[0] && position.x <= shape.maxVoxel[0]
|
||||
&& position.y >= shape.minVoxel[1] && position.y <= shape.maxVoxel[1]
|
||||
&& position.z >= shape.minVoxel[2] && position.z <= shape.maxVoxel[2])
|
||||
{
|
||||
overlay = max(overlay, -VF_FieldPrimitiveDistance(shape,
|
||||
position.x, position.y, position.z));
|
||||
++distanceEvaluations;
|
||||
// The CPU uploads sparse interpolation magnitudes. The GPU decides
|
||||
// ordinary signs, except for the one-quantum band where float sign
|
||||
// noise can flip an entire cell against the packed CPU cache.
|
||||
const bool packedSignOverride=(packedProxy&0x40000000u)!=0u;
|
||||
const bool packedRockSign=(packedProxy&0x80000000u)!=0u;
|
||||
const uint magnitudeQ=packedProxy&0x3fffffffu;
|
||||
const float magnitude=magnitudeQ==0u?4.125f:(float)magnitudeQ*(1.0f/256.0f);
|
||||
const bool negative=packedSignOverride?packedRockSign:(density<=0.0f);
|
||||
density=negative?-magnitude:magnitude;
|
||||
}
|
||||
}
|
||||
const float base = min(position.z - FarStrataLo, FarStrataHi - position.z);
|
||||
float rockDistance = VF_FieldSmoothMin(base, max(cave, overlay), FarStrataBlend);
|
||||
else
|
||||
{
|
||||
const FarFieldSpatialBin range=FarFieldFindBin(position);
|
||||
float cave=100000.0f,overlay=-100000.0f;
|
||||
[loop]
|
||||
for(uint item=0;item<range.count;++item)
|
||||
{
|
||||
const VF_FieldPrimitive shape=FarShapes[FarSpatialBinShapeIds[range.offset+item]];
|
||||
if((shape.flags&VF_FIELD_PRIMITIVE_CARVE)!=0)
|
||||
{
|
||||
if(position.x<shape.minVoxel[0]-6||position.x>shape.maxVoxel[0]+6
|
||||
||position.y<shape.minVoxel[1]-6||position.y>shape.maxVoxel[1]+6
|
||||
||position.z<shape.minVoxel[2]-6||position.z>shape.maxVoxel[2]+6)continue;
|
||||
float distance=VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z);
|
||||
++distanceEvaluations;
|
||||
if((shape.flags&VF_FIELD_PRIMITIVE_HAS_FLOOR)!=0)
|
||||
{
|
||||
const float floorZ=VF_FieldPrimitiveFloorAt(shape,position.x,position.y);
|
||||
distance=VF_FieldSmoothMax(distance,floorZ-position.z,shape.floorBlendRadius);
|
||||
}
|
||||
cave=VF_FieldSmoothMin(cave,distance,shape.blendRadius);
|
||||
}
|
||||
else if(position.x>=shape.minVoxel[0]&&position.x<=shape.maxVoxel[0]
|
||||
&&position.y>=shape.minVoxel[1]&&position.y<=shape.maxVoxel[1]
|
||||
&&position.z>=shape.minVoxel[2]&&position.z<=shape.maxVoxel[2])
|
||||
{
|
||||
overlay=max(overlay,-VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z));
|
||||
++distanceEvaluations;
|
||||
}
|
||||
}
|
||||
const float base=min(position.z-FarStrataLo,FarStrataHi-position.z);
|
||||
const float rockDistance=VF_FieldSmoothMin(base,max(cave,overlay),FarStrataBlend);
|
||||
const float rawDensity=-rockDistance;
|
||||
const float magnitude=max(abs(rawDensity),0.05f);
|
||||
density=rockDistance>=0.0f?-magnitude:magnitude;
|
||||
}
|
||||
const uint protectedAir = (FarProtectedAir[index >> 5] >> (index & 31)) & 1;
|
||||
const uint protectedSolid = (FarProtectedSolid[index >> 5] >> (index & 31)) & 1;
|
||||
const float rawDensity = -rockDistance;
|
||||
const float magnitude = max(abs(rawDensity), 0.05f);
|
||||
float density = rockDistance >= 0.0f ? -magnitude : magnitude;
|
||||
if (protectedAir != 0) density = magnitude;
|
||||
else if (protectedSolid != 0) density = -magnitude;
|
||||
const float protectedMinimum=NearFieldMode!=0?1.0f/256.0f:0.05f;
|
||||
if(protectedAir!=0)density=max(abs(density),protectedMinimum);
|
||||
else if(protectedSolid!=0)density=-max(abs(density),protectedMinimum);
|
||||
FarFieldDensity[index] = density;
|
||||
FarNearNormalDensityOutput[index]=nearNormalDensity;
|
||||
if (FarEvalCounterEnabled != 0) FarFieldEvalCounters[index] = distanceEvaluations;
|
||||
}
|
||||
|
||||
@@ -320,12 +546,12 @@ float FarRockDistanceAt(float3 position,int3 cachedBinCoord,FarFieldSpatialBin c
|
||||
|
||||
float FarConnectorRadius(float3 center, float3 direction, float nominalRadius)
|
||||
{
|
||||
// Keep the bounded signed-distance refinement, but stop once the residual
|
||||
// reaches the same acceptance bound used by the original fallback. A
|
||||
// spatial-bin cache avoids re-hashing nearby probes.
|
||||
const float3 nominalPosition=center+direction*nominalRadius;
|
||||
const int3 cachedBinCoord=int3(floor(nominalPosition/max(1.0f,(float)FarSpatialBinSize)));
|
||||
const FarFieldSpatialBin cachedRange=FarFieldFindBin(nominalPosition);
|
||||
// Keep the bounded signed-distance refinement, but stop once the residual
|
||||
// reaches the same acceptance bound used by the original fallback. A
|
||||
// spatial-bin cache avoids re-hashing nearby probes.
|
||||
const float maxOffset=max(16.0f,nominalRadius*0.75f);
|
||||
const float minRadius=max(0.0f,nominalRadius-maxOffset);
|
||||
const float maxRadius=nominalRadius+maxOffset;
|
||||
@@ -470,8 +696,8 @@ void BuildFarConnectorRingsCS(uint3 groupId : SV_GroupID,
|
||||
}
|
||||
if (ringId == 0)
|
||||
{
|
||||
const uint maxVerticesPerTile = CellCount * 15;
|
||||
const uint slot = maxVerticesPerTile > 0 ? OutputBaseVertex / maxVerticesPerTile : 0;
|
||||
const uint slot = OutputVertexCapacity > 0
|
||||
? OutputBaseVertex / OutputVertexCapacity : 0;
|
||||
const uint panelBaseVertex = slot * FarConnectorPanelVertexCapacity;
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 20, FarConnectorVertexCount);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 24, FarConnectorVertexCount > 0 ? 1 : 0);
|
||||
@@ -547,8 +773,8 @@ void EmitFarConnectorPanelsCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
vertex.normal = ring.normals[cornerIndex].xyz;
|
||||
const float connectorLength = length(connector.end - connector.start);
|
||||
vertex.uv = float2(ring.t * connectorLength / 256.0f, (float)side / 4.0f);
|
||||
const uint maxVerticesPerTile = CellCount * 15;
|
||||
const uint slot = maxVerticesPerTile > 0 ? OutputBaseVertex / maxVerticesPerTile : 0;
|
||||
const uint slot = OutputVertexCapacity > 0
|
||||
? OutputBaseVertex / OutputVertexCapacity : 0;
|
||||
const uint base = slot * FarConnectorPanelVertexCapacity;
|
||||
const uint byteAddress = (base + vertexId) * 32;
|
||||
FarConnectorVertexBytes.Store3(byteAddress, asuint(vertex.position));
|
||||
@@ -644,7 +870,7 @@ void ScanGroupsCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
GroupOffsets[i] = offset;
|
||||
offset += InputGroupVertexCounts[i];
|
||||
}
|
||||
DrawArgs.Store(OutputArgsByteOffset + 0, offset);
|
||||
DrawArgs.Store(OutputArgsByteOffset + 0, min(offset, OutputVertexCapacity));
|
||||
DrawArgs.Store(OutputArgsByteOffset + 4, 1);
|
||||
DrawArgs.Store(OutputArgsByteOffset + 8, OutputBaseVertex);
|
||||
DrawArgs.Store(OutputArgsByteOffset + 12, 0);
|
||||
@@ -672,11 +898,13 @@ void EmitCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
const uint index = dispatchId.x;
|
||||
if (index >= CellCount) return;
|
||||
const uint caseIndex = Cases[index];
|
||||
const uint base = OutputBaseVertex + LocalOffsets[index] + PrefixGroups[index / 256];
|
||||
const uint localBase = LocalOffsets[index] + PrefixGroups[index / 256];
|
||||
if(localBase>=OutputVertexCapacity)return;
|
||||
const uint base = OutputBaseVertex + localBase;
|
||||
const uint3 cell = uint3(index % CellsPerAxis,
|
||||
(index / CellsPerAxis) % CellsPerAxis,
|
||||
index / (CellsPerAxis * CellsPerAxis));
|
||||
[unroll]
|
||||
[loop]
|
||||
for (uint slot = 0; slot < 15; ++slot)
|
||||
{
|
||||
uint tableSlot = slot;
|
||||
@@ -684,10 +912,12 @@ void EmitCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
else if (slot % 3 == 2) tableSlot = slot - 1;
|
||||
const int edgeValue = Triangles[caseIndex * 16 + tableSlot];
|
||||
if (edgeValue < 0) break;
|
||||
if(localBase+slot>=OutputVertexCapacity)break;
|
||||
const uint edge = uint(edgeValue);
|
||||
const float rootT=EdgeRootParameter(int3(cell),edge,Density,MidpointCellIds,MidpointData);
|
||||
GpuVertex v;
|
||||
v.position = EdgePosition(int3(cell), edge, Density, MidpointCellIds, MidpointData);
|
||||
v.normal = EdgeNormal(int3(cell), edge, Density, MidpointCellIds, MidpointData);
|
||||
v.position = EdgePosition(int3(cell),edge,rootT);
|
||||
v.normal = EdgeNormal(int3(cell),edge,rootT,Density);
|
||||
v.uv = v.position.xy / VoxelSizeCm;
|
||||
const uint byteAddress = (base + slot) * 32;
|
||||
VertexBytes.Store3(byteAddress, asuint(v.position));
|
||||
|
||||
Reference in new issue
Block a user