I'm trying to use a Shader material to create an area of transparency around my camera.
To achieve that, I'm checking if a vertex is inside a given radius, if so, I set its color with a custom opacity value < 1, such as 0.4.
This is working fine, but sometimes some transparent geometries are blocking the elements behind it. I took a look into the docs and some other similar question and figured out that usually we set the depthWrite = false and transparent = true only to transparent materials.
My problem is that I have only one material representing all my geometries but I want to set depthWrite = false; transparent = true to all vertices inside the radius and depthWrite = true; transparent = false to the ones outside it. Does anyone knows if this is something achievable or if there is another possible solution?
Thanks in advance
answering my own question! Hope this can help someone
It looks like it was not possible the way I was doing because I had only one material for the visible and transparent part.
Then, I decided to have 2 different shader materials rendering the same geometries, the visible and transparent ones, to set the depthWrite and transparent properties accordingly.
Also I discarded, using the fragment shader, the visible portion inside my transparent area and discarded the invisible one in the visible area.
It looks like it's working fine now
Related
I have been experimenting with converting the custom attribute BufferGeometry example (https://threejs.org/examples/webgl_buffergeometry_custom_attributes_particles.html) into a fly through animation and I find that the sprites have dark backgrounds (and those that I create myself as well) if depthTest is set to true. See the image.
The sprite in the custom attribute example has a transparent background but this appears to be ignored when it is rendered if depthTest is set to true.
I have tried numerous custom blending rules but cannot find a way to remove the background, only to reduce the effect a bit. The background disappears if depthTest is set to false.
Is this a known limitation? Is there a work around?
I am modifying this question to add clearer images with a different ball sprite (also with a transparent background). This image has depthTest set to true for the custom ShaderMaterial used in the https://threejs.org/examples/webgl_buffergeometry_custom_attributes_particles.html three.js example.
By comparison, this uses multiple PointsMaterials from a different three.js example (https://threejs.org/examples/webgl_points_sprites.html), also with depthTest set to true and using the PointsMaterial map parameter for the sprite.
As you can see, the second PointsMaterial example works as expected. Because PointsMaterial only accepts one fixed size and color, I need to create 36 different point geometries to render this image.
I would prefer to use the custom shader in the first example (which has custom size and color attributes and requires only one geometry). Is there a way to define a custom shader to support depthTest like the PointsMaterial does?
WebGL has a depth buffer that's used to find out which pixels are hiding behind other pixels. If a pixel is hiding behind another, it doesn't get rendered. Typically that's what you would want, why would you waste GPU time rendering pixels you're never going to see? Consider the example depthbuffer below, white squares are closer to the camera, while darker squares are further away:
If there was a square hiding behind another one, you wouldn't see it in the depthBuffer, you'd only see the closest one. The depthBuffer doesn't know that your squares have partial transparency and you want some parts to be see-through. This is an issue in other realtime engines too, this is an example from Unity:
The squares that are closer are blocking part of the squares that are further away in the depthBuffer, so the occluded pixels don't get rendered. That's why setting depthTest to false is useful. You're basically telling the renderer to stop testing which pixels are behind others, and just render all of them. Here it is again with depthTest = false:
If you must set depthTest on, (for instance, if you want the sprites to hide behind a solid wall) you could set depthWrite to false on the sprite material. It would still check which pixels are behind others, but the pixels from the sprites wouldn't get written to the depthBuffer, so no sprite could block another sprite.
depthTest: true; // Tests pixel depth
depthTest: false; // Does not test pixel depth
depthWrite: true; // Writes pixel depth to depthBuffer
depthWrite: false; // Does not write pixel depth to depthBuffer
As far as blending mode, I prefer THREE.AdditiveBlending because it gives the particles a nice glowing effect when they accumulate one on top of the other. But that's up to you.
Transparency artefact problem
Hello,
I have an issue with three.js. I import a "big" glb model on my scene which is not transparent, but if the model is covered by itself on the camera view, the foreground become transparent. (as you can see on the picture, the background montain is on foreground)
I tried some solutions like :
depthTest to false on glb material
sortOrder to false
Use logarithmicDepthBuffer
Change material transparent to false
Change alphaTest from 0 to 1 by 0.1 steps
But nothing works. If someone have the solution :)
Thank you !
Rendering of transparent objects cannot be done quite properly. You first need to render any non-transparent objects, and then render transparent surfaces from back to front, so that any new ones blend on top of what was behind it. There are a number of cases where this cannot be done, especially when rendering transparent objects that may overlap themselves.
Fixing this would involve cutting the problematic objects (even single triangles) into smaller pieces so that the ordering can be preserved, and that is often nearly impossible. Since you are working with Three.js, see if you could alter your design so that this isn't a problem, or that the artifacts of incorrect rendering order aren't too much noticeable.
Thanks to donmccurdy which have find my solution on the three.js forum.
Finally my glb file was Transparent :( So there is two solutions.
Solution 1:
Find how the model is transparent and fix it.
Solution 2:
Changing it back to opaque, and restoring the default depthWrite value.
mesh = content.getObjectByName('mesh_0');
mesh.material.transparent = false;
mesh.material.depthWrite = true;
I am struggling with the common transparency sorting issue. I know there are ways around it (like manually sorting the objects or order-independent transparency) but all that can become quite fiddly. I'd be ok if there was a way to have objects that are partly opaque and partly 100% transparent and have them intersect correctly.
In theory this should be possible. Opaque pixels would be rendered to color buffer and z buffer in the standard way and transparent pixels are just left out.
What I'm looking for is something like indexed transparency as it was used with gif files, for instance that all pixels of an object that have the color #FF00FF are not rendered.
I just don't know if and how this would be possible using three.js. Also, I want to be able to use it with custom shaders.
EDIT: Thanks for your comments so far and sorry for the confusion. This is more of a conceptional thing than a specific problem with my code. It's just that I am often faced with the issue that parts of transparent objects cut out parts of other transparent objects which should be in front of them. Also, transparent objects do not intersect correctly, it's always that one covers another. I understand why this happens and that it is a problem which is inherent to the way transparency is treated. But often I only need parts of an object completely transparent, no partial-shine-through-alpha transparency. Which could be possible if there was a way to leave out certain pixels of objects and render the rest like a normal opaque object.
Let's assume I want to have a metal chain and each segment is a PlaneGeometry thing with a texture that shows the shape of an O (and the rest transparent). Now the chain should be shown with correct interlinkage so to say.
Any help welcome!
Cheers!
If you are rendering a three.js scene, and your texture maps contain no partially-transparent pixels -- that is, each pixel is either 100% opaque or 100% transparent, then you can achieve a proper rendering by setting
material.alphaTest = 0.5;
//material.transparent = true; // likely not needed
The same holds true if you are using a binary alpha-map.
If you are writing a custom shader, then you can achieve the same effect by using a pattern like the following in your fragment shader:
if ( texelColor.a < 0.5 ) discard;
three.js r.84
i am trying to cast an shadow on an totally transparent plane in SceneKit on OSX. I am struggling with this problem since several hours and do not come to any solution.
My Purpose is to generate an Screenshot of several objects with an transparent background and just the shadow on an invisible Plane.
Do you have any suggestions for me how i can make this with apples SceneKit?
Do i have to program my own shader, can i make this work with shadermodifiers or can i use built in functionallity?
UPDATE:
I find an alternative solution for anyone who needs:
create a white plane under 3D model, note that the color of plane must be pure white.
set blend mode of plane's material to SCNBlendModeMultiply.
set light model of plane's material to SCNLightingModelLambert.
This works because any color multiply white color (1, ,1, 1) return itself And lambert light model will not take account of directional light, So the plane will always be background color which look like transparent. Another benefit of this solution is you don't need change light‘s shadow rendering mode.
For people who used to inspector of Xcode.
According to SceneKit: What's New.
First, add a plane under you model. Then prevent it from writing to colorBuffer.
Second, change your light model's shadow rendering mode to deferred. Notice that you must use light which can cast shadows.
Oily Guo, your solution works. Here the solution is in code:
Configuration of the light source:
light.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
light.shadowMode = .deferred
And for the floor (ie. SCNFloor underneath your objects):
material.diffuse.contents = UIColor.white
material.colorBufferWriteMask = SCNColorMask(rawValue: 0)
I do not have an answer to your question, however I have a workaround:
Render your scene and keep the image in memory
Change all the materials in your object for pure black, no specular
Change the plane and the sky to a fully white material, lights to white
Render the scene to another image
On the second image, apply the CIColorInvertand CIMaskToAlpha Core Image filters
Using Core Image apply the Alpha Mask to the first render.
You'll get an image with a correct Alpha channel, and transparent shadows. You will need to tweak the materials and lights to get the results you want.
The shadow may become lighter on the edges, and the only way around that is rendering it as yet another image, and filling it with black after the Mask to Alpha step.
Is it possible in three.js to create transparent/invisible plane, which can receive shadows? My aim is to draw some object on 3d canvas without background (so I can see webpage behind the canvas element). I want the object to cast shadows on the background and I thought, if I could make an invisble plane, then webpage background is still visible. Shadows are casted on the plane and it seems like shadows are on the webpage.
Right now I can make a white plane with opacity 0.5 (or similar), but this way the plane is visible. Ideally the plane should be completely invisble (except for shadows).
EDIT: I created an exampled (based on this): http://jsfiddle.net/s5YGu/2/
Here I have opacity 0.5, but you can see the plane. If I set opacity to 0, then the whole plane and the shadows disappear.
I made this work by hacking on basic shader, here is working shader code http://pastie.org/5088640
It has no drawbacks AFAIK, like opacity: 0.1
Yes, you can achieve the effect you want, and it looks pretty good on my machine at least. :-)
Here is a fiddle: http://jsfiddle.net/ZXdV3/25/
There are a couple of issues. First, you will have to set alpha: true in the renderer constructor args.
Second, although I assume you'd like the plane to be completely transparent, you'll have to settle for material.opacity = 0.1, or so.
Third, if you are placing a canvas over the webpage, and you want the web page to be interactive, you are going to have to suppress pointer events in the canvas (I'm not sure if IE supports this, though): container.style.pointerEvents = 'none';
three.js r.67