Threejs - Draw a transparent texture with MeshDepthMaterial - javascript

So I'm trying to have depth of field effect on some pixelart I made.
For that I need a texture with the depth information. So I render my texture on a plane using MeshDepthMaterial, but all I get is a grey rectangle, it completly ignores the alpha data in the texture (which is only 1 or 0, nothing inbetween).
Of course I'm using
depthMaterial.transparent = true;
depthMaterial.alphaTest = 0.5;
depthMaterial.needsUpdate = true; //just to be sure
Just for your interest this is my pixelart and how it is rendered: http://imgur.com/a/TLQOe

MeshDepthMaterial does not read the surface texture at all.
Instead you would need to either override the value of the 'depth' ShaderChunk to include UV's and your alpha-test-able texture, or add an extra ShaderMaterial to do the work yourself. Depending on the overall needs of your application, one approach would be better than the other. If no non-textured objects need to cast shadows, the former would be the simplest to maintain. If not, you'll need to do more work on managing which rendertargets get what and how.

Related

THREE.js What does this vertices mean in the box geometry?

I have created a box geometry as below,
const hand1geo = new THREE.BoxGeometry(2, 0.01, 0.2);
const material_sidehand = new THREE.MeshBasicMaterial({ color: 0x3cc1b7 });
const sidehand = new THREE.Mesh(hand1geo, material_sidehand);
What I want to do is to extract vertices from this box and I use this,
this.sidehand.attributes.position.array
And what I got is as following,
The picture of result. I really don't understand why it just spanned 72 elements(24 vectors) with same value. Why there are 24 vectors here and where have them been defined? Because I wanna use raycaster to do the collision detection later on.
I tried to use this.sidehand.vertices but it doesn't work.
I tried to use this.sidehand.vertices but it doesn't work.
I don't know what references you used but Mesh never had a property called vertices. You probably refer to the former Geometry class which indeed had this property. However, this class has been deprecated and BufferGeometry is used now instead.
I really don't understand why it just spanned 72 elements(24 vectors) with same value.
The values are not identical. BoxGeometry defines all vertices of the box in local space in a flat array so the data can be directly used by the WebGL API (which is good for performance).
There are 24 vectors because the geometry defines for each side of the box four vertices. Each side is composed of two triangles. This is done so it's possible to generate proper normals and texture coordinates.
I suggest you reconsider to use raw geometry data for collision detection. You are going to achieve much better performance by working with bounding volumes instead.

How to swap low resolution with high resolution textures dynamically and improve texture loading in general

In my ThreeJS project I want to swap out one texture with another but as soon as I do this the UV's are completely broken. You can see how I do it in the code below:
var texture = new THREE.TextureLoader().load( "T_Test_1k_BaseColor.jpg" );
function loadDraco6141012( src ) {
var loader = new THREE.GLTFLoader().setPath('');
loader.setDRACOLoader( new THREE.DRACOLoader() );
loader.load( src, function( gltf ) {
gltf.scene.traverse( function ( child ) {
if ( child.isMesh ) {
child.material.envMap = envMap;
child.position.y -= 0.6
mesh = child;
// This needs to trigger only after the texture has fully loaded
setTimeout(function(){
mesh.material.map = texture;
}, 5000);
// either way the uv doesn't seem to be correct anymore, what happened..?
}
} );
scene.add( gltf.scene );
}
}
You can see the whole thing in action here www.dev.openstring-studios.com As you can see there are several things very wrong with this example.
As said before the loading time is still pretty slow, how could this be improved? Would using a database like MySQL improve performance?
Why are th UV's broken, this looks horrible, what could be the problem? And just to be clear the green texture map is the same as the blue one, they are only different in color.
Here's the ThreeJs Documentation / MeshStandardMaterial about how applying maps should work. I cannot explain why it doesn't work out here. Why are the UV's suddenly broken?
You shouldn't ask multiple questions in a single post, but I'll try:
You can improve loading times by drastically reducing the polygon count of your pot. Looking at the network tab in the dev tools, I noticed your .gltf is 3.67MB, which is unnecessarily large for a simple pot. You probably don't need this level of detail, you could remove 2/3rds the # of vertices, and your pot would still look good.
It also looks like you're exporting textures bundled in the GLTF, which is helping make your filesize that big. Maybe it's auto-exporting textures in really large sizes (4096x4096)? You should try exporting your textures separately, convert them to a compressed format (JPG), and make sure they're not unnecessarily large (1024x1024 could work). Then you can load them separately.
There is no way to load a texture in that way. You'd have to load them manually in incrementally larger sizes (256, 512, 1024, etc...). TextureLoader has a callback that lets you know when the texture has been loaded.
UVs aren't broken, you're just loading a second texture that doesn't follow the same layout as the first texture. Make sure this image https://www.dev.openstring-studios.com/T_Test_1k_BaseColor.jpg follows the same layout as your original (green) texture.
Lastly, is there a reason why you separated the pot into 5 different meshes? Whenever possible, you should try making it just one mesh to reduce the number of WebGL drawcalls and get a bit of a performance boost.

Wiew port for group of mesh in threejs

What is the best way/practice of creating viewport for group of meshes in three js?
In my case I have THREE.Group of a lot THREE.Mesh instances. My goal is to create viewport for this group, where meshes will be visible.
One solution that I see is to use local clipping planes. threejs example
But I'm concerned that I have to assign clipping planes for every THREE.Mesh material rather than set it once for THREE.Group.
Also I need to recalcutate clipping planes when I move or rotate THREE.Group.
You could look into the stencil buffer:
webgl.stencilFunc()
webgl.stencilOp()
With or without threejs, the principle is the same.
disable depth write
disable depth test
disable color write
enable stencil operation (write a value to stencil buffer)
draw an invisible shape that writes to the stencil buffer (you probably want a screen space quad)
enable 1,2,3
change stencil operation (only draw where stencil buffer is 1 for example)
draw your group
depending on when you're doing this, you could change the stencil op here
and then draw the rest of the scene where buffer is 0 (outside of that shape from 5)
Three.js does not have stencil abstractions unless they've been implemented recently. This means that there is no "magical" property of say transparent which manages a bunch of webgl state under the hood, you have to actually manage this yourself. This means that you have to get the webgl context and do webgl operations on it manually.
There are many ways to do this.
var myScreenSpaceQuad = new THREE.Mesh( new THREE.PlaneBufferGeometry(2,2,1,1), myQuadShaderMaterial )
var scene1 = new THREE.Scene()
var scene2 = new THREE.Scene()
var sceneMask = new THREE.Scene()
sceneMask.add(myScreenSpaceQuad)
//...
myRenderFunction(){
//gl stencil op
//...
myRenderer.render(myCamera, sceneMask)
//more stencil
//...
myRenderer.render(myCamera, scene1)
//some more stencil...
myRenderer.render(myCamera, scene2)
}
I'll try to write a working example. For screen space quad you can take a look at this.

Simulate an infinite number of objects

On this example we can move inside a field of spheres but into certain limits. I want to be able to move infinitely among them. How can I do that ?
The trick is to reuse the spheres that are behind the camera and put them in front of it. Look at how it is done in this example. Here the programmer knows that the user will continue in the same direction so he removes the trees that come at a certain position.
If you use something like the example you quoted, you cannot know which direction the user will take. And so, you can use the same trick, but have to code it an other way. The most obvious is to check the distances with all the spheres regularly, if the user moves. If one sphere is too far behind the camera, you mirror it so it faces the camera, behind the fog.
'Regularly' can mean two things depending on your real number of spheres in your scene :
If you have a small scene and few spheres you can check those distances in your render loop. Neither cheap nor useful, 60 per seconds, but that can be the first coding step
Then the best way would be to use a web worker : you send the positions of the camera and those of the spheres, you let the worker compute all the stuff in its thread, and send instructions back : 'move those spheres to those positions'. Every seconds is more reasonable in the threejs example, but up to you to decide that depending on your scene.
NOTE : if you have a lot of spheres, or any meshes you use instead, like more than 20-30, having a mesh for each of them will slower performances. With few trees on the examples i linked it is ok, but with more objects and/or a heavier scene,
think about merging them all in a single geometry. You can check which sphere is where by deducing from the vertices indices, or adding an attribute that defines each sphere.
this will also impact the worker delay : it will have more to compute so it will need more time.
NOTE 2 : Note 1 would of course delete the level of details that the example aims to illustrate :) (Unless you also implement your own while checking the distances of the spheres....)
If you want to have an illusion of infinite world then you could:
Break your world space into regions (for example cubes).
Detect which region you are currently in.
Make sure you have objects (spheres) in neighbour regions. If some of regions are empty - fix it.
Clear regions which are not needed anymore.
For this you might want to have some class like this:
Class Region {
bool isEmpty = true;
Vector3 center;
float radius; // or 'range'
Array<Sphere> = null; // storage of your objects
// constructors / destructor
generateObjects(params); // perlin noise might be helpful there
removeObjects();
}
and do something like this periodically:
void updateRegions() {
computeClosestGridCoord(myPosition); // which is center of your current region
lookForNeighbourRegions(regionsArray); // and add new Region if needed
deleteOldRegionsStuff(regionsArray);
}

Three.js outlines

Is it possible to have an black outline on my 3d models with three.js?
I would have graphics which looks like Borderlands 2. (toon shading + black outlines)
I'm sure I came in late. Let's hope this would solve someone's question later.
Here's the deal, you don't need to render everything twice, the overhead actually is not substantial, all you need to do is duplicate the mesh and set the duplicate mesh's material side to "backside". No double passes. You will be rendering two meshes instead, with most of the outline's geometry culled by WebGL's "backface culling".
Here's an example:
var scene = new THREE.Scene();
//Create main object
var mesh_geo = new THREE.BoxGeometry(1, 1, 1);
var mesh_mat = new THREE.MeshBasicMaterial({color : 0xff0000});
var mesh = new THREE.Mesh(mesh_geo, mesh_mat);
scene.add(mesh);
//Create outline object
var outline_geo = new THREE.BoxGeometry(1, 1, 1);
//Notice the second parameter of the material
var outline_mat = new THREE.MeshBasicMaterial({color : 0x00ff00, side: THREE.BackSide});
var outline = new THREE.Mesh(outline_geo, outline_mat);
//Scale the object up to have an outline (as discussed in previous answer)
outline.scale.multiplyScalar(1.5);
scene.add(outline);
For more details on backface culling, check out: http://en.wikipedia.org/wiki/Back-face_culling
The above approach works well if you want to add an outline to objects, without adding a toon shader, and thus losing "realism".
Toon shading by itself supports edge detection. They've developed the 'cel' shader in Borderlands to achieve this effect.
In cel shading devs can either use the object duplication method (done at the [low] pipeline level), or can use image processing filters for edge detection. This is the point at which performance tradeoff is compared between the two techniques.
More info on cel: http://en.wikipedia.org/wiki/Cel_shading
Cheers!
Yes it is possible but not in a simple out-of-the-box way. For toon shading there are even shaders included in /examples/js/ShaderToon.js
For the outlines I think the most commonly suggested method is to render in two passes. First pass renders the models in black, and slightly larger scale. Second pass is normal scale and with the toon shaders. This way you'll see the larger black models as an outline. It's not perfect but I don't think there's an easy way out. You might have more success searching for "three.js hidden line rendering", as, while different look, somewhat similar method is used to achieve that.
Its a old question but here is what i did.
I created a Outlined Cel-shader for my CG course. Unfortunately it takes 3 rendering passes. Im currently trying to figure out how to remove one pass.
Here's the idea:
1) Render NormalDepth image to texture.
In vertex shader you do what you normally do, position to screen space and normal to screen space.
In fragment shader you calculate the depth of the pixel and then create the normal color with the depth as the alpha value
float ndcDepth = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far) / (gl_DepthRange.far - gl_DepthRange.near);
float clipDepth = ndcDepth / gl_FragCoord.w;
2) Render the scene on to a texture with cel-shading. I changed the scene override material.
3)Make quad and render both textures on the quad and have a orto camera look at it. Cel-shaded texture is just renderd on quad but the normaldepth shaded on that you use some edge detection and then with that you know when the pixel needs to be black(edge).

Categories

Resources