It’s 1:50 am so this will be a short post.
This week I was trying to get a sphere up so that I could test that specular lighting was working correctly. Actually I found out it was working correctly about part way through, but I wanted to get the sphere up anyway.
I wanted to use the ‘pushed out normals’ method of sphere generation. You create a unit cube, then calculate the vector from the center of the cube to each vertex, then you normalize that vertex. Now you have the surface normal. Then you scale that vector by the radius of the circle, and that new value is the vertex position you need to set. This is fairly intuitive and was how I was drawing my cylinders back when I was using immediate mode, however I hadn’t thought about using it for a sphere until I blundered across this amazing solution on Stack Overflow.
I don’t have enough reputation to uprep that guy but when I get there, he gets a +1 from me. There’s a great explanation and even animated gifs to get the idea across. It’s simple and elegant, and I do recommend investigating it.
So first of all I had to make a manifold of triangles and bam I can only display 12 triangles. Huh? Well to cut a long story short, I wasted six hours tracking down the bug. Turns out there was a ghost in my code from when I had copied a tutorial answer; my glDrawELements line was stuck on 36 vertices from when I was drawing my cube.
Wasting probably isn’t the right word. I spent a lot of time reading up on various other parts of code that I suspected, and in the process tidied up a couple of things. I’m also a lot more confident in the buffer creation/vertex attribute section. Before I had largely copied the code with only a loose understanding of what I was doing, so naturally I suspected that was where the error was. I researched it and was satisfied that I was doing it correctly. Then as I was writing up my question on Stack Overflow, I wrote the lines, “No matter what I do, I can’t draw more than thirty six vertices. It’s almost as if there’s somewhere in the code that sets a limit.” This seemed oddly specific. So put ’36’ in search and what pops up? glDrawElements is set to only draw 36 vertices.
Ha. Ha. Ha. Ha.
Well, if you’re a programmer, you know that laugh. The laugh of ‘Yes I found the bug’ and ‘How could it have been so obvious?’ and ‘I can’t believe I wasted x hours on this.’
Anyway, once that was done, the rest of the process was fairly trivial.
So here we have a unit cube made up of 25x25x6 polygons (3750!) with four vertexes per polygon and vertexes weighing in at a 44 bytes a pop; I am now burning a hefty 660,000 bytes. I later found I can bump it up to around 200×200 polygons per face (over a gig) before it explodes, but even then I’m still running at 60 fps.
Now, I set each vertex normal as if it was coming from the midpoint of the sphere, rather than the surface. It looks kind of cool.
Finally, set each vertex position to it’s (scaled) normal, subtract the midpoint to recenter it, and done.
And that’s it. I fiddled with the lighting values a little bit to try to get that white dot less eye melting.
That’s where I’ll leave it for now. Once I start trying to represent actual materials I’ll spend more time making more realistic lights.
I also discovered that when objects are near the edge of my view window, I am getting some perspective distortion. I have noticed this in games occasionally (World of Tanks especially has noticeable perspective distortion) and it was the big problem I had with my raytracer back in Uni. That’s the result of having too-large a view window; I had it set to 60 degrees and I’ve now bumped it down to 45 degrees. There’s still distortion but it’s not so noticeable. I’ll probably need to look in to this at a later date and find a better solution. I’d like a wide angle view with no distortion!
Anyway, now we’re rocking a couple thousand vertexes, what does the video card have to say? 280 fps! Hmm, that’s quite the dent. Well, I still have some tidying up to do – there are many duplicate vertices, roughly a third, that I need to weld together, which should shave the number of vertexes drawn down a great deal.
I’ll probably tackle textures next. I want to see if I can wrap a map of the world around that blue marble.
Minor update. This week I did more tidying up, specifically:
I was representing the vertex position data with vec4’s and w set to 1 because I was using vertexes to represent the lights also. It further made matrix calculations trivial. However, I was unnecessarily sending an extra four bytes per vertex. I’ve set the positions to vec3’s now and adjusted the rest of the code accordingly.
I added some basic vertex duplication welds. At the moment, if a vertex attempts to enter the manifold within 0.0001 (one millimitre) of an existing vertex, it doesn’t get added, and instead the existing vertex is referred to instead.
This cut down on a significant number of vertexes and thus triangles; on a simple 2 x 2 sphere that’s 26 vertices instead of 96; on a 3 x 3 sphere that’s 56 instead of 216.
The original 25 x 25 sphere actually had a whopping 15,000 vertices, now it’s down to 3752.
At the moment, the execution time is fairly painful because my algorithm needs optimisation.
I want to create a general purpose manifold vertex-welder/normal calculator/vertex indexer section that I can throw any manifold in to and the program will be okay. However, this kind of thing is fairly CPU intensive so I would be better off using this program to create ‘known good’ meshes that can then be loaded in the real program. Checking the welds is an intensive process that does not need to be done at execution time.
I had some minor trouble with floating point comparisons of vertexes. In the end, I decided that within the context of positional space, within one tenth of a millimetre was ‘good enough’ for what I was looking for.
I am probably going to work on optimisation next because the execution time bothers me. I have a stack of for loops four tiers deep, and the very last one iterates over the entire list of existing vertexes. I think I can break that last loop down into only looking at ‘likely’ clashes (e.g. against the neighbours of this neighbours vertex).
Next up: I think I’m still basically using intermediate mode for my shapes. I read a great tutorial where Nicol Bolas uses billboards to represent his spheres entirely in the shader. I knew about billboards and that they’re mostly used for signs and particle effects, but representing a sphere is new to me. I’ll have to look at it soon, too.