LoginSignup
2
2

More than 5 years have passed since last update.

レイマーチングでthree.jsのPerspectiveCameraを使う

Posted at

WebGLでレイマーチングするときでも、three.jsのTHREE.OrbitControlsなどでカメラを制御できたら便利だろうなと思って調べました。

先に結果をお見せすると、こんな感じです。上がTHEEE.OrbitControlsで制御したTHREE.PerspectiveCameraで普通にレンダリングしたもので、下が同じカメラの情報を基にレイマーチングでレンダリングしたものです。色以外は同じように表示されています。
raymarghing-with-threejs-camera.gif

以下で、実際に動いているものを確認できます。
https://aadebdeb.github.io/study-three.js/raymarching-with-threejs-camera.html

具体的に何をしているかというと、three.jsのPerspectiveCamearaの情報をレイマーチングを行うシェーダーにuniformで渡して、その情報を基にレイを生成しています。

レイマーチングを行うシェーダー側はこのようになっています。perspectiveCamera関数の設計に関してはこの記事も参考にしてください。

raymarchのコード(抜粋)

uniform vec2 resolution;
uniform vec3 cameraPosition;
uniform vec3 cameraDirection;
uniform float cameraAspect;
uniform float cameraFov;

void perspectiveCamera(in vec2 uv, in vec3 position, in vec3 cameraDirection, in float fov, in float aspect, out vec3 origin, out vec3 dir) {
  vec2 st = uv * 2.0 - 1.0;
  float radian = fov * PI / 180.0;
  float h = tan(radian * 0.5);
  float w = h * aspect;
  vec3 right = normalize(cross(cameraDirection, vec3(0.0, 1.0, 0.0)));
  vec3 up = normalize(cross(right, cameraDirection));
  dir = normalize(right * w * st.x + up * h * st.y + cameraDirection);
  origin = position;
}

void main() {
  vec3 rayOrigin, rayDirection;
  perspectiveCamera(gl_FragCoord.xy / resolution, cameraPosition, cameraDirection, cameraFov, cameraAspect, rayOrigin, rayDirection);
  vec3 color = raymarch(rayOrigin, rayDirection);
  gl_FragColor = vec4(color, 1.0);
}

JavaScript側でTHREE.PerspectiveCameraの情報をシェーダー側に渡す箇所はこのようになっています。このコードではTHREE.RawShaderMaterialのunifomsパラメータでunifomを渡すことを想定しています。
THREE.PerspectiveCamera#getWorldDirection#getWorldPositionでワールド座標系でのカメラの方向と位置をそれぞれ引数に渡した変数に入れてくれます。このメソッドをレンダリングループのOrbitControls#updateを実行した後などで呼ぶことでレイマーチングのシェーダー側でも現在のカメラの情報を利用することができるようになります。

const raymarchUniforms = {
  resolution: {value: new THREE.Vector2(window.innerWidth, window.innerHeight / 2.0)},
  cameraPosition: {value: new THREE.Vector3()},
  cameraDirection: {value: new THREE.Vector3()},
  cameraAspect: {value: camera.aspect},
  cameraFov: {value: camera.fov},
};

// called in rendering loop
camera.getWorldDirection(raymarchUniforms.cameraDirection.value);
camera.getWorldPosition(raymarchUniforms.cameraPosition.value);
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2