Three.js outlines - javascript

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).

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.

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.

How do I change the basic parameters of a Geometry in Three.js (for example, radius, number of vertices, etc.)

I create a tetrahedron of radius 3
// create a tetrahedron
var tetGeometry = new THREE.TetrahedronGeometry(3);
var tetMaterial = new THREE.MeshLambertMaterial(
{color: 0x20f020, transparent:true, opacity:0.6});
tet = new THREE.Mesh(tetGeometry, tetMaterial);
tet.name='tet';
tet.castShadow = true;
Later, I want the tetrahedron to grow:
// change hedron
scene.getObjectByName('tet').radius = control.hedronRadius;
That doesn't work.
// change vertices
scene.getObjectByName('tet').detail = control.hedronVertices;
That doesn't work either.
scene.getObjectByName('tet').verticesNeedUpdate;
And this doesn't help.
So how do I change the radius of a tetrahedron (or any Geometry) and how do I change the vertices.
In the documentation I see references to:
Geometry
.dynamic
.morph
.verticesNeedUpdate
.scale
And also references to bones and skeletons and skinned meshes used to animate geometries.
How do I change these aspects of Geometries in general?
What's the most reasonable, suggested way then to grow the radius of a Tetrahedron, or change the number of vertices show it becomes a different number polyhedron?
To change geometry you need to use:
morphTargets: true
I've prepared an example using a tetrahedron as you mention in jsfiddle.
Use sliders to change geometry.
To make some custom vertices and "fill" them by faces, you need to understand a lot of things from math, like; point, vector, etc.
I've done 2 simple flat objects, triangle and square in jsfiddle.
I hope that you'll easy understand how it works in general.

Threejs - Draw a transparent texture with MeshDepthMaterial

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.

Double sided transparent shader looks buggy

I have made a little test that allows you to experiment with shaders in a 3D environment using three.js.
There's a sphere in the scene that shows the shader.
The demo shader I have created is a very simple shader that uses a 2D noise implementation. A big part of the sphere remains black, which I made transparent. I want the other side of the sphere to be visible too. So I have enabled transparency and set rendering side to double-sided.
material = new THREE.ShaderMaterial({
'uniforms': uniforms,
'fragmentShader': $('textarea#input-fragment').val(),
'vertexShader': $('textarea#input-vertex').val()
});
material.side = THREE.DoubleSide;
material.transparent = true;
On this example, the buggyness is easier to notice.
When the sphere is viewed from the top, you only see the shader from the outer side. When viewed from the side there seems to be a bit choppyness, and when viewed from the bottom it seems to be working.
These are the different angles (top - side - bottom):
Here's the important bit of my fragment shader:
void main() {
float r = cnoise(vNormal.yz * 2.0 + t);
float g = cnoise(vNormal.xz * -1.0 + t);
float b = cnoise(vNormal.xy * -2.0 + t);
// opacity ranges assumable from 0 - 3, which is OK
gl_FragColor = vec4(r, g, b, r + g + b);
}
So why am I seeing the choppy edges and why does the viewing angle matters?
There is nothing wrong with your shader. You can also see the effect if you set:
gl_FragColor = vec4( 1.0, 1.0, 1.0, 0.5 );
Self-transparency is tricky in three.js.
For performance reasons in WebGLRenderer, depth sorting works only between objects (based on their position), not within a single object.
The rendering order of the individual faces within an object cannot be controlled.
This is why from some viewing angles your scene looks better than from others.
One work-around is to explode the geometry into individual meshes of one face each.
Another work-around (your best bet, IMO) is to replace your transparent, double-sided sphere with two transparent spheres in the same location -- a front-sided one and a back-sided one.
three.js r.56
Very similar to what I ran into. The WHY to understand this is best explained on Three.js Transparency fundamentals.
Without more details on your code or goals, here is an alternate solution as of version r128. Just add one more line to your material:
material.depthTest: false,
in a nutshell, your shader is fine as #WestLangley mentioned, but during rendering transparency, the depth of pixels in relation to one another is taken into account as well - ending up in certain pixels not rendering. This is where your "buggy-ness" came from. Not really a bug, but the way your scene is rendered by default until told to do otherwise. There are a lot of *issues you can run into that compete with your expectations so I recommend reading up on the link I posted.
*One such issue: If there are other objects in your scene, then of course since you turned off depthTest you can get the incorrect object placement as an object that should be in the background can get rendered in the foreground.

Categories

Resources