Unityでアナモルフィックディストーションを作成する

みなさんこんにちは! 今、私はVRTechで働いており、その仕事の一環として、私がお話ししたい興味深い問題に出会いました。 タスクは、アナモルフィック画像表示を取得することでした。 アナモルフィックディストーションとは何か、そのようなディストーションの平面への線形マッピングの最も単純なケースの計算方法を説明し、Unityを使用して実装したソリューションも提供します。



画像



最近、仕事で興味深い仕事を提供されました。写真を変換してアナモルフィックな歪みを作成する方法をリーフレットに頼ることです。 私は数学が好きで、この理由で私は彼らのことを聞いたことがないので興味を持ちました。 アナモルフィックディストーションとは何ですか?



一般に、光学アナモルフィゼーションは画像の歪みの方法であり、これは相互に垂直な2つの方向で異なるスケールの変換を意味します。 一般に、有望な歪みを除去するために使用されますが、芸術でもかなり人気のあるツールになりました。







信頼できる効果を生み出すには、視点が非常に重要な詳細です。 路上で3Dアートを見た場合、ステップが正しいこと、ステップが残っていること、錯覚が解散していることがわかります。



そのような変換がどのように行われるかについての記事を読んで、一般的に複雑なものは何もないことに気付いた後、私はプログラマーとして、インターネットのどこかに確かに既製のライブラリがあると考えました。 さらに、理想的には、このようなライブラリは、視点、推定画像の高さ、およびその他のバンをパラメータで設定する機能を提供する必要があります。 いくつかの解決策を見つけましたが、どれも機能しませんでした。 したがって、私は自分で書かなければなりませんでした。



そのような画像が一般的にどのように構築されるかを見てみましょう。 最初に、目的の結果が取得され、グリッドに分割されます。 グリッドが細かいほど、ソリューションはより正確になります。 Unityでは、グリッド内のセルの数を調整できるメッシュを生成することにしました。







次に、マップを作成する視点と平面を選択します。 その後、フラットなケースでは簡単です。グリッドポイントと視点を通る線を描くだけです。







私はそれをよく知っているので、選択はユニティに落ちました。また、インターネットで見つけたすべての記事は数学ではなく人気のある言語で書かれており、不必要なジェスチャーなしで(プリンタにソリューションを印刷せずに)アルゴリズムの正確性を検証したかったのです、ここで何が間違っているのかを考えます)。 Unityでは、結果を3D空間でカメラを通して見ることができるため、これは実装が非常に簡単です。 さらに、3D、ベクターなどを操作するためのツールが実装されています。



機能のリストによると、ソリューションは、画像、ユーザーの視点、ユーザーの視点からの結果の高さ(メートル単位)、およびソリューションの精度を受け取ることでした。



これらすべてをできるだけ単純にするために、私はそれを解決する最も簡単な方法を作ることにしました。 UV座標を正しく配置するメッシュを生成し、その上に目的の画像を引き出します。 次に、投影を使用して、新しい頂点と法線を持つ古いメッシュを生成します。



メッシュの構造は単純なので、最初のタスクは非常に簡単に解決されます。単純なメッシュを生成し、三角形に正しく番号を付ける必要がありました。 この場合のメッシュは、画像全体が引き伸ばされる平面であるため、UV座標の正しい配置も難しくありません。

あなたはすでにテスト用の猫を見てきましたが、私は「光」なしでそれを見せますので、それはより美しいです。







メッシュが生成された後、新しい頂点と新しい法線を計算する必要がありました。 2つの点を通る線と3つの点で定義される平面の交点を決定することにより、新しい頂点を定義することにしました。 平面と直線の交点を決定する方法では、平面の法線を決定する必要があるため、新しい法線も計算されました。



ネタバレ見出し
private Vector3 GetIntersectionOfLineAndPlane( Vector3 linePoint1, Vector3 linePoint2, Vector3 planePoint1, Vector3 planePoint2, Vector3 planePoint3, ref Vector3 planeNormal) { Vector3 result = new Vector3(); planeNormal = Vector3.Cross(planePoint2 - planePoint1, planePoint3 - planePoint1); planeNormal.Normalize(); Debug.Log(planeNormal.ToString()); var distance = Vector3.Dot(planeNormal, planePoint1 - linePoint1); var w = linePoint2 - linePoint1; var e = Vector3.Dot(planeNormal, w); if(e != 0) { result = new Vector3( linePoint1.x + wx * distance / e, linePoint1.y + wy * distance / e, linePoint1.z + wz * distance / e); } else { result = Vector3.one * (-505); } return result; } }
      
      







すべて、ソリューションの準備ができました! 一般に、タスクは非常に単純ですが、同時に興味深いものです。 この効果により、かなり面白いゲームを作成し、プレイヤーの頭脳を破壊することさえできると思います。 ビデオでは、作業の結果を見ることができます。 (ビデオの元の画像は非常に暗いです。光を調整しなかったためです)







少し後で機能を完成させて、反射面だけでなく複数の平面に投影して、このような効果を実現できるようにします。







ソリューションコードを直接使用して、さらに開発を続けることもできます




All Articles