javascript - Three.js smooth particles opacity cut on depthTest - Stack Overflow

I have a particle system in three.js 0.173.0 with soft particle edges, and when z soring occurs the opa

I have a particle system in three.js 0.173.0 with soft particle edges, and when z soring occurs the opaque pixels overwrite the scene rather than additionally drawn on the top, when the particle's order is higher than its original place in the draw order. I've tried a couple of things, double pass, different material settings... but the problem seemed to persist. I've made a codepen demonstrating the issue. Please check it out and leave a comment if you have any ideas how to resolve it.

Codepen example

import * as THREE from ";;

let camera, scene, renderer, material, points;

const vertexShader = `
attribute float u_id;
varying vec2 vUv;
varying vec3 vColor;

void main() {
    vec3 newPosition = position;

    //
    // THE CIRCLES HAVE TO BE POSITIONED IN THE SHADER
    //
    
    if (u_id == 0.0) {
        newPosition = vec3(-0.6, 0.0, 1.2);
        vColor = vec3(1.0, 0.0, 0.0);
    }
    if (u_id == 1.0) {
        newPosition = vec3(0.0, 0.0, 1.0);
        vColor = vec3(0.0, 1.0, 0.0);
    }
    if (u_id == 2.0) {
        newPosition = vec3(0.6, 0.0, 1.1);
        vColor = vec3(0.0, 0.0, 1.0);
    }
    vUv = newPosition.xy;
    gl_PointSize = 200.0; // Control the circle size
    gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
`;

const fragmentShader = `
precision mediump float;
varying vec2 vUv;
varying vec3 vColor;

void main() {
    vec2 uv = gl_PointCoord - vec2(0.5); // Center the UVs in the circle
    float dist = length(uv);
    float alpha = smoothstep(0.5, 0.1, dist); // Smooth fade at edges

    vec3 color = vColor; //vec3(1.0, 0.0, 0.0); // Red color

    if (dist > 0.5) discard; // Make the circle smooth
    gl_FragColor = vec4(color, alpha);
}
`;

const VERTEX_COUNT = 3;

// Initial vertex positions are [0, 0, 0]
const getInitialVertices = () => {
    const vertices = new Float32Array(VERTEX_COUNT * 3);
    return vertices;
};

// Generate vertex IDs
const getVertexIds = () => {
    const ids = new Float32Array(VERTEX_COUNT);
    for (let i = 0; i < VERTEX_COUNT; i++) {
        ids[i] = i;
    }
    return ids;
};

function init() {
    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera(
        50,
        window.innerWidth / window.innerHeight,
        1,
        10000
    );
    camera.position.z = 4;
    scene.add(camera);

    const uniforms = {};

    const geometry = new THREE.BufferGeometry();
    geometry.setAttribute("position", new THREE.BufferAttribute(getInitialVertices(), 3));
    geometry.setAttribute("u_id", new THREE.BufferAttribute(getVertexIds(), 1));

    material = new THREE.ShaderMaterial({
        uniforms: uniforms,
        vertexShader: vertexShader,
        fragmentShader: fragmentShader,
        transparent: true,
        //
        // THE OPACITY CUT OCCURES WHEN DEPTH TEST IS ON
        //
        depthTest: true,
    });

    points = new THREE.Points(geometry, material);
    scene.add(points);

    renderer = new THREE.WebGLRenderer({ alpha: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
}

function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}

window.addEventListener("load", () => {
    init();
    animate();
});

I have a particle system in three.js 0.173.0 with soft particle edges, and when z soring occurs the opaque pixels overwrite the scene rather than additionally drawn on the top, when the particle's order is higher than its original place in the draw order. I've tried a couple of things, double pass, different material settings... but the problem seemed to persist. I've made a codepen demonstrating the issue. Please check it out and leave a comment if you have any ideas how to resolve it.

Codepen example

import * as THREE from "https://esm.sh/three";

let camera, scene, renderer, material, points;

const vertexShader = `
attribute float u_id;
varying vec2 vUv;
varying vec3 vColor;

void main() {
    vec3 newPosition = position;

    //
    // THE CIRCLES HAVE TO BE POSITIONED IN THE SHADER
    //
    
    if (u_id == 0.0) {
        newPosition = vec3(-0.6, 0.0, 1.2);
        vColor = vec3(1.0, 0.0, 0.0);
    }
    if (u_id == 1.0) {
        newPosition = vec3(0.0, 0.0, 1.0);
        vColor = vec3(0.0, 1.0, 0.0);
    }
    if (u_id == 2.0) {
        newPosition = vec3(0.6, 0.0, 1.1);
        vColor = vec3(0.0, 0.0, 1.0);
    }
    vUv = newPosition.xy;
    gl_PointSize = 200.0; // Control the circle size
    gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
`;

const fragmentShader = `
precision mediump float;
varying vec2 vUv;
varying vec3 vColor;

void main() {
    vec2 uv = gl_PointCoord - vec2(0.5); // Center the UVs in the circle
    float dist = length(uv);
    float alpha = smoothstep(0.5, 0.1, dist); // Smooth fade at edges

    vec3 color = vColor; //vec3(1.0, 0.0, 0.0); // Red color

    if (dist > 0.5) discard; // Make the circle smooth
    gl_FragColor = vec4(color, alpha);
}
`;

const VERTEX_COUNT = 3;

// Initial vertex positions are [0, 0, 0]
const getInitialVertices = () => {
    const vertices = new Float32Array(VERTEX_COUNT * 3);
    return vertices;
};

// Generate vertex IDs
const getVertexIds = () => {
    const ids = new Float32Array(VERTEX_COUNT);
    for (let i = 0; i < VERTEX_COUNT; i++) {
        ids[i] = i;
    }
    return ids;
};

function init() {
    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera(
        50,
        window.innerWidth / window.innerHeight,
        1,
        10000
    );
    camera.position.z = 4;
    scene.add(camera);

    const uniforms = {};

    const geometry = new THREE.BufferGeometry();
    geometry.setAttribute("position", new THREE.BufferAttribute(getInitialVertices(), 3));
    geometry.setAttribute("u_id", new THREE.BufferAttribute(getVertexIds(), 1));

    material = new THREE.ShaderMaterial({
        uniforms: uniforms,
        vertexShader: vertexShader,
        fragmentShader: fragmentShader,
        transparent: true,
        //
        // THE OPACITY CUT OCCURES WHEN DEPTH TEST IS ON
        //
        depthTest: true,
    });

    points = new THREE.Points(geometry, material);
    scene.add(points);

    renderer = new THREE.WebGLRenderer({ alpha: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
}

function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}

window.addEventListener("load", () => {
    init();
    animate();
});
Share Improve this question edited Mar 7 at 14:27 AmirHossein_Khakshouri 4461 gold badge4 silver badges13 bronze badges asked Mar 7 at 9:36 David SzucsDavid Szucs 656 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 2

Try set

material.depthWrite = false;

Thanks to this, the particles do not save their position in the depth buffer and the transparent particles are correctly put on each other "...without creating z-index artifacts."

https://threejs./docs/#api/en/materials/Material.depthWrite

<script type="importmap">
  {
"imports": {
  "three": "https://unpkg/[email protected]/build/three.module.js",
  "three/addons/": "https://unpkg/[email protected]/examples/jsm/"
}
  }
</script>

<script type="module">
import * as THREE from 'three';

let camera, scene, renderer, material, points;

const vertexShader = `
attribute float u_id;
varying vec2 vUv;
varying vec3 vColor;

void main() {
vec3 newPosition = position;

//
// THE CIRCLES HAVE TO BE POSITIONED IN THE SHADER
//

if (u_id == 0.0) {
    newPosition = vec3(-0.6, 0.0, 1.2);
    vColor = vec3(1.0, 0.0, 0.0);
}
if (u_id == 1.0) {
    newPosition = vec3(0.0, 0.0, 1.0);
    vColor = vec3(0.0, 1.0, 0.0);
}
if (u_id == 2.0) {
    newPosition = vec3(0.6, 0.0, 1.1);
    vColor = vec3(0.0, 0.0, 1.0);
}
vUv = newPosition.xy;
gl_PointSize = 200.0; // Control the circle size
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
`;
const fragmentShader = `
precision mediump float;
varying vec2 vUv;
varying vec3 vColor;

void main() {
vec2 uv = gl_PointCoord - vec2(0.5); // Center the UVs in the circle
float dist = length(uv);
float alpha = smoothstep(0.5, 0.1, dist); // Smooth fade at edges

vec3 color = vColor; //vec3(1.0, 0.0, 0.0); // Red color

if (dist > 0.5) discard; // Make the circle smooth
gl_FragColor = vec4(color, alpha);
}
`;

const VERTEX_COUNT = 3;

// Initial vertex positions are [0, 0, 0]
const getInitialVertices = () => {
const vertices = new Float32Array(VERTEX_COUNT * 3);
return vertices;
};

// Generate vertex IDs
const getVertexIds = () => {
const ids = new Float32Array(VERTEX_COUNT);
for (let i = 0; i < VERTEX_COUNT; i++) {
    ids[i] = i;
}
return ids;
};
function init() {
scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera(
    50,
    window.innerWidth / window.innerHeight,
    1,
    10000
);
camera.position.z = 4;
scene.add(camera);

const uniforms = {};

const geometry = new THREE.BufferGeometry();
geometry.setAttribute("position", new THREE.BufferAttribute(getInitialVertices(), 3));
geometry.setAttribute("u_id", new THREE.BufferAttribute(getVertexIds(), 1));

material = new THREE.ShaderMaterial({
    uniforms: uniforms,
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    transparent: true,
    //
    // THE OPACITY CUT OCCURES WHEN DEPTH TEST IS ON
    //
    depthTest: true,
});
  
material.depthWrite = false;

points = new THREE.Points(geometry, material);
scene.add(points);

renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}

window.addEventListener("load", () => {
init();
animate();
});

</script>

Edit

I messed around and search a little bit... because with these output parameters, using transparency can be tricky. It seems that the approach where z-sorting depends on the camera position and later subsequent adjustment of the fragment is the most sensible (because you wrote that you will not manipulate the camera). The solution is not perfect (because there is no such solution at the moment, as far as I know), but it is probably the most sensible, apart from some sophisticated ones.

Also take a look at this thread, because that's where I got the code.

https://discourse.threejs./t/need-help-making-transparency-show-correctly-in-point-cloud/51971

Also maybe this will be interested for you

https://developer.nvidia/gpugems/gpugems2/part-vi-simulation-and-numerical-algorithms/chapter-46-improved-gpu-sorting

*{margin:0}
<script type="importmap">
  {
"imports": {
  "three": "https://unpkg/[email protected]/build/three.module.js",
  "three/addons/": "https://unpkg/[email protected]/examples/jsm/"
}
  }
</script>

<script type="module">
import * as THREE from 'three';

let camera, scene, renderer, points;

function init() {
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xffffff);
  camera = new THREE.PerspectiveCamera(
    50,
    window.innerWidth / window.innerHeight,
    1,
    10000
  );

  camera.position.z = 10;

  const geometry = new THREE.BufferGeometry();
  const numPoints = 3;
  // ppositions for three points _ particles:
  // Red: (-0.5, 0, 1.2), Blue: (0.5, 0, 1.1), Green: (0, 0, 1.0)
  const positions = new Float32Array([-0.5, 0, 1.2, 0.5, 0, 1.1, 0.0, 0, 1.0]);
  const colors = new Float32Array([1, 0, 0, 0, 0, 1, 0, 1, 0]);
  geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
  geometry.setAttribute("customColor", new THREE.BufferAttribute(colors, 3));

  const vertexShader = `
    attribute vec3 customColor;
    varying vec3 vColor;
    void main(){
      vColor = customColor;
      gl_PointSize = 80.0;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `;
  const fragmentShader = `
    precision mediump float;
    varying vec2 vUv;
    varying vec3 vColor;

    void main() {
    vec2 uv = gl_PointCoord - vec2(0.5); // Center the UVs in the circle
    float dist = length(uv);
    float alpha = smoothstep(0.5, 0.1, dist); // Smooth fade at edges

    vec3 color = vColor; //vec3(1.0, 0.0, 0.0); // Red color

    if (dist > 0.5) discard; // Make the circle smooth
    gl_FragColor = vec4(color, alpha);
    }
  `;
  const material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    transparent: true,
    depthTest: true,
  });

  points = new THREE.Points(geometry, material);
  scene.add(points);

  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);
}
function depthSortGeometry(geometry, camera) {
  const positionAttribute = geometry.attributes.position;
  const colorAttribute = geometry.attributes.customColor;

  const depthArray = Array.from({ length: positionAttribute.count }, (_, i) => {
    const pos = new THREE.Vector3().fromBufferAttribute(positionAttribute, i);
    return camera.position.distanceTo(pos);
  });

  const indices = depthArray
    .map((depth, i) => i)
    .sort((a, b) => depthArray[b] - depthArray[a]);

  const newPositionAttribute = new THREE.BufferAttribute(
    new Float32Array(positionAttribute.count * 3),
    3
  );
  const newColorAttribute = new THREE.BufferAttribute(
    new Float32Array(colorAttribute.count * 3),
    3
  );
  for (let i = 0; i < indices.length; i++) {
    newPositionAttribute.setXYZ(
      i,
      positionAttribute.getX(indices[i]),
      positionAttribute.getY(indices[i]),
      positionAttribute.getZ(indices[i])
    );
    newColorAttribute.setXYZ(
      i,
      colorAttribute.getX(indices[i]),
      colorAttribute.getY(indices[i]),
      colorAttribute.getZ(indices[i])
    );
  }

  const sortedGeometry = new THREE.BufferGeometry();
  sortedGeometry.setAttribute("position", newPositionAttribute);
  sortedGeometry.setAttribute("customColor", newColorAttribute);

  return sortedGeometry;
}

function animate() {
  requestAnimationFrame(animate);

  const sortedGeometry = depthSortGeometry(points.geometry, camera);
  points.geometry.dispose();
  points.geometry = sortedGeometry;

  renderer.render(scene, camera);
}

window.addEventListener("load", () => {
init();
animate();
});
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});

</script>

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744938533a4602175.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信