I have a sphere set to drawn with point primitives and a material with custom shaders.
If I pass vertex colors that are not transparent, I can see, when rotating the sphere, that all points pass the depth test correctly.
Now I apply a green color to the scene background, and supply vertex colors that have only red and blue components and transparency.
The sphere looks like this ( I added a white rectangle around the part in question):
I can see that all points blend with the scene background because they all have some green color in them.
But some of them, when they overlap with each other, do not blend - the rectangle on top keeps it color above the background and the bottom rectangle the same.
Some of them do blend though.
Why does it happen like this and how do I fix it?
My other question is that I set transparency to 0.1, which is pretty low, but the amount of green points acquire from the background is about a half. Is this blending not linear?
Thank you!
The code:
<body>
<script type='x-shader/x-vertex' id='cities_vert'>
attribute vec4 clr;
out vec4 rgba;
void main() {
gl_PointSize = 20.0;
vec4 camSpace = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * camSpace;
rgba = clr;
}
</script>
<script type='x-shader/x-fragment' id='cities_frag'>
in vec4 rgba;
void main() {
gl_FragColor = rgba;
}
</script>
<div id="container"></div>
<script type="module">
import * as THREE from './js/three.module.js';
import { OrbitControls } from './js/OrbitControls.js';
import Stats from './js/stats.module.js';
// perf utils
let stats;
// math utils
// https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB_alternative
function HSVtoRGB(h, s, v) { // [0,360], [0,1], [0,1] => [0,1], [0,1], [0,1]
function chan(n) {
var k = (n + h / 60) % 6;
return v - v * s * Math.max(0, Math.min(k, 4 - k, 1));
}
return [chan(5), chan(3), chan(1)];
}
// viewport utils
let viewportMargins = [20, 50, 50, 50];
function getViewport() {
var vp = [
window.innerWidth - viewportMargins[1] - viewportMargins[3],
window.innerHeight - viewportMargins[0] - viewportMargins[2]];
return vp;
}
// THREE globals
let camera, controls, scene, renderer;
// world globals
let globe;
init();
animate();
function init() {
// scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0x003300);
var vp = getViewport();
// camera
camera = new THREE.PerspectiveCamera(60, vp[0] / vp[1], 1, 1000);
camera.position.set(0, 0, 300);
// renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(vp[0], vp[1], true, viewportMargins);
document.body.appendChild(renderer.domElement);
// stats
stats = new Stats();
container.appendChild(stats.dom);
// controls
controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.dampingFactor = 0.05;
controls.enablePan = false;
controls.minDistance = 10;
controls.maxDistance = 1000;
controls.minPolarAngle = 0;
controls.maxPolarAngle = Math.PI;
// world
// globe
var cities_geom = new THREE.SphereBufferGeometry(102, 20, 20);
var cities_cnt = cities_geom.attributes.position.count;
var cities_clr = new Float32Array(cities_cnt * 4);
for (var i = 0; i < cities_cnt * 4; i += 4) {
var h = 360 * Math.random();
[cities_clr[i], cities_clr[i + 1], cities_clr[i + 2]] = [Math.random(), 0.0, Math.random()]; // HSVtoRGB(h, 1.0, 1.0);
cities_clr[i + 3] = 0.1;
}
cities_geom.setAttribute('clr', new THREE.Float32BufferAttribute(cities_clr, 4));
var cities_shaders = new THREE.ShaderMaterial({
vertexShader: document.getElementById('cities_vert').textContent,
fragmentShader: document.getElementById('cities_frag').textContent,
transparent: true
});
var cities = new THREE.Points(cities_geom, cities_shaders);
globe = new THREE.Group();
globe.add(cities);
scene.add(globe);
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
scene.add(ambientLight);
// window
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
var vp = getViewport();
camera.aspect = vp[0] / vp[1];
camera.updateProjectionMatrix();
renderer.setSize(vp[0], vp[1], true, viewportMargins);
}
function animate() {
requestAnimationFrame(animate);
controls.update();
render();
stats.update();
}
function render() {
renderer.render(scene, camera);
}
</script>
</body>