camera.updateProjectionMatrix()
是 Three.js 中一个常用的方法,尤其是在相机参数(如视场角、宽高比、近剪切面、远剪切面)发生变化后,需要更新相机的投影矩阵。以下是其详细讲解:
camera.updateProjectionMatrix()
方法描述
- 用途:当相机的参数发生变化时,更新相机的投影矩阵。
- 适用对象:此方法适用于所有继承自
THREE.Camera
的相机类型,包括THREE.PerspectiveCamera
和THREE.OrthographicCamera
。
使用场景
- 当你更改了相机的某些属性,例如视场角 (
fov
)、宽高比 (aspect
)、近剪切面 (near
) 或远剪切面 (far
) 后,需要调用此方法来更新投影矩阵。 - 在响应窗口大小变化的函数(如
onWindowResize
)中,通常会调用此方法以确保投影矩阵与新的窗口尺寸匹配。
示例代码
以下是如何在 THREE.PerspectiveCamera
中使用 updateProjectionMatrix()
的示例:
// 创建一个透视相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// 设置相机的位置
camera.position.z = 5;
// 当窗口大小发生变化时更新相机的宽高比并更新投影矩阵
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
});
属性和方法
相关属性
对于 THREE.PerspectiveCamera
,以下是一些相关属性:
fov
(视场角):表示相机垂直视场角,单位为度。aspect
(宽高比):表示相机视图的宽高比,通常为 canvas 的宽高比。near
(近剪切面):表示从相机到最近可渲染物体的距离。far
(远剪切面):表示从相机到最远可渲染物体的距离。
对于 THREE.OrthographicCamera
,以下是一些相关属性:
left
:表示相机视锥体的左边界。right
:表示相机视锥体的右边界。top
:表示相机视锥体的上边界。bottom
:表示相机视锥体的下边界。near
:表示相机视锥体的近剪切面。far
:表示相机视锥体的远剪切面。
相关方法
updateProjectionMatrix()
:更新相机的投影矩阵,使其反映当前的相机参数。
camera.updateProjectionMatrix()
是一个在更改相机属性后必须调用的方法,以确保相机的投影矩阵是最新的,从而正确渲染场景。没有输入参数或返回值,主要用于更新相机的内部状态。
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - instancing test (single triangle)</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>
<div id="container"></div>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - instancing demo (single triangle)
<div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
</div>
<script id="vertexShader" type="x-shader/x-vertex">
precision highp float;
uniform float sineTime;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec3 offset;
attribute vec4 color;
attribute vec4 orientationStart;
attribute vec4 orientationEnd;
varying vec3 vPosition;
varying vec4 vColor;
void main(){
vPosition = offset * max( abs( sineTime * 2.0 + 1.0 ), 0.5 ) + position;
vec4 orientation = normalize( mix( orientationStart, orientationEnd, sineTime ) );
vec3 vcV = cross( orientation.xyz, vPosition );
vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
vColor = color;
gl_Position = projectionMatrix * modelViewMatrix * vec4( vPosition, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
precision highp float;
uniform float time;
varying vec3 vPosition;
varying vec4 vColor;
void main() {
vec4 color = vec4( vColor );
color.r += sin( vPosition.x * 10.0 + time ) * 0.5;
gl_FragColor = color;
}
</script>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
let container, stats;
let camera, scene, renderer;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 10 );
camera.position.z = 2;
scene = new THREE.Scene();
// 创建一个 Vector4 实例,用于存储四元数
const vector = new THREE.Vector4();
const instances = 50000;
const positions = [];
const offsets = [];
const colors = [];
const orientationsStart = [];
const orientationsEnd = [];
// 定义单个三角形的顶点位置
positions.push( 0.025, - 0.025, 0 );
positions.push( - 0.025, 0.025, 0 );
positions.push( 0, 0, 0.025 );
// 为每个实例化的三角形设置属性
for ( let i = 0; i < instances; i ++ ) {
// 随机生成偏移量
offsets.push( Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 );
// 随机生成颜色
colors.push( Math.random(), Math.random(), Math.random(), Math.random() );
// 随机生成起始方向四元数
vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
vector.normalize();
orientationsStart.push( vector.x, vector.y, vector.z, vector.w );
// 随机生成结束方向四元数
vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
vector.normalize();
orientationsEnd.push( vector.x, vector.y, vector.z, vector.w );
}
// 创建 InstancedBufferGeometry 实例
const geometry = new THREE.InstancedBufferGeometry();
geometry.instanceCount = instances; // 设置实例化数量
// 将顶点位置属性添加到几何体
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
// 将偏移量属性添加到几何体
geometry.setAttribute( 'offset', new THREE.InstancedBufferAttribute( new Float32Array( offsets ), 3 ) );
// 将颜色属性添加到几何体
geometry.setAttribute( 'color', new THREE.InstancedBufferAttribute( new Float32Array( colors ), 4 ) );
// 将起始方向四元数属性添加到几何体
geometry.setAttribute( 'orientationStart', new THREE.InstancedBufferAttribute( new Float32Array( orientationsStart ), 4 ) );
// 将结束方向四元数属性添加到几何体
geometry.setAttribute( 'orientationEnd', new THREE.InstancedBufferAttribute( new Float32Array( orientationsEnd ), 4 ) );
// 创建原始着色器材质
const material = new THREE.RawShaderMaterial( {
uniforms: {
'time': { value: 1.0 },
'sineTime': { value: 1.0 }
},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
side: THREE.DoubleSide,
forceSinglePass: true,
transparent: true
} );
// 创建网格并添加到场景中
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
// 创建 WebGL 渲染器
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
// 检查 WebGL2 支持
if ( renderer.capabilities.isWebGL2 === false && renderer.extensions.has( 'ANGLE_instanced_arrays' ) === false ) {
document.getElementById( 'notSupported' ).style.display = '';
return;
}
// 添加 GUI 控制
const gui = new GUI( { width: 350 } );
gui.add( geometry, 'instanceCount', 0, instances );
// 添加统计信息
stats = new Stats();
container.appendChild( stats.dom );
// 添加窗口调整事件监听器
window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
const time = performance.now();
const object = scene.children[ 0 ];
object.rotation.y = time * 0.0005;
object.material.uniforms[ 'time' ].value = time * 0.005;
object.material.uniforms[ 'sineTime' ].value = Math.sin( object.material.uniforms[ 'time' ].value * 0.05 );
renderer.render( scene, camera );
}
</script>
</body>
</html>