Cel-shaded terrain and performance on iOS / OpenGL ES 2.0

Started by
10 comments, last by blueshogun96 11 years, 1 month ago

I'm experimenting with full screen terrain rendering on the iPad, using a cel shader that goes something like this:

1) render the terrain unto screen with cel lighting

2) render the depth only into a texture

3) render this texture unto the screen using a sobel filter

The performance of step 1 alone is currently adequate (although I haven't started optimize it yet). But I'm having a little trouble getting good performance when all passes are combined. The depth buffer is apparently rendered twice, which seems unnecessary. Is there any way to combine these passes, or otherwise optimize this approach?

[attachment=13699:Landscape.jpg]

openwar - the real-time tactical war-game platform

Advertisement

Don't know what is supported by OGL ES (multiple render targets ? read depth texture ? etc.) Atleast you could try to write the linear depth into the alpha channel in the first path. 8 bit is not the best resolution, but it could be enough for this kind of low-detail terrain.

On iPad 2 and later, there is some kind of depth texture format, I am not sure, but I think you can attach it as your framebuffers depth texture target.

I would however expect the outline fullscreen shader to be the bottleneck in your case. So whatever calculations you do on the texture coordinates, do them in your vertex shader and in most cases you should unroll your loops.

output the depth into the alpha channel, it's not very accurate, but enough for your filter.

as an optimization for your current 3 phases, swap 1 and 2. it's beneficial, especially on mobile devices, to render one screen in one go, instead of switching from it to a temporary and back, as your GPU needs to store and restore the framebuffer into internal caches. doing it in one go means 67% traffic saving (just one write, instead of write, read, write).

The depth buffer is apparently rendered twice

Because it is already written during pass #1, which makes pass #2 completely superfluous. Just get rid of it.

Additionally, PowerVR chips are different from desktop cards and suffer from fill-rate more than desktops do, so eliminating pass #2 will help a lot, but it also suffers from dependent texture reads more-so than desktop cards do, so as was mentioned by _Slin_ you will want to avoid dependent texture reads (refer to IMAGINE TECHNOLOGY’s documentation) by calculating the texture coordinates you need for your sobel filter in the vertex shader, storing them in only the X and Y of the vectors you pass to the fragment shaders, and in the fragment shaders use those coordinates as-is without modifying them at all.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Thanks for the tips, I've eliminated dependent texture reads, and will continue to experiment with alpha channels.

I didn't mention it, but there's a fourth pass where I render the trees using point sprites, which makes it harder to get rid of pass 2. Or is there any way to share the same depth buffer with both screen rendering and the sobel filter that I miss?

openwar - the real-time tactical war-game platform

Yes. Set it as the depth buffer for pass #4 (which doesn’t exist since #2 was removed, so it would be pass #3).

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

I managed to get this to work by a lot of optimizing, basically using the original setup.

- Going from sobel to cross filter for faster edge fragment shader (half the number of texture reads). Not as pretty but OK in this context.

- I was sloppily using a lot of fragment discards in many shaders, now I only do a few edge triangles in a special shader.

- Lots of small tweaks as suggested above and in the imgtec document.

I didn't use the alpha channel for the depth buffer, it seemed to produce artifacts, and I didn't notice any performance gain (although I'm not sure I did it right). I guess I could do more research to see if this is feasible some how, but the frame rate is ok now so I'm leaving it as a future to-do item.

openwar - the real-time tactical war-game platform

Still looks pretty darn good to me. Tbh, I think that this would make a very interesting and educational article. I often thought about how iOS would handle multiple rendering passes and post processing effects and now I feel encouraged to experiment for myself.

I do have a question for you, are you using any CPU based optimizations for your game for your post processing effects or is it all GPU/shader based? Sorry if I'm missing something obvious.

Shogun.

It's mainly GPU-based. E.g. I kept moving color/shadow calculations up the chain, and ended up pre-calculating a color-lookup texture, so that the vertex shader does simple normal calculations and feeds the resulting brightness value to the fragment shader to use as color texture coordinate + simple step-based shadow. I'm maybe sacrificing physical realism, but as realistic rendering is not my goal, this isn't an issue.

For the discard optimization, I keep two vertex buffers, one for triangles that has fragments that are to be discarded, and one for all-visible fragments. Here it's the CPU that decides for each triangle which buffer it goes into. The buffers are static, so this is done during level load time.

As for the edge-filter-pass, it's too much for the original iPad to handle, so it's only used on newer if your running on a newer iPad.

openwar - the real-time tactical war-game platform

This topic is closed to new replies.

Advertisement