0
\$\begingroup\$

I am trying to procedurally generate planets in Unity. The actual planet is already done, but now I am trying to add clouds. For this I created a PBR-Graph shader that draws the clouds based on some noise. It works fine on a quad and even on a plane.

The problem is, that I now created 6 meshes that form a sphere, using a script to go around the planet. I have applied the shaded material to the meshes, but for some reason it behaves, as if the entire mesh would be a single pixel, as in I don't get the white cloud shapes moving around with transparent parts inbetween, like I do on a plane, but instead the entire mesh is transparent or white or gray or partially transparent, like a single pixel from the plane. As the clouds move on the plane, the meshes around the planet flicker and change accordingly.

Could someone help me to figure this out? I don't know why its doing that and its really weird.

Basically the entire mesh acts like a single pixel of the texture, while objects like planes and quads work correctly. I can even get it to work on a sphere, but only on one I created manually by doing new 3D-Object - Sphere and then applying the shaded material to it.

As I want to be able to generate planets with a script, this wouldn't work as it involves me doing something manually...

Here is my code:

This is how the meshes are created:

for (int i = 0; i < 6; i++){

        if(cloudMeshFilters[i] == null){

            GameObject meshObj = new GameObject("cloudMesh");
            meshObj.transform.parent = transform;

            meshObj.AddComponent<MeshRenderer>();
            cloudMeshFilters[i] = meshObj.AddComponent<MeshFilter>();
            cloudMeshFilters[i].sharedMesh = new Mesh();

        }

        cloudMeshFilters[i].GetComponent<MeshRenderer>().sharedMaterial = cloudMaterial;

        cloudMeshes[i] = new CloudMeshes(shapeSettings, cloudHeight, cloudMeshFilters[i].sharedMesh, resolution, directions[i]);

    }

And this is the cloudMeshes class:

public class CloudMeshes
{

int resolution;
Vector3 localUp;
Vector3 axisA;
Vector3 axisB;
Mesh mesh;
ShapeSettings settings;
int height;

public CloudMeshes(ShapeSettings settings, int height, Mesh mesh, int resolution, Vector3 localUp)
{
    this.mesh = mesh;
    this.resolution = resolution;
    this.localUp = localUp;
    this.settings = settings;
    this.height = height;

    axisA = new Vector3(localUp.y, localUp.z, localUp.x);
    axisB = Vector3.Cross(localUp, axisA);
}

public void ConstructMesh(){

    Vector3[] vertices = new Vector3[resolution * resolution];
    int[] triangles = new int[(resolution - 1) * (resolution - 1) * 6];
    int triIndex = 0;

    for(int y = 0; y < resolution; y++){
        for(int x = 0; x < resolution; x++){

            int i = x + y * resolution;
            Vector2 percent = new Vector2(x,y) / (resolution - 1);
            Vector3 pointOnUnitCube = localUp + (percent.x - .5f) * 2 * axisA + (percent.y - .5f) * 2 * axisB;
            Vector3 pointOnUnitSphere = pointOnUnitCube.normalized;
            vertices[i] = pointOnUnitSphere * (settings.planetRadius + height);

            if (x != resolution - 1 && y != resolution - 1)
            {
                triangles[triIndex] = i;
                triangles[triIndex + 1] = i + resolution + 1;
                triangles[triIndex + 2] = i + resolution;

                triangles[triIndex + 3] = i;
                triangles[triIndex + 4] = i + 1;
                triangles[triIndex + 5] = i + resolution + 1;
                triIndex += 6;
            }

        }
    }

    mesh.Clear();
    mesh.vertices = vertices;
    mesh.triangles = triangles;
    mesh.RecalculateNormals();

}

}

The ConstructMesh() method is called after the for loop at the top has run.

Sorry for any incorrect indentations, that happend when pasting the code here...

\$\endgroup\$
2
  • \$\begingroup\$ Can you show us how you created your 6 meshes, and importantly, how you assigned their texture coordinates and normal vectors? Also note that you can always create a "manual" sphere via script with CreatePrimitive(), if the built-in sphere mesh serves your needs. \$\endgroup\$
    – DMGregory
    Aug 14 '20 at 17:44
  • 1
    \$\begingroup\$ Looks like you forgot to generate UV texture coordinates for your meshes, which it looks like your shader tries to use. How do you want the texture space to be unwrapped over these mesh surfaces? \$\endgroup\$
    – DMGregory
    Aug 14 '20 at 18:09
1
\$\begingroup\$

Thanks to @DMGregory! I had indeed forgotten to generate the uvs for the meshes. I fixed that and now the shaded material is displayed correctly!

Here is the new ConstructMesh Method:

    public void ConstructMesh(){

    Vector3[] vertices = new Vector3[resolution * resolution];
    int[] triangles = new int[(resolution - 1) * (resolution - 1) * 6];
    Vector2[] uvs = new Vector2[(resolution * resolution)];
    int triIndex = 0;

    for(int y = 0; y < resolution; y++){
        for(int x = 0; x < resolution; x++){

            int i = x + y * resolution;
            Vector2 percent = new Vector2(x,y) / (resolution - 1);
            Vector3 pointOnUnitCube = localUp + (percent.x - .5f) * 2 * axisA + (percent.y - .5f) * 2 * axisB;
            Vector3 pointOnUnitSphere = pointOnUnitCube.normalized;
            vertices[i] = pointOnUnitSphere * (settings.planetRadius + height);
            uvs[i] = new Vector2((float)x / resolution, (float)y / resolution);

            if (x != resolution - 1 && y != resolution - 1)
            {
                triangles[triIndex] = i;
                triangles[triIndex + 1] = i + resolution + 1;
                triangles[triIndex + 2] = i + resolution;

                triangles[triIndex + 3] = i;
                triangles[triIndex + 4] = i + 1;
                triangles[triIndex + 5] = i + resolution + 1;
                triIndex += 6;
            }

        }
    }

    mesh.Clear();
    mesh.vertices = vertices;
    mesh.triangles = triangles;
    mesh.RecalculateNormals();
    mesh.uv = uvs;

}
\$\endgroup\$
0
0
\$\begingroup\$

I can even get it to work on a sphere, but only on one I created manually by doing new 3D-Object - Sphere and then applying the shaded material to it.

As I want to be able to generate planets with a script, this wouldn't work as it involves me doing something manually...

Have you considered creating a prefab from the "Create" menu sphere and simply changing the material assigned to instances of the prefab? If you need to distort the mesh afterward, you can do this with a shader, or by copying the mesh data into a new mesh and manipulating the vertices of the new mesh.

\$\endgroup\$
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.