For a Unity project, I need to create a script that modifies the mesh of a 3D object during runtime so that it follows the shape of the terrain underneath it.
Currently, I’ve achieved this result (before/after):
Before After
The model correctly follows the curve of the ground, but the thickness is not preserved at all. The model becomes very thin, and all details are lost.
before after
I’ve created a diagram showing what’s happening vs. what should happen:
graph
Here’s the current code:
public LayerMask groundLayer;
public float maxRaycastDistance = 100f;
public float deformationStrength = 1.0f;
private MeshFilter meshFilter;
private Mesh originalMeshe;
private Mesh workingMeshe;
private Vector3[] originalVertice;
private Vector3[] deformedVertice;
public float height = 3f;
private void InitializeMeshe()
{
meshFilter = GetComponent<MeshFilter>();
var meshRenderers = GetComponent<MeshRenderer>();
height = meshRenderers.bounds.size.y;
originalMeshe = meshFilter.sharedMesh;
workingMeshe = Instantiate(originalMeshe);
originalVertice = workingMeshe.vertices;
deformedVertice = new Vector3[originalVertice.Length];
meshFilter.mesh = workingMeshe;
}
public void DeformMeshe()
{
Vector3[] deformedVerts = new Vector3[originalVertice.Length];
for (int j = 0; j < originalVertice.Length; j++)
{
Vector3 worldPos = meshFilter.transform.TransformPoint(originalVertice[j]);
Vector3 closestPoint = worldPos;
Vector3? groundHeight = GetGroundHeight(worldPos);
if (groundHeight == null)
{
deformedVerts[j] = originalVertice[j];
continue;
}
float closestDistance = Vector3.Distance(worldPos, groundHeight.Value);
if (closestDistance < maxRaycastDistance) closestPoint = groundHeight.Value;
Vector3 targetPosition = Vector3.Lerp(worldPos, closestPoint, deformationStrength);
targetPosition.y += height;
deformedVerts[j] = meshFilter.transform.InverseTransformPoint(targetPosition);
}
workingMeshe.vertices = deformedVerts;
workingMeshe.RecalculateNormals();
workingMeshe.RecalculateBounds();
}
public Vector3? GetGroundHeight(Vector3 worldPos)
{
if (Physics.Raycast(worldPos, Vector3.down, out RaycastHit hitDown, maxRaycastDistance, groundLayer))
return hitDown.point;
if (Physics.Raycast(worldPos, Vector3.up, out RaycastHit hitUp, maxRaycastDistance, groundLayer))
return hitUp.point;
return null;
}
How can I fix my code to achieve my goal?
- The initial technique involves casting raycasts toward the ground to determine its height and adjusting the Y position of the vertices accordingly.
- I tried using the Normals to maintain a consistent distance between the points.
Vector3 normalWorld = meshFilter.transform.TransformDirection(vertexNormals[j]);
float originalDistance = Vector3.Dot(originalVertice[j], vertexNormals[j]);
Vector3 adjustedPosition = closestPoint + normalWorld * originalDistance;
Vector3 targetPosition = Vector3.Lerp(worldPos, adjustedPosition, deformationStrength);
Result with Normals
- Still using the Normals, I attempted to move the points along a plane. The result is identical to attempt 2.
Vector3 displacement = closestPoint - worldPos;
Vector3 displacementOnPlane = displacement - Vector3.Dot(displacement, worldNormal) * worldNormal;
Vector3 targetPosition = worldPos + displacementOnPlane * deformationStrength;
targetPosition += worldNormal * height;
For a Unity project, I need to create a script that modifies the mesh of a 3D object during runtime so that it follows the shape of the terrain underneath it.
Currently, I’ve achieved this result (before/after):
Before After
The model correctly follows the curve of the ground, but the thickness is not preserved at all. The model becomes very thin, and all details are lost.
before after
I’ve created a diagram showing what’s happening vs. what should happen:
graph
Here’s the current code:
public LayerMask groundLayer;
public float maxRaycastDistance = 100f;
public float deformationStrength = 1.0f;
private MeshFilter meshFilter;
private Mesh originalMeshe;
private Mesh workingMeshe;
private Vector3[] originalVertice;
private Vector3[] deformedVertice;
public float height = 3f;
private void InitializeMeshe()
{
meshFilter = GetComponent<MeshFilter>();
var meshRenderers = GetComponent<MeshRenderer>();
height = meshRenderers.bounds.size.y;
originalMeshe = meshFilter.sharedMesh;
workingMeshe = Instantiate(originalMeshe);
originalVertice = workingMeshe.vertices;
deformedVertice = new Vector3[originalVertice.Length];
meshFilter.mesh = workingMeshe;
}
public void DeformMeshe()
{
Vector3[] deformedVerts = new Vector3[originalVertice.Length];
for (int j = 0; j < originalVertice.Length; j++)
{
Vector3 worldPos = meshFilter.transform.TransformPoint(originalVertice[j]);
Vector3 closestPoint = worldPos;
Vector3? groundHeight = GetGroundHeight(worldPos);
if (groundHeight == null)
{
deformedVerts[j] = originalVertice[j];
continue;
}
float closestDistance = Vector3.Distance(worldPos, groundHeight.Value);
if (closestDistance < maxRaycastDistance) closestPoint = groundHeight.Value;
Vector3 targetPosition = Vector3.Lerp(worldPos, closestPoint, deformationStrength);
targetPosition.y += height;
deformedVerts[j] = meshFilter.transform.InverseTransformPoint(targetPosition);
}
workingMeshe.vertices = deformedVerts;
workingMeshe.RecalculateNormals();
workingMeshe.RecalculateBounds();
}
public Vector3? GetGroundHeight(Vector3 worldPos)
{
if (Physics.Raycast(worldPos, Vector3.down, out RaycastHit hitDown, maxRaycastDistance, groundLayer))
return hitDown.point;
if (Physics.Raycast(worldPos, Vector3.up, out RaycastHit hitUp, maxRaycastDistance, groundLayer))
return hitUp.point;
return null;
}
How can I fix my code to achieve my goal?
- The initial technique involves casting raycasts toward the ground to determine its height and adjusting the Y position of the vertices accordingly.
- I tried using the Normals to maintain a consistent distance between the points.
Vector3 normalWorld = meshFilter.transform.TransformDirection(vertexNormals[j]);
float originalDistance = Vector3.Dot(originalVertice[j], vertexNormals[j]);
Vector3 adjustedPosition = closestPoint + normalWorld * originalDistance;
Vector3 targetPosition = Vector3.Lerp(worldPos, adjustedPosition, deformationStrength);
Result with Normals
- Still using the Normals, I attempted to move the points along a plane. The result is identical to attempt 2.
Vector3 displacement = closestPoint - worldPos;
Vector3 displacementOnPlane = displacement - Vector3.Dot(displacement, worldNormal) * worldNormal;
Vector3 targetPosition = worldPos + displacementOnPlane * deformationStrength;
targetPosition += worldNormal * height;
Share
Improve this question
edited Nov 20, 2024 at 11:31
DarkBee
15.6k8 gold badges72 silver badges117 bronze badges
asked Nov 20, 2024 at 11:29
TakaseTakase
31 silver badge3 bronze badges
1 Answer
Reset to default 1It seems that you only need to move the vertices vertically. You need a horizontal reference plane, and the distance that each vertex on the mesh needs to move is based on the vertical distance from its corresponding position on the reference plane to the terrain mesh.
- red: reference plane
- blue: terrain
- black: mesh
- gray: deformed mesh
var p = worldPos;
p.y = REF_Y; // The position of the reference plane
float closestDistance = Vector3.Distance(p, groundHeight.Value);
var targetPosition = worldPos;
targetPosition.y -= closestDistance * deformationStrength;
targetPosition.y += height;
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742361296a4429444.html
评论列表(0条)