Skip to content

Instantly share code, notes, and snippets.

@gtk2k
Last active June 8, 2018 10:41
Show Gist options
  • Save gtk2k/fb127ee6ca901d843b8cf78e6863fb7b to your computer and use it in GitHub Desktop.
Save gtk2k/fb127ee6ca901d843b8cf78e6863fb7b to your computer and use it in GitHub Desktop.
spherical to cubemap
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