We've got two new releases on the Unity Asset Store.
Future City Pack by our new hire (awesome stuff) and BundleLoader, a script to take the pain out of downloading and caching asset bundles. BundleLoader is free, because it saves so much pain, it can only make the world a happier place. :-)
Wednesday, February 23, 2011
Saturday, February 19, 2011
Unity3D supports Sony Xperia Play
Yes it is true, Unity3D will support the Xperia Play. I know this will make many developers quite happy.
Sunday, February 13, 2011
Planet Shader V3.
This shader is much better than my previous efforts.
It has specular mapping, bump mapping and a fake atmospheric scattering effect, based on rim lighting. To get the partial crescent glow, I calculate the rim lighting based on the view direction plus a small component of the light direction. All in a single pass!
This shader will be available in the Unity Asset Store in version 1.5 of my Spacebox Extension.
Saturday, February 12, 2011
Spacebox Released!
Spacebox is a procedural content generator for space environments. The good news is, it is now available on the Unity Asset Store!
You can try out out a demo on the web over here.
Spacebox generates seamless cube maps which you can use as a Skybox material in Unity3D. You can customise any number of star and nebulae layers with different textures and colours to produce a unique space environment.
Labels:
Games,
Spacecraft,
Unity3D
Thursday, February 10, 2011
Camera.layerCullDistances Tip.
This is a Unity3D tip.
The Camera.layerCullDistances variable is an array of floats that allow you to have a custom farClipDistance per layer.
Something that is not mentioned in the docs, however, is that the culling distance cannot be greater than your Camera.farClipPlane value. If it is, it will be clamped down to this value.
The Camera.layerCullDistances variable is an array of floats that allow you to have a custom farClipDistance per layer.
Something that is not mentioned in the docs, however, is that the culling distance cannot be greater than your Camera.farClipPlane value. If it is, it will be clamped down to this value.
Tuesday, February 08, 2011
Ripple Shader
Thank to Adrian Boeing I was inspired this morning to hack together a ripple shader for Unity3D. Thanks for the math Adrian. You can see the animated effect here.
Shader "DM/Ripple Shader" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Texture", 2D) = "white" {}
_Scale ("Scale", Range(0.5,500.0)) = 3.0
_Speed ("Speed", Range(-50,50.0)) = 1.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
Cull Off
CGPROGRAM
#pragma surface surf Lambert
#include "UnityCG.cginc"
half4 _Color;
half _Scale;
half _Speed;
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half2 uv = (IN.uv_MainTex - 0.5) * _Scale;
half r = sqrt (uv.x*uv.x + uv.y*uv.y);
half z = sin (r+_Time[1]*_Speed) / r;
o.Albedo = _Color.rgb * tex2D (_MainTex, IN.uv_MainTex+z).rgb;
o.Alpha = _Color.a;
o.Normal = (z, z, z);
}
ENDCG
}
FallBack "Diffuse"
}
Uniform Points On a Sphere
As promised, here is my code for creating a uniform set of points on a sphere.
using UnityEngine;
using System.Collections.Generic;
public class PointsOnSphere : MonoBehaviour {
public GameObject prefab;
public int count = 10;
public float size = 20;
[ContextMenu("Create Points")]
void Create () {
var points = UniformPointsOnSphere(count, size);
for(var i=0; i<count; i++) {
var g = Instantiate(prefab, transform.position+points[i], Quaternion.identity) as GameObject;
g.transform.parent = transform;
}
}
Vector3[] UniformPointsOnSphere(float N, float scale) {
var points = new List<Vector3>();
var i = Mathf.PI * (3 - Mathf.Sqrt(5));
var o = 2 / N;
for(var k=0; k<N; k++) {
var y = k * o - 1 + (o / 2);
var r = Mathf.Sqrt(1 - y*y);
var phi = k * i;
points.Add(new Vector3(Mathf.Cos(phi)*r, y, Mathf.Sin(phi)*r) * scale);
}
return points.ToArray();
}
}
Monday, February 07, 2011
Per Vertex Ambient Occlusion
If you want to bake ambient occlusion into your mesh, here is one way to do it.
This script modifies models on import if it has a filename that ends with "-AO". You can adjust the samples parameter to change the quality / time ratio. I find 1000 samples takes a few seconds, but still provides a good quality.
If you want to use the baked ambient occlusion colours, you need to use a shader that blends these colours in with your material colours. This is a shader I use to visualize vertex colours only.
If you combine this calculated colour, with existing vertex colours on your mesh, you can get quite nice results. The image below uses no lighting, no textures and is very cheap to render. It has 700 vertices.
If you can afford the extra vertices, you can get even better results. The below screenshot shows the same model with 3000 vertices.
There is a problem with the ambient occlusion calculation. I simply use random points on a sphere when creating the samples. This is not ideal, as the set of points are not uniformly distributed on the sphere. This is fairly easy to do, and I'll show how to do this in a later post. Stay tuned!
This script modifies models on import if it has a filename that ends with "-AO". You can adjust the samples parameter to change the quality / time ratio. I find 1000 samples takes a few seconds, but still provides a good quality.
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
class AddVertexAO : AssetPostprocessor
{
int samples = 1000;
void OnPostprocessModel (GameObject go)
{
if (go.name.EndsWith ("-AO")) {
AddAO (go);
}
}
void AddAO (GameObject go)
{
var mf = go.GetComponent<MeshFilter> ();
mf.sharedMesh.Optimize ();
var co = go.GetComponent<MeshCollider> ();
var destoryCollider = co == null;
if (co == null)
go.AddComponent<MeshCollider> ();
var mesh = mf.sharedMesh;
var normals = mesh.normals;
var vertices = mesh.vertices;
var rotations = new Vector3[samples];
var radius = Mathf.Max(mesh.bounds.size.x, mesh.bounds.size.y, mesh.bounds.size.z);
for (var i = 0; i < samples; i++) {
rotations[i] = go.transform.position + (Random.onUnitSphere*radius);
}
var nVertices = new List<Vector3>();
var nColors = new List<Color>();
var nNormals = new List<Vector3>();
var nTriangles = new List<int>();
var index = 0;
foreach (var i in mesh.triangles) {
var n = normals[i];
var v = vertices[i];
var c = Color.white;
var hits = 0f;
foreach(var s in rotations) {
if(Physics.Linecast(s, go.transform.position+v)) {
hits += (1f/samples);
} else {
hits -= (1f/samples);
}
}
c *= (1-hits);
nVertices.Add(v);
nNormals.Add(n);
nColors.Add(c);
nTriangles.Add(index);
index += 1;
}
mesh.vertices = nVertices.ToArray();
mesh.colors = nColors.ToArray();
mesh.normals = nNormals.ToArray();
mesh.triangles = nTriangles.ToArray();
mesh.Optimize();
}
}
If you want to use the baked ambient occlusion colours, you need to use a shader that blends these colours in with your material colours. This is a shader I use to visualize vertex colours only.
Shader "DM/Vertex Coloured" {
Properties {
_Color ("Main Color", Color) = (0.5,0.5,0.5,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf None
float4 _Color;
struct Input {
float4 color : COLOR;
};
half4 LightingNone (SurfaceOutput s, half3 lightDir, half atten) {
half4 c;
c.rgb = s.Albedo;
c.a = s.Alpha;
return c;
}
void surf (Input IN, inout SurfaceOutput o) {
half4 c = _Color * IN.color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
Fallback "Diffuse"
}
If you combine this calculated colour, with existing vertex colours on your mesh, you can get quite nice results. The image below uses no lighting, no textures and is very cheap to render. It has 700 vertices.
If you can afford the extra vertices, you can get even better results. The below screenshot shows the same model with 3000 vertices.
There is a problem with the ambient occlusion calculation. I simply use random points on a sphere when creating the samples. This is not ideal, as the set of points are not uniformly distributed on the sphere. This is fairly easy to do, and I'll show how to do this in a later post. Stay tuned!
Friday, February 04, 2011
Tip when using self lit materials.
Thursday, February 03, 2011
Tuesday, February 01, 2011
A 3D Skybox
A 3D skybox sits between your game world and the 2D skybox texture commonly used as a background for your scene.
Why do you need it?
It allows you to have 3D scenery in your background, which the player cannot interact with, yet is still effected by lighting, can use shaders and provide a much higher level of detail than the 2D skybox. This is how I do it in Unity3D.
Why do you need it?
It allows you to have 3D scenery in your background, which the player cannot interact with, yet is still effected by lighting, can use shaders and provide a much higher level of detail than the 2D skybox. This is how I do it in Unity3D.
using UnityEngine;I build my 3D skybox as a child of an empty game object which has the above component attached. I build my skybox at a 1/32 scale, and in the component, I set the level scale to 32. This means that when I hit the play button, the 3D skybox is scaled up and moved out the extremities of my game level.
using System.Collections;
public class Skybox3D : MonoBehaviour {
public float levelScale = 32;
Camera skyCam;
void Start () {
var min = Vector3.zero;
var max = Vector3.zero;
foreach(Transform i in transform) {
i.gameObject.layer = gameObject.layer;
if(i.renderer == null) continue;
var bmax = i.renderer.bounds.max;
var bmin = i.renderer.bounds.min;
min.x = Mathf.Min(min.x, bmin.x);
min.y = Mathf.Min(min.y, bmin.y);
min.z = Mathf.Min(min.z, bmin.z);
max.x = Mathf.Max(max.x, bmax.x);
max.y = Mathf.Max(max.y, bmax.y);
max.z = Mathf.Max(max.z, bmax.z);
}
float absMax;
if(min.sqrMagnitude > max.sqrMagnitude)
absMax = min.magnitude * levelScale * 1.5f;
else
absMax = max.magnitude * levelScale * 1.5f;
transform.localScale = Vector3.one * levelScale;
skyCam = new GameObject("Sky Camera", typeof(Camera)).camera;
skyCam.transform.parent = Camera.main.transform;
skyCam.nearClipPlane = absMax / 3;
skyCam.farClipPlane = absMax;
skyCam.cullingMask = 1 << gameObject.layer;
skyCam.depth = Camera.main.depth - 1;
Camera.main.cullingMask ^= 1 << gameObject.layer;
if(Camera.main.clearFlags != CameraClearFlags.Depth) {
Debug.LogWarning("The main camera must have clear flags set to depth only.");
}
}
}
Subscribe to:
Posts (Atom)
Popular Posts
-
These are the robots I've been working on for the last 12 months. They each weigh about 11 tonnes and have a 17 meter reach. The control...
-
This hard-to-see screenshot is a Generic Node Graph Editing framework I'm building. I'm hoping it can be used for any kind of node...
-
So, you've created a car prefab using WheelCollider components, and now you can apply a motorTorque to make the whole thing move along. ...
-
MiddleMan: A Pub/Sub and Request/Response server in Go. This is my first Go project. It is a rewrite of an existing Python server, based o...
-
Why would I ask that question? Python 3 has been available for some time now, yet uptake is slow. There aren't a whole lot of packages i...
-
It is about 8 degrees C this morning. So cold, especially when last week we had high twenties. To help solve the problem, a friend suggeste...
-
After my last post, I decided to benchmark the scaling properties of Stackless, Kamaelia, Fibra using the same hackysack algorithm. Left axi...
-
I'm now using bzr instead of svn. I'm pushing my repositories to: http://exactlysimilar.org/bzr/ I'm also auto publishing docume...
-
Possibly slightly more correct lighting. The rim light is now only applied in the direction of the sun, rather than being purely based on vi...
-
I've just read a newspaper article (courtesy of Kranzky ) from WA Business News documenting the malfeasance, gross negligence and misc...