Testing out some rendering ideas for WebGL. I had created something along these lines a long time ago http://www-cs-students.stanford.edu/~amitp/_test/isometric-visibility.html for Flash but WebGL exposes more GPU features than Flash did, so I want to try:
- point sprites? (decided: no)
- instanced rendering? (decided: yes, but it’s not a performance win)
- multipass cube rendering (rotation and normals in uniforms instead of in attributes!)?
- billboard sprites
- cube outlines in a post-process pass, by outputting normal and z value at each pixel?
- billboard outlines in a pre-process pass, by constructing a new texture with outlines baked in
- shading so lower elevations are darker (cheesy ambient occlusion)
- visibility test in the gpu, outputting a visibility mask? if it’s fast enough, add lights
- eyes! this is my chance to do eyes 👀
1.1 Sprite rendering
I initially copied the sprites into a separate texture to add padding and outlines. I also ran into a difference between Safari and Chrome/Firefox. In Safari canvas, drawing a canvas onto another canvas with shadow will apply the shadow, then clip to the rect I want to draw. On Chrome/Firefox it clips first, then applies the shadow. I ended up having to make an extra copy to clip first, then apply the shadow in a separate draw.
But I threw all that away. I’m now using the unaltered spritesheet.
First problem I ran into was that when I drew a sprite, it would leak pixels from adjacent sprites. I was using GL_NEAREST so I thought it would be fine, but noooo, it looks like I get texture coordinates outside the 0,0 – 1,1 range! Maybe for antialiasing? I don’t know.
In any case the fix was to clamp the coordinates to the sprite range and then do the texture lookup.
I ended up implementing outlines for my cubes, and extended it to sprites. I assign each surface a surface type, 0.0 for ground, 0.5 for billboard sprites, 1.0 for cube faces. Then whenever the surface type changes from one output pixel to the next, I can draw an outline.
TODO: THIS DOESN’T HANDLE OVERLAPPING BILLBOARDS so maybe I need to apply the outline in the sprite renderer, and then suppress it later? Ugh.
1.2 Cube rendering
I draw each face of the cube in a separate draw call. That lets me put the normal into a uniform, and then I can put four corners into instanced rendering so that I can just put sprite positions in once.
I draw the normals into a texture, and then whenever the normal changes, I can draw a border. I think this is overkill, as the surface type should be enough.
TODO: try surface type without normals. Maybe each sprite can be its own surface? That would fix the overlapping billboard problem. And it would mean I don’t need to save the normals. I need the normals only for lighting. → this works!
- Brightness based on distance to player
- Do I also want to make something based on the normal?
What about actual lights?
The way I draw billboards doesn’t work with the light/shadow system, so I had to apply one matrix to orient the billboards for lighting purposes, and apply a different matrix to orient the billboards for rendering purposes. I think there must be a simpler way but I haven’t investigated.
1.3.2 Cube tops
The top of a wall block will always be in shadow.
Possible fix: sample from the closest edge of the square. This doesn’t work well because there’s not continuity between adjacent wall blocks.
Possible fix: sample from both the horizontal and vertical edges of the square, and pick the max. This works better but interior corners never get lit up.
Possible fix: sample from both the horizontal and vertical edges and also the diagonal corner. This helps with interior corners but now non-corners get lit up.
Possible fix: sample from all edges and light up the block if any wall is lit. This is more expensive and looks wrong, as it’s not continuous with the floors.
Possible fix: distinguish interior corners by writing the wall vs floor data to the shadow map in another channel.