# How to make 360 degree videos

Posted 6 years ago
14920 Views
|
12 Replies
|
46 Total Likes
|
12 Replies
Sort By:
Posted 6 years ago

All the code:

## Viewing polyhedron

SetDirectory[NotebookDirectory[]];
$HistoryLength = 2; ClearAll[RefineSphere] RefineSphere[{fi_, vc_}] := Module[{vcc, max = Max[fi], nfi, avgs, midpoints, newtriangles, newvc}, vcc = DeleteDuplicates[ Sort /@ Flatten[Subsets[#, {2}] & /@ fi, 1]]; nfi = MapThread[Rule, {vcc, max + Range[Length[vcc]]}]; avgs = Mean[vc[[#]]] & /@ vcc; newvc = Normalize /@ (vc~Join~avgs); midpoints = Partition[#, 2, 1, 1] & /@ fi; newtriangles = MapThread[ Append[Flatten[#, 1] & /@ ({#2, Partition[Sort /@ RotateRight[#1], 2, 1, 1]}\[Transpose]), Sort /@ #1] &, {midpoints, fi}]; newtriangles = Flatten[Replace[newtriangles, nfi, {3}], 1]; {newtriangles, newvc} ] refine = 0 sphereFI = PolyhedronData["Icosahedron", "FaceIndices"]; sphereVC = N@PolyhedronData["Icosahedron", "VertexCoordinates"]; {sphereFI, sphereVC} = Nest[RefineSphere, {sphereFI, sphereVC}, refine]; sphereVC *= 40; Graphics3D[GraphicsComplex[sphereVC, Polygon[sphereFI]], Lighting -> "Neutral"] ## Vectors size = 600; (* each view will be rendered this size squared *) vangle = 80 \[Degree]; (* view angle is 80\[Degree] *) (* calculate the various viewing angles *) viewvectorup = viewvectors = Normalize[Mean[Part[sphereVC, #]]] & /@ sphereFI; crosslen = (Norm /@ viewvectors) Tan[vangle/2]; viewvectorup = Normalize[{0, 0, 1} - ({0, 0, 1}.Normalize[#]) Normalize[#]] & /@ viewvectorup; viewvectorup *= crosslen; viewvectorright = MapThread[Normalize@*Cross, {viewvectors, viewvectorup}]; viewvectorright *= crosslen; (* make black-white masks for each view, note that we use 0.96 \ viewing angle here such that each of the views overlaps a bit *) masks = MapThread[ Rasterize[ Graphics3D[{EdgeForm[], White, GraphicsComplex[sphereVC, Polygon[#1]]}, Boxed -> False, Lighting -> "Neutral", ViewVertical -> {0, 0, 1}, ViewVector -> {{0, 0, 0}, #2}, ViewAngle -> (0.96 vangle), ImageSize -> {size, size}, Background -> None], "Image", Background -> None] &, {sphereFI, viewvectors}]; (* this is what all the viewingvectors look like (blue), green is the \ pointing-up vector, and in red is the vector to the right *) gr = Graphics3D[ MapThread[{Blue, Arrow[Tube[{{0, 0, 0}, #1}]], Green, Arrow[Tube[{#1, #1 + #2}]], Red, Arrow[Tube[{#1, #1 + #3}]]} &, {viewvectors, viewvectorup, viewvectorright}]] ## Functions vvv = {viewvectors, viewvectorright, viewvectorup}\[Transpose]; normvvv = Map[Normalize, vvv, {2}]; nvvv = Map[Norm, vvv, {2}]; svv = vvv/nvvv^2; ClearAll[invtransfunc]; invtransfunc[{\[Phi]_, \[Theta]_}, n_] := Module[{vp}, vp = {Cos[\[Phi]] Sin[\[Theta]], Sin[\[Theta]] Sin[\[Phi]], Cos[\[Theta]]}; If[vp.vvv[[n, 1]] <= 0, vp = (vp nvvv[[n, 1]]/(vp.normvvv[[n, 1]])); {vp.svv[[n, 2]], vp.svv[[n, 3]]} , {-2, -2} ] ]; ClearAll[MakeScene] MakeScene[t_] := Module[{rot, p1, p2}, rot = 2 \[Pi] t; p1 = ParametricPlot3D[{-8.2 + (8 + Cos[v]) Sin[u + t], (8 + Cos[v]) Cos[u + t], Sin[v] + 0.75}, {u, 0, 2 Pi}, {v, 0, 2 Pi}, ViewVector -> {{0, 0, 0}, {0, 1, 0}}, ViewAngle -> 80 \[Degree], PlotStyle -> Directive[Green, Opacity[1]], MeshShading -> {{Red, Blue}, {Blue, Red}}, MeshFunctions -> {#4 &, #5 &}, PlotPoints -> 80, Axes -> False, Mesh -> {51, 10}, Lighting -> {{"Ambient", White}}, ViewVertical -> {0, 0, 1}]; p2 = Graphics3D[{Orange, Sphere[{-8.2 - 8 Sin[2 rot], 8 Cos[2 rot], 0.45}, 0.2]}, Lighting -> {{"Ambient", White}}]; Show[{p1, p2}] ] ClearAll[Make360] Make360[scenef_, t_] := Module[{scn, views, antetransform, posttransform, \[Alpha]cs, imgout}, \[Beta]++; scn = scenef[t]; views = MapThread[ Rasterize[ Show[scn, ViewVector -> {{0, 0, 0}, #1}, ViewVertical -> {0, 0, 1}, ViewAngle -> vangle, Boxed -> False, ImageSize -> {size, size}, Background -> White], "Image"] &, {viewvectors}]; antetransform = MapThread[ImageMultiply, {masks, views}]; posttransform = Table[ImageTransformation[antetransform[[n]], invtransfunc[#, n] &, DataRange -> {{-1, 1}, {-1, 1}}, PlotRange -> {{-\[Pi], \[Pi]}, {0, \[Pi]}}, Padding -> Transparent] , {n, Length[antetransform]} ]; \[Alpha]cs = AlphaChannel /@ posttransform; posttransform = RemoveAlphaChannel /@ posttransform; \[Alpha]cs = Binarize[#, 0.95] & /@ \[Alpha]cs; posttransform = MapThread[SetAlphaChannel, {posttransform, \[Alpha]cs}]; imgout = ImageCompose[First[posttransform], Rest[posttransform]]; imgout = RemoveAlphaChannel[imgout]; imgout ] ## Calculation/Rendering/Export SetDirectory[NotebookDirectory[]]; Dynamic[\[Beta]] \[Beta] = 0; n = 150; t = Most[Subdivide[0, 1, n]]; fns = "out" <> ToString[#] <> ".png" & /@ Range[Length[t]]; CloseKernels[]; LaunchKernels[4]; DistributeDefinitions[Make360, MakeScene, n, t, fns, i, vvv, normvvv, svv, nvvv, invtransfunc, masks, size, vangle, viewvectors, viewvectorright, viewvectorup, sphereFI, sphereVC]; SetSharedVariable[\[Beta]]; ParallelEvaluate[$HistoryLength = 2];

ParallelDo[
If[! FileExistsQ[fns[[i]]],
out = Make360[MakeScene, t[[i]]];
Export[fns[[i]], out];
]
,
{i, 1, Length[fns]}
,
Method -> "FinestGrained"
]
Attachments:
Posted 6 years ago
 A final note: the rendering took a LOT of time (order of 10 hours). The spherical to x-y coordinates calculation can probably be sped up using compile. Furthermore, instead of rendering 20 images, it would be nice to make a function that for each ?-? pixel chooses the 'right' view and get that specific pixel, that would speed it up ~20x.And a Python script was used to 'inject' some metadata for YouTube to recognize it as 360 degree video: Have a look here: 360 metadata youtubeThe scripts have changed slightly since they posted that video and you have to call (also through the Terminal) gui.py, this will show you a small interface in which you can select your video and have it injected with some metadata. Then simply uploading it to YouTube and they do the rest.Creating the sphere can now be done much easier in version 10, by using regions: DiscretizeRegion[Sphere[], MaxCellMeasure -> 1] DiscretizeRegion[Sphere[], MaxCellMeasure -> 0.1] DiscretizeRegion[Sphere[], MaxCellMeasure -> 0.01] This will also create the polygons that are more and more refined, it looks like it is using the same algorithm to refine!
Posted 6 years ago
 Awesome, simply awesome, @Sander Huisman, I've just read about these yesterday and you just happen to run in with this beautiful post! Thanks so much for sharing, I'll pass this along to my various connections.
Posted 6 years ago
 Thanks! Glad you like it! I'm not yet sure if I will use it for something useful, but now I have some idea how to do it! It, however, take a LOT of time! So if you want to do higher resolution and more frames it will take considerable amount of time! Some renderers (like 3DS max) can render immediately in 360 degree format, the Wolfram language not yet! But that does make it more fun ;)
Posted 6 years ago
 Hi Matthias,The python script is VERY simple (at least on Mac). But should be equally simple on Linux, windows: not so sure. Just download it here: https://github.com/google/spatial-media and follow the video to change the permissions chmod ....... and the run it ./gui.py in the window you can browse your file and resave it. DONE
Posted 6 years ago
 Wow! Clap! Clap! Clap! This can only be the start of something big. How complicated is the python script that you need to upload to youtube?
Posted 6 years ago
 This is what it should look like, and that is about all you need to do:
Posted 6 years ago
 Thanks for the pointer!
Posted 6 years ago
 Sander, I tried the above code but looks like my outputs are always coming out zero. No errors are there so not sure what is missing. Any tips?Best, Vijay
Posted 6 years ago
 - another post of yours has been selected for the Staff Picks group, congratulations !We are happy to see you at the tops of the "Featured Contributor" board. Thank you for your wonderful contributions, and please keep them coming!