GLSL: ShaderMaterial with custom shader is not transparent - three.js

I try to draw a texture onto a sphere like this:
script(type='x-shader/x-vertex')#Vertex
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
script(type='x-shader/x-fragment')#Fragment
uniform sampler2D baseTexture;
varying vec2 vUv;
void main() {
vec4 baseColor = texture2D( baseTexture, vUv );
gl_FragColor = baseColor;
}
this.materials = new THREE.ShaderMaterial( {
uniforms: this.uniforms,
vertexShader: document.getElementById( 'Vertex' ).textContent,
fragmentShader: document.getElementById( 'Fragment' ).textContent,
transparent: true,
blending: THREE.AdditiveBlending
});
This does work fine, but the texture is not transparent, even if I change the alpha value. Transparent pixels from my texture are just black.
But if I write baseColor.a = 0.0, I cannot see the texture anymore, but also not what lies behind it in the scene. I think I'm missing mixing the texture with the background somehow?
How can I achieve this with GLSL in three.js?
Thanks

I have no idea how THREE.js works under hood but I see you set blending to be additive.That's not what you want for alpha blending.Alpha blending uses
this function :
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
while additive uses:
glBlendFunc(GL_ONE, GL_ONE);
So make sure you use the first one and that your texture has in fact alpha channel as A component of RGBA.

Related

Changing color of occluded part of mesh within shader

I am working with THREE.JS and want to be able to have a mesh that changes the occluded part of itself into a different color.
Simple Example
The above image is a simple example, where the wall is in front of the Mesh and obstructing part of the Mesh but not all of it. The visible part of the mesh should be colored green whilst the occluded part should be colored red. Note that the wall is not transparent; the occluded part of the mesh should still be rendered using depthTest = false.
I've tried messing around with some basic shaders, but I don't really how to get started. Currently, the core parts of my code looks like this:
// My cube's material
const overlayMat = new THREE.ShaderMaterial({
uniforms: {
"maskTexture": { value: null },
},
vertexShader:
`varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`,
fragmentShader:
`varying vec2 vUv;
uniform sampler2D maskTexture;
void main() {
vec4 maskColor = texture2D(maskTexture, vUv);
float visibilityFactor = 1.0 - maskColor.g > 0.0 ? 1.0 : 0.5;
// Attempt to set the green value to the visibility factor
gl_FragColor = vec4(1.0, visibilityFactor, 0.0, 1.0);
}`,
depthTest: false,
depthWrite: false,
transparent: true
});
// My mask
let renderTargetMaskBuffer = new THREE.WebGLRenderTarget(innerWidth, innerHeight, {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat
});
// Inside my animate function:
function animate() {
// ...
overlayMat.uniforms["maskTexture"].value = renderTargetMaskBuffer.depthTexture;
renderer.render(scene, camera);
}
Which does not work, the cube remains one constant color.
Full code (with the "wall" that occludes part of the mesh) (JSFiddle)

Three.js ShaderMaterial artifacts

I tried a very simple test using Three.js ShaderMaterial.
I load a 2048x2048 jpg image as a texture for my height map and apply it to deform a PlaneBufferGeometry in the vertex shader.
I also apply the same texture for the diffuse color in the fragment shader.
Globally it works but I see some big artifacts as shown in this screenshot
The artifact always appears along a line parallel to the X axis and passing through the camera.
I have the problem on all three.js version I tried (r105, r114)
The code is yet very simple, anyone know what am I doing wrong ?
Javascript
var textureLoader = new THREE.TextureLoader();
var testTextureBump = textureLoader.load( './front_b.jpg' );
var testGeometry = new THREE.PlaneBufferGeometry(3000, 3000, 500, 500);
var testUniforms = {
uTextureBump: { value: testTextureBump }
};
var testMaterial = new THREE.ShaderMaterial({
uniforms: testUniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
side: THREE.FrontSide,
blending: THREE.NormalBlending,
depthWrite: false,
wireframe: false,
transparent: true
});
var testMesh = new THREE.Mesh( testGeometry, testMaterial );
scene.add( testMesh );
Vertex shader
uniform sampler2D uTextureBump;
varying vec2 vUv;
void main() {
vUv = uv;
vec4 diffuseTexture = texture2D(uTextureBump, uv);
vec3 positionHeight = position.xyz;
positionHeight.z += diffuseTexture.r * 20.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(positionHeight, 1.0);
}
Fragment shader
precision highp float;
precision highp int;
uniform sampler2D uTextureBump;
varying vec2 vUv;
void main (void) {
vec4 texture = texture2D(uTextureBump, vUv);
gl_FragColor = vec4( texture.rgb, 1.0 );
}
You can see the problem in this demo
Move your mouse on the left or right and you'll see the artifacts.
You can fly around as I use the standard THREE.FlyControl service.
The corresponding project file can be download here

Setting texture to geometry in Three.js

I have this geometry: Picture
I want to add the same effect that mountains are with snow texture and so on:
Texture splatting with Three.js
Little background what info I give to shaders from Three.js:
//importing grass and snow textures:
var grassTexture = new THREE.ImageUtils.loadTexture( 'images/grass-512.jpg' );
grassTexture.wrapS = grassTexture.wrapT = THREE.RepeatWrapping;
var snowTexture = new THREE.ImageUtils.loadTexture( 'images/snow-512.jpg' );
snowTExture.wrapS = snowTExture.wrapT = THREE.RepeatWrapping;
this.customUniforms = {
grassTexture: { value: grassTexture },
snowTexture: { value: snowTexture },
};
var customMaterial = new THREE.ShaderMaterial({
uniforms: customUniforms,
side: THREE.DoubleSide,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
});
//creating mesh, geometry is the model in picture.
mesh = new THREE.Mesh(geometry, customMaterial);
Vertex and fragment shaders:
//vertexShader:
varying vec2 vUV;
void main(){
vUV = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
I get full red model with this:
//fragmentShader:
void main(){
gl_FragColor = vec4(1, 0.0, 0.0, 1.0) ;
}
I want textures that are higher with snowTexture and lower with grassTexture.
uniform sampler2D grassTexture;
uniform sampler2D snowTexture;
varying vec2 vUV;
//Something like this?:
vec4 grass = texture2D( grassTexture, vUV);
vec4 snow = texture2D( snowTexture, vUV);
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0) + grass + snow;
This really not that hard to understand, let me walk you through the logic.
In your case, you don't want to use a displacement map. So, you need to set up a varying height on your vertexShader to map your vertices up-coordinates [0,1] to your fragmentShader.
//vertexShader:
varying vec2 vUV;
varying float height;
void main() {
vUV = uv;
float maxPosition = 30.0; // this is an example value.
height = max( 0.0, min(1.0, position.y/maxPosition ) ); // assuming +y is up
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
Now you can access height from your fragmentShader and use that information to select where you want your transitions to occur.
uniform sampler2D grassTexture;
uniform sampler2D snowTexture;
varying vec2 vUV;
varying float height;
void main(){
vec4 grass = (1.0 - smoothstep( 0.48, 0.52, height)) * texture2D( grassTexture, vUV);
vec4 snow = (smoothstep(0.48, 0.52, height) - 0.0) * texture2D( snowTexture, vUV);
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0) + grass + snow;
}
The link provided uses function smoothstep to make a gradual transition between the textures. We can create transitions using the follow pattern ( a - b ) * textureColor.
In this case, a controls when the texture starts to contribute to the fragment color.
b controls when the texture stops contributing.
In other words, your grass texture will have already started contributing at every height, so we map a to 1.0. It stops contributing around 0.5, so we give b a smooth fade-out as it approaches that 0.5.
Your snow texture, on the other hand, will only start contributing around 0.5. So, we give a a smooth fade-in as it approaches 0.5. It will never stop contributing, so we set b as 0.0.
Hope this clears things up for you.

Using FBO particles with a first person camera in Three.js

I am attempting to make a first person experiment incorporating FBO particles (similar to Mr. Doob's sporel.
I create the position of my particles in a data texture, and then in my vertex shader I do a lookup on the texture to get the position of that particle.
uniform sampler2D map;
varying vec2 vUv;
varying vec3 vPosition;
void main(void) {
vec2 uv = position.xy;
vec4 data = texture2D(map, uv);
vPosition = data.xyz;
gl_PointSize = 1.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1.0);
This is working great, and initially I see a field of particles as shown here...
The problem is when I walk a bit and rotate, all of the particles suddenly disappear. I did a bunch of searching around for a solution, and found this thread which suggested setting material.depthWrite to false. I did this, to no avail- here is my ShaderMaterial definition:
var particleMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: shaders.vertexShaders.floor,
fragmentShader: shaders.fragmentShaders.floor,
blending: THREE.AdditiveBlending,
depthWrite: false,
})
I am at a loss right now and any help would be greatly appreciated!
Thanks! Eric

Can't change opacity anymore when upgrading from Three.js r52 to r55

Basically I'm upgrading my app, from r52 to r55. This app use animations (Tweens) for updating lines, but also a ParticleSystem. Everything worked just fine in r52, scaling, moving and changing opacity.
I used these WebGLRenderer constructor settings:
clearColor: 0x1f1f1f
clearAlpha: 1
antialias: true
sortObjects: false
And a simple shader I took from the examples:
<script type="x-shader/x-vertex" id="vertexshader">
attribute float size;
attribute vec3 customColor;
attribute float customOpacity;
varying vec3 vColor;
varying float vOpacity;
void main() {
vColor = customColor;
vOpacity = customOpacity;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = size * ( 300.0 / length( mvPosition.xyz ) );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
uniform vec3 color;
uniform sampler2D texture;
varying vec3 vColor;
varying float vOpacity;
void main() {
gl_FragColor = vec4( color * vColor, vOpacity );
gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );
}
</script>
I initialized the particle ShaderMaterial using:
blending : THREE.AdditiveBlending
depthTest : false
transparent : false
and the ParticleSystem by manually setting:
system.sortParticles = true
system.matrixAutoUpdate = true
system.visible = true
system.dynamic = true
So here how it renders in Three.js r52:
Now I've read the Migration wiki page, and concluded I only had to change a few names, nothing in the WebGLRenderer constructor, materials or shaders attributes.
I've upgraded to r55 and now visuals are broken:
Lines and particles are now all bright (opacity not taken in account).
Moreover for particles now the alpha mask is broken (if you watch carefully the color is different, and there is a "square cut" when overlapping with other particles, something I had in r52 and fixed by simply tuning the WebGLRender settings)
What could have changed? I tried to change settings in the WebGL constructor, alphas, background colors.. but it didn't help.
Likely, you need to set your shader material to transparent:
material.transparent = true;
three.js r.55

Resources