Circle opacity is incorrect

Circle is created with an incorrect opacity.
This usually results in much darker than desired opacity.

This is the circle shape creation code.

const geometry = new Shape().absarc(startPoint.x, startPoint.y, startPoint.distanceTo(endPoint), 0, Math.PI * 2, true)
        const plane = new ShapeGeometry(geometry)


        const shapeArr: number[] = []

        geometry.getPoints().forEach(item => {
            shapeArr.push(item.x)
            shapeArr.push(item.y)
            shapeArr.push(0)
        })

        const mesh = drawGeometry(plane, shapeArr, 5, hexToRgba(circleBorder), hexToRgba(circleFill))

This function converts hex to rgb.

export const hexToRgba = (str: string) => {
    let r = 0;
    let g = 0;
    let b = 0;
    let a = 255;
    const result: string = str
        .replace('#', '')
        // Replace short notation with long notation
        .replace(/^([0-F]{6})$/i, (match, hex) => {
            var m = hex.match(/.{1,2}/g);
            r = parseInt(m[0], 16);
            g = parseInt(m[1], 16);
            b = parseInt(m[2], 16);
            a = 255;
            return `${r}${g}${b}`;
        })
        // Convert RRGGBBAA to rgba(r, g, b, a) notation
        .replace(/^([0-F]{8})$/i, (match, hex) => {
            const value = Number.parseInt(hex, 16);

            const bitsPerChannel = 8;
            const bitMask = 0xff;

            a = value & bitMask;
            b = (value >> bitsPerChannel) & bitMask;
            g = (value >> (bitsPerChannel * 2)) & bitMask;
            r = (value >> (bitsPerChannel * 3)) & bitMask;
            return `${r}${g}${b}${a}`;
        });

    return {
        r: r,
        g: g,
        b: b,
        a: a / 255,
        originA: a,
    };
};

export interface RGBA {
    r: number;
    g: number;
    b: number;
    a: number;
    originA: number;
}

This function combines Shape and LineGeometry.

/**
 *
 * @param geometry
 * @param points Line Points
 * @param thickness Line Width
 * @param edge Edge Color
 * @param fill Fill Color
 * @returns
 */
export function drawGeometry(
    plane: BufferGeometry,
    tmpLine: number[],
    thickness: number,
    edge: RGBA,
    fill: RGBA,
) {

    const line = new LineGeometry().setPositions(tmpLine)
    const borderPosition = (line.attributes.position as BufferAttribute).array
    const planePosition = (plane.attributes.position as BufferAttribute).array

    const borderIndex = (line.index as BufferAttribute).array;
    const planeIndex = (plane.index as BufferAttribute).array;


    const edgeMaterial = new LineMaterial({
        color: new Three.Color(`rgb(${edge.r},${edge.g},${edge.b})`).getHex(),
        opacity: edge.a,
        linewidth: thickness,
        transparent: true,
        resolution: new Three.Vector2(window.innerWidth, window.innerHeight),
    });


    const Material = new Three.MeshBasicMaterial({
        color: new Three.Color(`rgb(${fill.r},${fill.g},${fill.b})`).getHex(),
        transparent: true,
        opacity: fill.a,
    });

    const mergeGeometry = new LineGeometry();
    const mergePosition = new Float32Array(
        borderPosition.length + planePosition.length,
    );

    const edgeUv = (line.attributes.uv as InstancedBufferAttribute).array;
    const fillUv = (plane.attributes.uv as InstancedBufferAttribute).array;
    const mergeUv = new Float32Array(edgeUv.length + fillUv.length);

    const mergeIndex = new Uint16Array(borderIndex.length + planeIndex.length);
    mergeGeometry.addGroup(0, borderIndex.length, 0);
    mergeGeometry.addGroup(borderIndex.length, planeIndex.length, 1);


    for (let i = 0; i < borderPosition.length; i += 3) {
        mergePosition[i] = borderPosition[i];
        mergePosition[i + 1] = borderPosition[i + 1];
        mergePosition[i + 2] = borderPosition[i + 2];
    }
    for (let i = borderPosition.length; i < mergePosition.length; i += 3) {
        mergePosition[i] = planePosition[i - borderPosition.length];
        mergePosition[i + 1] = planePosition[i - borderPosition.length + 1];
        mergePosition[i + 2] = planePosition[i - borderPosition.length + 2];
    }
    mergeGeometry.setAttribute(
        'position',
        new Three.BufferAttribute(mergePosition, 3),
    );

    for (let i = 0; i < borderIndex.length; i += 1) {
        mergeIndex[i] = borderIndex[i];
    }
    for (let i = borderIndex.length; i < mergeIndex.length; i += 1) {
        mergeIndex[i] =
            planeIndex[i - borderIndex.length] + borderPosition.length / 3;
    }
    mergeGeometry.setIndex(new Three.BufferAttribute(mergeIndex, 1));


    for (let i = 0; i < edgeUv.length; i += 2) {
        mergeUv[i] = edgeUv[i];
        mergeUv[i + 1] = edgeUv[i + 1];
    }

    for (let i = edgeUv.length; i < mergeUv.length; i += 2) {
        mergeUv[i] = fillUv[i - edgeUv.length];
        mergeUv[i + 1] = fillUv[i - edgeUv.length + 1];
    }
    mergeGeometry.setAttribute('uv', new Three.BufferAttribute(mergeUv, 2));
    //mergeGeometry.setAttribute("instanceDistanceEnd", line.attributes.instanceDistanceEnd as BufferAttribute)
    //mergeGeometry.setAttribute("instanceDistanceStart", line.attributes.instanceDistanceStart as BufferAttribute)
    mergeGeometry.setAttribute("instanceEnd", line.attributes.instanceEnd as BufferAttribute)
    mergeGeometry.setAttribute("instanceStart", line.attributes.instanceStart as BufferAttribute)


    const mesh = new Three.Mesh(mergeGeometry, [edgeMaterial, Material]);
    console.log(mesh)
    return mesh;

}

I don’t know why it appears darker than the opacity I want.
The 151 patch doesn’t seem to be related at all. If you know the reason, please advise. Thank you.

circle Example
circle Example1
circle Example2
circle Example3

The alphas for these two colors are 0x21:
circle Example1circle Example3

When you build your materials, what is the value of fill.a? It should be approximately 0.129.

I tried the colors directly and they look OK. So, I expect that something with the values got mangled. As you are the only one that can debug this, I hope you can find the issue.

My test case:

var object = new THREE.Mesh(
	new THREE.PlaneGeometry( 1, 1 ),
   	new THREE.MeshBasicMaterial( {
			color: 0x89836F,
			transparent: true,
			opacity: 0x21/255 } )
    );	

var object2 = new THREE.Mesh(
	new THREE.PlaneGeometry( 1, 1 ),
   	new THREE.MeshBasicMaterial( {
			color: 0x625295,
			transparent: true,
			opacity: 0x21/255 } )
    );	

And the result with white background is:

image

It looks exactly like these:
circle Example1circle Example3

1 Like

Normal squares and triangles do not have these symptoms. This happens for circles created with shape or circleGeometry.

And a wireframe of this shape is formed.
1

If you change:

const mergeGeometry = new LineGeometry();

to:

const mergeGeometry = new Three.BufferGeometry();

the transparency will be OK, but then the border line will be destroyed … because the geometry is not a LineGeometry any more.

My suggestion is to split the border and the interior into two objects and put them in a group. In this way you will not have to merge different types of geometries. The code will be much shorter and easier to manage.

1 Like

sorry. It needs to be just one object to interact with other features.

Then build the border as a sequence of thin and long rectangles. Their geometry can be merged with the interior geometry.

I solved it. Setting the depthFunc to Three.LessDepth set the correct transparency.

1 Like