diff options
| author | Christine Dodrill <me@christine.website> | 2021-02-24 21:04:15 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-02-24 21:04:15 -0500 |
| commit | f8c2897765b41e90c7e32944ae412f8c56c90b4a (patch) | |
| tree | f0751961e560a5c61d617083bf6dade5ed4414bd | |
| parent | c6482fe2fca344241b18a857769e5715c230acc6 (diff) | |
| download | xesite-f8c2897765b41e90c7e32944ae412f8c56c90b4a.tar.xz xesite-f8c2897765b41e90c7e32944ae412f8c56c90b4a.zip | |
Vrchat writeup (#333)
* first part of the VRChat for gmeets writeup
Signed-off-by: Christine Dodrill <me@christine.website>
* more words
Signed-off-by: Christine Dodrill <me@christine.website>
* polishing touches
Signed-off-by: Christine Dodrill <me@christine.website>
| -rw-r--r-- | .vscode/settings.json | 10 | ||||
| -rw-r--r-- | blog/convoluted-vrchat-gchat-setup-2021-02-24.markdown | 214 | ||||
| -rw-r--r-- | static/blog/vrchat/alvr_graph.svg | 172 | ||||
| -rw-r--r-- | static/blog/vrchat/simple_graph.svg | 73 | ||||
| -rw-r--r-- | static/blog/vrchat/total_graph.svg | 173 |
5 files changed, 642 insertions, 0 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bf2bd93 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.wordWrap": "wordWrapColumn", + "editor.wordWrapColumn": 80, + "editor.wordBasedSuggestions": false, + "[markdown]":{ + "editor.wordWrap": "wordWrapColumn", + "editor.wordWrapColumn": 80, + "vim.textwidth": 80, + }, +}
\ No newline at end of file diff --git a/blog/convoluted-vrchat-gchat-setup-2021-02-24.markdown b/blog/convoluted-vrchat-gchat-setup-2021-02-24.markdown new file mode 100644 index 0000000..d813455 --- /dev/null +++ b/blog/convoluted-vrchat-gchat-setup-2021-02-24.markdown @@ -0,0 +1,214 @@ +--- +title: "My Convoluted VRChat Google Meet Setup" +date: 2021-02-24 +tags: + - oculusquest2 + - vr + - vrchat +--- + +# My Convoluted VRChat Google Meet Setup + +Recently the place I work for sent us all VR headsets. I decided to see what it +would take to use that headset to make my camera show a virtual avatar instead +of my meat body face. This is the story of my journey through chaining things +together to make work meetings a bit more fun by using a 3D avatar instead of +myself in some of them. + +[This post uses SVG for diagrams to help explain what's going on here. You may +need to use a browser with SVG support in order to get the best experience with +this article. All the diagrams will be explained after the fact so that people +using screen readers are not left out.](conversation://Mara/hacker) + +<center> + +<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Working at <a href="https://twitter.com/Tailscale?ref_src=twsrc%5Etfw">@Tailscale</a> is great. They sent us all an Oculus Quest 2! <a href="https://t.co/dDhbwO9cFd">pic.twitter.com/dDhbwO9cFd</a></p>— Cadey A. Ratio (@theprincessxena) <a href="https://twitter.com/theprincessxena/status/1362871906597224456?ref_src=twsrc%5Etfw">February 19, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> + +</center> + +So, let's cover the basics from a high level. At a high level a webcam is just +a video source that may or may not have a microphone attached to it. So in +order to get my avatar to show up in a video call, I need some way to make some +window on my computer act as a webcam. This will make the overall dependency +list look like this (for those of you using screen readers I will describe +this diagram below): + +<center> + + + +</center> + +VRChat renders to the Desktop which is picked up by OBS which has the ability +to pretend to be a webcam, which is finally picked up by Google Meet. + +If the VR headset that I got from work was a tethered to the PC kind of VR +headset like the Valve Index or HTC Vive, the next steps would involve full +body tracking or something so that I could have my movements in real life +transfer into movements that my avatar makes. + +However, the VR headset we got sent was an Oculus Quest 2. This is a +_standalone_ VR headset that is basically an Android tablet that you strap +to your face. This makes things a bit more technically challenging because +now you need some way to get the video to the headset and the motion tracking +data from the headset and to the computer at 90 times per second. This requires +a bit more cleverness. + +The Oculus desktop software ships with a feature called Oculus Link that allows +you to use a gaming PC to render the VR data to your headset by sending the +video streams over USB. I had to dig around for a compatible cable (It needs to +be a specific kind of USB-3 to USB-C-3 cable with at least 5 gigabits per +second of transfer capacity) since the ones that +[Oculus sells](https://www.oculus.com/accessories/oculus-link/) are both at +least CAD$110 and out of stock anywhere I can find them in Canada. The 0.75 +meter long cable I had been using was good enough to get me through the first +couple days of experimenting with VR, but it was clear that a better solution +was needed. + +I did some digging and found a bit of software called +[ALVR](https://github.com/alvr-org/alvr#readme) that claimed to let me do VR +from my computer wirelessly. So I set it up on the Quest and on my tower, +which brought up the dependency graph to this: + +<center> + + + +</center> + +ALVR talks with its counterpart on the Quest. This allows you to stream the VR +video and audio bidirectionally. You also need to bring Virtual Audio Cable +into the setup so that you can hear stuff in the game and so that other people +can hear you using the headset mic. However, ALVR is not available on the Quest +store. You need to install [SideQuest](https://sidequestvr.com/setup-howto) for +that. + +[SideQuest lets you sideload Android APK files to your Quest 2 because the +Quest 2 is basically an Android tablet that you strap to your face!](conversation://Mara/happy) + +So I used SideQuest to install the ALVR client on my Quest 2, and then I opened +up VRChat and was able to do everything I was able to do with the wired cable. +It worked beautifully until it didn't. I started running into issues with the +video stream just dying. The foveated encoding (tl;dr: attempting to hack the +image quality based on how eyes work so you don't notice the artifacting as +much) could only do so much and it just ended up not working. Even when I was +only doing it for short amounts of time. There is a lot of WiFi noise in my +apartment or something and it was really interfering with ALVR's stream +encoding. The latency was also noticeable after a bit. + +However, when it worked it worked beautifully. I had to upgrade to the nightly +build of ALVR in order to get game audio and the headset mic working, but once +it all worked it was really convenient. I could walk around my apartment and +I'd also walk around in-game. + +A friend told me that the best experience I could have with wireless VR using a +Quest 2 would be to use [Virtual Desktop](https://www.vrdesktop.net). Apparently +Virtual Desktop has a +[patch that enables SteamVR support](https://sidequestvr.com/app/16), so I +purchased Virtual Desktop on a whim and decided to give it a go. + +Virtual Desktop made ALVR look like a tech demo. All of the latency issues were +solved instantly. Virtual Desktop also made it convenient for me to access my +tower's monitors while in VR, and it has the best typing experience in VR that +I've ever used. + +This brings the dependency graph up to this: + +<center> + + + +</center> + +Now all that was left was to make the camera view look somewhat like it does +when I'm using my work laptop's webcam to make video calls. I started out by taking a picture of my office from about the angle that my laptop sits at. +I ended up with this image: + +<center> + + + +</center> + +Then with some clever use of the +[Chroma key filter in VRChat](https://docs.vrchat.com/docs/vrchat-201812) +I was able to get some basic compositing of my avatar onto the picture. I +fiddled with the placement of things and then I was able to declare success +with this image I posted to Twitter: + +<center> + + + +</center> + +And it worked! I was able to make a call in Google Meet to myself and my +avatar's lip movements synchronized somewhat with the words I was saying. I +had waifu mode enabled! + +[The avatar being used there is based on a character from Xenoblade Chronicles +2 named Pneuma.](conversation://Mara/hacker) + +However, this setup was really janky. I didn't actually get the proper angle +for what my work laptop's camera would actually see. Everything was offset to +the side and it was at way the wrong angle in general. I'm also not sure if I +messed up the sizing of the background image in the OBS view, it looks kinda +stretched on my end as I'm writing this post. + +So I decided that the best way to get the most accurate angle was to record a +video loop using my work laptop's webcam. After some googling I found +[webcamera.io](https://webcamera.io) which let me record some footage of my +office from my work laptop's camera angle. I got down under the desk (so I was +out of view of the camera) and then recorded a 45 second loop of my office +doing nothing (however the flag was slightly moving in the breeze from the desk +fan). + +I also found a VRChat world that claimed to be as optimized as you could +possibly make a VRChat world. It was a blue cube about 30m by 30m. Checking +with SteamVR it brought my frame times down to 3 milliseconds with the stream +camera set up for OBS. It looks like this: + +<center> + + + +</center> + +It's very minimal. You can make the walls go away if you want, which somehow makes it render faster on my RX5700. I'm not sure what's going on there. + +[I'd heckin' love to get a new GPU but until the Bitcoin prices go down we may +be stuck with this setup for a while. An RTX 3070 would really be useful about +now.](conversation://Mara/hacker) + +Anyways, with this minimal world incurring very little to no GPU load, I was +free to do video calls all I wanted. I even did a call with the CEO of the +company I work for with a setup like this. It was fun. + +Now I had everything set up. I can pop on the headset, load up the world, open +OBS, VRChat, Virtual Desktop and get everything set up in about 5 minutes at +worst. Then I can use the seeing your desktop side of Virtual Desktop to +actually watch the meeting and be able to see screen sharing. They can hear me +because Virtual Desktop pipes the headset microphone audio back to my tower, +and the meeting audio comes over my headphones. + +Also at some point I needed to bring AutoHotKey into the mix, so I borrowed +this AutoHotKey script from [SuperUser](https://superuser.com/a/429845) to +resize the VRChat window so that it would fit perfectly into the OBS view: + +```ahk +#=:: ; [Win]+[=] + WinGet, window, ID, A + InputBox, width, Resize, Width:, , 140, 130 + InputBox, height, Resize, Height:, , 140, 130 + WinMove, ahk_id %window%, , , , width, height + return +``` + +Making the VRChat window smaller also helped with the frame times, because it +needed to render less detail per frame. This helped push the framerate +comfortably above 72 FPS in my VR view. + +That is how I get a 3d avatar to show up instead of pictures of the meat golem +I am cursed inside of for work meetings. I will also use this for streaming +coding in the future, so you can all witness the power of a VTube coding stream +where I write Rust or something.
\ No newline at end of file diff --git a/static/blog/vrchat/alvr_graph.svg b/static/blog/vrchat/alvr_graph.svg new file mode 100644 index 0000000..1da23cd --- /dev/null +++ b/static/blog/vrchat/alvr_graph.svg @@ -0,0 +1,172 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.40.1 (20161225.0304) + --> +<!-- Title: G Pages: 1 --> +<svg width="661pt" height="308pt" + viewBox="0.00 0.00 661.01 308.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 304)"> +<title>G</title> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-304 657.0118,-304 657.0118,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_0</title> +<polygon fill="#d3d3d3" stroke="#d3d3d3" points="150.8243,-8 150.8243,-107 374.9943,-107 374.9943,-8 150.8243,-8"/> +<text text-anchor="middle" x="262.9093" y="-90.4" font-family="Times,serif" font-size="14.00" fill="#000000">Quest</text> +</g> +<g id="clust2" class="cluster"> +<title>cluster_1</title> +<polygon fill="#d3d3d3" stroke="#d3d3d3" points="8,-115 8,-292 645.0118,-292 645.0118,-115 8,-115"/> +<text text-anchor="middle" x="326.5059" y="-275.4" font-family="Times,serif" font-size="14.00" fill="#000000">PC</text> +</g> +<!-- AVA --> +<g id="node1" class="node"> +<title>AVA</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="328.7382" cy="-45" rx="38.0139" ry="29.3315"/> +<text text-anchor="middle" x="328.7382" y="-49.2" font-family="Times,serif" font-size="14.00" fill="#000000">ALVR</text> +<text text-anchor="middle" x="328.7382" y="-32.4" font-family="Times,serif" font-size="14.00" fill="#000000">Quest</text> +</g> +<!-- AVR --> +<g id="node3" class="node"> +<title>AVR</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="328.7382" cy="-164" rx="42.2164" ry="29.3315"/> +<text text-anchor="middle" x="328.7382" y="-168.2" font-family="Times,serif" font-size="14.00" fill="#000000">ALVR</text> +<text text-anchor="middle" x="328.7382" y="-151.4" font-family="Times,serif" font-size="14.00" fill="#000000">desktop</text> +</g> +<!-- AVA->AVR --> +<g id="edge7" class="edge"> +<title>AVA->AVR</title> +<path fill="none" stroke="#000000" d="M328.7382,-74.75C328.7382,-91.2066 328.7382,-107.6631 328.7382,-124.1197"/> +<polygon fill="#000000" stroke="#000000" points="325.2383,-124.3662 328.7382,-134.3662 332.2383,-124.3662 325.2383,-124.3662"/> +</g> +<!-- A --> +<g id="node2" class="node"> +<title>A</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="202.0445" cy="-50" rx="43.4415" ry="18"/> +<text text-anchor="middle" x="202.0445" y="-45.8" font-family="Times,serif" font-size="14.00" fill="#000000">Android</text> +</g> +<!-- A->AVA --> +<g id="edge1" class="edge"> +<title>A->AVA</title> +<path fill="none" stroke="#000000" d="M245.2909,-48.2933C256.4871,-47.8514 268.6178,-47.3727 280.071,-46.9207"/> +<polygon fill="#000000" stroke="#000000" points="280.4983,-50.4066 290.3525,-46.5149 280.2222,-43.4121 280.4983,-50.4066"/> +</g> +<!-- AVR->AVA --> +<g id="edge6" class="edge"> +<title>AVR->AVA</title> +<path fill="none" stroke="#000000" d="M328.7382,-134.3662C328.7382,-117.9097 328.7382,-101.4531 328.7382,-84.9965"/> +<polygon fill="#000000" stroke="#000000" points="332.2383,-84.75 328.7382,-74.75 325.2383,-84.75 332.2383,-84.75"/> +</g> +<!-- VAC --> +<g id="node11" class="node"> +<title>VAC</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="463.4742" cy="-164" rx="39.2066" ry="41.0911"/> +<text text-anchor="middle" x="463.4742" y="-176.6" font-family="Times,serif" font-size="14.00" fill="#000000">Virtual</text> +<text text-anchor="middle" x="463.4742" y="-159.8" font-family="Times,serif" font-size="14.00" fill="#000000">Audio</text> +<text text-anchor="middle" x="463.4742" y="-143" font-family="Times,serif" font-size="14.00" fill="#000000">Cable</text> +</g> +<!-- AVR->VAC --> +<g id="edge12" class="edge"> +<title>AVR->VAC</title> +<path fill="none" stroke="#000000" d="M370.9998,-164C384.6622,-164 399.9322,-164 414.0648,-164"/> +<polygon fill="#000000" stroke="#000000" points="414.1153,-167.5001 424.1152,-164 414.1152,-160.5001 414.1153,-167.5001"/> +</g> +<!-- SVR --> +<g id="node4" class="node"> +<title>SVR</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="202.0445" cy="-170" rx="48.6714" ry="18"/> +<text text-anchor="middle" x="202.0445" y="-165.8" font-family="Times,serif" font-size="14.00" fill="#000000">SteamVR</text> +</g> +<!-- SVR->AVR --> +<g id="edge5" class="edge"> +<title>SVR->AVR</title> +<path fill="none" stroke="#000000" d="M250.3034,-167.7145C258.7549,-167.3143 267.5869,-166.896 276.1488,-166.4905"/> +<polygon fill="#000000" stroke="#000000" points="276.5916,-169.9736 286.4148,-166.0044 276.2604,-162.9814 276.5916,-169.9736"/> +</g> +<!-- VRC --> +<g id="node5" class="node"> +<title>VRC</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="66.7294" cy="-206" rx="42.8751" ry="18"/> +<text text-anchor="middle" x="66.7294" y="-201.8" font-family="Times,serif" font-size="14.00" fill="#000000">VRChat</text> +</g> +<!-- VRC->SVR --> +<g id="edge4" class="edge"> +<title>VRC->SVR</title> +<path fill="none" stroke="#000000" d="M102.9914,-196.3527C118.1616,-192.3167 136.0376,-187.5609 152.3981,-183.2082"/> +<polygon fill="#000000" stroke="#000000" points="153.5873,-186.5137 162.3513,-180.5602 151.7875,-179.749 153.5873,-186.5137"/> +</g> +<!-- DESK --> +<g id="node10" class="node"> +<title>DESK</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="202.0445" cy="-229" rx="43.4396" ry="18"/> +<text text-anchor="middle" x="202.0445" y="-224.8" font-family="Times,serif" font-size="14.00" fill="#000000">Desktop</text> +</g> +<!-- VRC->DESK --> +<g id="edge10" class="edge"> +<title>VRC->DESK</title> +<path fill="none" stroke="#000000" d="M106.5966,-212.7764C120.7849,-215.188 136.9469,-217.9351 151.8865,-220.4745"/> +<polygon fill="#000000" stroke="#000000" points="151.5496,-223.9673 161.9947,-222.1926 152.7227,-217.0663 151.5496,-223.9673"/> +</g> +<!-- OBS --> +<g id="node6" class="node"> +<title>OBS</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="328.7382" cy="-235" rx="29.6339" ry="18"/> +<text text-anchor="middle" x="328.7382" y="-230.8" font-family="Times,serif" font-size="14.00" fill="#000000">OBS</text> +</g> +<!-- VIRC --> +<g id="node7" class="node"> +<title>VIRC</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="463.4742" cy="-241" rx="56.7561" ry="18"/> +<text text-anchor="middle" x="463.4742" y="-236.8" font-family="Times,serif" font-size="14.00" fill="#000000">VirtualCam</text> +</g> +<!-- OBS->VIRC --> +<g id="edge8" class="edge"> +<title>OBS->VIRC</title> +<path fill="none" stroke="#000000" d="M358.6405,-236.3316C370.0698,-236.8406 383.6086,-237.4435 397.0947,-238.044"/> +<polygon fill="#000000" stroke="#000000" points="397.2155,-241.5528 407.3614,-238.5012 397.527,-234.5597 397.2155,-241.5528"/> +</g> +<!-- GM --> +<g id="node8" class="node"> +<title>GM</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="596.5569" cy="-197" rx="40.4098" ry="29.3315"/> +<text text-anchor="middle" x="596.5569" y="-201.2" font-family="Times,serif" font-size="14.00" fill="#000000">Google</text> +<text text-anchor="middle" x="596.5569" y="-184.4" font-family="Times,serif" font-size="14.00" fill="#000000">Meet</text> +</g> +<!-- VIRC->GM --> +<g id="edge9" class="edge"> +<title>VIRC->GM</title> +<path fill="none" stroke="#000000" d="M503.043,-227.9177C517.7504,-223.0551 534.5739,-217.4929 549.8596,-212.4391"/> +<polygon fill="#000000" stroke="#000000" points="551.2489,-215.6662 559.6447,-209.204 549.0515,-209.02 551.2489,-215.6662"/> +</g> +<!-- SQ --> +<g id="node9" class="node"> +<title>SQ</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="66.7294" cy="-141" rx="50.9599" ry="18"/> +<text text-anchor="middle" x="66.7294" y="-136.8" font-family="Times,serif" font-size="14.00" fill="#000000">SideQuest</text> +</g> +<!-- SQ->AVA --> +<g id="edge3" class="edge"> +<title>SQ->AVA</title> +<path fill="none" stroke="#000000" d="M103.9261,-128.7112C141.2245,-116.2351 200.2025,-96.0887 250.6301,-77 262.0869,-72.6632 274.3899,-67.7466 285.765,-63.0913"/> +<polygon fill="#000000" stroke="#000000" points="287.15,-66.3061 295.0628,-59.2607 284.4835,-59.8339 287.15,-66.3061"/> +</g> +<!-- SQ->A --> +<g id="edge2" class="edge"> +<title>SQ->A</title> +<path fill="none" stroke="#000000" d="M90.6096,-124.9405C112.7736,-110.0351 145.9219,-87.7427 170.305,-71.3449"/> +<polygon fill="#000000" stroke="#000000" points="172.5102,-74.0798 178.8551,-65.595 168.6038,-68.2712 172.5102,-74.0798"/> +</g> +<!-- DESK->OBS --> +<g id="edge11" class="edge"> +<title>DESK->OBS</title> +<path fill="none" stroke="#000000" d="M245.2909,-231.0481C259.3432,-231.7136 274.8677,-232.4488 288.6771,-233.1028"/> +<polygon fill="#000000" stroke="#000000" points="288.6958,-236.6075 298.8502,-233.5846 289.027,-229.6153 288.6958,-236.6075"/> +</g> +<!-- VAC->GM --> +<g id="edge13" class="edge"> +<title>VAC->GM</title> +<path fill="none" stroke="#000000" d="M501.9672,-173.545C516.4509,-177.1364 533.1046,-181.266 548.3623,-185.0494"/> +<polygon fill="#000000" stroke="#000000" points="547.6019,-188.4668 558.1504,-187.4765 549.2867,-181.6725 547.6019,-188.4668"/> +</g> +</g> +</svg>
\ No newline at end of file diff --git a/static/blog/vrchat/simple_graph.svg b/static/blog/vrchat/simple_graph.svg new file mode 100644 index 0000000..1f80347 --- /dev/null +++ b/static/blog/vrchat/simple_graph.svg @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.40.1 (20161225.0304) + --> +<!-- Title: G Pages: 1 --> +<svg width="610pt" height="123pt" + viewBox="0.00 0.00 609.61 123.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 119)"> +<title>G</title> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-119 605.611,-119 605.611,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_1</title> +<polygon fill="#d3d3d3" stroke="#d3d3d3" points="8,-8 8,-107 593.611,-107 593.611,-8 8,-8"/> +<text text-anchor="middle" x="300.8055" y="-90.4" font-family="Times,serif" font-size="14.00" fill="#000000">PC</text> +</g> +<!-- VRC --> +<g id="node1" class="node"> +<title>VRC</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="58.6871" cy="-45" rx="42.8751" ry="18"/> +<text text-anchor="middle" x="58.6871" y="-40.8" font-family="Times,serif" font-size="14.00" fill="#000000">VRChat</text> +</g> +<!-- DESK --> +<g id="node5" class="node"> +<title>DESK</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="180.5935" cy="-45" rx="43.4396" ry="18"/> +<text text-anchor="middle" x="180.5935" y="-40.8" font-family="Times,serif" font-size="14.00" fill="#000000">Desktop</text> +</g> +<!-- VRC->DESK --> +<g id="edge3" class="edge"> +<title>VRC->DESK</title> +<path fill="none" stroke="#000000" d="M101.6668,-45C109.8542,-45 118.5168,-45 126.9873,-45"/> +<polygon fill="#000000" stroke="#000000" points="127.1794,-48.5001 137.1794,-45 127.1793,-41.5001 127.1794,-48.5001"/> +</g> +<!-- OBS --> +<g id="node2" class="node"> +<title>OBS</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="289.6291" cy="-45" rx="29.6339" ry="18"/> +<text text-anchor="middle" x="289.6291" y="-40.8" font-family="Times,serif" font-size="14.00" fill="#000000">OBS</text> +</g> +<!-- VIRC --> +<g id="node3" class="node"> +<title>VIRC</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="412.0734" cy="-45" rx="56.7561" ry="18"/> +<text text-anchor="middle" x="412.0734" y="-40.8" font-family="Times,serif" font-size="14.00" fill="#000000">VirtualCam</text> +</g> +<!-- OBS->VIRC --> +<g id="edge1" class="edge"> +<title>OBS->VIRC</title> +<path fill="none" stroke="#000000" d="M319.5824,-45C327.4013,-45 336.1504,-45 345.0646,-45"/> +<polygon fill="#000000" stroke="#000000" points="345.1584,-48.5001 355.1584,-45 345.1584,-41.5001 345.1584,-48.5001"/> +</g> +<!-- GM --> +<g id="node4" class="node"> +<title>GM</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="545.1562" cy="-45" rx="40.4098" ry="29.3315"/> +<text text-anchor="middle" x="545.1562" y="-49.2" font-family="Times,serif" font-size="14.00" fill="#000000">Google</text> +<text text-anchor="middle" x="545.1562" y="-32.4" font-family="Times,serif" font-size="14.00" fill="#000000">Meet</text> +</g> +<!-- VIRC->GM --> +<g id="edge2" class="edge"> +<title>VIRC->GM</title> +<path fill="none" stroke="#000000" d="M468.8985,-45C477.4369,-45 486.1935,-45 494.5894,-45"/> +<polygon fill="#000000" stroke="#000000" points="494.6128,-48.5001 504.6128,-45 494.6128,-41.5001 494.6128,-48.5001"/> +</g> +<!-- DESK->OBS --> +<g id="edge4" class="edge"> +<title>DESK->OBS</title> +<path fill="none" stroke="#000000" d="M224.0006,-45C232.4355,-45 241.241,-45 249.5623,-45"/> +<polygon fill="#000000" stroke="#000000" points="249.7795,-48.5001 259.7795,-45 249.7795,-41.5001 249.7795,-48.5001"/> +</g> +</g> +</svg>
\ No newline at end of file diff --git a/static/blog/vrchat/total_graph.svg b/static/blog/vrchat/total_graph.svg new file mode 100644 index 0000000..dd2f813 --- /dev/null +++ b/static/blog/vrchat/total_graph.svg @@ -0,0 +1,173 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<!-- Generated by graphviz version 2.40.1 (20161225.0304) + --> +<!-- Title: G Pages: 1 --> +<svg width="671pt" height="332pt" + viewBox="0.00 0.00 670.88 332.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 328)"> +<title>G</title> +<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-328 666.8816,-328 666.8816,4 -4,4"/> +<g id="clust1" class="cluster"> +<title>cluster_0</title> +<polygon fill="#d3d3d3" stroke="#d3d3d3" points="150.8243,-8 150.8243,-131 525.6487,-131 525.6487,-8 150.8243,-8"/> +<text text-anchor="middle" x="338.2365" y="-114.4" font-family="Times,serif" font-size="14.00" fill="#000000">Quest</text> +</g> +<g id="clust2" class="cluster"> +<title>cluster_1</title> +<polygon fill="#d3d3d3" stroke="#d3d3d3" points="8,-139 8,-316 654.8816,-316 654.8816,-139 8,-139"/> +<text text-anchor="middle" x="331.4408" y="-299.4" font-family="Times,serif" font-size="14.00" fill="#000000">PC</text> +</g> +<!-- VD --> +<g id="node1" class="node"> +<title>VD</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="473.3439" cy="-63" rx="44.1104" ry="29.3315"/> +<text text-anchor="middle" x="473.3439" y="-67.2" font-family="Times,serif" font-size="14.00" fill="#000000">Virtual</text> +<text text-anchor="middle" x="473.3439" y="-50.4" font-family="Times,serif" font-size="14.00" fill="#000000">Desktop</text> +</g> +<!-- VDS --> +<g id="node4" class="node"> +<title>VDS</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="333.6731" cy="-188" rx="47.086" ry="41.0911"/> +<text text-anchor="middle" x="333.6731" y="-200.6" font-family="Times,serif" font-size="14.00" fill="#000000">Virtual</text> +<text text-anchor="middle" x="333.6731" y="-183.8" font-family="Times,serif" font-size="14.00" fill="#000000">Desktop</text> +<text text-anchor="middle" x="333.6731" y="-167" font-family="Times,serif" font-size="14.00" fill="#000000">Streamer</text> +</g> +<!-- VD->VDS --> +<g id="edge4" class="edge"> +<title>VD->VDS</title> +<path fill="none" stroke="#000000" d="M450.736,-88.362C431.0544,-107.6122 401.9557,-134.0523 377.6943,-154.9249"/> +<polygon fill="#000000" stroke="#000000" points="375.3377,-152.3348 370.0053,-161.4899 379.8831,-157.6583 375.3377,-152.3348"/> +</g> +<!-- VDM --> +<g id="node2" class="node"> +<title>VDM</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="333.6731" cy="-57" rx="45.275" ry="41.0911"/> +<text text-anchor="middle" x="333.6731" y="-69.6" font-family="Times,serif" font-size="14.00" fill="#000000">Virtual</text> +<text text-anchor="middle" x="333.6731" y="-52.8" font-family="Times,serif" font-size="14.00" fill="#000000">Desktop</text> +<text text-anchor="middle" x="333.6731" y="-36" font-family="Times,serif" font-size="14.00" fill="#000000">VR mod</text> +</g> +<!-- VDM->VD --> +<g id="edge3" class="edge"> +<title>VDM->VD</title> +<path fill="none" stroke="#000000" d="M379.0202,-58.948C391.5906,-59.488 405.3731,-60.0801 418.4262,-60.6408"/> +<polygon fill="#000000" stroke="#000000" points="418.5601,-64.1497 428.7011,-61.0822 418.8606,-57.1562 418.5601,-64.1497"/> +</g> +<!-- A --> +<g id="node3" class="node"> +<title>A</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="202.0445" cy="-68" rx="43.4415" ry="18"/> +<text text-anchor="middle" x="202.0445" y="-63.8" font-family="Times,serif" font-size="14.00" fill="#000000">Android</text> +</g> +<!-- A->VDM --> +<g id="edge2" class="edge"> +<title>A->VDM</title> +<path fill="none" stroke="#000000" d="M244.7805,-64.4286C255.4706,-63.5353 267.0909,-62.5642 278.2968,-61.6277"/> +<polygon fill="#000000" stroke="#000000" points="278.7592,-65.1014 288.433,-60.7806 278.1762,-58.1257 278.7592,-65.1014"/> +</g> +<!-- VDS->VD --> +<g id="edge5" class="edge"> +<title>VDS->VD</title> +<path fill="none" stroke="#000000" d="M363.2937,-155.8547C384.2099,-135.824 412.4271,-110.4172 435.0521,-91.1733"/> +<polygon fill="#000000" stroke="#000000" points="437.4099,-93.7634 442.8005,-84.6424 432.8986,-88.411 437.4099,-93.7634"/> +</g> +<!-- GM --> +<g id="node9" class="node"> +<title>GM</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="606.4267" cy="-216" rx="40.4098" ry="29.3315"/> +<text text-anchor="middle" x="606.4267" y="-220.2" font-family="Times,serif" font-size="14.00" fill="#000000">Google</text> +<text text-anchor="middle" x="606.4267" y="-203.4" font-family="Times,serif" font-size="14.00" fill="#000000">Meet</text> +</g> +<!-- VDS->GM --> +<g id="edge6" class="edge"> +<title>VDS->GM</title> +<path fill="none" stroke="#000000" d="M380.5611,-192.8134C429.5797,-197.8455 506.2147,-205.7126 556.3811,-210.8625"/> +<polygon fill="#000000" stroke="#000000" points="556.0498,-214.3468 566.355,-211.8864 556.7647,-207.3834 556.0498,-214.3468"/> +</g> +<!-- SVR --> +<g id="node5" class="node"> +<title>SVR</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="202.0445" cy="-188" rx="48.6714" ry="18"/> +<text text-anchor="middle" x="202.0445" y="-183.8" font-family="Times,serif" font-size="14.00" fill="#000000">SteamVR</text> +</g> +<!-- SVR->VDS --> +<g id="edge10" class="edge"> +<title>SVR->VDS</title> +<path fill="none" stroke="#000000" d="M250.6841,-188C259.0146,-188 267.7397,-188 276.273,-188"/> +<polygon fill="#000000" stroke="#000000" points="276.5472,-191.5001 286.5471,-188 276.5471,-184.5001 276.5472,-191.5001"/> +</g> +<!-- VRC --> +<g id="node6" class="node"> +<title>VRC</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="66.7294" cy="-230" rx="42.8751" ry="18"/> +<text text-anchor="middle" x="66.7294" y="-225.8" font-family="Times,serif" font-size="14.00" fill="#000000">VRChat</text> +</g> +<!-- VRC->SVR --> +<g id="edge7" class="edge"> +<title>VRC->SVR</title> +<path fill="none" stroke="#000000" d="M101.2253,-219.293C117.6099,-214.2074 137.4417,-208.0518 155.1663,-202.5504"/> +<polygon fill="#000000" stroke="#000000" points="156.2483,-205.8793 164.7613,-199.5722 154.1732,-199.194 156.2483,-205.8793"/> +</g> +<!-- DESK --> +<g id="node11" class="node"> +<title>DESK</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="202.0445" cy="-242" rx="43.4396" ry="18"/> +<text text-anchor="middle" x="202.0445" y="-237.8" font-family="Times,serif" font-size="14.00" fill="#000000">Desktop</text> +</g> +<!-- VRC->DESK --> +<g id="edge12" class="edge"> +<title>VRC->DESK</title> +<path fill="none" stroke="#000000" d="M108.8022,-233.7311C121.6615,-234.8715 135.9668,-236.1401 149.4342,-237.3344"/> +<polygon fill="#000000" stroke="#000000" points="149.2616,-240.8328 159.5317,-238.2299 149.88,-233.8601 149.2616,-240.8328"/> +</g> +<!-- OBS --> +<g id="node7" class="node"> +<title>OBS</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="333.6731" cy="-265" rx="29.6339" ry="18"/> +<text text-anchor="middle" x="333.6731" y="-260.8" font-family="Times,serif" font-size="14.00" fill="#000000">OBS</text> +</g> +<!-- VIRC --> +<g id="node8" class="node"> +<title>VIRC</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="473.3439" cy="-255" rx="56.7561" ry="18"/> +<text text-anchor="middle" x="473.3439" y="-250.8" font-family="Times,serif" font-size="14.00" fill="#000000">VirtualCam</text> +</g> +<!-- OBS->VIRC --> +<g id="edge8" class="edge"> +<title>OBS->VIRC</title> +<path fill="none" stroke="#000000" d="M363.2937,-262.8793C376.2998,-261.9481 392.129,-260.8147 407.6812,-259.7012"/> +<polygon fill="#000000" stroke="#000000" points="408.1043,-263.18 417.8288,-258.9747 407.6044,-256.1979 408.1043,-263.18"/> +</g> +<!-- VIRC->GM --> +<g id="edge9" class="edge"> +<title>VIRC->GM</title> +<path fill="none" stroke="#000000" d="M515.087,-242.7672C529.0844,-238.6652 544.7902,-234.0626 559.1786,-229.8461"/> +<polygon fill="#000000" stroke="#000000" points="560.28,-233.1706 568.8921,-226.9995 558.3114,-226.4531 560.28,-233.1706"/> +</g> +<!-- SQ --> +<g id="node10" class="node"> +<title>SQ</title> +<ellipse fill="#ffffff" stroke="#ffffff" cx="66.7294" cy="-165" rx="50.9599" ry="18"/> +<text text-anchor="middle" x="66.7294" y="-160.8" font-family="Times,serif" font-size="14.00" fill="#000000">SideQuest</text> +</g> +<!-- SQ->A --> +<g id="edge1" class="edge"> +<title>SQ->A</title> +<path fill="none" stroke="#000000" d="M89.3781,-148.7644C111.9913,-132.5542 146.8811,-107.5436 171.8411,-89.6512"/> +<polygon fill="#000000" stroke="#000000" points="174.1309,-92.3162 180.2192,-83.6453 170.0526,-86.6269 174.1309,-92.3162"/> +</g> +<!-- DESK->VDS --> +<g id="edge11" class="edge"> +<title>DESK->VDS</title> +<path fill="none" stroke="#000000" d="M232.9056,-229.3394C247.3882,-223.3979 265.0521,-216.1514 281.5218,-209.3948"/> +<polygon fill="#000000" stroke="#000000" points="283.1245,-212.5204 291.0478,-205.4868 280.4677,-206.0442 283.1245,-212.5204"/> +</g> +<!-- DESK->OBS --> +<g id="edge13" class="edge"> +<title>DESK->OBS</title> +<path fill="none" stroke="#000000" d="M242.2526,-249.0257C259.0321,-251.9577 278.4424,-255.3493 295.05,-258.2512"/> +<polygon fill="#000000" stroke="#000000" points="294.5265,-261.7127 304.9797,-259.9863 295.7315,-254.8172 294.5265,-261.7127"/> +</g> +</g> +</svg>
\ No newline at end of file |
