摄像机

我们前面已经使用过layer上自带的透视相机,实际上,我们还可以创建并使用新的相机。

  1. const vertex = /* glsl */ `
  2. attribute vec2 uv;
  3. attribute vec3 position;
  4. uniform mat4 modelViewMatrix;
  5. uniform mat4 projectionMatrix;
  6. varying vec2 vUv;
  7. varying vec4 vMVPos;
  8. varying vec3 vPos;
  9. void main() {
  10. vUv = uv;
  11. vPos = position;
  12. vMVPos = modelViewMatrix * vec4(position, 1.0);
  13. gl_Position = projectionMatrix * vMVPos;
  14. }
  15. `;
  16. const fragment = /* glsl */ `
  17. precision highp float;
  18. uniform sampler2D tMap;
  19. varying vec2 vUv;
  20. varying vec4 vMVPos;
  21. varying vec3 vPos;
  22. void main() {
  23. vec3 tex = texture2D(tMap, vUv).rgb;
  24. float dist = length(vMVPos);
  25. float fog = smoothstep(2.0, 15.0, dist);
  26. tex = mix(tex, vec3(1), fog * 0.8);
  27. tex = mix(tex, vec3(1), smoothstep(1.0, 0.0, vPos.y));
  28. gl_FragColor.rgb = tex;
  29. gl_FragColor.a = 1.0;
  30. }
  31. `;
  32. const {Scene} = spritejs;
  33. const {Camera, Mesh3d, Cylinder, shaders} = spritejs.ext3d;
  34. const container = document.getElementById('container');
  35. const scene = new Scene({
  36. container,
  37. displayRatio: 2,
  38. });
  39. const layer = scene.layer3d('fglayer', {
  40. camera: {
  41. fov: 45,
  42. },
  43. ambientColor: 'white',
  44. });
  45. layer.camera.attributes.pos = [6, 6, 12];
  46. // layer.camera.lookAt([0, 0, 0]);
  47. const texture = layer.createTexture('https://p5.ssl.qhimg.com/t01749f23f82ef86c9f.jpg');
  48. const program = layer.createProgram({
  49. vertex,
  50. fragment,
  51. texture,
  52. });
  53. const model = layer.loadModel('https://s4.ssl.qhres.com/static/4d2c8de20e171997.json');
  54. const size = 20;
  55. const num = size * size;
  56. for(let i = 0; i < num; i++) {
  57. const tree = new Mesh3d(program, {model});
  58. layer.append(tree);
  59. tree.attributes.pos = [(i % size - size * 0.5) * 2, 0, (Math.floor(i / size) - size * 0.5) * 2];
  60. tree.attributes.y += Math.sin(tree.attributes.x * 0.5) * Math.sin(tree.attributes.z * 0.5) * 0.5;
  61. tree.attributes.rotateY = Math.random() * Math.PI * 2;
  62. tree.attributes.scale = 0.8 + Math.random() * 0.3;
  63. }
  64. const cameraProgram = layer.createProgram({
  65. ...shaders.NORMAL,
  66. cullFace: null,
  67. });
  68. // const frustumTransform = new Group3d();
  69. const cameraShape = new Cylinder(cameraProgram, {
  70. radiusBottom: 0.2,
  71. height: 0.7,
  72. radialSegments: 4,
  73. openEnded: true,
  74. rotateOrder: 'XYZ',
  75. rotateX: -90,
  76. rotateY: 45,
  77. });
  78. function cameraPath(time, y) {
  79. const x = 4 * Math.sin(time);
  80. const z = 2 * Math.sin(time * 2);
  81. return [x, y, z];
  82. }
  83. // Add camera used for demonstrating frustum culling
  84. const frustumCamera = new Camera(layer.gl, {
  85. fov: 65,
  86. far: 10,
  87. });
  88. frustumCamera.append(cameraShape);
  89. layer.append(frustumCamera);
  90. layer.setOrbit();
  91. layer.tick((t) => {
  92. frustumCamera.attributes.pos = cameraPath(t * 0.001, 2);
  93. const target = cameraPath(t * 0.001 + 1, 1);
  94. frustumCamera.lookAt(target);
  95. frustumCamera.updateMatrixWorld();
  96. frustumCamera.updateFrustum();
  97. // Traverse all meshes in the scene
  98. layer.traverse((node) => {
  99. if(!node.body.draw) return;
  100. if(node === cameraShape) return;
  101. // perform the frustum test using the demo camera
  102. node.attributes.display = frustumCamera.frustumIntersects(node) ? '' : 'none';
  103. });
  104. });

我们通过

  1. frustumCamera.updateMatrixWorld();
  2. frustumCamera.updateFrustum();

来更新相机的位置变换和视锥,然后就可以通过frustumIntersects来判断对应的目标元素是否处在相机的视野中。

正交相机

除了透视相机之外,我们也可以构造正交相机,正交相机使用orthographic projection(正交投影)来进行投影,在这种投影模式下,无论物体距离相机距离远或者近,在最终渲染的图片中物体的大小都保持不变。

正交相机:

摄像机 - 图1

透视相机:

摄像机 - 图2

要构造正交相机,我们需要给Camera确定以下参数:

  • left — 摄像机视锥体左侧面。
  • right — 摄像机视锥体右侧面。
  • top — 摄像机视锥体上侧面。
  • bottom — 摄像机视锥体下侧面。
  • near — 摄像机视锥体近端面。
  • far — 摄像机视锥体远端面。