I am using a simple shader, here it is
void main() {
vec2 p = -1.0 + 2.0 * v_tex_coord;
float len = length(p);
vec2 uv = v_tex_coord - (p/len)*(sin(len*22.-u_time*5.28) + 1.)*0.1;
gl_FragColor = texture2D(u_texture, uv) * u_color;
}
This is how I set it up to use it for nodes with different colors
self.swirlShader.attributes = [SKAttribute(name: "u_color", type: SKAttributeType.vectorFloat4)]
From time to time it glitches and adding nodes to the scene affects how it works, basically it changes the texture of the node to the sine function.
glitch example
This alternates between all nodes really fast, only when some particle is emitting, like the bottom-left one.
When 2 nodes collide they explode and when that happens it also crashes and the last instruction in the stack trace is [SKAttributeValue copyValueTo: type: count:]
Also getting this from time to time
Execution of the command buffer was aborted due to an error during execution. Caused GPU Hang Error (IOAF code 3)
EDIT: I found out that using a new instance of the shader for each node and increasing the number of nodes drastically increases the occurrence of the crash, but not the glitch, it actually changed it and made it less frequent. Here is the glitch with new instance for every node: glitch with new shader instances
UPDATE: It appears to be some kind of threading issue related to the access of the shader or the execution of the shader. Also, why is the CPU time going up and down when using a shader and steady low when not? I thought shaders work directly on the GPU.
UPDATE 2: Found out that it was crashing because I was adding too many nodes to the scene(around 3600). I don't know why though, because it isn't consistent and only happens when using a shader.
The glitch is still present and I can't figure out what exactly is causing it. I think it is again from adding nodes to the scene, but it is not consistent enough to run a lot of tests.
I tried removing all actions and doing manual alpha, rotation etc. updates, but that wasn't the cause. In one of the tests everything froze and started jumping back and forth between 2-3 frames, as well as jumping forward a few times, something is causing a GPU hang error.
I am not sure how the shader is being affected by anything outside it's variables, but it's messing with the GPU.
Related
I encountered weird behaviour when trying to create dissolve shader for iOS spritekit. I have this basic shader that for now only changes alpha of texture depending on black value of noise texture:
let shader = SKShader(source: """
void main() {\
vec4 colour = texture2D(u_texture, v_tex_coord);\
float noise = texture2D(noise_tex, v_tex_coord).r;\
gl_FragColor = colour * noise;\
}
""", uniforms: [
SKUniform(name: "noise_tex", texture: spriteSheet.textureNamed("dissolve_noise"))
])
Note that this code is called in spriteSheet preload callback.
On simulator this consistently gives expected result ie. texture with different alpha values all over the place. On actual 14.5.1 device it varies:
Applied directly to SKSpriteNode - it makes whole texture semi-transparent with single value
Applied to SKEffectNode with SKSpriteNode as its child - I see miniaturized part of a whole spritesheet
Same as 2 but texture is created from image outside spritesheet - it works as on simulator (and as expected)
Why does it behave like this? Considering this needs to work on iOS 9 devices I'm worried 3 won't work everywhere. So I'd like to understand why it happens and ideally get sure way to force 1 or at least 2 to work on all devices.
After some more testing I finally figured out what is happening. The textures in the shader are whole spritesheets instead of separate textures on devices, so the coordinates go all over the place. (which actually makes more sense than simulator behaviour now that I think about it)
So depending if I want 1 or 2 I need to apply different maths. 2 is easier, since display texture is first rendered onto a buffer, so v_text_coord will take full [0.0, 1.0], so all I need is noise texture rect to do appropriate transform. For 1 I need to additionally provide texture rect to first change it into [0.0, 1.0] myself and then apply that to noise coordinates.
This will work with both spritesheets loaded into the shader or separate images, just in later case it will do some unnecessary calculations.
I have a Metal-backed SceneKit project where I'd like to draw a curve that is modified on the CPU every frame. This seems like it should be a simple task; one that I've done in OpenGL many times.
Something like:
array_of_points[0].x = 25
array_of_points[0].y = 35
upload_array_to_gpu()
render_frame()
It seems that modifying the SCNGeometrySource of an SCNNode is not possible. Recreating and setting the node.geometry works but the position lags / jitters compared to other nodes in the scene. Recreating the entire node each frame also lags.
One possible avenue might be a geometry shader modifier, but I'd still have to somehow pass in my cpu-computed curve positions.
Another option is to use metal APIs directly. Though this approach seems like it will require lots more code, and I couldn't find too much info about mixing SceneKit with Metal.
I also tried setting the preferredRenderingAPI to OpenGL, but it doesn't change the renderingAPI.
We are porting a game to SpriteKit and I've run in to a bit of a problem. Some objects in our game have triangle-strip trails attached to them. The vertex buffers of the trails are continuously updated as the objects move in the world to create a seamless and flowing effect (there are constraints on how many vertices are in the buffer and how often we emit new vertices).
In the previous implementation we simply updated the affected vertices in the corresponding buffer whenever we emitted new vertices. In SceneKit it seems I am unable to update geometry sources, unless I use geometrySourceWithBuffer:vertexFormat:semantic:vertexCount:dataOffset:dataStride:. To do this however, it seems I need a Metal device, command queue and encoder, to be able to submit commands to update my buffer before it renders.
Is there any way I can do this with a normal SCNView, or do I have to do everything manually with a CAMetalLayer, creating my own metal device etc?
In essence, everything we need, bar this trail geometry is available in SpriteKit, so I was hoping there was some way I could get a hold of the metal device and command queue used by the SKView and simply use that to upload my geometry. This would make things a lot simpler.
I've used a particle system to get a similar effect. The particle emitter is attached to the moving object, with particles set to fade out and eventually die. You might need to set the emitter's targetNode to your scene, depending on the effect you want.
emitter = SKEmitterNode()
emitter.name = marbleNodeNames.trail.rawValue
emitter.particleTexture = SKTexture(imageNamed: "spark")
emitter.particleAlphaSpeed = -1.0
emitter.particleLifetime = 2
emitter.particleScale = 0.2
marbleSprite.addChild(emitter)
I'm making multilevel game based on SpriteKit.
Everything works well except one thing: when user plays long time, changes many levels, etc... then SpriteKit starts losing textures.
I mean there is no big red cross like when image load fails but just empty space like nothing is there.
Hours of debugging and googling did not produce any results.
How can I deal with that bug?
I think I might be having a related issue, except the loss of textures occurs when I am rapidly running actions on a SKSpriteNode. In my code, I run an action each time I get a touch and when the touches are rapid and the animations are firing quickly, the base texture of the SKSpriteNode seemingly disappears. No memory warnings, not a peep from the console; the SKSpriteNode's texture is magically set to nil.
I get the impression from your question that this isn't your exact cause, but you are having the same symptoms. Unfortunately I don't know what is causing it. What I've done to work around the issue has been to constantly check if the texture on my SKSprite node has been set to nil immediately after I run an SKAction and then re-assign it if needed.
So, an abridged version (in Swift) of what I'm doing looks like this :
func doAnimation( ) {
_character.runAction(someSKAction, withKey: "animation")
//Whoops!, we lost our base texture again!
if _character.texture == nil {
let atlas = SKTextureAtlas(named: "someAtlasName")
let texture = atlas.textureNamed("idleFrame" )
_character.texture = texture
}
}
This is not really solution so much as a workaround, but it might be adaptable to your situation until you (or someone else on SO) figures it out. I won't argue that it's not ugly.
BTW, you are not alone with the disappearing texture issue; I wrote a similar response to a similar question here.
I have an object that is moving very fast (max velocity 900). When it reaches max speed it starts to create trailing objects or motion blur.
But I just want it to be the object moving fast. I am running on 60 fps.
I like the speed of the object but I don't like how its getting rendered (motion blur). How do I handle this?
This object bounces all around the screen with a restitution of 1.02, because I want to make it pick up speed as it keeps bouncing. I want to make it go faster thats why I did the 1.02 restitution.
The motion blur may simply be due to the LCD display having an "afterglow". So the position the object was in the previous frame is still a little brighter in the next frame because it takes some time for the crystals inside the LCD to return to the unlit state.
This causes "motion blur" on any moving object on the screen, and is of course more noticeable the faster the object moves. You may even be able to make out multiple versions of the same objects at different light levels trailing behind the object's position.
This effect may also depend somewhat on the device and model, and is often called 'ghosting'.
Regardless, there's nothing you can do about the "motion blur" caused by the LCD screen's afterglow effect. Here's a good article explaining the effects and their causes.
Hmm... you'll have trouble getting it to render smoothly.
At that speed (900 points per second) it will move 15 points every FRAME if running at 60 fps. That's a significant amount to move in such a short amount of time. In about 1/3 of a second it will travel entirely across the screen.
I'm guessing it will be getting to the limit of the ability of the hardware. Both the processor, the screen and your actual eyes. I imagine you'll also hit physics errors too with it possibly escaping through walls etc...
Can you show a video of how it is currently behaving?