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.

Thursday, August 18, 2011

Unity GPU Noise (Part 2)

Sheesh! Yet another 5 months have passed by!

When I was creating the demo of GPU Noise in Unity, I kept getting a "function 'tex2Dlod' not supported in this profile" error. I couldn't figure out why except that it was an OpenGL issue. So, I simply forced my shaders to exclude OpenGL and OpenGL ES renderers (#pragma exclude_renderers opengl gles). Obviously this is not the best solution, so I decided to dive into it again.

It turns out that the error goes away if you instead force the shader to compile to GLSL (#pragma glsl). I have no idea why this fixes the problem, since it was an OpenGL issue in the first place, but whatever. Forcing the shaders to compile to GLSL brought up two other issues that I had to fix.

1) GLSL doesn't support default parameters (Cg and HLSL do). This means that I had to go duplicate all of my shader functions that used them, which were a lot.

2) GLSL doesn't support the switch statement. I had to go manually unroll my switch statements to be if-else trees. This one really confused me because all of the GLSL documentation seems to indicate that switch is indeed in the language, so this might just be a Unity issue.

You can check out the updated demo here:

Hopefully it works on Mac now, but I haven't been able to test it out yet. Feel free to let me know!

Tuesday, March 8, 2011

Unity GPU Noise & Unity Issues

It's amazing how much time keeps slipping by without any posts. Another 5 months have somehow passed since my last post. That means that I have only written 2 posts in the past year! I need to get back to my good update schedule.

GPU Noise in Unity

First, I'd like to show a simple demo I made of various types of noise on the GPU in Unity. You can check out the live demo on the web by clicking on the image below. You need to have a Shader Model 3.0 GPU in order to run the demo.

As you can see, there are 9 different types of noise in the demo and they are all implemented in Cg shaders on the GPU.
  • Perlin - "Standard" Perlin Noise (1 octave)
  • fBm - fractional Brownian motion summation of Perlin Noise (8 octaves)
  • Turbulence - fBm variation that sums the absolute value of the noise result (8 octaves)
  • Ridged - fBm variation from the book "Texturing and Modeling: A Procedural Approach" that performs a special ridge function on the noise result (8 octaves)
  • Voronoi (F1) - A "noise" type that uses a field of random 3D points and determines the distance to the closest point
  • F2 - Voronoi that determines the distance to the second closest point
  • F2 - F1 - Voronoi that determines the difference between the first and second closest points
  • (F1 + F2) / 2 - Voronoi that averages the distance between the two closest points
  • Crater - Voronoi variation that makes crater-like structures

Have fun messing around with the sliders and seeing how the noise can be manipulated on the fly!

Unity Issues

After completing the GPU noise demo in Unity (it's actually been done since November), I started proceeding with implementing a procedural planet generator in Unity. It quickly introduced me to some of the issues and limitations that Unity has.
  • Cannot disable/control view frustum culling.
    This is the worst issue I ran into. The way I implement the planet is to have a small cube that is "blown out" to form a sphere and then offset with noise all on the GPU (à la displacement mapping). Unity has a camera system that determines which objects are inside each camera's view frustum and automatically culls (doesn't draw) any objects that are outside of the view frustum. The problem is, while the small cube is not in the view frustum, the final displaced planet is in the frustum. This causes the planet to suddenly stop rendering if the camera is pointed a certain direction.

    I tried several fixes such as blowing the cube up to be the same size of the planet and forcing the view angle to be the maximum value (179 degrees) when Unity does the frustum check. Neither of these got rid of the problem entirely and just reduced how much it happened. In order to fix it, game developers should have control over which objects should be culled, or be able to disable frustum culling entirely.

  • Cannot run editor scripts when paused.
    This problem stems from another problem in Unity. Unity doesn't send the View matrix (by design) or Projection matrix (current bug) to the shaders, so you need a script to send them manually. Because of this, you need to have this script continually updating the shader variables even when the game is paused. If they are not updated then the objects render in the same place on the screen no matter where you move the camera. This is a horrible problem with no real fix.

  • Cannot create a custom collider.
    Unity provides no simple way to create your own physics collider object. You can inherit from their built-in Collider class, but you cannot override any methods inside of it. As you can see from the link, I came up with a work-around for what I specifically was trying to accomplish: make a spherical planet collider. It is very similar to what I did in Bullet in the past.

  • Cannot write depth in pixel shader. (Also explained here.)
    I tried implementing a logarithmic depth buffer, but I was greeted with a Cg compilation error when trying to write to the depth register. This is actually a bug in the Cg compiler. It's been fixed in the compiler, but Unity is still using an old version of the compiler which has the bug. I need to test the latest releases of Unity to see if this is still a problem.