# Circle opacity is incorrect

**URL:** <https://discourse.threejs.org/t/circle-opacity-is-incorrect/54496>\
**Category:** Questions\
**Tags:** line2, materials, geometry\
**Created:** [August 1, 2023, 10:36am UTC](https://discourse.threejs.org/t/circle-opacity-is-incorrect/54496 "2023-08-01T10:36:27Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![1\_1](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/1_1/32/34094_2.png) [@1\_1](https://discourse.threejs.org/u/1_1)\
**Post date:** [August 1, 2023, 10:36am UTC](https://discourse.threejs.org/t/circle-opacity-is-incorrect/54496/1 "2023-08-01T10:36:27Z")

</div>

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

This is the circle shape creation code.

```javascript
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.

```javascript
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.

```javascript
/**
 *
 * @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](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/a/f/af4539ed9b227c1dd767d7f36225703acfe20905.png)  
 ![circle Example1](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/6/c/6c1fcc7afff3954690386d61d55cc92d4eb3cee4.png)  
 ![circle Example2](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/d/e/de39b914a98198060e5a8d5af62e2bd9d42be1ec.png)  
 ![circle Example3](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/c/a/ca0a33c391ce353e00ac429719881a364129b889.png)

---

<div class="post-metadata">

**Author:** ![PavelBoytchev](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/pavelboytchev/32/38639_2.png) [@PavelBoytchev](https://discourse.threejs.org/u/PavelBoytchev)\
**Post date:** [August 1, 2023, 6:50pm UTC](https://discourse.threejs.org/t/circle-opacity-is-incorrect/54496/2 "2023-08-01T18:50:52Z")

</div>

The alphas for these two colors are 0x21:  
 ![circle Example1](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/6/c/6c1fcc7afff3954690386d61d55cc92d4eb3cee4.png) ![circle Example3](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/c/a/ca0a33c391ce353e00ac429719881a364129b889.png)

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:

```javascript
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](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/8/2/822d054dd4c2d54b4a2e83dfdcd0725e73b2952f.png)

It looks exactly like these:  
 ![circle Example1](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/6/c/6c1fcc7afff3954690386d61d55cc92d4eb3cee4.png) ![circle Example3](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/c/a/ca0a33c391ce353e00ac429719881a364129b889.png)

---

<div class="post-metadata">

**Author:** ![1\_1](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/1_1/32/34094_2.png) [@1\_1](https://discourse.threejs.org/u/1_1)\
**Post date:** [August 2, 2023, 2:00am UTC](https://discourse.threejs.org/t/circle-opacity-is-incorrect/54496/3 "2023-08-02T02:00:20Z")

</div>

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](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/3/5/3527026dfa5fbffb5b030de6a8e53e938469648e.jpeg)

---

<div class="post-metadata">

**Author:** ![PavelBoytchev](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/pavelboytchev/32/38639_2.png) [@PavelBoytchev](https://discourse.threejs.org/u/PavelBoytchev)\
**Post date:** [August 2, 2023, 6:47am UTC](https://discourse.threejs.org/t/circle-opacity-is-incorrect/54496/4 "2023-08-02T06:47:54Z")

</div>

If you change:

```javascript
const mergeGeometry = new LineGeometry();

```

to:

```javascript
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.

---

<div class="post-metadata">

**Author:** ![1\_1](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/1_1/32/34094_2.png) [@1\_1](https://discourse.threejs.org/u/1_1)\
**Post date:** [August 2, 2023, 8:01am UTC](https://discourse.threejs.org/t/circle-opacity-is-incorrect/54496/5 "2023-08-02T08:01:09Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![PavelBoytchev](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/pavelboytchev/32/38639_2.png) [@PavelBoytchev](https://discourse.threejs.org/u/PavelBoytchev)\
**Post date:** [August 2, 2023, 8:05am UTC](https://discourse.threejs.org/t/circle-opacity-is-incorrect/54496/6 "2023-08-02T08:05:01Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![1\_1](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/1_1/32/34094_2.png) [@1\_1](https://discourse.threejs.org/u/1_1)\
**Post date:** [August 2, 2023, 9:07am UTC](https://discourse.threejs.org/t/circle-opacity-is-incorrect/54496/7 "2023-08-02T09:07:52Z")

</div>

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