- 最后登录
- 2014-10-23
- 注册时间
- 2011-7-19
- 阅读权限
- 90
- 积分
- 81303
- 纳金币
- -1
- 精华
- 11
|
Full-blown 3D
It's time to add the third dimension! This will turn our graph from a grid into a cube, which we can use for volumetric representations. In other words, we'll create a tiny voxel system.
Duplicate Graph 2 and Grapher2 and change them into Graph 3 and Grapher3, just like we did for the second graph. Don't forget to disable Graph 2.
We'll make a few changes to Grapher3. First, we'll limit resolution to 25, which translates to 15235 points. We also need to initialize the Y position of the points and the green color component.
private void CreatePoints () {
if(resolution < 2){
resolution = 2;
}
else if(resolution > 25){
resolution = 25;
}
currentResolution = resolution;
emitter.ClearParticles();
emitter.Emit(resolution * 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++){
for(int y = 0; y < resolution; y++){
Vector3 p = new Vector3(x, y, z) * increment;
points.position = p;
points[i++].color = new Color(p.x, p.y, p.z);
}
}
}
}
Right now Graph 3 looks the same as Graph 2, except that it seems a little brighter. This is because we still set the Y positions in the Update method. So all points with the same X and Z position will collapse to the same Y position. We must no longer set the Y position, but the color's alpha component instead. That way our functions will define the volume's density.
void Update () {
if(currentResolution != resolution){
CreatePoints();
}
FunctionDelegate f = functionDelegates[(int)function];
for(int i = 0; i < points.Length; i++){
Color c = points.color;
c.a = f(points.position, Time.timeSinceLevelLoad);
points.color = c;
}
emitter.particles = points;
}
Now our graph looks like a mostly solid cube. The functions aren't very visible, because they do not vary along the Y axis. Only the two animated functions, Sine and Ripple, produce somewhat interesting results.
Let's change Linear into f(x,y,z) = 1 - x - y - z. That way it starts solid at (0, 0, 0) and fades to transparent along a straight line. We can do a similar thing with Exponential as well. Even better, let's animate them a bit so it's more interesting to look at.
private static float Linear (Vector3 p, float t) {
return 1f - p.x - p.y - p.z + 0.5f * Mathf.Sin(t);
}
private static float Exponential (Vector3 p, float t) {
return 1f - p.x * p.x - p.y * p.y - p.z * p.z + 0.5f * Mathf.Sin(t);
}
Next, we update Parabola so it will produce a cylinder, once again with a little pulsating animation. We also add the third dimension to Ripple, turning it into a sphere-spawning animation.
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 + 0.5f * Mathf.Sin(t);
}
private static float Ripple (Vector3 p, float t){
float squareRadius =
(p.x - 0.5f) * (p.x - 0.5f) +
(p.y - 0.5f) * (p.y - 0.5f) +
(p.z - 0.5f) * (p.z - 0.5f);
return Mathf.Sin(4 * Mathf.PI * squareRadius - 2f * t);
}
Finally, we'll update Sine too. We transform it into eight blobs by multiplying the square sines of X, Y, and Z together. We only animate the Z-based sine, but we make a distinction here. The top and bottom half of the graph will move in opposite directions. What does the question mark do?
private static float Sine (Vector3 p, float t){
float x = Mathf.Sin(2 * Mathf.PI * p.x);
float y = Mathf.Sin(2 * Mathf.PI * p.y);
float z = Mathf.Sin(2 * Mathf.PI * p.z + (p.y > 0.5f ? t : -t));
return x * x * y * y * z * z;
}
A nice variant would be one where all voxels are either fully visible or fully transparent. It would result in a solid but pixelated appearance. Let's add an absolute field to toggle such behaviour, along with a threshold field that determines how solid a voxel must be before it becomes visible. Each update, if absolute is switched on, we'll loop over all points and modify their alpha component based on threshold. Here's the complete script.
using UnityEngine;
public class Grapher3 : MonoBehaviour {
public enum FunctionOption {
Linear,
Exponential,
Parabola,
Sine,
Ripple
}
private delegate float FunctionDelegate (Vector3 p, float t);
private static FunctionDelegate[] functionDelegates = {
Linear,
Exponential,
Parabola,
Sine,
Ripple
};
public bool absolute;
public float threshold = 0.5f;
public FunctionOption function;
public int resolution = 10;
private int currentResolution;
private Particle[] points;
private ParticleEmitter emitter;
void Start () {
emitter = GetComponent<articleEmitter>();
CreatePoints();
}
private void CreatePoints () {
if(resolution < 2){
resolution = 2;
}
else if(resolution > 25){
resolution = 25;
}
currentResolution = resolution;
emitter.ClearParticles();
emitter.Emit(resolution * 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++){
for(int y = 0; y < resolution; y++){
Vector3 p = new Vector3(x, y, z) * increment;
points.position = p;
points[i++].color = new Color(p.x, p.y, p.z);
}
}
}
}
void Update () {
if(currentResolution != resolution){
CreatePoints();
}
FunctionDelegate f = functionDelegates[(int)function];
for(int i = 0; i < points.Length; i++){
Color c = points.color;
c.a = f(points.position, Time.timeSinceLevelLoad);
points.color = c;
}
if(absolute){
for(int i = 0; i < points.Length; i++){
Color c = points.color;
c.a = c.a >= threshold ? 1f : 0f;
points.color = c;
}
}
emitter.particles = points;
}
private static float Linear (Vector3 p, float t) {
return 1f - p.x - p.y - p.z + 0.5f * Mathf.Sin(t);
}
private static float Exponential (Vector3 p, float t) {
return 1f - p.x * p.x - p.y * p.y - p.z * p.z + 0.5f * Mathf.Sin(t);
}
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 + 0.5f * Mathf.Sin(t);
}
private static float Sine (Vector3 p, float t){
float x = Mathf.Sin(2 * Mathf.PI * p.x);
float y = Mathf.Sin(2 * Mathf.PI * p.y);
float z = Mathf.Sin(2 * Mathf.PI * p.z + (p.y > 0.5f ? t : -t));
return x * x * y * y * z * z;
}
private static float Ripple (Vector3 p, float t){
float squareRadius =
(p.x - 0.5f) * (p.x - 0.5f) +
(p.y - 0.5f) * (p.y - 0.5f) +
(p.z - 0.5f) * (p.z - 0.5f);
return Mathf.Sin(4 * Mathf.PI * squareRadius - 2f * t);
}
}
We can now produce nice graphs from data with up to three dimensions! It's possible to create very intricate volumetric stuff, only your imagination and mathematical knowledge is the limit. |
|