Vertex Colors with non int values not working


#1

Hey guys,

i created a flat shaded terrain with vertex colors, but the problem is, if i assign a color with non int values inside (as example like new Color(0.8f, 0, 0, 1)) the shading stops working. For better understanding what my problem is here two pictures, one with a green value of 1 and once with 0.8f:

The correct one:

The not working one:

Im using Lighting.j3md as the material with UseVertexColor true and Ambient Diffuse and Specular set to gray, white and white.
Hoping for help…


#2

I think we will have to see code. I can assure you that non-int vertex colors absolutely works.

Note: in your second picture it looks like you’ve somehow wiped out the normals. Going to guess that maybe you set your vertex colors to the normal buffer instead of the color buffer?


#3

Thank you for the quick reply, here is the code of the terrain mesh generation, ignore the first constructor, the second one does apply 1 color to the whole terrain (temporarily), i hope you find
what we are searching for:

package terrain;

import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.util.BufferUtils;
import java.awt.Color;

/**
 * Generates a mesh where every triangle got there own
 * corner points without sharing them with other triangles.
 * The result is a flat shaded mesh.
 * 
 * @author Simon Pointner
 */
public class FlatShadedTerrain extends Geometry {
    float[][] heightmap;
    int size;
    private static final int SCALE = 5;

    /*
     * Generates the island shaped flat shaded mesh
     * 
     * @param heightmap the heightmap used for building the mesh
     * @param size      the sidelength the heightmap got
     */
    public FlatShadedTerrain(float[][] heightmap, int size) {
        this.mesh = new Mesh();
        this.heightmap = heightmap;
        this.size = size;
    
        generateMesh();
        this.mesh.updateBound();
    }

    /*
     * Generates the island shaped flat shaded mesh with the given
     * color for all vertices
     * 
     * @param heightmap the heightmap used for building the mesh
     * @param size      the sidelength the heightmap got
     * @param color     the color getting applied
     */
    public FlatShadedTerrain(float[][] heightmap, int size, Color color) {
        this.mesh = new Mesh();
        this.heightmap = heightmap;
        this.size = size;
    
        generateMesh();
        this.mesh.setBuffer(Type.Color, 4, BufferUtils.createFloatBuffer(setVertexColors(color)));
        this.mesh.updateBound();
    }

    /*
     * Generates all needed values for and fits them together
     * into the flat shaded mesh.
     */
    private void generateMesh() {
        /*
         * Calculate the amount of points needed to generate
         * each triangles corners inside the mesh.
         * Each square got 6 points.
         */
        int points = (size - 1) * (size - 1) * 6;
    
        /*
         * Those variables store the vertices, indexes and normals
         * of the triangles describing the mesh.
         */
        Vector3f[] vertices = new Vector3f[points];
        int[] indexes  = new int[points];
        float[] normals = new float[points * 3];
    
        /*
         * All vertices are sorted already so the vertices are in
         * the correct order and dont have to be set in a special order.
         */
        for(int i = 0; i < points; i++) {
            indexes[i] = i;
        }
    
    /*
     * Sets and calculates all vertices and normals of the mesh
     */
    for(int y = 0; y < size - 1; y++) {
        for(int x = 0; x < size - 1; x++) {
            setVertices(x, y, vertices);
            Vector3f[] temp_normals = calculateNormals(x, y, vertices);
            setNormals(x, y, normals, temp_normals);
        }
    }
    
        /*
         * Adds all calculated values to the mesh
         */
        this.mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices));
        this.mesh.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(indexes));
        this.mesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals));
    }

    /*
     * Sets the vertices at position x and y for the mesh
     * 
     * @param x         the x coordinate
     * @param y         the y coordinate
     * @param vertices  the storage for the calculated vertices
     */
    private void setVertices(int x, int y, Vector3f[] vertices) {
        int position = ((y * (size - 1)) + x) * 6;
    
        vertices[position    ] = new Vector3f((x * SCALE)        , heightmap[x][y]        , (y * SCALE)        );
        vertices[position + 1] = new Vector3f((x * SCALE)        , heightmap[x][y + 1]    , (y * SCALE) + SCALE);
        vertices[position + 2] = new Vector3f((x * SCALE) + SCALE, heightmap[x + 1][y]    , (y * SCALE)        );
        vertices[position + 3] = new Vector3f((x * SCALE)        , heightmap[x][y + 1]    , (y * SCALE) + SCALE);
        vertices[position + 4] = new Vector3f((x * SCALE) + SCALE, heightmap[x + 1][y + 1], (y * SCALE) + SCALE);
        vertices[position + 5] = new Vector3f((x * SCALE) + SCALE, heightmap[x + 1][y]    , (y * SCALE)        );
    }

    /*
     * Calculates the 2 normals of a square plate in the mesh
     * 
     * @param x         the x coordinate
     * @param y         the y coordinate
     * @param vertices  the vertices the normals are calculated from
     * 
     * @return normals  the 2 normals in an vector array
     */
    private Vector3f[] calculateNormals(int x, int y, Vector3f[] vertices) {
        Vector3f[] normals = new Vector3f[2];

        Vector3f site_1 = vertices[((y * (size - 1)) + x) * 6].subtract(vertices[((y * (size - 1)) + x) * 6 + 1]);
        Vector3f site_2 = vertices[((y * (size - 1)) + x) * 6].subtract(vertices[((y * (size - 1)) + x) * 6 + 2]);
        Vector3f site_3 = vertices[((y * (size - 1)) + x) * 6 + 3].subtract(vertices[((y * (size - 1)) + x) * 6 + 4]);
        Vector3f site_4 = vertices[((y * (size - 1)) + x) * 6 + 3].subtract(vertices[((y * (size - 1)) + x) * 6 + 5]);

        normals[0] = site_1.cross(site_2);
        normals[1] = site_3.cross(site_4);
    
        return normals;
    }

    /*
     * Sets the calculated normals for the mesh
     * 
     * @param x             the x coordinate
     * @param y             the y coordinate
     * @param normals       the normals storage
     * @param temp_normals  the vector array with the normals inside
     */
    private void setNormals(int x, int y, float[] normals, Vector3f[] temp_normals) {
        int position = ((y * (size - 1)) + x) * 6 * 3;
    
        for(int i = 0; i < 6; i++) {
            if(i < 3) {
                normals[position + (i * 3)    ] = temp_normals[0].getX();
                normals[position + (i * 3) + 1] = temp_normals[0].getY();
                normals[position + (i * 3) + 2] = temp_normals[0].getZ();
            }
            else {
                normals[position + (i * 3)    ] = temp_normals[1].getX();
                normals[position + (i * 3) + 1] = temp_normals[1].getY();
                normals[position + (i * 3) + 2] = temp_normals[1].getZ();
            }            
        }
    }

    /*
     * Applies the given color to all vertices
     * 
     * @param color   the color being applied
     * 
     * @return colors the array with the colors inside
     */
    private float[] setVertexColors(Color color) {
        /*
         * Calculate the amount of points needed to generate
         * each triangles corners inside the mesh.
         * Each square got 6 points.
         */
        int points = (size - 1) * (size - 1) * 6;
    
        float[] colors = new float[points * 4];
    
        for(int y = 0; y < size - 1; y++) {
            for(int x = 0; x < size - 1; x++) {
                int position = ((y * (size - 1)) + x) * 6 * 4;
            
                for(int i = 0; i < 6; i++) {
                    colors[position + (i * 4)    ] = color.getRed();
                    colors[position + (i * 4) + 1] = color.getGreen();
                    colors[position + (i * 4) + 2] = color.getBlue();
                    colors[position + (i * 4) + 3] = color.getAlpha();
                }
            }
        }
    
        return colors;
    }
}

And here is the Code in the main class that generates and adds it to the scene:

@Override
public void simpleInitApp() {
    Material material = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
    material.setBoolean("UseVertexColor", true);
    material.setColor("Ambient", ColorRGBA.Gray);
    material.setColor("Diffuse", ColorRGBA.White);
    material.setColor("Specular", ColorRGBA.White);

    IslandHeightmap island = new IslandHeightmap(500, 0.0001, (int) (Math.random() * 100000));
    heightmap = island.getHeightmap();
    
    FlatShadedTerrain terrain = new FlatShadedTerrain(heightmap, 500, new Color(1, 0, 1, 1));
    terrain.setMaterial(material);
    rootNode.attachChild(terrain);
    
    AmbientLight light = new AmbientLight();
    light.setColor(ColorRGBA.DarkGray);
    rootNode.addLight(light);
    
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection(new Vector3f(-0.2f, -1,-0.5f));
    rootNode.addLight(sun);
    
    flyCam.setMoveSpeed(500);
}

#4

And the only difference between the two images is that in one you call:
this.mesh.setBuffer(Type.Color, 4, BufferUtils.createFloatBuffer(setVertexColors(color)));
…and in the other you didn’t?

Or are there other differences?


#5

no the only difference was that once i call the constructor with the color (0, 1, 0, 1) and once with (0, 0.8f, 0, 1)

but i found the “mistake”, quit funny and i dont know why it matter but jeah, the problem was, i used java.awt.Color and not jMonkeys ColorRGBA, now with that one it is working, also Alpha values with BlendMode.Alpha activated.

But as always, thank you for your help pspeed


#6

For awt.Color passed 4 ints, this makes a color almost completely black and almost completely transparent. It’s the equivalent of calling JME color with:
new ColorRGBA(1/255f, 0, 1/255f, 1/255f)


#7

???

new Color(1, 0, 1, 1)

for me this one makes a purple, non transparent color?


#8

Because those are all int then it should be invoking this constructor:
http://docs.oracle.com/javase/6/docs/api/java/awt/Color.html#Color(int,%20int,%20int,%20int)

You are expecting it to be invoking this one:
http://docs.oracle.com/javase/6/docs/api/java/awt/Color.html#Color(float,%20float,%20float,%20float)

I think your interpretation of the results is incorrect. If you were to print the values of the color itself or otherwise display it directly then I think it would be nearly black. There may be other factors in play making it look purple on your terrain.


#9

nah, whatever :wink: thanks for your help, what do you think about the first results (with a high frequency of perlin):


#10

By the way, this is why. You were interpretting the color values as int, too. That’s why (1, 0, 1, 1) was solid purple. You set the color incorrectly but were using it incorrectly also.

So when you used the float-based constructor the values were going to be huge.

Edit: P.S.: Looks nice.


#11

pspeed i hope youll read this, cause the scene is fine so far, but theres one problem with the alpha rendering of the terrain, look at these two pictures, they are taken in the same scene but with a different angle, it seams like the alpha value isnt working underneath a certain angle, any idea where this comes from? if you need code let me know:

Thanks in advance.


#12

Familiarize yourself with transparency and geometry sorting:


#13

Thank you, helped me!