Why is my mix in my shader not properly mixing in Threejs? - three.js

I have been messing with this for awhile and I cannot get the shader to mix properly. Currently it is displaying the two colors that are supposed to be mixed as two separate colors on each hemisphere. I am new to threejs and often certain details I am missing. I would appreciate any help regarding this code.
import * as THREE from 'three';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
renderer.setClearColor(0x000000);
const spotLight = new THREE.SpotLight(0xFFFFFF);
scene.add(spotLight);
spotLight.position.set(0, 0, 100);
spotLight.castShadow = true;
spotLight.angle = 0.2;
spotLight.intensity = 0.2;
camera.position.set(0.27, 0, 500);
//Black center
var geom = new THREE.SphereBufferGeometry(100, 32, 32);
var mat = new THREE.MeshPhongMaterial({
color: 0x000000
});
const material = new THREE.ShaderMaterial({
vertexShader: `
varying vec3 vPosition;
void main() {
vPosition = position;
vec4 localPosition = vec4(position, 1.0);
vec4 worldPosition = modelMatrix * localPosition;
vec4 viewPosition = viewMatrix * worldPosition;
vec4 clipPosition = projectionMatrix * viewPosition;
gl_Position = clipPosition;
}
`,
fragmentShader: `
varying vec3 vPosition;
void main() {
float depth = vPosition.x;
vec3 color1 = vec3(1., 1.0, 1.0);
vec3 color2 = vec3(0., .0, 1.0);
vec3 mixedColor = mix(color1, color2, depth);
gl_FragColor = vec4(mixedColor, 1.0);
}
`
});
var core = new THREE.Mesh(geom, material);
scene.add(core);
var geom = new THREE.SphereBufferGeometry(1, 15, 15);
var mat = new THREE.MeshBasicMaterial({
color: 0xffffff
});
var atoms = new THREE.Object3D();
scene.add(atoms);
for (var i = 0; i < 150; i++) {
var nucleus = new THREE.Mesh(geom, mat);
var size = Math.random() * 6 + 1.5;
nucleus.speedX = (Math.random() - 0.5) * 0.08;
nucleus.speedY = (Math.random() - 0.5) * 0.08;
nucleus.speedZ = (Math.random() - 0.5) * 0.08;
nucleus.applyMatrix4(new THREE.Matrix4().makeScale(size, size, size));
nucleus.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 100 + Math.random() * 10, 0));
nucleus.applyMatrix4(new THREE.Matrix4().makeRotationX(Math.random() * (Math.PI * 2)));
nucleus.applyMatrix4(new THREE.Matrix4().makeRotationY(Math.random() * (Math.PI * 2)));
nucleus.applyMatrix4(new THREE.Matrix4().makeRotationZ(Math.random() * (Math.PI * 2)));
atoms.add(nucleus);
}
const _matrix = new THREE.Matrix4();
function updateNucleus(a) {
for (var i = 0; i < atoms.children.length; i++) {
var part = atoms.children[i];
part.applyMatrix4(_matrix.makeRotationX(part.speedX));
part.applyMatrix4(_matrix.makeRotationY(part.speedY));
part.applyMatrix4(_matrix.makeRotationZ(part.speedZ));
}
}
//Create scene
var necks = [];
var cubesObject = new THREE.Object3D();
scene.add(cubesObject);
function animate(a) {
requestAnimationFrame( animate );
updateNucleus(a);
renderer.render(scene,camera);
};
animate();
window.addEventListener('resize', function(){
camera.aspect = window.innerWidth / this.window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
})

Related

Can we use ThreeJs Helpers with InstancedMeshes?

I am newbie to Threejs and I am trying to use axesHelper to visualise the three axis of all the objects in a scene. Earlier I was using simple Meshes and all the helpers were working fine. Recently I've moved few objects to Instanced Meshes and the helpers have stopped working for them. I have confirmed this by trying with two helpers.(arrow and axes helper)
I am using react-three-fiber to write for writing my threejs in JSX style.
Here is the code which used to work for me.
<mesh
position={[geometry.position.x, geometry.position.y, geometry.position.z]}
rotation={angle}
geometry={geometry}>
<meshLambertMaterial color={'red'} />
<axesHelper />
</mesh>
Here is the new one which doesn't work.
<instancedMesh
ref={ref}
args={[(null as unknown) as BufferGeometry, (null as unknown) as Material, objects.length]}>
<coneBufferGeometry args={[0.1, 0.1]}>
<instancedBufferAttribute attachObject={['attributes', 'color']} args={[colorArray, 3]}/>
</coneBufferGeometry>
<meshLambertMaterial vertexColors />
<axesHelper />
</instancedMesh>
So now I wanted to confirm if we can use these helpers for instanced meshes or not? I tried looking into three's documentation but was not able to find any answer related to this question.
I am on Threejs r127.
As an option, you can decompose the matrices of instances and pass these values in attributes for InstancedBufferGeometry, based on geometry of AxesHelper.
It's just an example, not the ultimate solution.
body{
overflow: hidden;
margin: 0;
}
<script type="module">
console.clear();
import * as THREE from "https://threejs.org/build/three.module.js";
import {OrbitControls} from "https://threejs.org/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 8, 13);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.setScalar(10);
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 1));
let cylGeom = new THREE.CylinderBufferGeometry(0.5, 1, 2, 8);
let cylMat = new THREE.MeshStandardMaterial({color: "red", roughness: 0.25, metalness: 0.75, polygonOffset: true, polygonOffsetFactor: 1});
let cylinder = new THREE.InstancedMesh(cylGeom, cylMat, 100);
let dummy = new THREE.Object3D();
let mat4 = new THREE.Matrix4();
let counter = 0;
let pos = [];
let rot = [];
let scl = [];
for(let z = 0; z < 10; z++){
for(let x = 0; x < 10; x++){
dummy.position.set(-4.5 + x, 0, -4.5 + z).multiplyScalar(4);
dummy.rotation.set(
Math.random() * Math.PI * 2,
Math.random() * Math.PI * 2,
Math.random() * Math.PI * 2
);
dummy.updateMatrix();
cylinder.setMatrixAt(counter, dummy.matrix);
pos.push(dummy.position.x, dummy.position.y, dummy.position.z);
rot.push(dummy.quaternion.x, dummy.quaternion.y, dummy.quaternion.z, dummy.quaternion.w);
scl.push(dummy.scale.x, dummy.scale.y, dummy.scale.z);
counter++;
}
}
cylinder.instanceMatrix.needsUpdate = true;
console.log(cylinder);
scene.add(cylinder);
let axesHelper = new THREE.AxesHelper(2);
console.log(axesHelper);
let lineGeom = new THREE.InstancedBufferGeometry().copy(axesHelper.geometry);
lineGeom.instanceCount = Infinity;
lineGeom.setAttribute("instT", new THREE.InstancedBufferAttribute(new Float32Array(pos), 3));
lineGeom.setAttribute("instR", new THREE.InstancedBufferAttribute(new Float32Array(rot), 4));
lineGeom.setAttribute("instS", new THREE.InstancedBufferAttribute(new Float32Array(scl), 3));
let lineMat = new THREE.LineBasicMaterial({
vertexColors: true,
onBeforeCompile: shader => {
shader.vertexShader = `
attribute vec3 instT;
attribute vec4 instR;
attribute vec3 instS;
// http://barradeau.com/blog/?p=1109
vec3 trs( inout vec3 position, vec3 T, vec4 R, vec3 S ) {
position *= S;
position += 2.0 * cross( R.xyz, cross( R.xyz, position ) + R.w * position );
position += T;
return position;
}
${shader.vertexShader}
`.replace(
`#include <begin_vertex>`,
`#include <begin_vertex>
transformed = trs(transformed, instT, instR, instS);
`
);
console.log(shader.vertexShader);
}
});
let lines = new THREE.LineSegments(lineGeom, lineMat);
scene.add(lines);
window.addEventListener( 'resize', onWindowResize );
let clock = new THREE.Clock();
renderer.setAnimationLoop(()=>{
let t = clock.getDelta();
for(let i = 0; i < counter; i++){
cylinder.getMatrixAt(i, mat4);
mat4.decompose(dummy.position, dummy.quaternion, dummy.scale);
dummy.rotation.x += t;
dummy.rotation.z += t*.5;
dummy.updateMatrix();
cylinder.setMatrixAt(i, dummy.matrix);
cylinder.instanceMatrix.needsUpdate = true;
linesTRS(i, dummy);
}
renderer.render(scene, camera);
})
function linesTRS(index, o){
lineGeom.attributes.instT.setXYZ(index, o.position.x, o.position.y, o.position.z);
lineGeom.attributes.instT.needsUpdate = true;
lineGeom.attributes.instR.setXYZW(index, o.quaternion.x, o.quaternion.y, o.quaternion.z, o.quaternion.w);
lineGeom.attributes.instR.needsUpdate = true;
lineGeom.attributes.instS.setXYZ(index, o.scale.x, o.scale.y, o.scale.z);
lineGeom.attributes.instS.needsUpdate = true;
}
function onWindowResize() {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( innerWidth, innerHeight );
}
</script>

Rotating a geometry using vertex shader in threejs

I am trying to rotate a box using vertex shader but its getting sheared for reason I can't figure out, following is the code, please help me. Here is the fiddle and my code of vertex shader
uniform float delta;
void main()
{
vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);
gl_Position = (projectionMatrix * modelViewPosition);
float new_x = gl_Position.x*cos(delta) - gl_Position.y*sin(delta);
float new_y = gl_Position.y*cos(delta) + gl_Position.x*sin(delta);
gl_Position.x = new_x;
gl_Position.y = new_y;
}
https://jsfiddle.net/co4vbhye/
If the viewport is rectangular , then this is compensated by the projection matrix.
This means that last transformation which has to be applied to the vertex coordinates is the projection matrix:
clip_position = projection * view * model * position
You've to apply the rotation to the vertex coordinate position. After that transform the result by the view matrix and projection matrix:
uniform float delta;
void main()
{
vec3 p = position.xyz;
float new_x = p.x*cos(delta) - p.y*sin(delta);
float new_y = p.y*cos(delta) + p.x*sin(delta);
gl_Position = projectionMatrix * modelViewMatrix * vec4(new_x, new_y, p.z, 1.0);
}
Furthermore, the aspect ration which is set to the projection matrix (PerspectiveCamera), has to match the aspect ration of the viewport (canvas):
either
//RENDERER
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
//CAMERA
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 10000);
or
//RENDERER
renderer = new THREE.WebGLRenderer();
renderer.setSize(CANVAS_WIDTH, CANVAS_HEIGHT);
//CAMERA
camera = new THREE.PerspectiveCamera(75, CANVAS_WIDTH / CANVAS_HEIGHT, 0.01, 10000);
See the example:
var renderer,
scene,
camera,
container = document.getElementById('Canvas_3');
//RENDERER
renderer = new THREE.WebGLRenderer();
//renderer.setClearColor(0xffffff);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
//CAMERA
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 10000);
//SCENE
scene = new THREE.Scene();
var customUniforms = {
delta: {
value: 0
}
};
var material = new THREE.ShaderMaterial({
uniforms: customUniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
});
var geometry = new THREE.BoxBufferGeometry(1, 1, 1, 0, 0, 0);
var mesh = new THREE.Mesh(geometry, material);
mesh.position.z = -5;
mesh.position.x = 0;
scene.add(mesh);
window.onresize = function() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
//RENDER LOOP
render();
var delta = 0;
function render() {
delta += 0.006;
if (delta > 1.57) delta = 0;
mesh.material.uniforms.delta.value = delta;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>
<div id="Canvas_3"></div>
<script type="x-shader/x-vertex" id="vertexShader">
uniform float delta;
void main()
{
vec3 p = position.xyz;
float new_x = p.x*cos(delta) - p.y*sin(delta);
float new_y = p.y*cos(delta) + p.x*sin(delta);
gl_Position = projectionMatrix * modelViewMatrix * vec4(new_x, new_y, p.z, 1.0);
}
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
uniform float delta;
void main() {
gl_FragColor = vec4(delta, 0.0, 1.0-delta, 1.0);
}
</script>

Not getting expect result from THREE.js/GLSL code

Apologies for the vague question but I wasn't sure how to phrase this. I'm trying to write some THREE.js/GLSL code that produces a circular gradient (for some SDF stuff). With the code below I would expect to see the gradient on the plane, but the plane remains white and nothing else renders on it. Can anyone tell me where I'm going wrong?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script type="x-shader/x-vertex" id="sdfVS">
varying vec2 vUv; // pass the uv coordinates of each pixel to the frag shader
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type="x-shader/x-fragment" id="sdfFS">
precision mediump float;
uniform vec2 u_resolution;
varying vec2 vUv;
float circle(vec2 pos, float radius)
{
return distance(pos, vec2(radius));
}
void main()
{
vec2 pos = (gl_FragCoord.xy / vUv) * 2.0 - 1.0;
float circle1 = circle(pos, 0.5);
vec3 color = vec3(circle1);
gl_FragColor = vec4(color, 1.0);
}
</script>
<script src="../../js/three.min.js"></script>
<script>
var scene, camera, renderer, aspect, geometry, material, plane;
var container;
var frustumSize = 2;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0000ff);
aspect = window.innerWidth / window.innerHeight;
camera = new THREE.OrthographicCamera( 0.5 * frustumSize * aspect / - 2, 0.5 * frustumSize * aspect / 2, frustumSize / 2, frustumSize / - 2, 0.1, 1 );
cameraOrthoHelper = new THREE.CameraHelper( camera );
scene.add( cameraOrthoHelper );
var width = 1;
var height = 1;
geometry = new THREE.PlaneGeometry(width, height);
material = new THREE.ShaderMaterial( {
vertexShader: document.getElementById('sdfVS').textContent,
fragmentShader: document.getElementById('sdfFS').textContent,
side: THREE.DoubleSide
} );
plane = new THREE.Mesh( geometry, material );
plane.rotation.x = 0;
plane.rotation.y = THREE.Math.degToRad( -90 );
plane.rotation.z = 0;
scene.add( plane )
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.position.x = -1;
camera.position.y = 0;
camera.position.z = 0;
camera.lookAt( scene.position );
camera.updateMatrixWorld();
renderer.render( scene, camera );
}
</script>
</body>
</html>
Can't get this line:
vec2 pos = (gl_FragCoord.xy / vUv) * 2.0 - 1.0;
Seems like you want to calculate uv coordinates, but if so, then you already have uvs (passed in vUv).
You can do the thing this way, as an option:
var scene, camera, renderer, aspect, geometry, material, plane;
var container;
var clock = new THREE.Clock();
var frustumSize = 2;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0000ff);
aspect = window.innerWidth / window.innerHeight;
camera = new THREE.OrthographicCamera(0.5 * frustumSize * aspect / -2, 0.5 * frustumSize * aspect / 2, frustumSize / 2, frustumSize / -2, 0.1, 1);
cameraOrthoHelper = new THREE.CameraHelper(camera);
scene.add(cameraOrthoHelper);
var width = 1;
var height = 1;
geometry = new THREE.PlaneGeometry(width, height);
material = new THREE.ShaderMaterial({
uniforms: {time: {value: 0}},
vertexShader: document.getElementById('sdfVS').textContent,
fragmentShader: document.getElementById('sdfFS').textContent,
side: THREE.DoubleSide
});
plane = new THREE.Mesh(geometry, material);
plane.rotation.x = 0;
plane.rotation.y = THREE.Math.degToRad(-90);
plane.rotation.z = 0;
scene.add(plane)
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
camera.position.x = -1;
camera.position.y = 0;
camera.position.z = 0;
camera.lookAt(scene.position);
camera.updateMatrixWorld();
var t = clock.getElapsedTime();
material.uniforms.time.value = t;
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script type="x-shader/x-vertex" id="sdfVS">
varying vec2 vUv; // pass the uv coordinates of each pixel to the frag shader
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type="x-shader/x-fragment" id="sdfFS">
precision mediump float;
uniform float time;
uniform vec2 u_resolution;
varying vec2 vUv;
float circle(vec2 uv, vec2 pos, float radius) {
return smoothstep(radius, 0., length(uv - pos));
}
void main()
{
vec2 uv = vUv * 2. - 1.;
vec2 pos = vec2(cos(time), sin(time));
float circle1 = circle(uv, pos, 1.0);
vec3 color = vec3(circle1);
gl_FragColor = vec4(color, 1.0);
}
</script>

GLSL/THREE.js Math to turn a shape into a cube issue

I am making code to turn a sphere into a cube in a vertex shader, but it seems to turn into this weird shape, my logic was this:
The commented out code was the iterative version.
vec3 p = position;
if(true)
{
if(p.y<s&&p.y>-s){
p.x = -(p.x-s);//p.x-=(p.x-s)*t*0.1;
}
if(p.x<s&&p.x>-s){
p.y = -(p.y-s);//p.y-=(p.y-s)*t*0.1;
}
}
gl_Position = projectionMatrix * modelViewMatrix * vec4( p, 1.0 );
But then that turns this:
Into this:
Any help appreciated.
Use THREE.BoxBufferGeometry() as a base, then add another buffer attribute with coodinates for a sphere formation, then interpolate (mix) those coordinates of the box and the sphere in the shader:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 100);
camera.position.set(1, 3, 5);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
scene.add(new THREE.GridHelper(10, 10));
var side = 2;
var rad = Math.sqrt(3) * 0.5 * side; // radius of the sphere is a half of cube's diagonal
var geom = new THREE.BoxBufferGeometry(side, side, side, 10, 10, 10);
var pos = geom.attributes.position;
var spherePos = []; // array of coordinates for the sphere formation
var vec3 = new THREE.Vector3(); // vector for re-use
for (let i = 0; i < pos.count; i++) {
vec3.fromBufferAttribute(pos, i).setLength(rad); // create coordinate for the sphere formation
spherePos.push(vec3.x, vec3.y, vec3.z);
}
geom.addAttribute("spherePos", new THREE.BufferAttribute(new Float32Array(spherePos), 3));
var mat = new THREE.ShaderMaterial({
uniforms: {
mixShapes: {
value: 0
}
},
vertexShader: `
uniform float mixShapes;
attribute vec3 spherePos;
void main() {
vec3 pos = mix(position, spherePos, mixShapes); // interpolation between shapes
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
void main() {
gl_FragColor = vec4(1.0, 0.0, 1.0,1.0);
}
`,
wireframe: true
});
var shape = new THREE.Mesh(geom, mat);
scene.add(shape);
var gui = new dat.GUI();
gui.add(mat.uniforms.mixShapes, "value", 0.0, 1.0).name("mixShapes");
renderer.setAnimationLoop(() => {
renderer.render(scene, camera)
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
If you want to get a cube from a sphere, you can clamp vertices to min and max vectors of a bounding box you need (but accuracy of this approach depends on the amount of vertices of the sphere):
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 100);
camera.position.set(1, 3, 5);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
scene.add(new THREE.GridHelper(10, 10));
var side = 2;
var rad = Math.sqrt(3) * 0.5 * side; // radius of the sphere is a half of cube's diagonal
var geom = new THREE.SphereBufferGeometry(rad, 36, 36);
var mat = new THREE.ShaderMaterial({
uniforms: {
mixShapes: {
value: 0
}
},
vertexShader: `
uniform float mixShapes;
attribute vec3 spherePos;
void main() {
vec3 pos = clamp(position, vec3(${-side * 0.5}), vec3(${side * 0.5})); // clamp to min and max vectors
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
void main() {
gl_FragColor = vec4(1.0, 0.0, 1.0,1.0);
}
`,
wireframe: true
});
var shape = new THREE.Mesh(geom, mat);
scene.add(shape);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera)
});
body {
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Particles follow texture

I have a sphere created with particles in three.js that works perfectly. Now I wanted to put these particles on top of a texture that I have of a world map simulating a 3D planet, I searched the internet but I did not find any information on how to do it, when I put the texture instead of it being outside it ends up getting inside each particle, how could I do that? Any idea ? Thank you all
here is my code
$( document ).ready(function() {
var globe = document.getElementById('globe')
var Maxwidth = window.innerWidth
var Maxheight = window.innerHeight
var scene = new THREE.Scene();
var renderer = new THREE.WebGLRenderer({antilias:true});
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize(Maxwidth,Maxheight)
globe.appendChild(renderer.domElement)
var camera = new THREE.PerspectiveCamera(60, Maxwidth / Maxheight,1,1000);
camera.position.z = 50;
var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.dampingFactor = 0.25;
controls.panningMode = THREE.HorizontalPanning; // default is
THREE.ScreenSpacePanning
controls.maxPolarAngle = Math.PI / 2;
var geometry = new THREE.SphereGeometry( 200, 42, 42 );
geometry.widthSegments = 42;
var colors = [];
for( var i = 0; i < geometry.vertices.length; i++ ) {
// random color
colors[i] = new THREE.Color();
//colors[i].setHSV( Math.random(), 1.0, 1.0 );
}
geometry.colors = colors;
// texture
var texture = new THREE.Texture( generateTexture( ) );
texture.needsUpdate = true; // important
// particle system material
var material = new THREE.ParticleBasicMaterial( {
size: 5,
map: texture,
blending: THREE.AdditiveBlending, // required
depthTest: false, // required
transparent: true,
opacity: 0.7,
vertexColors: true // optional
} );
material.map = THREE.ImageUtils.loadTexture('../img/point_picker.png')
material.anisotropy = 0;
material.magFilter = THREE.NearestFilter;
material.minFilter = THREE.NearestFilter;
var union = new THREE.ParticleSystem( geometry, material );
function generateTexture( ) {
var size = 128;
var canvas = document.createElement( 'canvas' );
canvas.width = size;
canvas.height = size;
var context = canvas.getContext( '2d' );
var centerX = size / 2;
var centerY = size / 2;
var radius = size / 2;
context.beginPath();
context.arc( centerX, centerY, radius, 0, 2 * Math.PI, false );
context.fillStyle = "#FFFFFF";
context.fill();
return canvas;
}
scene.add(union)
renderer.setClearColor(0x2675AD)
renderer.render(scene,camera)
controls.update();
function render(delta){
requestAnimationFrame(render);
renderer.render(scene,camera)
union.rotation.y += 0.0009
}
render()
});
I need something like this
So, this is the option I was talking about in my comment:
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(1.25, 7, 7);
camera.lookAt(scene.position);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0x080808);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var geom = new THREE.SphereBufferGeometry(5, 120, 60);
var colors = [];
var color = new THREE.Color();
var q = 0xffffff * 0.25;
for (let i = 0; i < geom.attributes.position.count; i++) {
color.set(Math.random() * q + q * 3);
color.toArray(colors, i * 3);
}
geom.addAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3));
var loader = new THREE.TextureLoader();
loader.setCrossOrigin('');
var texture = loader.load('https://learningthreejs.com/data/2013-09-16-how-to-make-the-earth-in-webgl/demo/bower_components/threex.planets/images/earthspec1k.jpg');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(1, 1);
var disk = loader.load('https://threejs.org/examples/textures/sprites/circle.png');
var points = new THREE.Points(geom, new THREE.ShaderMaterial({
vertexColors: THREE.VertexColors,
uniforms: {
visibility: {
value: texture
},
shift: {
value: 0
},
shape: {
value: disk
},
size: {
value: 0.125
},
scale: {
value: window.innerHeight / 2
}
},
vertexShader: `
uniform float scale;
uniform float size;
varying vec2 vUv;
varying vec3 vColor;
void main() {
vUv = uv;
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( scale / length( mvPosition.xyz ) );
gl_Position = projectionMatrix * mvPosition;
}
`,
fragmentShader: `
uniform sampler2D visibility;
uniform float shift;
uniform sampler2D shape;
varying vec2 vUv;
varying vec3 vColor;
void main() {
vec2 uv = vUv;
uv.x += shift;
vec4 v = texture2D(visibility, uv);
if (length(v.rgb) > 1.0) discard;
gl_FragColor = vec4( vColor, 1.0 );
vec4 shapeData = texture2D( shape, gl_PointCoord );
if (shapeData.a < 0.5) discard;
gl_FragColor = gl_FragColor * shapeData;
}
`,
transparent: true
}));
scene.add(points);
var blackGlobe = new THREE.Mesh(geom, new THREE.MeshBasicMaterial({
color: 0x000000
}));
blackGlobe.scale.setScalar(0.99);
points.add(blackGlobe);
var clock = new THREE.Clock();
var time = 0;
render();
function render() {
requestAnimationFrame(render);
time += clock.getDelta();
points.material.uniforms.shift.value = time * 0.1;
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.91.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.91.0/examples/js/controls/OrbitControls.js"></script>

Resources