Sunday, September 25, 2011

Unity GPU Noise (Part 4)

Well, I'm a stubborn individual, so I continued working on the Android version of GPU Noise. I have actually been successful and made a little progress. Not without encountering more issues, of course.

First, I must say that GLSL hates conditional branches (including for loops). The main reason why my example scene wasn't running on Android was because I used an if-else tree in my shader (previously a switch statement) to select the noise type based upon a variable that was set on the Material by the C# side. While this works perfectly fine on Windows and DirectX, it's not the case for Android. I had to separate each noise type into its own shader/material and then have logic on the C# side to select the correct Material.

This is where things start to get strange. The basic Perlin noise function worked fine (as well as Voronoi), but none of my summations (like fBm) worked. They are simply for loops that call the Perlin function repeatedly. For testing, instead of having it take a variable for the number of times to loop, I hard-coded it to 4 octaves. This didn't work either. However, if I manually unrolled the for loop to have the code repeated 4 times, it finally worked.

Now, a little aside about my implementation. In order to speed up the noise functions, I precompute some values and store them in various textures which the noise functions then sample from. I wanted the noise functions to be accessible in both a vertex shader and pixel shader, so I used the tex2Dlod function to sample the textures. This is because tex2Dlod is the only permitted function for sampling from a vertex shader. (Automatic mip-mapping doesn't work in a vertex shader, so the developer must specify the mip-map level to use.)

Thinking that using tex2Dlod was somehow causing my for loop problems, I decided to look into a different implementation. I found a GLSL implementation of Perlin noise that didn't use any arrays or texture lookups (amazing!), thus making it highly cross-platform. I promptly ported it over to Cg in Unity. I was dismayed to discover that it yielded really terrible looking visual artifacts. The problem was even worse when using any summation. I quickly made a test scene in WebGL to test the shader in its original GLSL. Here, as you can see, it all worked perfectly. Going back to the Cg port, I went over line by line to see where it was failing. I first tried replacing the optimized "hacks" he used in place of modulus. That didn't fix it. Then, I looked at his inverse square root function. If I replaced his function with a call to rsqrt, the artifacts disappeared!

Armed with a new implementation, I tried running my test scene on Android again. And again, my summations failed. I happened to discover that the summations only worked when given certain octaves (for loop counter). This is the strangest thing of all. If I gave it 1, it worked. If I gave it 2 through 8, it failed. If I gave it 9 or greater, it worked.

I must say, I'm entirely perplexed. I have all of my noise functions working on an Android device now, but only with certain ranges on the summations. Why doesn't it work with 4 octaves when it works with 10? Does anyone have any insight into this strange issue?

Thursday, September 15, 2011

Unity GPU Noise (Part 3)

As I mentioned previously, I had updated my GPU Noise to hopefully get it working on Macs. One of my friends tried the web player build on his Mac, and I was saddened to see that it still came out as a black, non-displaced sphere.

As I was investigating, I found the Shader.isSupported API which looked like it might be helpful. I noticed in the documentation, however, that it will return true if any of the Fallback shaders will work. In my noise shader, I had given it a fallback of "VertexLit", thus why only a flat, black sphere was rendered on the Mac. I didn't want the API to always return true (since VertexLit obviously was working), so I commented out the Fallback statement. Bad move! Not only did the web player fail to load on a Mac, it completely locked the entire OS requiring a reboot! Since my friend's Mac is a dual-boot with Windows 7 on it, I had him try it out on the Windows side. Everything worked perfectly!

In order to help me with debugging possible hardware issues, I wrote a simple little Unity web player that listed the hardware specs according to how Unity sees the machine. It's been pretty interesting and helpful. You can run it over here.

Using that app, I was able to see that the Mac only runs OpenGL 2.1! For comparison, when I forced my Windows build to run in OpenGL mode (using -force-opengl), it reported that it was running OpenGL 4.1. My hatred for Apple increases daily.

Deciding to switch gears for a bit, I started looking into deploying the GPU Noise on my EVO 3D to see how it ran. (No, I don't have a personal Android license for Unity yet, but I do have one from work. I;m not going to publish anything from it, just use it to test my stuff after work.) So, I got everything setup and sent my package over to my phone, where I was greeted with the "Powered by Unity" splashscreen. That was it; it never made it beyond that point. I hooked up logcat and added some debug lines to see what errors were occurring. That wasn't helpful at all. It showed Unity loading, timing out, and then reloading. My level file was never loaded, none of my scripts were ever executed. If I switched my Material to be something else (I used a particle material), it would load properly and run. So, it was an issue with my noise Material, but I have no idea what, due to the lack of errors.

As it stands now, I'm not quite sure how to proceed. Do I release this on the Unity Asset Store as it is now and put a disclaimer that it only works on Windows currently, or do I invest more time and effort to get it working on Mac/Android first? Opinions?

Monday, September 12, 2011

Procedural Primitives (Unity Asset Store)

I've released my first package, Procedural Primitives, on the Unity Asset Store. Check it out here.

It's based on the code I wrote to generate primitives in XNA. You can read about that code and download it on my old blog post here.

Basically, it allows you to create your own primitive 3D shapes (planes, cylinders, cones, and boxes) in Unity. Sure, Unity already has all of these as built-in primitives, but you can't change the resolution of these shapes at all. Plus, they provide no ability to create just the Mesh object and not a full GameObject. Procedural Primitives lets you pick the quality you want and gives you the option to create only the Mesh or the a full GameObject containing MeshFilter, MeshRenderer, and Collider components.

Procedural Primitives allows you to create the shapes either in the Unity Editor by way of a custom editor window, or you can call the methods in script.

To create them in the editor, you choose GameObject --> Create Other --> Procedural Primitive from the menu:

A new window will pop up that will allow you to tweak several parameters (depending upon the chosen shape) and then create a new primitive shape.

To create a new shape in script, you simply call one of the static methods on the Primitive class along with the desired parameters:

All money I get from the sales of Procedural Primitives will help fund my purchase of an Android license of Unity.

I'm currently preparing my noise package release. It should be coming out sometime in the next week.