Last active
June 8, 2018 10:41
-
-
Save gtk2k/fb127ee6ca901d843b8cf78e6863fb7b to your computer and use it in GitHub Desktop.
spherical to cubemap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.Networking; | |
using WebSocketSharp; | |
public class TextureDownload : MonoBehaviour | |
{ | |
class ImageData | |
{ | |
public ImageData(int width, int height) | |
{ | |
this.width = width; | |
this.height = height; | |
} | |
public int width; | |
public int height; | |
public byte[] data; | |
} | |
public string imgURL; | |
WebSocket ws; | |
UnityMainThreadDispatcher dispatcher; | |
static string[] textureNames; | |
static Dictionary<string, Action<Vector3, float, float>> orientations; | |
IEnumerator GetText() | |
{ | |
UnityWebRequest www = UnityWebRequest.Get("http://www.my-server.com"); | |
yield return www.SendWebRequest(); | |
if (www.isNetworkError || www.isHttpError) | |
{ | |
Debug.Log(www.error); | |
} | |
else | |
{ | |
www.downloadHandler.data; | |
} | |
} | |
private void Awake() | |
{ | |
dispatcher = UnityMainThreadDispatcher.Instance(); | |
textureNames = new string[] { "_LeftTex", "_UpTex", "_FrontTex", "_RightTex", "_DownTex", "_BackTex" }; | |
orientations = new Dictionary<string, Action<Vector3, float, float>>(){ | |
{ "pz", (cube, x, y) => { cube.x = -1; cube.y = -x; cube.z = -y;}}, | |
{ "nz", (cube, x, y) => { cube.x = 1; cube.y = x; cube.z = -y;}}, | |
{ "px", (cube, x, y) => { cube.x = x; cube.y = -1; cube.z = -y;}}, | |
{ "nx", (cube, x, y) => { cube.x = -x; cube.y = 1; cube.z = -y;}}, | |
{ "py", (cube, x, y) => { cube.x = -y; cube.y = -x; cube.z = 1;}}, | |
{ "ny", (cube, x, y) => { cube.x = y; cube.y = -x; cube.z = -1;}} | |
}; | |
} | |
// Use this for initialization | |
void Start() | |
{ | |
/*ws = new WebSocket("ws://localhost:7777"); | |
ws.OnOpen += (sender, e) => | |
{ | |
Debug.Log("ws connect"); | |
ws.Send(imgURL); | |
}; | |
ws.OnMessage += (sender, e) => | |
{ | |
dispatcher.Enqueue(() => | |
{ | |
var faceIdx = e.RawData[0]; | |
var width = BitConverter.ToInt32(e.RawData, 1); | |
var height = BitConverter.ToInt32(e.RawData, 5); | |
var img = new byte[e.RawData.Length - 1]; | |
Buffer.BlockCopy(e.RawData, 9, img, 0, e.RawData.Length - 9); | |
var tex = new Texture2D(width, height); | |
tex.LoadImage(img); | |
var material = GameObject.Find("Cube").GetComponent<Renderer>().material; | |
material.SetTexture(textureNames[faceIdx], tex); | |
}); | |
}; | |
ws.Connect();*/ | |
} | |
// Update is called once per frame | |
void Update() | |
{ | |
} | |
int clamp(double x, double min, double max) | |
{ | |
return (int)Math.Min(max, Math.Max(x, min)); | |
} | |
double mod(double x, double n) | |
{ | |
return ((x % n) + n) % n; | |
} | |
int readIndex(int x, int y, int width) | |
{ | |
return 4 * (y * width + x); | |
} | |
Action<double, double, int> copyPixelNearest(ImageData read, ImageData write) | |
{ | |
int width = read.width; | |
int height = read.height; | |
byte[] data = read.data; | |
return (xFrom, yFrom, to) => | |
{ | |
int nearest = readIndex( | |
clamp(Math.Round(xFrom), 0, width - 1), | |
clamp(Math.Round(yFrom), 0, height - 1), | |
width | |
); | |
for (var channel = 0; channel < 3; channel++) | |
{ | |
write.data[to + channel] = data[nearest + channel]; | |
} | |
}; | |
} | |
Action<double, double, int> copyPixelBilinear(ImageData read, ImageData write) | |
{ | |
int width = read.width; | |
int height = read.height; | |
byte[] data = read.data; | |
return (xFrom, yFrom, to) => | |
{ | |
int xl = clamp(Math.Floor(xFrom), 0, width - 1); | |
int xr = clamp(Math.Ceiling(xFrom), 0, width - 1); | |
double xf = xFrom - xl; | |
int yl = clamp(Math.Floor(yFrom), 0, height - 1); | |
int yr = clamp(Math.Ceiling(yFrom), 0, height - 1); | |
double yf = yFrom - yl; | |
int p00 = readIndex(xl, yl, width); | |
int p10 = readIndex(xr, yl, width); | |
int p01 = readIndex(xl, yr, width); | |
int p11 = readIndex(xr, yr, width); | |
for (var channel = 0; channel < 3; channel++) | |
{ | |
double p0 = data[p00 + channel] * (1 - xf) + data[p10 + channel] * xf; | |
double p1 = data[p01 + channel] * (1 - xf) + data[p11 + channel] * xf; | |
write.data[to + channel] = (byte)Math.Ceiling(p0 * (1 - yf) + p1 * yf); | |
} | |
}; | |
} | |
// performs a discrete convolution with a provided kernel | |
Action<double, double, int> kernelResample(ImageData read, ImageData write, int filterSize, Func<double, double> kernel) | |
{ | |
int width = read.width; | |
int height = read.height; | |
byte[] data = read.data; | |
int twoFilterSize = 2 * filterSize; | |
int xMax = width - 1; | |
int yMax = height - 1; | |
double[] xKernel = new double[4]; | |
double[] yKernel = new double[4]; | |
return (xFrom, yFrom, to) => | |
{ | |
var xl = Math.Floor(xFrom); | |
var yl = Math.Floor(yFrom); | |
double xStart = xl - filterSize + 1; | |
double yStart = yl - filterSize + 1; | |
for (var i = 0; i < twoFilterSize; i++) | |
{ | |
xKernel[i] = kernel(xFrom - (xStart + i)); | |
yKernel[i] = kernel(yFrom - (yStart + i)); | |
} | |
for (var channel = 0; channel < 3; channel++) | |
{ | |
double q = 0; | |
for (var i = 0; i < twoFilterSize; i++) | |
{ | |
double y = yStart + i; | |
int yClamped = clamp(y, 0, yMax); | |
double p = 0; | |
for (var j = 0; j < twoFilterSize; j++) | |
{ | |
var x = xStart + j; | |
var index = readIndex(clamp(x, 0, xMax), yClamped, width); | |
p += data[index + channel] * xKernel[j]; | |
} | |
q += p * yKernel[i]; | |
} | |
write.data[to + channel] = (byte)Math.Round(q); | |
} | |
}; | |
} | |
Action<double, double, int> copyPixelBicubic(ImageData read, ImageData write) | |
{ | |
var b = -0.5f; | |
var kernel = new Func<double, double>(x => | |
{ | |
x = Math.Abs(x); | |
double x2 = x * x; | |
double x3 = x * x * x; | |
return x <= 1 ? | |
(b + 2) * x3 - (b + 3) * x2 + 1 : | |
b * x3 - 5 * b * x2 + 8 * b * x - 4 * b; | |
}); | |
return kernelResample(read, write, 2, kernel); | |
} | |
Action<double, double, int> copyPixelLanczos(ImageData read, ImageData write) | |
{ | |
int filterSize = 5; | |
Func<double, double> kernel = x => | |
{ | |
if (x == 0) | |
{ | |
return 1; | |
} | |
else | |
{ | |
var xp = Math.PI * x; | |
return filterSize * Math.Sin(xp) * Math.Sin(xp / filterSize) / (xp * xp); | |
} | |
}; | |
return kernelResample(read, write, filterSize, kernel); | |
} | |
void renderFace(ImageData readData, string face, int rotation, string interpolation, int maxWidth = int.MaxValue) | |
{ | |
int faceWidth = Math.Min(maxWidth, readData.width / 4); | |
int faceHeight = faceWidth; | |
Vector3 cube = new Vector3(); | |
var orientation = orientations[face]; | |
ImageData writeData = new ImageData(faceWidth, faceHeight); | |
var copyPixel = | |
interpolation == "linear" ? copyPixelBilinear(readData, writeData) : | |
interpolation == "cubic" ? copyPixelBicubic(readData, writeData) : | |
interpolation == "lanczos" ? copyPixelLanczos(readData, writeData) : | |
copyPixelNearest(readData, writeData); | |
for (var x = 0; x < faceWidth; x++) | |
{ | |
for (var y = 0; y < faceHeight; y++) | |
{ | |
var to = 4 * (y * faceWidth + x); | |
// fill alpha channel | |
writeData.data[to + 3] = 255; | |
// get position on cube face | |
// cube is centered at the origin with a side length of 2 | |
orientation(cube, (2 * (x + 0.5f) / faceWidth - 1), (2 * (y + 0.5f) / faceHeight - 1)); | |
// project cube face onto unit sphere by converting cartesian to spherical coordinates | |
var r = Math.Sqrt(cube.x * cube.x + cube.y * cube.y + cube.z * cube.z); | |
var lon = mod(Math.Atan2(cube.y, cube.x) + rotation, 2 * Math.PI); | |
var lat = Math.Acos(cube.z / r); | |
copyPixel(readData.width * lon / Math.PI / 2 - 0.5, readData.height * lat / Math.PI - 0.5, to); | |
} | |
} | |
//postMessage({ face, imgData: writeData }); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment