Issue with threejs and shaders showing an sphere glow - three.js

I am working on a 3d visualization. I need to show a world with its atmosphere and I based on an example that was with threejs r40 but mine is r71.
When I try to add the atmosphere (kind of a glow to the sphere that represents the world) I am getting the following error:
three.min.js:445 Uncaught TypeError: c.addEventListener is not a function
My code is the following:
scene.add(createAtmosphere());
function createAtmosphere() {
shader = Shaders['atmosphere'];
uniforms = THREE.UniformsUtils.clone(shader.uniforms);
mesh = new THREE.Mesh(new THREE.Sphere(200, 40, 30),
new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader
})
);
mesh.scale.x = mesh.scale.y = mesh.scale.z = 1.1;
mesh.flipSided = true;
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
return mesh;
}
and this is the shader
var Shaders = {
'atmosphere' : {
uniforms: {},
vertexShader: [
'varying vec3 vNormal;',
'void main() {',
'vNormal = normalize( normalMatrix * normal );',
'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
'}'
].join('\n'),
fragmentShader: [
'varying vec3 vNormal;',
'void main() {',
'float intensity = pow( 0.8 - dot( vNormal, vec3( 0, 0, 1.0 ) ), 12.0 );',
'gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 ) * intensity;',
'}'
].join('\n')
}
};
I solved another shader issue but I cant find what is wrong in this one (I am not familiar with shaders and it driving me crazy).
Thanks in advance

You have to create your mesh sending a SphereGeometry, not a Sphere.
http://threejs.org/docs/#Reference/Objects/Mesh
http://threejs.org/docs/#Reference/Extras.Geometries/SphereGeometry
scene.add(createAtmosphere());
function createAtmosphere() {
shader = Shaders['atmosphere'];
uniforms = THREE.UniformsUtils.clone(shader.uniforms);
mesh = new THREE.Mesh(new THREE.SphereGeometry(200, 40, 30),
new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader
})
);
mesh.scale.x = mesh.scale.y = mesh.scale.z = 1.1;
mesh.flipSided = true;
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
return mesh;
}

Related

Threejs latest version 0.130.1 is not rendering shadermaterial

We were using Three 0.115 version and everything was working. Since we got vulnerability issues for < 0.125, we decided to upgrade to latest version. Then we are getting issues with shader material.
We have an application that uses Point cloud rendered with buffer geometry(positions, sizes and colors bufferattributes) and shadermaterial.
function vertexShader() {
return `attribute float size;
attribute vec3 customColor;
varying vec3 vColor;
attribute float visibility;
varying float vVisible;
void main() {
vColor = customColor;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( 300.0 / -mvPosition.z );
gl_Position = projectionMatrix * mvPosition;
vVisible = visibility;
}`
}
function fragmentShader() {
return `uniform vec3 color;
uniform sampler2D pointTexture;
varying vec3 vColor;
varying float vVisible;
void main() {
gl_FragColor = vec4( color * vColor, 1.0 );
gl_FragColor = gl_FragColor * texture2D( pointTexture, gl_PointCoord );
if ( gl_FragColor.a < ALPHATEST ) discard;
if (vVisible < 0.5) discard;
}`
}
and in our javascript init code.
const material = new THREE.ShaderMaterial({
uniforms: {
color: { value: new THREE.Color(0xffffff) },
texture: { value: new THREE.TextureLoader().load(circle) },
resolution: { value: new THREE.Vector2() },
},
vertexShader: vertexShader(),
fragmentShader: fragmentShader(),
alphaTest: 0.9,
blending: THREE.AdditiveBlending
});
there is no error in console. But points are not rendered. we use raycast for detecting points and that works without any issue.
Any idea why after upgrading to latest version of three, rendering of points fails? is this something to do with shadermaterial?
Thanks for the help :)
You define the texture uniform like so:
texture: { value: new THREE.TextureLoader().load(circle) },
and in the shader you have this line
uniform sampler2D pointTexture;
I don't understand how this code ever worked since the uniform names do not match. I've aligned the names in the following example which is loosely based on your code.
let camera, scene, renderer;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 3;
scene = new THREE.Scene();
const geometry = new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(1, 0, 0), new THREE.Vector3()]);
const material = new THREE.ShaderMaterial({
uniforms: {
color: {
value: new THREE.Color(0xffffff)
},
pointTexture: {
value: new THREE.TextureLoader().load('https://threejs.org/examples/textures/sprite.png')
},
resolution: {
value: new THREE.Vector2()
},
},
vertexShader: vertexShader(),
fragmentShader: fragmentShader(),
alphaTest: 0.9,
blending: THREE.AdditiveBlending
});
const 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 animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
function vertexShader() {
return `
void main() {
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = ( 300.0 / -mvPosition.z );
gl_Position = projectionMatrix * mvPosition;
}`
}
function fragmentShader() {
return `
uniform sampler2D pointTexture;
void main() {
gl_FragColor = texture2D( pointTexture, gl_PointCoord );
}`
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.130.1/build/three.min.js"></script>

Threejs globe glow issue

We are trying to achieve a glow around a sphere/globe using this code :
var sphereGeom = new THREE.SphereGeometry(3, 40, 40);
var moonTexture = new THREE.TextureLoader().load(
"../../assets/moon-map.png"
);
var moonMaterial = new THREE.MeshPhongMaterial({ map: moonTexture });
var moon = new THREE.Mesh(sphereGeom, moonMaterial);
moon.position.set(0, 0, 0);
this.add(moon);
var customMaterial = new THREE.ShaderMaterial({
uniforms: {
c: { type: "f", value: 0.4 },
p: { type: "f", value: 2.3 },
glowColor: { type: "c", value: new THREE.Color(0xffffff) },
viewVector: { type: "v3", value: new THREE.Vector3(10, 10, 10) }
},
vertexShader: `uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;
void main()
{
vec3 vNormal = normalize( normalMatrix * normal );
vec3 vNormel = normalize( normalMatrix * viewVector );
intensity = pow( c - dot(vNormal, vNormel), p );
// intensity = 0;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`,
fragmentShader: `uniform vec3 glowColor;
varying float intensity;
void main()
{
vec3 glow = glowColor * intensity;
gl_FragColor = vec4( glow, 1.0 );
}`,
side: THREE.BackSide,
blending: THREE.AdditiveBlending,
transparent: true
});
moonGlow = new THREE.Mesh(sphereGeom.clone(), customMaterial.clone());
moonGlow.position.set(moon.position.x, moon.position.y, moon.position.z);
moonGlow.scale.multiplyScalar(1.1);
this.add(moonGlow);
This all looks good and we are getting the desired result. BUT when we are setting the WebGLRenderer’s alpha to true (for getting a gradient in the background body element) then the whole glow is being surrounded by a black border.
Is there some way for getting a gradient color in the background and in the front showing this sphere with glow?
You can apply a texture to scene.background to achieve the desired effect. In this case, it's not necessary to apply alpha = true to WebGLRenderer.
var loader = new THREE.TextureLoader();
var texture = loader.load( 'textures/gradient.jpg' );
scene.background = texture;
Demo: https://jsfiddle.net/f2Lommf5/4946/

How to increase line thickness in three.js edges geometry using shaders?

I'm trying to replicate the effect shown in this Three.js example but instead of showing the wireframe and an opaque box, I'd like to show just the edges without any faces (like what is shown when using the THREE.EdgesGeometry.) I know that setting the linewidth property doesn't work and that using shaders is necessary but I'm not really sure where to begin. For reference, these are the shaders being used in the above Three.js example:
Vertex Shader:
attribute vec3 center;
varying vec3 vCenter;
void main() {
vCenter = center;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
Fragment Shader:
varying vec3 vCenter;
float edgeFactorTri() {
vec3 d = fwidth( vCenter.xyz );
vec3 a3 = smoothstep( vec3( 0.0 ), d * 1.5, vCenter.xyz );
return min( min( a3.x, a3.y ), a3.z );
}
void main() {
gl_FragColor.rgb = mix( vec3( 1.0 ), vec3( 0.2 ), edgeFactorTri() );
gl_FragColor.a = 1.0;
}
I've gotten as far as figuring out that changing what d gets multiplied by (1.5 in the example) is what determines the thickness of the line but I'm completely lost as to how the vCenter variable is actually used (it's a vec3 that is either [1, 0, 0], [0, 1, 0] or [0, 0, 1]) or what I could use to make the THREE.EdgesGeometry render with thicker lines like in the example.
Here is what happens when I try rendering the edges geometry with these shaders:
<script type="x-shader/x-vertex" id="vertexShader">
attribute vec3 center;
varying vec3 vCenter;
void main() {
vCenter = center;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
varying vec3 vCenter;
uniform float lineWidth;
float edgeFactorTri() {
float newWidth = lineWidth + 0.5;
vec3 d = fwidth( vCenter.xyz );
vec3 a3 = smoothstep( vec3( 0.0 ), d * newWidth, vCenter.xyz );
return min( min( a3.x, a3.y ), a3.z );
}
void main() {
gl_FragColor.rgb = mix( vec3( 1.0 ), vec3( 0.2 ), edgeFactorTri() );
gl_FragColor.a = 1.0;
}
</script>
Javascript:
size = 150
geometry = new THREE.BoxGeometry(size, size, size);
material = new THREE.MeshBasicMaterial({ wireframe: true });
mesh = new THREE.Mesh(geometry, material);
mesh.position.x = -150;
scene.add(mesh);
//
// geometry = new THREE.BufferGeometry().fromGeometry(new THREE.BoxGeometry(size, size, size));
geometry = new THREE.EdgesGeometry(new THREE.BoxGeometry(size, size, size));
setupAttributes(geometry);
material = new THREE.ShaderMaterial({
uniforms: { lineWidth: { value: 10 } },
vertexShader: document.getElementById("vertexShader").textContent,
fragmentShader: document.getElementById("fragmentShader").textContent
});
material.extensions.derivatives = true;
mesh = new THREE.Mesh(geometry, material);
mesh.position.x = 150;
scene.add(mesh);
//
geometry = new THREE.BufferGeometry().fromGeometry(new THREE.SphereGeometry(size / 2, 32, 16));
setupAttributes(geometry);
material = new THREE.ShaderMaterial({
uniforms: { lineWidth: { value: 1 } },
vertexShader: document.getElementById("vertexShader").textContent,
fragmentShader: document.getElementById("fragmentShader").textContent
});
material.extensions.derivatives = true;
mesh = new THREE.Mesh(geometry, material);
mesh.position.x = -150;
scene.add(mesh);
jsFiddle
As you can see in the fiddle, this is not what I'm looking for, but I don't have a good enough grasp on how the shaders work to know where I'm going wrong or if this approach would work for what I want.
I've looked into this answer but I'm not sure how to use it as a ShaderMaterial and I can't use it as a shader pass (here are the shaders he uses for his answer.)
I've also looked into THREE.MeshLine and this issue doesn't seem to have been resolved.
Any guidance would be greatly appreciated!
You want to modify this three.js example so the mesh is rendered as a thick wireframe.
The solution is to modify the shader and discard fragments in the center portion of each face -- that is, discard fragments not close to an edge.
You can do that like so:
void main() {
float factor = edgeFactorTri();
if ( factor > 0.8 ) discard; // cutoff value is somewhat arbitrary
gl_FragColor.rgb = mix( vec3( 1.0 ), vec3( 0.2 ), factor );
gl_FragColor.a = 1.0;
}
You can also set material.side = THREE.DoubleSide if you want.
updated fiddle: https://jsfiddle.net/vy0we5wb/4.
three.js r.89

How to fit noise shader into my plane geometry

The following link is my pen:
https://codepen.io/johnhckuo/pen/RxrXxX
And here is my code in fragment shader:
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
vec3 worldtoEye = eye - worldPosition;
vec3 eyeDirection = normalize(worldtoEye);
vec3 pos = vec3(st.x*5.0, st.y*5.0, u_time*0.5);
color = vec3(fbm(pos) + 1.0);
//color = vec3(noise(pos)*0.5 + 1.0);
vec3 sunLight = vec3(1., 1., 1.);
color *= (diffuseLight(sunLight) + specularLight(eyeDirection));
vec3 oceanBlue = vec3(0.109, 0.419, 0.627);
gl_FragColor = vec4(oceanBlue * color, 1.0);
}
Here is the code of plane geometry:
var plane_geometry = new THREE.PlaneBufferGeometry( 2000, 2000, 32 );
var customMaterial = new THREE.ShaderMaterial(
{
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
}
);
customMaterial.side = THREE.DoubleSide;
var surface = new THREE.Mesh( plane_geometry, customMaterial );
surface.position.set(0,0,0);
scene.add( surface );
I've created a shader and tried to apply it onto my plane.
But whenever I zoom in/out, the shader since to be fixed to the screen and not zooming in/out correspondingly.
Any suggestions is appreciated !
Change this line:
vec2 st = gl_FragCoord.xy/u_resolution.xy;
to
vec2 st = uVu.xy* 2.0;
and fiddle with 2.0 (the resolution)
It's also easy to test out vert + frag shaders on ShaderFrog, as in: https://shaderfrog.com/app/view/1997

Three.js - Put two materials on the same object

I was wondering how to put two materials on the same object...
I have a shader material that I use for the colors, but I also want to have a basing lambert shading.
Here is the code :
var material1 = new THREE.ShaderMaterial( {
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
var material2 = new THREE.MeshLambertMaterial({
color: 0xffffff,
});
var materials = [material1, material2];
var group = new THREE.Object3D();
var plane = new THREE.Mesh(geometry, materials);
Can you help me ?
Here is a fiddle that shows how to incorporate the lambert chuck into your own shader.
Is this what you wanted?
It calculates in the vertex shader the same as the THREE.MeshLambertMaterial.
var myShader = {
uniforms : THREE.UniformsUtils.merge([
THREE.UniformsLib['lights'],
{
color : {type:"c", value:new THREE.Color(0x0000ff)}
}
]),
vertexShader : [
THREE.ShaderChunk[ "common" ],
'varying vec3 vLightFront;',
THREE.ShaderChunk[ "lights_lambert_pars_vertex" ],
'void main() {',
' vec3 transformedNormal = normalMatrix * normal;',
' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
THREE.ShaderChunk[ "lights_lambert_vertex" ],
' gl_Position = projectionMatrix * mvPosition;',
'}'
].join('\n'),
fragmentShader : [
'uniform vec3 color;',
'varying vec3 vLightFront;',
'void main() {',
' vec3 outgoingLight = color.rgb + vLightFront;',
' gl_FragColor = vec4(outgoingLight, 1.0);',
'}',
].join('\n'),
};
material = new THREE.ShaderMaterial({
uniforms : myShader.uniforms,
vertexShader : myShader.vertexShader,
fragmentShader : myShader.fragmentShader,
lights: true
});

Resources