- 最后登录
- 2014-10-23
- 注册时间
- 2011-7-19
- 阅读权限
- 90
- 积分
- 81303
 
- 纳金币
- -1
- 精华
- 11
|
Adding an extra dimension
Up to this points we're only using the X axis for input, and in one case time as well. Now we will make a new graph object that uses the Z axis too, thus producing a grid instead of a line.
Create a new Unity object just like Graph 1 along with a new grapher script, calling them Graph 2 and Grapher2 instead. You can speed this up by duplicating them and then making the necessary changes. Disable Graph 1 by toggling the checkbox in front of its name field, because we're not using it anymore. Copy the code from Grapher1 to Grapher2, only changing the class name to Grapher3. We'll modify the rest of the code in a moment.
To change the line into a square grid, we change the CreatePoints method of Grapher2. We need to create a lot more points and use a nested for loop to initialize them. We now set the Z position and the blue color component too.
private void CreatePoints () {
if(resolution < 2){
resolution = 2;
}
currentResolution = resolution;
emitter.ClearParticles();
emitter.Emit(resolution * resolution);
points = emitter.particles;
float increment = 1f / (resolution - 1);
int i = 0;
for(int x = 0; x < resolution; x++){
for(int z = 0; z < resolution; z++){
Vector3 p = new Vector3(x * increment, 0f, z * increment);
points.position = p;
points[i++].color = new Color(p.x, 0f, p.z);
}
}
}
Now we have a nice flat grid! But shouldn't it show the Linear function? It does, but currently only for the first row of points along the Z axis. If you select a different function, only these points will change while the rest remain as they are. This is because in the Update method only loops over resolution points, while it should loop over all of them. We'll use the Length property that every array has to fix this.
void Update () {
if(currentResolution != resolution){
CreatePoints();
}
FunctionDelegate f = functionDelegates[(int)function];
for(int i = 0; i < points.Length; i++){
Vector3 p = points.position;
p.y = f(p.x);
points.position = p;
Color c = points.color;
c.g = p.y;
points.color = c;
}
emitter.particles = points;
}
Now we can see our functions again, extended along to Z axis. Increasing the resolution results in a nice smooth surface. However, you'll notice that if you set the resolution higher than 127, part of the graph will disappear. This is because there is a limit to how many particles a Particle Renderer will display. So it's a good idea to limit the resolution to 127, which translates to 16129 points.
private void CreatePoints () {
if(resolution < 2){
resolution = 2;
}
else if(resolution > 127){
resolution = 127;
}
currentResolution = resolution;
emitter.ClearParticles();
emitter.Emit(resolution * resolution);
points = emitter.particles;
float increment = 1f / (resolution - 1);
int i = 0;
for(int x = 0; x < resolution; x++){
for(int z = 0; z < resolution; z++){
Vector3 p = new Vector3(x * increment, 0f, z * increment);
points.position = p;
points[i++].color = new Color(p.x, 0f, p.z);
}
}
}
There's something else going on that's pretty weird. Try rotating the perspective view while displaying the parabola. From some angles, the graph is drawn wrong. This is because the particles are drawn in the order that we've created them, they don't take view direction into account. You can fix this by setting the Stretch Particles field of the Particle Renderer component to Sorted Billboard. While this makes sure that the graph is show correctly from all view angles, it results in a massive performance hit as well. It's best to stick to the Billboard option and simply look at the graph from only a few angles.
Let's update our function code so we can take advantage of the new dimension. First change the input paramaters of FunctionDelegate to a vector and a float instead of just a single float. While we could specify the X and Z position separately, we'll simply give it the entire position vector. We'll also include the current time, instead of having to look it up inside the functions themselves.
private delegate float FunctionDelegate (Vector3 p, float t);
Now we need to update the function methods accordingly and change how the delegate is called.
void Update () {
if(currentResolution != resolution){
CreatePoints();
}
FunctionDelegate f = functionDelegates[(int)function];
for(int i = 0; i < points.Length; i++){
Vector3 p = points.position;
p.y = f(p, Time.timeSinceLevelLoad);
points.position = p;
Color c = points.color;
c.g = p.y;
points.color = c;
}
emitter.particles = points;
}
private static float Linear (Vector3 p, float t) {
return p.x;
}
private static float Exponential (Vector3 p, float t) {
return p.x * p.x;
}
private static float Parabola (Vector3 p, float t){
p.x = 2f * p.x - 1f;
return p.x * p.x;
}
private static float Sine (Vector3 p, float t){
return 0.5f + 0.5f * Mathf.Sin(2 * Mathf.PI * p.x + t);
}
We're ready to include Z in our mathematical functions! For example, change the Parabola function to f(x,z) = 1 - (2x - 1)2 * (2z - 1)2. We can also go wild with the Sine function, layering multiple sines to get a complex oscillating effect.
private static float Parabola (Vector3 p, float t){
p.x = 2f * p.x - 1f;
p.z = 2f * p.z - 1f;
return 1f - p.x * p.x * p.z * p.z;
}
private static float Sine (Vector3 p, float t){
return 0.50f +
0.25f * Mathf.Sin(4 * Mathf.PI * p.x + 4 * t) * Mathf.Sin(2 * Mathf.PI * p.z + t) +
0.10f * Mathf.Cos(3 * Mathf.PI * p.x + 5 * t) * Mathf.Cos(5 * Mathf.PI * p.z + 3 * t) +
0.15f * Mathf.Sin(Mathf.PI * p.x + 0.6f * t);
}
|
|