- Home /
irregularities with procedurally generated sphere
I have created a procedurally generated sphere; but it has a strange crease, a 'pinch point' at the poles, and the lighting is off at the seam.
How can I correct this so the lighting is uniform? And if possible remove the 'point' at the poles? (although this is not so detrimental as it is masked when the deform is applied).
Here's the full script :
#pragma strict
@script RequireComponent(MeshFilter)
@script RequireComponent(MeshRenderer)
public var radius : float = 1.0;
public var segments : int = 5;
public var perlinRange : float = 0.25;
private var calcAngle : float;
private var posX : float[];
private var posY : float[];
private var posX2 : float[];
private var posY2 : float[];
private var myMesh : Mesh;
private var verts : Vector3[];
private var uv : Vector2[];
private var tris : int[];
function Start()
{
CreateSphere();
}
function Update()
{
if (Input.GetMouseButtonUp(1))
{
CreateSphere();
}
}
function CreateSphere()
{
var i : int = 0;
// ----
calcAngle = 0;
posX = new float[segments + 1];
posY = new float[segments + 1];
// Calculate Circle on X-Y
for (i = 0; i < segments + 1; i ++)
{
posX[i] = Mathf.Sin( calcAngle * Mathf.Deg2Rad );
posY[i] = Mathf.Cos( calcAngle * Mathf.Deg2Rad );
calcAngle += 180.0 / parseFloat(segments);
}
// ----
calcAngle = 0;
posX2 = new float[ (segments * 2) + 1 ];
posY2 = new float[ (segments * 2) + 1 ];
// Calculate Circle on X-Z
for (i = 0; i < (segments * 2) + 1; i ++)
{
posX2[i] = Mathf.Sin( calcAngle * Mathf.Deg2Rad );
posY2[i] = Mathf.Cos( calcAngle * Mathf.Deg2Rad );
calcAngle += 180.0 / parseFloat(segments);
}
// ----
// Vertices - Add Perlin Noise
verts = new Vector3[ ( segments + 1 ) * ( (segments * 2) + 1 )];
var p : int = 0;
for (i = 0; i < (segments + 1); i ++)
{
for (p = 0; p < (segments * 2) + 1; p ++)
{
if ( i == 0 || i == segments ) // align top and bottom verts to same vert
{
verts[ p + (i * ( (segments * 2) + 1 )) ] = new Vector3( posX2[p] * posX[i], posY[i], posY2[p] * posX[i] ) * radius * ( Random.Range(1.0 - perlinRange, 1.0) );
if (p != 0) // leave first in row randomly generated (no need to make equal to self)
{
verts[ p + (i * ( (segments * 2) + 1 )) ] = verts[ 0 + (i * ( (segments * 2) + 1 )) ];
}
}
else if ( p == (segments * 2) ) // at end of row
{
verts[ p + (i * ( (segments * 2) + 1 )) ] = verts[ 0 + (i * ( (segments * 2) + 1 )) ];
}
else
{
verts[ p + (i * ( (segments * 2) + 1 )) ] = new Vector3( posX2[p] * posX[i], posY[i], posY2[p] * posX[i] ) * radius * ( Random.Range(1.0 - perlinRange, 1.0) );
}
}
}
// ----
// UVs
uv = new Vector2[verts.length];
//var calcUVchunk : Vector2 = Vector2( 1.0 / ( (segments * 2) + 1 ), 1.0 / (segments + 1) );
var calcUVchunk : Vector2 = Vector2( 1.0 / (segments * 2), 1.0 / (segments) );
for (i = 0; i < (segments + 1); i ++)
{
for (p = 0; p < (segments * 2) + 1; p ++)
{
uv[ p + (i * ( (segments * 2) + 1 )) ] = new Vector2( 1.0 - (calcUVchunk.x * p), 1.0 - (calcUVchunk.y * i) );
//Debug.Log( " " + uv[ p + (i * ( (segments * 2) + 1 )) ] + " : " + Vector2( calcUVchunk.x * p, 1.0 - (calcUVchunk.y * i) ) );
}
}
// ----
// Triangles
tris = new int[ ( segments * segments * 4 * 3 ) ];
var t : int = 0;
for (i = 0; i < segments; i ++)
{
for (p = 0; p < (segments * 2); p ++)
{
tris[ t + 0 ] = ( (i + 0) * ( (segments * 2) + 1 ) ) + p + 0;
tris[ t + 1 ] = ( (i + 1) * ( (segments * 2) + 1 ) ) + p + 0;
tris[ t + 2 ] = ( (i + 1) * ( (segments * 2) + 1 ) ) + p + 1;
tris[ t + 3 ] = ( (i + 0) * ( (segments * 2) + 1 ) ) + p + 1;
tris[ t + 4 ] = ( (i + 0) * ( (segments * 2) + 1 ) ) + p + 0;
tris[ t + 5 ] = ( (i + 1) * ( (segments * 2) + 1 ) ) + p + 1;
t += 6;
}
}
// ----
// Show Mesh
var theMeshFilter : MeshFilter = this.gameObject.GetComponent(MeshFilter) as MeshFilter;
theMeshFilter.mesh.vertices = new Vector3[verts.length];
theMeshFilter.mesh.uv = new Vector2[uv.length];
theMeshFilter.mesh.triangles = new int[tris.length];
theMeshFilter.mesh.vertices = verts;
theMeshFilter.mesh.uv = uv;
theMeshFilter.mesh.triangles = tris;
theMeshFilter.mesh.RecalculateBounds();
theMeshFilter.mesh.RecalculateNormals();
// Calculate Tangents
TangentSolver(theMeshFilter.mesh);
// assign default material if there is none
if (!theMeshFilter.renderer.material)
{
theMeshFilter.renderer.material.color = Color.white;
}
}
note : yes, I do reference Perlin Noise but don't use it. The original line was
verts[ p + (i * ( (segments * 2) + 1 )) ] = new Vector3( posX2[p] * posX[i], posY[i], posY2[p] * posX[i] ) * radius * ( Mathf.PerlinNoise(1.0 - perlinRange, 1.0) );
but all this did was scale the whole object. I have alot to learn yet ....
Answer by Fattie · Aug 21, 2012 at 01:04 PM
Ah you are just letting unity calculate the normals is that correct?
very likely you'll have to figure out and enter the normals MANUALLY ... that's probably your only issue here.
I'd say that when you write code to make mesh, it's usually more conceptually difficult and takes more lines of code to do the normals than the triangles themselves.
Fortunately ........ it's very easy to do this on a sphere as all the normals just point outwards!!! Heh! (Well that's how you'd normally have the normals on a sphere!)
So it shouldn't give you too much trouble in this project.
Suggest .. If you take a moment to write a utility routine that simply shows you all the normals, you'd quickly see the problem w/ the normals Unity generated for you.
I was gonna say ... this age-old post may have much useful information for you,
http://answers.unity3d.com/questions/193695/in-unity-is-there-a-fast-way-to-find-nearby-triang.html
it occurs to me, in fact the sundry downloadable full projects in that question/answer, in fact include various code to display normals, I believe.
i find when you are writing code to build mesh, it's basically essential during development to have something that shows you all your normals......... (use gizmos, debug or whatever) Otherwise it's hard to have a clue what the hell is going on with your handiwork you know! Heh!
Note on deforming the sphere...
I think you mention you may want to deform the sphere. You then have some wonderful thinking to do, concerning the new normals after you deform the sphere! Now this is real programming, thinking! Good luck!
IF you can assume the verts are pretty evenly spaced all over the sphere (unlikely at the poles?) you can more or less just take a right angle from the verts nearby. That's a good cheap solution for many situations. Video games are all about fast cheap solutions, so that could help.
(Indeed don't forget that very often you have to CREATE AROUND a good chep solution. IE, for this very reason, you may have to in fact limit yourself to only using sphere models that involve reasonably evenly-spaced verts everywhere.)
In any event, deforming a sphere, the hard work is all about the normals.
It's trivial enough to deform a sphere. Hey you got the last page of my rigging pad! That's good luck!
Thanks, I imagined your diagram when you commented (hence the D'oh - Eureka moment). You previously directed me to that link, but the mediafire link is not working - content is removed/no longer there (I actually wanted to check out your spheres before starting this). I have made a few meshes now (and used animation curves to animate the verts) but this is the first time I have had a problem with Unity-calculated normals. Adding your suggestion of animating the normals is a great idea, thanks =]
I have to do some research and testing methinks, and write a utility routine as you suggest.
edit : yes it is designed on quads, and the uv's mapped like Equirectangular
ah, that's too bad about the dead links. perhaps someone cleverer than me can recover it from the intraverse, I don't have any local copies any more.
Regarding the pinching. I am fairly certain that, yes, that is indeed strictly due to the issues of the normals. Once you program in the normals, I believe that will be perfect.
An interesting philosophical issues. You're going to have two different verts in the same place of course (I just mean where two triangles touch). The normals should be identical, watch out for some sort of bizarre mathematical woe where they are slightly different (even though they're conceptually meant to be on the same spot).
This raises an interesting point ... when you're generating your own mesh, I would urge you to NOT bother sharing any verts. There is utterly no reason, whatsoever, to share verts ... ever.
When you are generating your own mesh, it's much much tidier if you do not share verts, it's just better in all ways. it makes you program more gooderer, makes the result easier to change on the fly and so on.
There was a tremendous discussion about this in an answer somewhere recently, possibly http://answers.unity3d.com/questions/293607/tiling-uv-mapping.html
I guess the overwhel$$anonymous$$g point is, sometimes I think when people first have a look at the mesh universe, they perhaps mistakenly think you "have to" or something share verts. This is utterly not the case, there is no reason to do it, no advantage whatsoever, etc. It's a pure "legacy issue" in the 3D world.
In any event your question ... I believe the seams etc are indeed PURELY RELATED TO THE NOR$$anonymous$$ALS. Let's see what happens !
I am amazed, that was a simple and eloquent fix. I simplified the normal calculation to :
for (p = 0; p < norms.length; p ++)
{
norms[p] = verts[p].normalized;
}
(as the sphere's centre is at the origin 0,0,0 ) and it looks perfect while rotating. So my last problem is the pinching at the poles. Apart from that, you get full credit on fixing and helping me understand normals, with good advice inbetween. $$anonymous$$any thanks =]
Edit : On further testing, it seems the 'pinching was more of an optical illusion from the normals not being correct. Thanks again.
Just to catchup : the pinching did seem to be an optical illusion (you stated while I was discovering this myself). I was there in that other question too! But my icosoheron started with 12 verts, and now has 60 verts so that I can correctly UV map a texture of certain layout correctly to each face (http://www.ridgenet.net/~jslayton/icos.jpg)
Thanks again, learned alot and was amused along the way.
The Verts at the poles have the same value (if ( i == 0 || i == segments ) // align top and bottom verts to same vert) same principle is applied to the seam, hopefully eli$$anonymous$$ating the "mathematical not exactly same" issue.
The deformation is already in place (var perlinRange), just need to actually implement PerlinNoise rather than Random.Range . If you attach the script to an empty gameObject, hit play and then right-click in the scene, this shows as working =]
This is to be a asteroid in my Space-Shooter, the next big task is to apply deformation of a collision to the mesh depending on the face that has collided, then apply decreasing damage to the surrounding verts. I shall now be aware of re-calculating the normals after this fact too, thanks.
I am glad you answered, and am very happy with the results (and keen to play around with animating norms!)
Fantastic, so you are going to deform the asteroid based on it getting bashed, great stuff !!!!
Your answer
Follow this Question
Related Questions
UV-ing a sphere procedurally 1 Answer
Normalizing several meshes towards a sphere. 1 Answer
How to subdivide a single face on a mesh? 0 Answers
Flat shading procedural generated mesh? 1 Answer
Triangles overlapping in mesh 0 Answers