SceneKit Deferred Shadows On Multiple Transparent Planes - ios

The Issue
I've set up a minimal SceneKit project with a scene that contains the default airplane with a transparent plane that acts as a shadow receiver. I've duplicated this setup so there are two airplanes and two transparent shadow planes.
There is a directional light that cast shadows and has its shadowMode property set to .deferred. When the two shadow planes overlap, the plane that is closer to the camera 'cuts out' the shadow on the plane that is further away from the camera.
I know this is due to the fact that the plane's material has its .writesToDepthBuffer property set to true. However, without this the deferred shadows don't work.
The Question
Is there a way to show shadows on multiple overlapping planes? I know I can use SCNFloor to show multiple shadows but I specifically want shadows on multiple planes with a different Y position. Think of a scenario in ARKit where multiple planes are detected.
The Code
I've set up a minimal project on GitHub here.

Making both Y values of shadow planes closer enough will solve the cutoff issue.

In SceneKit it's a regular behaviour of two different planes that have a shadow projections. For getting a robust shadows use just one 3d object (plane or custom-shape geometry if you need different floor levels) as a shadow catcher.
If you have several 3D objects with Writes depth option turned On use Rendering order properties for each object. Nodes with greater rendering orders are rendered last. Default value of Rendering order is zero.
For instance:
geoNodeOne.renderingOrder = -1 /* Rendered first */
geoNodeTwo.renderingOrder = 50 /* Rendered last */
But in your case Rendering order property is useless because one shadow-projected plane blocks the other one.
To model a custom-shape geometry use Extrude Tool in 3D modelling app (like Maya or 3dsMax):

Related

How to exclude model's shadows on itself?

I have a SCNLight with type SCNLightTypeDirectional. When scene rendered, model casts shadows on itself and It wasn't I expectation. How to exclude model's shadows on itself?
Or how to smooth the shadows edge? It looks very unnatural now.
There is the scenario :
Well, I find a simple way to achieve this but loss some material details.
Change the light model of material to SCNLightingModelConstant and exclude model from lighting calculation of your SCNLight.
1. set light model
SCNLightingModelConstant only consider ambient light to shading, so We need ambient lights to keep model visible.
model.geometry.materials.firstObject.lightingModelName = SCNLightingModelConstant;
2. set category bit mask of model and lights
model.categoryBitMask = 1;
directionalLight.categoryBitMask = ~1UL;
If results of bitwise AND of categoryBitMask is zero, node will not take consideration into light illumination, so there no self-shadows anymore. Shadows model casted will still remain in scene.

What is the meaning of "nonuniformly scaled texture" in SpriteKit?

On SKCropNode class reference, some examples to specify a mask are given.
Here they are:
This means a crop node can use simple masks derived from a piece of artwork, but it can also use more sophisticated masks. For example, here are some ways you might specify a mask:
An untextured sprite that limits content to a rectangular portion of the scene.
A textured sprite is a precise per-pixel mask. But consider also the benefits of a nonuniformly scaled texture. You could use a nonuniformly scaled texture to create a mask for a resizable user-interface element (such as a health bar) and then fill the masked area with dynamic content.
A collection of nodes can dynamically generate a complex mask that changes each time the frame is rendered.
The second example introduce nonuniformly scaled texture: what's the meaning of this?
This does not help me to understand this second example!
A non-uniformly scaled texture is a texture that is applied to a sprite with xScale != yScale.

Blending and Depth Test

My application shows big amount of objects in orthographic projection.
Each object is combined from few sprites on the same vertex with different textures.
For simplify:
Each object has 2 sprites: filled rectangle and outline rectangle
I've have one mesh (vertices and attributes) for each sprite type - one for all the objects fills and one for all the objects outlines. Total 2 meshes.
I'm drawing my objects (Sprites) in 2 phases - The fills mesh (which contains all fills sprites), and outlines mesh (which contains all outlines sprites)
For each object sprites - (in all the meshes) - i'm giving the same z-value so it will be drawn correctly.
Using depthFunc(LEQUAL) for drawing when same z value
as shown below:
The problem is when i want to change transparency for all objects. The depth is blocking from drawing some objects so i can't see the object behind. If i'm disabling the depth test suddenly all the outlines will jump to the front (looks much worse in the real application when the object is combined with much more than 2 sprites).
Sorting the objects will not work here because i'm drawing each mesh (fills and outlines) one after each other.
Is there any trick to do to make it happened ?

Drawing a concave polygon in OpenGL

I have a concave polygon I need to draw in OpenGL.
The polygon is defined as a list of points which form its exterior ring, and a list of lists-of-points that define its interior rings (exclusion zones).
I can already deal with the exclusion zones, so a solution for how to draw a polygon without interior rings will be good too.
A solution with Boost.Geometry will be good, as I already use it heavily in my application.
I need this to work on the iPhone, namely OpenGL ES (the older version with fixed pipeline).
How can I do that?
Try OpenGL's tessellation facilities. You can use it to convert a complex polygon into a set of triangles, which you can render directly.
EDIT (in response to comment): OpenGL ES doesn't support tessellation functions. In this case, and if the polygon is static data, you could generate the tessellation offline using OpenGL on your desktop or notebook computer.
If the shape is dynamic, then you are out of luck with OpenGL ES. However, there are numerous libraries (e.g., CGAL) that will perform the same function.
It's a bit complicated, and resource-costly method, but any concave polygon can be drawn with the following steps (note this methos works surely on flat polygons, but I also assume you try to draw on flat surface, or in 2D orthogonal mode):
enable stencil test, use glStencilFunc(GL_ALWAYS,1,0xFFFF)
disable color mask to oprevent unwanted draws: glColorMask(0,0,0,0)
I think you have the vertices in an array of double, or in other form (strongly recommended as this method draws the same polygon multiple times, but using glList or glBegin-glEnd can be used as well)
set glStencilOp(GL_KEEP,GL_KEEP,GL_INCR)
draw the polygon as GL_TRIANGLE_FAN
Now on the stencil layer, you have bits set >0 where triangles of polygon were drawn. The trick is, that all the valid polygon area is filled with values having mod2=1, this is because the triangle fan drawing sweeps along polygon surface, and if the selected triangle has area outside the polygon, it will be drawn twice (once at the current sequence, then on next drawings when valid areas are drawn) This can happens many times, but in all cases, pixels outside the polygon are drawn even times, pixels inside are drawn odd times.
Some exceptions can happen, when order of pixels cause outside areas not to be drawn again. To filter these cases, the reverse directioned vertex array must be drawn (all these cases work properly when order is switched):
- set glStencilFunc(GL.GL_EQUAL,1,1) to prevent these errors happen in reverse direction (Can draw only areas inside the polygon drawn at first time, so errors happening in the other direction won't apperar, logically this generates the intersectoin of the two half-solution)
- draw polygon in reverse order, keeping glStencilFunc to increase sweeped pixel values
Now we have a correct stencil layer with pixel_value%2=1 where the pixel is truly inside the polygon. The last step is to draw the polygon itself:
- set glColorMask(1,1,1,1) to draw visible polygon
- keep glStencilFunc(GL_EQUAL,1,1) to draw the correct pixels
- draw polygon in the same mode (vertex arrays etc.), or if you draw without lighting/texturing, a single whole-screen-rectangle can be also drawn (faster than drawing all the vertices, and only the valid polygon pixels will be set)
If everything goes well, the polygon is correctly drawn, make sure that after this function you reset the stencil usage (stencil test) and/or clear stencil buffer if you also use it for another purpose.
Check out glues, which has tessellation functions that can handle concave polygons.
I wrote a java classe for a small graphical library that do exacly what you are looking for, you can check it here :
https://github.com/DzzD/TiGL/blob/main/android/src/fr/dzzd/tigl/PolygonTriangulate.java
It receive as input two float arrays (vertices & uvs) and return the same vertices and uvs reordered and ready to be drawn as a list of triangles.
If you want to exclude a zone (or many) you can simply connect your two polygones (the main one + the hole) in one by connecting them by a vertex, you will end with only one polygone that can be triangulate like any other with the same function.
Like this :
To better understand zoomed it will look like :
Finally it is just a single polygon.

XNA Layered Sprite problem

I have a game object that manages several sprite objects. Each of the sprites overlap each other a bit, and drawing them looks just fine when they are at 100% opacity. If I set their opacity to say, 50% that is when it all goes to pot because any overlapping area is not 50% opaque due to the multiple layers.
EDIT: Ooops! For some reason I thought that I couldn't upload images. Anyway....
http://postimage.org/image/2fhcmn6s/ --> Here it is. Guess I need more rep for proper inclusion.
From left to right:
1. Multiple sprites, 100% opacity. Great!
2. Both are 50%, but notice how the overlap region distinguishes them as two sprites.
3. This is the desired behavior. They are 50% opaque, but in terms of the composite image.
What is the best way to mitigate this problem? Is a render target a good idea? What if I have hundreds of these 'multi-sprites'?
Hope this makes sense. Thanks!
Method 1:
If you care about the individual opacity of each sprite, then render the image on the background to a rendertarget texture of the same size using 50% or whatever opacity you want the sprite to have against the background. Then draw this rendertarget with 100% opacity.
In this way, all sprites will be blended against the background only, and other sprites will be ignored.
Method 2:
If you don't care about setting the individual opacity of each sprite, then you can just draw all sprites with 100% opacity to a rendertarget. Then draw that render target over your background at 50% opacity.
Performance concerns:
I mentioned two examples of drawing to rendertargets, each for a different effect.
Method 1:
You want to be able to specify a different opacity for each sprite.
If so, you need to render every sprite to a rendertarget and then draw that rendertarget texture to the final texture. Effectively, this is the same cost as drawing twice as many sprites as you need. In this case, that's 400 draw calls, which can be very expensive.
If you batch the calls though, and use a single large rendertarget for all of the sprites, you might get away with just 2 draw calls (depending on how big your sprites are, and the max size of a texture).
Method 2:
You don't need different opacity per each sprite.
In this case you can almost certainly get away with just 2 draw calls, regardless of sprite size.
Just batch all draw calls of the sprites (with 100% opacity) to draw to a rendertarget. That's one draw call.
Now draw that rendertarget on top of your background image with the desired opacity (e.g. 50% opacity), and all sprites will have this opacity.
This case is easier to implement.
The first thing your example images reminded me of is the "depth-buffer and translucent surfaces" problem.
In a 3D game you must sort your translucent surfaces from back-to-front and draw them only after you have rendered the rest of your scene - all with depth reading and writing turned on. If you don't do this you end up with your 3rd image, when you normally want your 2nd image with the glass being translucent over the top of what is behind it.
But you want the 3rd image - with some transparent surfaces obscuring other ones - so you could just deliberately cause this depth problem!
To do this you need to turn on depth reads and writes and set your depth function so that a second sprite drawn at the same depth as a previously drawn sprite does not render.
To achieve this in XNA 4.0 you need to pass, to SpriteBatch.Begin, a DepthStencilState with its DepthBufferFunction set to CompareFunction.Less (by default it is less-than-or-equal-to) and DepthBufferEnable and DepthBufferWriteEnable set to true.
There may be interactions with the sprite's layerDepth parameter (I cannot remember how it maps to depth by default).
You may also need to use BasicEffect as your shader for your sprite batch - specifically so you can set a projection matrix with appropriate near and far planes. This article explains how to do that. And you may also need to clear your depth buffer before hand.
Finally, you need to draw your sprites in the correct order - with the unobscured sprite first.
I am not entirely sure if this will work and if it will work reliably (perhaps you will get some kind of depth fighting issue, I am not sure). But I think it's worth a try, given that you can leave your rendering code essentially normal and just adjust your render state.
You should try the stuff in Andrew's answer first, but if that doesn't work, you could still render all of the sprites (assuming they all have the same opacity) onto a RenderTarget(2D) with 100% opacity, and then render that RenderTarget to the screen with 50%.
Something like this in XNA 4.0:
RenderTarget2D rt = new RenderTarget2D(graphicsDevice,
graphicsDevice.PresentationParameters.BackBufferWidth,
graphicsDevice.PresentationParameters.BackBufferHeight);
GraphicsDevice.SetRenderTarget(rt);
//Draw sprites
GraphicsDevice.SetRenderTarget(null);
//Then draw rt (also a Texture2D) with 50% opacity. For example:
spriteBatch.Begin();
spriteBatch.Draw(rt, Vector2.Zero, Color.FromArgb(128, Color.White));
spriteBatch.End();

Resources