Creating Procedural Planets in Unity — Part 3

public class PolySet : HashSet<Polygon> {...public void ApplyAmbientOcclusionTerm(float AOForOriginalVerts,
float AOForNewVerts) {
foreach (Polygon poly in this) {
for (int i = 0; i < 3; i++) {
float aoTerm = (poly.m_Vertices[i] >
m_StitchedVertexThreshold) ?
AOForNewVerts : AOForOriginalVerts;
Vector2 uv = poly.m_UVs[i];
uv.y = aoTerm;
poly.m_UVs[i] = uv;
}
}
}
...}
Shader "Custom/VertexColoredShader" {    
Properties {
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Glossiness("Smoothness", Range(0,1)) = 0.5
_Metallic("Metallic", Range(0,1)) = 0.0
_AmbientOcclusionIntensity("AO Intensity", Range(0,1)) = 0.5
}
SubShader {
Tags{ "RenderType" = "Opaque"}
LOD 200
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma surface surf Standard fullforwardshadows addshadow
#pragma target 3.0
sampler2D _MainTex; struct Input {
float2 uv_MainTex;
float4 color : Color;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
half _Alpha;
half _AmbientOcclusionIntensity;
void surf(Input IN, inout SurfaceOutputStandard o) {
fixed4 c = IN.color;
float ambientOcclusionStrength = IN.uv_MainTex.y;
o.Albedo = c * lerp(1.0, (1.0f - _AmbientOcclusionIntensity),
ambientOcclusionStrength);
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
}
ENDCG
}
FallBack "Diffuse"
}
public class Edge {    
public Polygon m_InnerPoly;
public Polygon m_OuterPoly;
public List<int> m_OuterVerts;
public List<int> m_InnerVerts;
public int m_InwardDirectionVertex;
public Edge(Polygon inner_poly, Polygon outer_poly) {
m_InnerPoly = inner_poly;
m_OuterPoly = outer_poly;
m_OuterVerts = new List<int>(2);
m_InnerVerts = new List<int>(2);
// The inward vertex is quite simply the one remaining vertex
// after we've found the two vertices that form the Edge:
foreach (int vertex in inner_poly.m_Vertices) {
if (outer_poly.m_Vertices.Contains(vertex))
m_InnerVerts.Add(vertex);
else
m_InwardDirectionVertex = vertex;
}
...

}
}
public EdgeSet : HashSet<Edge> {  public Dictionary<int, Vector3> GetInwardDirections(List<Vector3> 
vertexPositions) {
var inwardDirections = new Dictionary<int, Vector3>();
var numContributions = new Dictionary<int, int>();

foreach(Edge edge in this) {
Vector3 innerVertexPosition =
vertexPositions[edge.m_InwardDirectionVertex];
Vector3 edgePosA = vertexPositions[edge.m_InnerVerts[0]];
Vector3 edgePosB = vertexPositions[edge.m_InnerVerts[1]];
Vector3 edgeCenter = Vector3.Lerp(edgePosA, edgePosB, 0.5f);
Vector3 innerVector = (innerVertexPosition -
edgeCenter).normalized;
for(int i = 0; i < 2; i++) {
int edgeVertex = edge.m_InnerVerts[i];
if (inwardDirections.ContainsKey(edgeVertex)) {
inwardDirections[edgeVertex] += innerVector;
numContributions[edgeVertex]++;
}
else {
inwardDirections.Add(edgeVertex, innerVector);
numContributions.Add(edgeVertex, 1);
}
}
}
// Now we average the contributions that each vertex received,
// and we can return the result.
foreach(KeyValuePair<int, int> kvp in numContributions) {
int vertexIndex = kvp.Key;
int contributionsToThisVertex = kvp.Value;
inwardDirections[vertexIndex] = (inwardDirections[vertexIndex]
/ contributionsToThisVertex).normalized;
}
return inwardDirections;
}
}
Shader "Custom/TransparentVertexColoredShader" {
Properties {
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Glossiness("Smoothness", Range(0,1)) = 0.5
_Metallic("Metallic", Range(0,1)) = 0.0
_Alpha("Alpha", Range(0,1)) = 0.5
}
SubShader {
Tags{ "RenderType" = "Opaque"}
LOD 200
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma surface surf Standard fullforwardshadows addshadow alpha
#pragma target 3.0

sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
float4 color : Color;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
half _Alpha;
void surf(Input IN, inout SurfaceOutputStandard o) {
fixed4 c = IN.color;
c.a = _Alpha;
float waveStrength = IN.uv_MainTex.y * (sin(_Time.y * 2.0) *
0.5f + 0.5f);
fixed4 waveColor = fixed4(1.0,1.0,1.0,1.0);
o.Albedo = lerp(c, waveColor, waveStrength);
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
public GameObject GenerateMesh(string name, Material material) {
...
Vector2[] uvs = new Vector2[vertexCount]; for (int i = 0; i < m_Polygons.Count; i++) {
var poly = m_Polygons[i];
...
uvs[i * 3 + 0] = poly.m_UVs[0];
uvs[i * 3 + 1] = poly.m_UVs[1];
uvs[i * 3 + 2] = poly.m_UVs[2];
...
}
terrainMesh.uv = uvs; terrainMesh.SetTriangles(indices, 0);
MeshFilter terrainFilter = meshObject.AddComponent<MeshFilter>();
terrainFilter.mesh = terrainMesh;
return meshObject;
}
public void Start() {
InitAsIcosohedron();
Subdivide(3);
CalculateNeighbors(); Color32 colorOcean = new Color32( 0, 80, 220, 0);
Color32 colorGrass = new Color32( 0, 220, 0, 0);
Color32 colorDirt = new Color32(180, 140, 20, 0);
Color32 colorDeepOcean = new Color32( 0, 40, 110, 0);

// Start by making everything ocean colored:
foreach (Polygon p in m_Polygons)
p.m_Color = colorOcean;
// Now we build a set of Polygons that will become the land.
// We do this by generating randomly sized spheres on the surface
// of the planet, and adding any Polygon that falls inside that
// sphere.
PolySet landPolys = new PolySet();
PolySet sides;
// Grab polygons that are inside random spheres. These will be the
// basis of our planet's continents.
for(int i = 0; i < m_NumberOfContinents; i++) {
float continentSize = Random.Range(m_ContinentSizeMin,
m_ContinentSizeMax);
PolySet newLand = GetPolysInSphere(Random.onUnitSphere,
continentSize, m_Polygons);
landPolys.UnionWith(newLand);
}
// While we're here, let's make a group of oceanPolys. It's pretty
// simple: Any Polygon that isn't in the landPolys set must be in
// the oceanPolys set instead.
var oceanPolys = new PolySet();
foreach (Polygon poly in m_Polygons) {
if (!landPolys.Contains(poly))
oceanPolys.Add(poly);
}
// Let's create the ocean surface as a separate mesh.
// First, let's make a copy of the oceanPolys so we can
// still use them to also make the ocean floor later.
var oceanSurface = new PolySet(oceanPolys);
sides = Inset(oceanSurface, 0.05f);
sides.ApplyColor(colorOcean);
sides.ApplyAmbientOcclusionTerm(1.0f, 0.0f);
if (m_OceanMesh != null)
Destroy(m_OceanMesh);
// Ocean Material should be assigned in the editor. It's just a
// material that uses the TransparentVertexShader that we created
// earlier.
m_OceanMesh = GenerateMesh("Ocean Surface", m_OceanMaterial);

// Back to land for a while! We start by making it green. =)
foreach (Polygon landPoly in landPolys) {
landPoly.m_Color = colorGrass;
}
// The Extrude function will raise the land Polygons up out of the
// water. It also generates a strip of new Polygons to connect the
// newly raised land back down to the water level. We can color
// this vertical strip of land brown like dirt.
sides = Extrude(landPolys, 0.05f);
sides.ApplyColor(colorDirt);
sides.ApplyAmbientOcclusionTerm(1.0f, 0.0f);

// Grab additional polygons to generate hills, but only from the
// set of polygons that are land.
PolySet hillPolys = landPolys.RemoveEdges();
sides = Inset(hillPolys, 0.03f);
sides.ApplyColor(colorGrass);
sides.ApplyAmbientOcclusionTerm(0.0f, 1.0f);
sides = Extrude(hillPolys, 0.05f);
sides.ApplyColor(colorDirt);
// Hills have dark ambient occlusion on the bottom, and light on
// top.
sides.ApplyAmbientOcclusionTerm(1.0f, 0.0f);
// Time to return to the oceans.
sides = Extrude(oceanPolys, -0.02f);
sides.ApplyColor(colorOcean);
sides.ApplyAmbientOcclusionTerm(0.0f, 1.0f);
sides = Inset(oceanPolys, 0.02f);
sides.ApplyColor(colorOcean);
sides.ApplyAmbientOcclusionTerm(1.0f, 0.0f);

var deepOceanPolys = oceanPolys.RemoveEdges();
sides = Extrude(deepOceanPolys, -0.05f);
sides.ApplyColor(colorDeepOcean);
deepOceanPolys.ApplyColor(colorDeepOcean);
// Okay, we're done! Let's generate a ground mesh for this planet. if (m_GroundMesh != null)
Destroy(m_GroundMesh);
m_GroundMesh = GenerateMesh("Ground Mesh", m_GroundMaterial);
}
Making Progress…

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store