Creating a Material that takes in the Color of the current vertex

I have created a terrain generator class that creates procedurally creates a terrain and modifies based on parameters including biomes, height,…etc.

Currently, my mesh is all white but I’m trying to color it based on the color I set for each vertex. I have tried a lot of research and all point me to using “Vertex Color” connected to the base color. Unfortunately, this doesn’t work. My mesh is completely white even with tons of testing and debugging.

Worst of all, I know this is possible. I have done this before (I’ll provide screenshots) and lost the material itself because of GitHub :(. The solution for the material shouldn’t be that complicated.

Any help would be greatly appreciated.

PS And yes I applied the material to the mesh LMAO


cs375projectpick

If vertex colors work for your engine version, then the only answer you have is that the object you have applied the material to has no vertex paint to display.

I’d suggest testing a different object that you specifically import with vertex paint for testing purposes.

Assuming that one works fine, then you know that the other mesh is just missing vertex paint data. You can probably re-import it witb that data.

Asauming neither works, first try to vertex paint the mesh in engine - its possible the import is broken and/or the colors never did get imported.
Using the engine itself to debug and paint the color will help.

Asauming not even the engine is able to apply vertex paint and have it color, then you know the engine is broken.
(Thing is, if vertex paint was broken you’d see a billion posts about it in the forum, and I havent seen any).

Thanks for getting back to me!

I don’t necessarily want to “paint” it. At least not manually, I have tested that and I know how that works. I have defined an algorithm in C++ to handle it for me. Essentially in my grid, I have a lot of points for instance [0][0] and I have defined an algorithm that creates these points and based on its “Height” I want to color it a certain color.

I’m not sure if that material node is what I’m looking for.

This is an iteration of the function that assigns the color I’m using for testing. Hopefully, if I didn’t explain it well this is more clear.

void ADiamondSquare::CreateVertices(const TArray<TArray>& NoiseMap)
{

if (ProceduralMesh)
{

    for (int X = 0; X < XSize; ++X)
    {
        for (int Y = 0; Y < YSize; ++Y)
        {
            float Z = NoiseMap[X][Y];
            UE_LOG(LogDiamondSquare, Log, TEXT("Value of Z at [%d][%d]: %f"), X, Y, Z);
            FLinearColor Color;

            if (Z >= 0.9f)
            {
                Color = FLinearColor::Blue;        
            }
            else if (Z >= 0.7f && Z < 0.9f)
            {
                Color = FLinearColor::Blue;
            }
            else if (Z >= 0.5f && Z < 0.7f)
            {
                Color = FLinearColor::Blue;
            }
            else if (Z >= 0.0f && Z < 0.5f)
            {
                Color = FLinearColor::Blue;
            }
            Colors.Add(Color.ToFColor(false));
            UE_LOG(LogTemp, Warning, TEXT("R: %f, G: %f, B: %f, A: %f"), Color.R, Color.G, Color.B, Color.A);

            Vertices.Add(FVector(X * Scale, Y * Scale, Z * ZMultiplier));
            UV0.Add(FVector2D(X * UVScale, Y * UVScale));
        }
    }
    Normals.Init(FVector(0.0f, 0.0f, 1.0f), Vertices.Num());
    Tangents.Init(FProcMeshTangent(1.0f, 0.0f, 0.0f), Vertices.Num());
    ProceduralMesh->CreateMeshSection(0, Vertices, Triangles, Normals, UV0, Colors, Tangents, true);
}

}

Surprised your system doesn’t just crash.
Thats a null reference.
Nothing in code defined what Colors is far as i can read off what you shared.

Also, the array must be the same lenght as the array of verts used to generate the section.

Willing to bet there’s a mismatch somewhere making it error out…

The problem you’re facing seems to stem from the material setup in combination with how you’re assigning vertex colors. Let’s break this down step by step:

  1. Material Setup:
  • Open your material in the Unreal Engine Material Editor.
  • Make sure you have a “Vertex Color” node.
  • Connect the “Vertex Color” node’s output to the “Base Color” input of your material. This will ensure that the mesh takes its colors from the vertex colors you assign in C++.
  1. **Assigning Vertex Colors:**Your code snippet stops before you actually assign the color. Here’s a basic continuation based on the height (Z value):

cppCopy code

if (Z >= 0.9f) { Color = FLinearColor::White; // For example purposes, you can change this. } else if (Z >= 0.8f) { Color = FLinearColor::Gray; } // Add more conditions based on Z values and assign appropriate colors. // Then, assign the color to the vertex.VertexColors.Add(Color);
  1. **Assigning Vertex Colors to Procedural Mesh:**After you have generated all your vertices and their corresponding colors, you’ll want to assign those colors to your procedural mesh. Assuming you have a ProceduralMeshComponent, you’d do something like:

cppCopy code

ProceduralMesh->CreateMeshSection_LinearColor(0, Vertices, Triangles, Normals, UV0, UV1, UV2, VertexColors, Tangents, bCreateCollision);

Make sure the VertexColors array you’re passing in is of the same size as the Vertices array.
4. Debugging:

  • Double-check that the vertex colors are being assigned correctly. Use the UE_LOG to log the colors you’re assigning to make sure they aren’t all white or some unexpected value.
  • Make sure your procedural mesh has the material with the “Vertex Color” node applied to it.
  • In the Editor, you can use the “Mesh Visualization” mode to check vertex colors. Go to Show > Visualize > Vertex Colors to see the vertex colors on your mesh. If they’re not showing correctly here, it means there’s an issue with how they’re being assigned in your code.
  1. Miscellaneous:
  • Be aware that if you have any light sources in the scene that are too bright or if the mesh is receiving too much light, it might appear white even if the vertex colors are correct. Ensure lighting isn’t washing out your colors.
  • Ensure that there are no post-processing effects or other materials affecting the final look of your mesh in the scene.

Hopefully, by following these steps, you’ll identify where the issue lies and get your terrain colored as desired. If you continue to have issues, you might consider breaking down the problem further and testing in isolation to pinpoint the exact source of the problem

He is adding the colors - using

ProceduralMesh->CreateMeshSection

Your way is just more code for the same thing.

Also your code snippets don’t mean jack:
No one can implement them without knowing what vertex is supposed to be.

Particularly without being sure that whatever Vertex is it has access to a definition of VertexColors.

vertex.VertexColors
Will error out if you initialize vertex with anything but the correct type.

You’ll notice there is no definition for vertex in the code above.
Vertices != vertex.

Even if you assume that the function inherits a definition of vertex, the code is still wrong, as he doesn’t assign the proper positions to it.

Message 1:
Colors are part of the class DiamondSquare

TArray Colors; this is the declaration as a private variable. If it wasn’t part of the class I would get a compiler error.

From my understanding, .add works just like .push_back for vectors in C++ so you wouldn’t have to allocate memory. The function does it for you every time you call .add. Essentially increasing the size of the array by 1 each time.

Message 2:
I added a screenshot of the material I’m using in the very first response (which is the same thing as you explained).

For the second step, I’m confused about how your example code is any different than mine. Also If my code is entering the IF statements that has to be the problem. I’m going to try to debug it and see.

these are some debug statements that proves the if statements work
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][137]: 0.020000
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][138]: 0.020000
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][139]: 0.020000
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][140]: 0.233341
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][141]: 0.228607
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][142]: 0.231662
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][143]: 0.240713
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][144]: 0.251554
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][145]: 0.261828
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][146]: 0.267834
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][147]: 0.265759
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][148]: 0.256107
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogDiamondSquare: Value of Z at [149][149]: 0.243826
LogDiamondSquare: Blue
LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000

I think this line of code covers adding it to the mesh component
ProceduralMesh->CreateMeshSection(0, Vertices, Triangles, Normals, UV0, Colors, Tangents, true);

Message 3:
The vertices are getting there locations from a separate function just assume that this is correct. Essentially every vertex holds [ x ] ,[ y ] (if this wasn’t correct the mesh wouldn’t even show up).

Thanks for the responses! I know this question isn’t easy LOL

Yes, if the array is properly initialized as an enumerable item.

But it cannot(rather shouldn’t) just be part of the class in this case, since this particular list you are adding to is supposed to be the same list as the current section of verticis you are focusing on.

It would, if both arrays, vertex and colors, have the same number of total values.

That had everything to do with the example of code above being incorrect and nothing at all with your process.
vertex in lowercase is likely not part of the class, so it needs to be defined.
Aomething like
string vertex = “123”
For instance. And obviously, doing that would create a crash since a string doesnt have a definition for VertexColors as a list you can add to…

We know your process works - or you wouldn’t have a mesh to show us pictures of.

The problem is likely to only be associative - meaning somehow the correct vertex color isnt assigned to the correct vertex.
Or (since its full white in the output) it isn’t assigned at all.

Again, first thing to check is that both sets of arrays, vertex and colors, match.

If you are only adding colors to a master list - that accepts no duplicates - then the arrays won’t match.

Try this, set the inital color as 0,0,0 / full black.
If the code runs, and your mesh in engine is all black, then you know that the code is at least working.
If you get no visual change, then you know your arrays or assignment system is messing up.

1 Like

Ok well, that was a huge problem that WE just solved but unfortunately is still coming up white.

You were completely correct. The arrays weren’t the same size…I added a simple .empty statement for colors and that solved the issue. It constantly was adding to itself when I checked it the size was like 95000 LOL.

These are a few of the debug statements I have now. It seems to be working correctly now but the mesh is still all white. For testing, I have every vertex set to blue btw.

LogTemp: Warning: R: 0.000000, G: 0.000000, B: 1.000000, A: 1.000000
LogTemp: Warning: Size of Colors array: 40000
LogTemp: Warning: Size of Vertices array: 40000
LogTemp: Warning: Vertex [0, 0]: X: 0.000000, Y: 0.000000, Z: 275.000000
LogTemp: Warning: Color [0, 0]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 1]: X: 100.000000, Y: 0.000000, Z: 279.811707
LogTemp: Warning: Color [0, 1]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 2]: X: 200.000000, Y: 0.000000, Z: 25.000004
LogTemp: Warning: Color [0, 2]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 3]: X: 300.000000, Y: 0.000000, Z: 25.000004
LogTemp: Warning: Color [0, 3]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 4]: X: 400.000000, Y: 0.000000, Z: 306.171967
LogTemp: Warning: Color [0, 4]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 5]: X: 500.000000, Y: 0.000000, Z: 320.028107
LogTemp: Warning: Color [0, 5]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 6]: X: 600.000000, Y: 0.000000, Z: 341.889130
LogTemp: Warning: Color [0, 6]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 7]: X: 700.000000, Y: 0.000000, Z: 330.725708
LogTemp: Warning: Color [0, 7]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 8]: X: 800.000000, Y: 0.000000, Z: 308.112091
LogTemp: Warning: Color [0, 8]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 9]: X: 900.000000, Y: 0.000000, Z: 0.000000
LogTemp: Warning: Color [0, 9]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 10]: X: 1000.000000, Y: 0.000000, Z: 0.000000
LogTemp: Warning: Color [0, 10]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 11]: X: 1100.000000, Y: 0.000000, Z: 0.000000
LogTemp: Warning: Color [0, 11]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 12]: X: 1200.000000, Y: 0.000000, Z: 0.000000
LogTemp: Warning: Color [0, 12]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 13]: X: 1300.000000, Y: 0.000000, Z: 0.000000
LogTemp: Warning: Color [0, 13]: R: 0, G: 0, B: 255, A: 255
LogTemp: Warning: Vertex [0, 14]: X: 1400.000000, Y: 0.000000, Z: 0.000000

1 Like

Print out and check if the arrays match.
Assuming they do, then the generated mesh should have the correct vertex colors applied…

If it does not, then something is missing off the procedural component - maybe you need to expressly set the value. This depends on whatever you are using as the inherited class, and what different funcitond it offers off the shelf.

Afaik, the function you are calling works when the arrays match up.

HOLY CRAP I GOT IT!!!
Thanks for all the help!

It was in this stupid section!!!

    ProceduralMesh->CreateMeshSection(0, Vertices, Triangles, Normals, UV0, Colors, Tangents, true);

this line was set to an empty color array so when it reupdated it never even inherited the colo LMAO.

if (recreateMesh) {
    auto NoiseMap = GeneratePerlinNoiseMap();

    Vertices.Reset();
    Triangles.Reset();
    UV0.Reset();

    CreateVertices(NoiseMap);
    CreateTriangles();

    UKismetProceduralMeshLibrary::CalculateTangentsForMesh(Vertices, Triangles, UV0, Normals, Tangents);

    ProceduralMesh->CreateMeshSection(0, Vertices, Triangles, Normals, UV0, Colors, Tangents, true);
    ProceduralMesh->SetMaterial(0, Material);
    recreateMesh = false;
}
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.