Disabling WebGL gl.BLEND doesn't Work - three.js

I use THREE.js and enable alpha canvas: (because I need to build my WebGL on top of something else)
this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas, antialias: false, alpha: true });
I set the clear color like this:
this.renderer.setClearColor(0xffffff, 0.0);
In each frame:
_render () {
renderer.clear();
gl.disable(gl.BLEND);
// ... something else doesn't need to be blended, whose alpha value is not 1.0
}
I'm curious why something else still gets blended with the white background even if I disable gl.BLEND.

three.js controls the blending. When you call renderer.render it will set the blending calling gl.enable(gl.BLEND) for each material depending on whether or not that material needs blending.
On top of that, even with blending off you can draw with a non 1.0 alpha which will end up giving use a canvas that can see through to the background.

Related

Three js texture offset not updating

I am currently trying to create a mesh that is colored using a datatexture, my initial coloring shows up just fine, but now my next goal is to offset the texture along the y axis. very similar to this example.
http://math.hws.edu/graphicsbook/demos/c5/textures.html
How I create my texture / mesh:
this.colorTexture = new DataTexture(colors, this.frameWidth, frameCount, RGBFormat, FloatType, UVMapping, RepeatWrapping, RepeatWrapping);
const material = new MeshBasicMaterial({
side: FrontSide,
vertexColors: true,
wireframe: false,
map: this.colorTexture
});
this.mesh = new Mesh(geometry, material);
How I attempt to animate the texture using offset:
this.mesh.material.map.offset.y -= 0.001;
this.mesh.material.map.needsUpdate = true;
this.mesh.material.needsUpdate = true;
this.mesh.needsUpdate = true;
I have confirmed that the function I'm using to try to offset is being called during each animation frame, however the visualization itself is not animating or showing changes apart from the initial positioning of the colors I wrote to the texture.
Any help is greatly appreciated :)
The uv transformation matrix of a texture is updated automatically as long as Texture.matrixAutoUpdate is set to true (which is also the default value). You can simply modulate Texture.offset. There is no need to set any needsUpdate flags (Mesh.needsUpdate does not exist anyway).
It's best if you strictly stick to the code from the webgl_materials_texture_rotation example. If this code does not work, please demonstrate the issue with a live example.

Why transparency doesn't work on my mesh?

I have two meshes on my scene. One cylinder and one classic plane in the middle.
I applied a png texture on my cylinder so we can see through. It seems to work for the cylinder.
On this screenshot you'll easily see my issue : I don't understand why my image is not visible behind my cylinder.
The code I used for my cylinder :
myCylinderMesh.material.transparent = true;
myCylinderMesh.material.side = THREE.DoubleSide;
How can I manage to see the part of the image hidden behind the cylinder ?
EDIT 1 :
I added the code that #ScieCode sent me :
myCylinderMesh.material.alphaTest = 0.5;
Here's the result :
It works better : now I can see the part missing of my image. But there's one thing missing : the opacity of my cylinder. I'm supposed to see my image behind the letters too.
Currently I have this opacity :
myCylinderMesh.material.opacity = 0.7;
Do you know what I am missing ? Thanks
EDIT 2 :
Here's the code for my two meshes :
Cylinder :
geoCylinder = new THREE.CylinderBufferGeometry( 0.4, 0.4, 2*Math.PI*0.4/(2048/128), 64, 1, true );
matCylinder = new THREE.MeshBasicMaterial( { map:texture, transparent:true, color:0x000000, alphaTest: 0.5, opacity: 0.6, side: THREE.DoubleSide } );
meshCylinder = new THREE.Mesh( geoCylinder, matCylinder );
Plane :
geoPlane = new THREE.PlaneBufferGeometry( 0.8, 0.8 );
matPlane = new THREE.MeshBasicMaterial( { map: texturePlane, transparent:true} );
meshPlane = new THREE.Mesh( geoPlane, matPlane );
This behavior happens because of how transparency rendering works internally. Transparent objects need to be sorted/rendered separately from opaque objects. This assures that objects will render as expected on the final image. (not always, though)
The problem here is that your plane geometry is inside the cylinder geometry, when sorting it will either be rendered first or later. Which, in turn, causes these artifacts you are experiencing here. The whole transparency render is a lot more complex than what I'm making it to be.
Since your plane object doesn't need to be translucent, you can simply set the alphaTest property of its material. Which will only render the fragment pixels with alpha greater than this value. This will also prevent that object from being considered transparent and it will always be rendered first, fixing the artifacts in your scene.
JSFiddle
Additional info: When using a transparent material with DoubleSide, you might experience self transparency problems. This happens for the same reason I just explained, but between faces of the same object. A possible solution for this problem is to set depthWrite = false, this prevent the object from writing to the depth buffer. So every face will get rendered, disregarding if another face occludes it.

Three.js webgl renderer for high DPI displays bug?

I have the following logic to create a Three.js R69 WebGL renderer that is supposed to handle high DPI displays. It did for quite a while, but about a week ago one and only one three.js page started rendering like the high DPI was correctly set, but my 3D coordinate origin became the upper left corner of the rendering canvas rather than the expected center of the canvas. (No changes to my environment that I can tell, maybe the browsers auto-updated. I'm testing with Chrome, FF and Safari on OSX 10.10.1)
// create our renderer:
gCex3.renderer = new THREE.WebGLRenderer({ antialias:true, alpha:true,
devicePixelRatio: window.devicePixelRatio || 1
});
// Three.js R69: I started needing to explicitly set this so clear alpha is 1:
gCex3.renderer.setClearColor( new THREE.Color( 0x000000 ), 1 );
gCex3.rendererDOM = $('#A3DH_Three_wrapper');
gCex3.rendererDOM.append( gCex3.renderer.domElement );
// fbWidth & fbHeight are w,h of a div located within the page:
gCex3.renderer.setSize( gs.fbWidth, gs.fbHeight, true ); // 'true' means update the canvas style
Checking the latest R69 examples, they don't seem to do anything special for high dpi displays. Checking the WebGLRenderer source, it looks like the devicePixelRatio logic is now embedded within the WebGLRenderer() function.
I've tried that minimal logic in the examples, specifically this:
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( new THREE.Color( 0x000000 ), 1 );
renderer.setSize( gs.fbWidth, gs.fbHeight, true );
And I see the same behavior: my coordinate system origin is the upper left of the rendering canvas.
Note that in Chrome, when running the javascript debugger, I see a "webgl context lost" event during the exiting of the previous page, but before this logic is being executed. Could the WebGLRenderer be getting created during the period when there is no WebGL context?

Threejs - Applying simple texture on a shader material

Using Threejs (67) with a Webgl renderer, I can't seem to get a plane with a shader material to wear its texture. No matter what I do the material would just stay black.
My code at the moment looks quite basic :
var grassT = new Three.Texture(grass); // grass is an already loaded image.
grassT.wrapS = grassT.wrapT = Three.ClampToEdgeWrapping;
grassT.flipY = false;
grassT.minFilter = Three.NearestFilter;
grassT.magFilter = Three.NearestFilter;
grassT.needsUpdate = true;
var terrainUniforms = {
grassTexture : { type: "t", value: grassT},
}
Then I just have this revelant part in the vertexShader :
vUv = uv;
And on the fragmentShader side :
gl_FragColor = texture2D(grassTexture, vUv);
This results in :
Black material.
No error in console.
gl_FragColor value is always (0.0, 0.0, 0.0, 1.0).
What I tryed / checked:
Everything works fine if I just apply custom plain colors.
All is ok if I use vertexColors with plain colors too.
My texture width / height is indeed a power of 2.
The image is on the same server than the code.
Tested others images with same result.
The image is actually loading in the browser debugger.
UVS for the mesh are corrects.
Played around with wrapT, wrapS, minFilter, magFilter
Adapted the mesh size so the texture has a 1:1 ratio.
Preloaded the image with requirejs image plugin and created the texture from THREE.Texture() instead of using THREE.ImageUtils();
Played around with needsUpdate : true;
Tryed to add defines['USE_MAP'] during material instanciation.
Tryed to add material.dynamic = true.
I have a correct rendering loop (interraction with terrain is working).
What I still wonder :
It's a multiplayer game using a custom port with express + socket.io. Am I hit by any Webgl security policy ?
I have no lights logic at the moment, is that a problem ?
Maybe the shader material needs other "defines" at instanciation ?
I guess I'm overlooking something simpler, this is why I'm asking...
Thanks.
I am applying various effects on the same shader. I have a custom API that merge all different effects uniforms simply by using Three.UniformsUtils.merge() However this function is calling the clone() method on the texture and this is causing to reset needsUpdate to false before the texture reach the renderer.
It appears that you should set your texture needsUpdate property to true when reaching the material level. On the texture level, if the uniform you set get merged, and therefore cloned, later in the process, it'll lose its needsUpdate property.
The issue is also detailled here: https://github.com/mrdoob/three.js/issues/3393
In my case the following wasn't working (grassT is my texture):
grassT.needsUpdate = true
while the following is running perfectly later on in the code:
material.uniforms.grassTexture.value.needsUpdate = true;
Image loading is asynchronous. Most likely, you are rendering your scene before the texture image loads.
You must set the texture.needsUpdate flag to true after the image loads. three.js has a utility that will do that for you:
var texture = THREE.ImageUtils.loadTexture( "texture.jpg" );
Once rendered, the renderer sets the texture.needsUpdate flag back to false.
three.js r.68

Displaying background colour through transparent PNG on material?

I'm making a case builder using THREE.js, the basics are i want to be able to change the height/width/length of a box, rotate it around, and also change the background color of the box.
This is it so far:
http://design365hosting.co.uk/casebuilder3D/
The dimension changing works, as does the dragging of the box, now i'm working with the background color change.
The way i want this to work is by using transparent PNGs as the faces of the box, and setting background colors so that this background colour shows through the transparent PNG.
This is how I'm currently doing it:
var texture = THREE.ImageUtils.loadTexture("images/crate.png");
materials.push(new THREE.MeshBasicMaterial({color:0xFF0000, map: texture}));
as you can see I set the material to have a background colour of red and overlay the transparent PNG, problem is, three.js seems to ignore the background colour and just show the transparent PNG, meaning no colour shows through.
The expected result should be a red box with the overlayed PNG.
Hope that made sense, can anyone help?
You are trying to apply your transparent texture as a decal.
The three.js built-in materials do not support what you are trying to do. If the PNG is partially transparent, then the material will be partially transparent.
You would like the material to remain opaque, and the texture to be applied as a decal.
You can do this by modifying the material's shader. For example,
var material = new THREE.MeshPhongMaterial( {
color: 0x0080ff,
map: texture
} );
material.onBeforeCompile = function ( shader ) {
const custom_map_fragment = THREE.ShaderChunk.map_fragment.replace(
`diffuseColor *= sampledDiffuseColor;`,
`diffuseColor = vec4( mix( diffuse, sampledDiffuseColor.rgb, sampledDiffuseColor.a ), opacity );`
);
shader.fragmentShader = shader.fragmentShader.replace( '#include <map_fragment>', custom_map_fragment );
};
three.js r.147

Resources