c# - How to modify a 3D mesh to follow terrain while preserving thickness? - Stack Overflow

For a Unity project, I need to create a script that modifies the mesh of a 3D object during runtime so

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?

  1. The initial technique involves casting raycasts toward the ground to determine its height and adjusting the Y position of the vertices accordingly.
  2. 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

  1. 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?

  1. The initial technique involves casting raycasts toward the ground to determine its height and adjusting the Y position of the vertices accordingly.
  2. 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

  1. 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
Add a comment  | 

1 Answer 1

Reset to default 1

It 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条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信