Странно, что я не смог найти ответ на этот простой вопрос. Я пытаюсь сопоставить размер частиц шейдера с мировыми координатами.
verts.push(new THREE.Vector3(-2.0, 0.0, 0.0));
colors.push(0.0, 1.0, 0.0);
size.push(1.0);
var geometry = new THREE.BufferGeometry().setFromPoints(verts);
geometry.addAttribute("color", new THREE.BufferAttribute(new Float32Array(colors), 3));
geometry.addAttribute("size", new THREE.BufferAttribute(new Float32Array(size), 1));
var material = new THREE.ShaderMaterial({
uniforms: {
resolution: new THREE.Uniform(new THREE.Vector2(renderer.domElement.width, renderer.domElement.height)),
texture: {
value: new THREE.TextureLoader().load(circularPoint) },
scale: {
value: window.innerHeight / 2 }
},
vertexShader: document.getElementById("vertexShader").textContent,
fragmentShader: document.getElementById("fragmentShader").textContent,
depthTest : true,
alphaTest: 0.9
})
material.extensions.fragDepth = true;
material.extensions.drawBuffers = true;
var points = new THREE.Points(geometry, material);
scene.add(points);
var sphere = new THREE.Mesh(new THREE.SphereGeometry(0.5, 8, 8), new THREE.MeshNormalMaterial({wireframe: true}));
sphere.position.set(-2.0, 0., 0.0);
scene.add(sphere);
Я установил размер частиц на 1,0 и радиус сферы на 0,5, надеясь, что они будут одинаковыми, но это не так.
Экспериментально я выяснил, что размер частиц как-то связан с параметрами камеры и размерами холста.
Может ли кто-нибудь помочь мне настроить шейдер, чтобы он соответствовал всем, так что размер практики n будеттого же размера, что и сферическая геометрия с радиусом 0,5 или кубическая с 1,0 гранями?
THREE.OrbitControls = function(e, t) {
var n, o, a, i, r;
this.object = e, this.domElement = void 0 !== t ? t : document, this.enabled = !0, this.target = new THREE.Vector3, this.minDistance = 0, this.maxDistance = 1 / 0, this.minZoom = 0, this.maxZoom = 1 / 0, this.minPolarAngle = 0, this.maxPolarAngle = Math.PI, this.minAzimuthAngle = -1 / 0, this.maxAzimuthAngle = 1 / 0, this.enableDamping = !1, this.dampingFactor = .25, this.enableZoom = !0, this.zoomSpeed = 1, this.enableRotate = !0, this.rotateSpeed = 1, this.enablePan = !0, this.keyPanSpeed = 7, this.autoRotate = !1, this.autoRotateSpeed = 2, this.enableKeys = !0, this.keys = {
LEFT: 37,
UP: 38,
RIGHT: 39,
BOTTOM: 40
}, this.mouseButtons = {
ORBIT: THREE.MOUSE.LEFT,
ZOOM: THREE.MOUSE.MIDDLE,
PAN: THREE.MOUSE.RIGHT
}, this.target0 = this.target.clone(), this.position0 = this.object.position.clone(), this.zoom0 = this.object.zoom, this.getPolarAngle = function() {
return E.phi
}, this.getAzimuthalAngle = function() {
return E.theta
}, this.saveState = function() {
s.target0.copy(s.target), s.position0.copy(s.object.position), s.zoom0 = s.object.zoom
}, this.reset = function() {
s.target.copy(s.target0), s.object.position.copy(s.position0), s.object.zoom = s.zoom0, s.object.updateProjectionMatrix(), s.dispatchEvent(c), s.update(), u = l.NONE
}, this.update = (n = new THREE.Vector3, o = (new THREE.Quaternion).setFromUnitVectors(e.up, new THREE.Vector3(0, 1, 0)), a = o.clone().inverse(), i = new THREE.Vector3, r = new THREE.Quaternion, function() {
var e = s.object.position;
return n.copy(e).sub(s.target), n.applyQuaternion(o), E.setFromVector3(n), s.autoRotate && u === l.NONE && M(2 * Math.PI / 60 / 60 * s.autoRotateSpeed), E.theta += p.theta, E.phi += p.phi, E.theta = Math.max(s.minAzimuthAngle, Math.min(s.maxAzimuthAngle, E.theta)), E.phi = Math.max(s.minPolarAngle, Math.min(s.maxPolarAngle, E.phi)), E.makeSafe(), E.radius *= b, E.radius = Math.max(s.minDistance, Math.min(s.maxDistance, E.radius)), s.target.add(g), n.setFromSpherical(E), n.applyQuaternion(a), e.copy(s.target).add(n), s.object.lookAt(s.target), !0 === s.enableDamping ? (p.theta *= 1 - s.dampingFactor, p.phi *= 1 - s.dampingFactor) : p.set(0, 0, 0), b = 1, g.set(0, 0, 0), !(!(T || i.distanceToSquared(s.object.position) > h || 8 * (1 - r.dot(s.object.quaternion)) > h) || (s.dispatchEvent(c), i.copy(s.object.position), r.copy(s.object.quaternion), T = !1))
}), this.dispose = function() {
s.domElement.removeEventListener("contextmenu", B, !1), s.domElement.removeEventListener("mousedown", Z, !1), s.domElement.removeEventListener("wheel", F, !1), s.domElement.removeEventListener("touchstart", X, !1), s.domElement.removeEventListener("touchend", _, !1), s.domElement.removeEventListener("touchmove", K, !1), document.removeEventListener("mousemove", Y, !1), document.removeEventListener("mouseup", z, !1), window.removeEventListener("keydown", I, !1)
};
var s = this,
c = {
type: "change"
},
m = {
type: "start"
},
d = {
type: "end"
},
l = {
NONE: -1,
ROTATE: 0,
DOLLY: 1,
PAN: 2,
TOUCH_ROTATE: 3,
TOUCH_DOLLY: 4,
TOUCH_PAN: 5
},
u = l.NONE,
h = 1e-6,
E = new THREE.Spherical,
p = new THREE.Spherical,
b = 1,
g = new THREE.Vector3,
T = !1,
v = new THREE.Vector2,
R = new THREE.Vector2,
O = new THREE.Vector2,
f = new THREE.Vector2,
y = new THREE.Vector2,
H = new THREE.Vector2,
w = new THREE.Vector2,
P = new THREE.Vector2,
j = new THREE.Vector2;
function C() {
return Math.pow(.95, s.zoomSpeed)
}
function M(e) {
p.theta -= e
}
function L(e) {
p.phi -= e
}
var N, A, k, x = (N = new THREE.Vector3, function(e, t) {
N.setFromMatrixColumn(t, 0), N.multiplyScalar(-e), g.add(N)
}),
D = (A = new THREE.Vector3, function(e, t) {
A.setFromMatrixColumn(t, 1), A.multiplyScalar(e), g.add(A)
}),
U = (k = new THREE.Vector3, function(e, t) {
var n = s.domElement === document ? s.domElement.body : s.domElement;
if (s.object.isPerspectiveCamera) {
var o = s.object.position;
k.copy(o).sub(s.target);
var a = k.length();
a *= Math.tan(s.object.fov / 2 * Math.PI / 180), x(2 * e * a / n.clientHeight, s.object.matrix), D(2 * t * a / n.clientHeight, s.object.matrix)
} else s.object.isOrthographicCamera ? (x(e * (s.object.right - s.object.left) / s.object.zoom / n.clientWidth, s.object.matrix), D(t * (s.object.top - s.object.bottom) / s.object.zoom / n.clientHeight, s.object.matrix)) : (console.warn("WARNING"), s.enablePan = !1)
});
function S(e) {
s.object.isPerspectiveCamera ? b /= e : s.object.isOrthographicCamera ? (s.object.zoom = Math.max(s.minZoom, Math.min(s.maxZoom, s.object.zoom * e)), s.object.updateProjectionMatrix(), T = !0) : (console.warn("WARNING"), s.enableZoom = !1)
}
function V(e) {
s.object.isPerspectiveCamera ? b *= e : s.object.isOrthographicCamera ? (s.object.zoom = Math.max(s.minZoom, Math.min(s.maxZoom, s.object.zoom / e)), s.object.updateProjectionMatrix(), T = !0) : (console.warn("WARNING"), s.enableZoom = !1)
}
function Z(e) {
if (!1 !== s.enabled) {
switch (e.preventDefault(), e.button) {
case s.mouseButtons.ORBIT:
if (!1 === s.enableRotate) return;
o = e, v.set(o.clientX, o.clientY), u = l.ROTATE;
break;
case s.mouseButtons.ZOOM:
if (!1 === s.enableZoom) return;
n = e, w.set(n.clientX, n.clientY), u = l.DOLLY;
break;
case s.mouseButtons.PAN:
if (!1 === s.enablePan) return;
t = e, f.set(t.clientX, t.clientY), u = l.PAN
}
var t, n, o;
u !== l.NONE && (document.addEventListener("mousemove", Y, !1), document.addEventListener("mouseup", z, !1), s.dispatchEvent(m))
}
}
function Y(e) {
var t, n;
if (!1 !== s.enabled) switch (e.preventDefault(), u) {
case l.ROTATE:
if (!1 === s.enableRotate) return;
! function(e) {
R.set(e.clientX, e.clientY), O.subVectors(R, v);
var t = s.domElement === document ? s.domElement.body : s.domElement;
M(2 * Math.PI * O.x / t.clientWidth * s.rotateSpeed), L(2 * Math.PI * O.y / t.clientHeight * s.rotateSpeed), v.copy(R), s.update()
}(e);
break;
case l.DOLLY:
if (!1 === s.enableZoom) return;
n = e, P.set(n.clientX, n.clientY), j.subVectors(P, w), 0 < j.y ? S(C()) : j.y < 0 && V(C()), w.copy(P), s.update();
break;
case l.PAN:
if (!1 === s.enablePan) return;
t = e, y.set(t.clientX, t.clientY), H.subVectors(y, f), U(H.x, H.y), f.copy(y), s.update()
}
}
function z(e) {
!1 !== s.enabled && (document.removeEventListener("mousemove", Y, !1), document.removeEventListener("mouseup", z, !1), s.dispatchEvent(d), u = l.NONE)
}
function F(e) {
var t;
!1 === s.enabled || !1 === s.enableZoom || u !== l.NONE && u !== l.ROTATE || (e.preventDefault(), e.stopPropagation(), (t = e).deltaY < 0 ? V(C()) : 0 < t.deltaY && S(C()), s.update(), s.dispatchEvent(m), s.dispatchEvent(d))
}
function I(e) {
!1 !== s.enabled && !1 !== s.enableKeys && !1 !== s.enablePan && function(e) {
switch (e.keyCode) {
case s.keys.UP:
U(0, s.keyPanSpeed), s.update();
break;
case s.keys.BOTTOM:
U(0, -s.keyPanSpeed), s.update();
break;
case s.keys.LEFT:
U(s.keyPanSpeed, 0), s.update();
break;
case s.keys.RIGHT:
U(-s.keyPanSpeed, 0), s.update()
}
}(e)
}
function X(e) {
if (!1 !== s.enabled) {
switch (e.touches.length) {
case 1:
if (!1 === s.enableRotate) return;
r = e, v.set(r.touches[0].pageX, r.touches[0].pageY), u = l.TOUCH_ROTATE;
break;
case 2:
if (!1 === s.enableZoom) return;
o = (n = e).touches[0].pageX - n.touches[1].pageX, a = n.touches[0].pageY - n.touches[1].pageY, i = Math.sqrt(o * o + a * a), w.set(0, i), u = l.TOUCH_DOLLY;
break;
case 3:
if (!1 === s.enablePan) return;
t = e, f.set(t.touches[0].pageX, t.touches[0].pageY), u = l.TOUCH_PAN;
break;
default:
u = l.NONE
}
var t, n, o, a, i, r;
u !== l.NONE && s.dispatchEvent(m)
}
}
function K(e) {
var t, n, o, a, i;
if (!1 !== s.enabled) switch (e.preventDefault(), e.stopPropagation(), e.touches.length) {
case 1:
if (!1 === s.enableRotate) return;
if (u !== l.TOUCH_ROTATE) return;
! function(e) {
R.set(e.touches[0].pageX, e.touches[0].pageY), O.subVectors(R, v);
var t = s.domElement === document ? s.domElement.body : s.domElement;
M(2 * Math.PI * O.x / t.clientWidth * s.rotateSpeed), L(2 * Math.PI * O.y / t.clientHeight * s.rotateSpeed), v.copy(R), s.update()
}(e);
break;
case 2:
if (!1 === s.enableZoom) return;
if (u !== l.TOUCH_DOLLY) return;
o = (n = e).touches[0].pageX - n.touches[1].pageX, a = n.touches[0].pageY - n.touches[1].pageY, i = Math.sqrt(o * o + a * a), P.set(0, i), j.subVectors(P, w), 0 < j.y ? V(C()) : j.y < 0 && S(C()), w.copy(P), s.update();
break;
case 3:
if (!1 === s.enablePan) return;
if (u !== l.TOUCH_PAN) return;
t = e, y.set(t.touches[0].pageX, t.touches[0].pageY), H.subVectors(y, f), U(H.x, H.y), f.copy(y), s.update();
break;
default:
u = l.NONE
}
}
function _(e) {
!1 !== s.enabled && (s.dispatchEvent(d), u = l.NONE)
}
function B(e) {
!1 !== s.enabled && e.preventDefault()
}
s.domElement.addEventListener("contextmenu", B, !1), s.domElement.addEventListener("mousedown", Z, !1), s.domElement.addEventListener("wheel", F, !1), s.domElement.addEventListener("touchstart", X, !1), s.domElement.addEventListener("touchend", _, !1), s.domElement.addEventListener("touchmove", K, !1), window.addEventListener("keydown", I, !1), this.update()
}, THREE.OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype), THREE.OrbitControls.prototype.constructor = THREE.OrbitControls, Object.defineProperties(THREE.OrbitControls.prototype, {
center: {
get: function() {
return console.warn("THREE.OrbitControls"), this.target
}
},
noZoom: {
get: function() {
return console.warn("THREE.OrbitControls"), !this.enableZoom
},
set: function(e) {
console.warn("THREE.OrbitControls"), this.enableZoom = !e
}
},
noRotate: {
get: function() {
return console.warn("THREE.OrbitControls"), !this.enableRotate
},
set: function(e) {
console.warn("THREE.OrbitControls"), this.enableRotate = !e
}
},
noPan: {
get: function() {
return console.warn("THREE.OrbitControls"), !this.enablePan
},
set: function(e) {
console.warn("THREE.OrbitControls"), this.enablePan = !e
}
},
noKeys: {
get: function() {
return console.warn("THREE.OrbitControls"), !this.enableKeys
},
set: function(e) {
console.warn("THREE.OrbitControls"), this.enableKeys = !e
}
},
staticMoving: {
get: function() {
return console.warn("THREE.OrbitControls"), !this.enableDamping
},
set: function(e) {
console.warn("THREE.OrbitControls"), this.enableDamping = !e
}
},
dynamicDampingFactor: {
get: function() {
return console.warn("THREE.OrbitControls"), this.dampingFactor
},
set: function(e) {
console.warn("THREE.OrbitControls"), this.dampingFactor = e
}
}
});
var circularPoint = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAF8WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDAgNzkuMTYwNDUxLCAyMDE3LzA1LzA2LTAxOjA4OjIxICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKE1hY2ludG9zaCkiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTExLTMwVDE4OjI4OjI1KzAzOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDE5LTExLTMwVDE4OjI4OjI1KzAzOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMS0zMFQxODoyODoyNSswMzowMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo5ZTcwYzU1Ni02ODI3LTRhY2EtOTM2Mi0yNTY2MzRmODFiNWQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDowY2MzZGE4OC04NGIxLWRkNDAtYWRjZS03MjE3ZjFhZTMyN2IiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ZGFlOTc3ZC1mYTM2LTQ3ZGUtOTNmZC1kMDVkOTRjNDVlOWUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiPiA8eG1wTU06SGlzdG9yeT4gPHJkZjpTZXE+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjdkYWU5NzdkLWZhMzYtNDdkZS05M2ZkLWQwNWQ5NGM0NWU5ZSIgc3RFdnQ6d2hlbj0iMjAxOS0xMS0zMFQxODoyODoyNSswMzowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo5ZTcwYzU1Ni02ODI3LTRhY2EtOTM2Mi0yNTY2MzRmODFiNWQiIHN0RXZ0OndoZW49IjIwMTktMTEtMzBUMTg6Mjg6MjUrMDM6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7xYfCfAAABw0lEQVRYhcWXPWsVQRhGz44iJigmiAjxE1PYKWIjSSGm1cLKwsIfEEELaxsL/0EKWwsbsVdBMcSIVa6dgt+FlUHRxibusZiMHwvZO/Heu/PANLvwnmdmd2aet1LJVABOAXPAMeAwMAnUwBfgPbACPAJ6uUVR+41J9br60Xy9VK+q4/3qt70M6rz6dRPgpj6pF/7HwG71wQDgpm6rY7kG9huXcNhaUnf1M7BHfTUCeNKijZX4G16p90cIT7q1kYH5DuBJZxO3Mp4DE+v7eCJ7/w6md8BRYC2sP7jSIRzgCHARoFID8AE40KEBgGfAbKXOAk87hgMIHAzAmQJwgAo4HYgXSykdD8B0QQPTAdhR0MDOQPwZSqkOwPeCBr4F4E1BA28Dm4lPw1evUk8Qs1zXqoG9AXhBmc/wGFhNu2ChgIEF4Pd1PEZchamO4D3gJGC6jn8A1zqC18Bl0vnjv5nwTgdp6KYtoXRcfT5C+D11S5sBjNF5aQTwu+q2Jm+jxmS7Mb0OQz/VGzZm3s9AGufU1wPAV9SZNkZOc7pVvaQuq3UGdE19qJ439hqt9dM5kKt9/GnPDxHbc4FVYrDtAU+Az7kFfwFboSKDalnNAwAAAABJRU5ErkJggg==";
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 10);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(0xEEEEEE, 1.0);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var verts = [],
colors = [],
size = [];
verts.push(new THREE.Vector3(-2.0, 0.0, 0.0));
colors.push(0.0, 1.0, 0.0);
size.push(1.0);
var geometry = new THREE.BufferGeometry().setFromPoints(verts);
geometry.addAttribute("color", new THREE.BufferAttribute(new Float32Array(colors), 3));
geometry.addAttribute("size", new THREE.BufferAttribute(new Float32Array(size), 1));
var material = new THREE.ShaderMaterial({
uniforms: {
resolution: new THREE.Uniform(new THREE.Vector2(renderer.domElement.width, renderer.domElement.height)),
texture: {
value: new THREE.TextureLoader().load(circularPoint)
},
scale: {
value: window.innerHeight / 2
}
},
vertexShader: document.getElementById("vertexShader").textContent,
fragmentShader: document.getElementById("fragmentShader").textContent,
depthTest: true,
alphaTest: 0.9
})
material.extensions.derivatives = true;
material.extensions.fragDepth = true;
material.extensions.drawBuffers = true;
var sphere = new THREE.Mesh(new THREE.SphereGeometry(0.5, 8, 8), new THREE.MeshNormalMaterial({
wireframe: true
}));
sphere.position.set(-2.0, 0., 0.0);
scene.add(sphere);
var points = new THREE.Points(geometry, material);
scene.add(points);
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var inverseMatrix = new THREE.Matrix4();
var ray = new THREE.Ray();
renderer.domElement.addEventListener("mousemove", onMouseMove, false);
function onMouseMove(event) {
camera.clearViewOffset();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
}
renderer.setAnimationLoop(() => {
renderer.render(scene, camera)
});
body {
overflow: hidden;
margin: 0;
}
<html>
<head>
<meta charset="utf-8">
<title>THREE.JS | D3.JS : 3D SCATTERPLOT [rc]</title>
<meta name="description" content="Ehno based on D3.JS | THREE.JS stack.">
<meta name="keywords" content="HTML,CSS,CSV,JavaScript,D3.JS,THREE.JS">
<meta name="author" content="Vladimir V. KUCHINOV">
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.min.js"></script>
</head>
<script type="x-shader/x-vertex" id="vertexShader">
attribute float size; attribute vec3 color; uniform float scale; uniform vec2 resolution; varying vec3 vColor; void main() { vColor = color; vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_PointSize = size * ( scale / -mvPosition.z ); gl_Position
= projectionMatrix * mvPosition; }
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
varying vec3 vColor; uniform sampler2D texture; void main() { gl_FragColor = vec4(vColor, 1.); gl_FragColor = gl_FragColor * texture2D(texture, gl_PointCoord); if (gl_FragColor.a
< 0.1) discard; } </script>
<body>
</body>
</html>