确认过了WebGL
你给个三角形A
深度是-0.5
,三角形B
深度是0
,开启深度检测后,画出来发现深度小的三角形(-0.5)
画在了前面,注意深度检测后就是深度大的在后面,由此得出WebGL/OpenGL 裁剪空间和NDC默认就是左手坐标系
然而为什么说一些引擎如threejs是右手坐标系
呢,是因为在投影矩阵里给第三行乘了个-1
,就是左乘一个Z方向反转矩阵(注意三行三列为-1,其他和单位矩阵一样),会导致z轴翻转
,转换成了所谓右手坐标系
,可以看下正射投影和透视投影算出的第三行和正常的投影矩阵比
RGB分别代表XYZ,相机位置是 0 0 200,目标物体位置是0 0 0
证明:
我们先假设相机坐标系是右手坐标系的,世界坐标系中相机位置是(0,0,200)
, 物体A绿色在(0,0,0)
, 物体B红色在(0,0,100)
,所以相机坐标系中,物体A绿色坐标为(0,0,-200)
,物体B红色坐标为(0,0,-100)
,我们知道THREE裁剪空间会对Z轴进行反转,所以经过裁剪空间处理后,物体A绿色坐标为(0,0,200)
,物体B红色坐标为(0,0,100)
,按照深度大的显示在后面的
原理,所以应该是红色物体显示在绿色物体前面
,由下图看假设是正确的
。
let camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 200);
camera.lookAt(new THREE.Vector3(0, 0,0));
const boxA = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10), new THREE.MeshPhongMaterial({
color: 'rgb(0,255,0)',
}));
boxA.position.x = 0;
boxA.position.y = 0;
boxA.position.z = 0;
scene.add(boxA);
const boxB = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10), new THREE.MeshPhongMaterial({
color: 'rgb(255,0,0)',
}));
boxB.position.x = 0;
boxB.position.y = 0;
boxB.position.z = 100;
scene.add(boxB);
以three的正射投影为例,near=-1,far=1,算出的投影矩阵如下图所示,这都和unity是统一的
,最终裁剪空间会对z轴进行反转
RGB分别代表XYZ,相机位置是 0 0 0,目标物体位置是0 0 10
unity相机空间是右手坐标系
,相机朝向为z轴的负方向,下面证明一下。
我们先假设相机坐标系是右手坐标系的,世界坐标系中相机位置是(0,0,-20)
, 物体A绿色在(0,0,0)
, 物体B红色在(0,0,-10)
,所以相机坐标系中,物体A绿色坐标为(0,0,-20)
,物体B红色坐标为(0,0,-10)
,我们知道Unity裁剪空间会对Z轴进行反转,所以经过裁剪空间处理后,物体A绿色坐标为(0,0,20)
,物体B红色坐标为(0,0,10)
,按照深度大的显示在后面的
原理,所以应该是红色物体显示在绿色物体前面
,由下图看假设是正确的
。
以unity的正射投影为例,near=-1,far=1,算出的投影矩阵如下图所示
所以unity的正射投影矩阵为
这里三行三列为-1,,最终裁剪空间会对z轴进行反转
这就是因为在Unity里当World Space变到Camera Space时,需要做一个反转z轴的操作(因此Unity的视图变换矩阵,相比文章前面的推理还需再乘以一个z轴反转的矩阵)。然后我们做投影变换到Clip Space时,又要给它转回来(再做一次z反转),使得near clip plane在-1,far clip plane在1。
three和unity的世界坐标系区别是three右手,unity左手,unity中使用从three计算得到的坐标应该世界矩阵再前乘 negatezMaT,对z轴反转
// 这里后面乘negatezMaT意思给模型矩阵前乘negatezMaT,将世界坐标系转到左手
Matrix4x4 viewMatrix = float16ToMatrix4x4(three.viewMatrix) * negatezMaT;
threejs
相机位置 (0,0,10)
,默认看向负半轴方向,小车位置是(0,0,0)
,具体代码如下,渲染结果如下。
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - glTF loader</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="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - GLTFLoader<br />
Battle Damaged Sci-fi Helmet by
<a href="https://sketchfab.com/theblueturtle_" target="_blank" rel="noopener">theblueturtle_</a><br />
<a href="https://hdrihaven.com/hdri/?h=royal_esplanade" target="_blank" rel="noopener">Royal Esplanade</a> by <a
href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a>
</div>
<div id = "test" style="width: 1180px; height: 695px;">
</div>
<script type="module">
import * as THREE from '../build/three.module.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
let camera, scene, renderer;
init();
render();
function init() {
// const container = document.createElement('div');
// document.body.appendChild(container);
var canvasContainer = document.getElementById("test")
document.body.appendChild(canvasContainer);
camera = new THREE.PerspectiveCamera(60, canvasContainer.clientWidth / canvasContainer.clientHeight, 0.3, 1000);
camera.position.set(0, 0, 10);
console.log(camera)
// camera.lookAt(new THREE.Vector3(0, 0, 0));
scene = new THREE.Scene();
scene.add(new THREE.DirectionalLight())
// scene.add(new THREE.AmbientLight())
window.camera = camera;
const loader = new GLTFLoader().setPath('models/gltf/DamagedHelmet/glTF/');
loader.load('DamagedHelmet.glb', function (gltf) {
scene.add(gltf.scene);
console.log(gltf.scene.position)
render();
});
let axes = new THREE.AxisHelper(10);
scene.add(axes);
//
var cameraPerspective = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.3, 20);
window.cameraPerspective = cameraPerspective;
cameraPerspective.position.set(0, 0, 10);
var cameraPerspectiveHelper = new THREE.CameraHelper( cameraPerspective );
window.cameraPerspectiveHelper = cameraPerspectiveHelper;
scene.add( cameraPerspectiveHelper );
scene.add( cameraPerspective );
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
// renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setSize(canvasContainer.clientWidth, canvasContainer.clientHeight);
canvasContainer.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.addEventListener('change', render); // use if there is no animation loop
controls.minDistance = 2;
controls.maxDistance = 100;
controls.target.set(0, 0, 0);
controls.update();
}
function render() {
window.cameraPerspectiveHelper.update()
window.cameraPerspective.updateMatrix()
renderer.render(scene, camera);
}
</script>
</body>
</html>
小车位置是(0,0,0)
three相机位置是(0,0,10)
unity中
相机位置(0,0,-10)
,小车位置(0,0,0)
,最终渲染结果如下
小车和相机在世界坐标系下位置如下图所示
three和unity相机的模型矩阵分别是
three和unity中视图矩阵分别是
three和unity中投影矩阵分别是
看出来两个引擎中
视图矩阵
是第三行三列正好相反
投影矩阵
是相同的
目前发现了两个问题:
1.问题1
就算在unity中修改视图矩阵和three中一样,最终显示结果是下面这样
但是
,这个渲染结果和threejs渲染结果还是不一样看着似乎一样,但是它是X轴取反了
,而且是我强制改了双面渲染
,不然它的模型三角面顶点顺序都变了,这个就涉及到了世界坐标系左手和右手的转换问题,这块令人困惑。
2.问题2
这个汽车模型在three的世界坐标系(右手)中和在unity的世界坐标系(左手)中摆放好像是X轴取反了!
这是为啥
可以看那个字母m的朝向,是不是很奇怪。