https://webglfundamentals.org/webgl/lessons/webgl-scene-graph.html
https://github.com/Myoungmin/WebGL_Fundamentals
WebGL Scene Graph
- 장면 그래프는 트리의 각 노드가 행렬을 생성하는 트리 구조이다.
- 대부분의 3D 엔진은 장면 그래프를 사용한다.
- 표출하고 싶은 것을 장면 그래프에 넣고, 엔진은 장면 그래프를 살펴보고 그릴 목록을 파악한다.
- 장면 그래프는 계층 구조라 부모 자식의 관계를 갖는다.
장면 그래프를 사용하는이유
행렬에 대한 부모 자식 관계 제공한다.
장면 그래프는 객체에 평행 이동, 회전, 스케일링하기 위한 많은 행렬 중 어떤 행렬 수식을 적용할지 도와주는 구조를 제공한다.
태양의 주위를 지구가 공전하고, 지구의 주위를 달이 공전하는 장면을 렌더링 한다고 가정해보자.
이러한 관계에서 태양의 궤도를 선회하는 달의 움직임을 계산하기 위해서는,
장면 그래프가 없다면 복잡한 수식을 수행해야 한다.
일반적으로 장면 그래프의 각 Node는 지역 공간을 나타낸다.
부모 자식간의 올바른 행렬 수식이 주어지면,
지역 공간에 있는 무엇이든 그 위에 있는 모든 걸 무시할 수 있다.
즉 장면 그래프의 구조를 적용하면,
달은 지구 궤도를 선회하는 것에만 신경쓰면 되고,
태양의 궤도를 선회하는 달의 움직임을 간단하게 적용할 수 있게된다.
장면 그래프로 달을 지구의 자식으로 만들면 지구의 궤도를 공전한다.
그리고 장면 그래프는 지구가 태양을 공전한다는 사실을 처리한다.
노드를 탐색하다 아래와 같이 행렬을 곱하여 이를 수행한다.
worldMatrix = greatGrandParent * grandParent * parent * self(localMatrix)
재귀함수로 이러한 구조를 간단하게 처리할 수 있다.
function computeWorldMatrix(currentNode, parentWorldMatrix) {
// 지역 행렬을 부모의 월드 행렬로 곱하여 "worldMatrix"를 계산
var worldMatrix = m4.multiply(parentWorldMatrix, currentNode.localMatrix);
// 모든 자식에게 동일하게 수행
currentNode.children.forEach(function(child) {
computeWorldMatrix(child, worldMatrix);
});
}
Node 객체 정의
var node = {
localMatrix: ..., // 노드에 대한 "지역" 행렬
worldMatrix: ..., // 노드에 대한 "월드" 행렬
children: [], // 자식 배열
thingToDraw: ??, // 노드에서 그리는 요소
};
Node 객체 초기화
var Node = function() {
this.children = [];
this.localMatrix = m4.identity();
this.worldMatrix = m4.identity();
};
노드의 부모를 설정하는 함수
Node.prototype.setParent = function(parent) {
// 부모에서 자식 제거
if (this.parent) {
var ndx = this.parent.children.indexOf(this);
if (ndx >= 0) {
this.parent.children.splice(ndx, 1);
}
}
// 새로운 부모에 자식 추가
if (parent) {
parent.children.append(this);
}
this.parent = parent;
};
부모-자식 관계를 기반으로 지역 행렬에서 월드 행렬을 계산하는 함수
Node.prototype.updateWorldMatrix = function(parentWorldMatrix) {
if (parentWorldMatrix) {
// 행렬이 전달되므로 수식을 수행하고 결과를 "this.worldMatrix"에 저장
m4.multiply(this.localMatrix, parentWorldMatrix, this.worldMatrix);
} else {
// 행렬이 전달되지 않았으니 "localMatrix"를 "worldMatrix"로 복사
m4.copy(this.localMatrix, this.worldMatrix);
}
// 모든 자식 처리
var worldMatrix = this.worldMatrix;
this.children.forEach(function(child) {
child.updateWorldMatrix(worldMatrix);
});
};
이렇게 구성한 것을 바탕으로 태양, 지구, 달을 그리기 위해서는 아래와 같은 순서로 장면 그래프를 구성한다.
- 노드를 만든다.
- setParent 함수로 노드를 연결한다.
- 객체 목록과 그릴 객체 목록을 만든다.
- 렌더링할 때 프레임마다 각 객체의 지역행렬을 약간 회전하여 업데이트한다.
- 지역행렬을 업데이트하고 모든 월드 행렬을 업데이트 한다.
- 각 객체에 대한 월드 뷰 투영 행렬을 구한다.
// 렌더링을 위한 모든 행렬 계산
objects.forEach(function(object) {
object.drawInfo.uniforms.u_matrix = m4.multiply(viewProjectionMatrix, object.worldMatrix);
});
부모 자식으로 연결하여 지구의 크기를 늘렸는데 의도하지 않은 달의 크기가 커지는 문제는 장면 그래프에 노드를 추가하여 해결할 수 있다.
기존의 장면그래프 구성이 아래와 같다면
태양
|
지구
|
달
노드를 추가하여 장면 그래프를 구성한다.
태양계 - 태양
|
|
|
지구 궤도 - 지구
|
|
|
달 궤도 - 달
이렇게 구성하면 객체별로 크기 조정을 할 수 있다.
이렇게 변경하면 부모에서 자전하도록 행렬로 설정하면 자식도 자동으로 자전이 적용되던 것은 개별적으로 설정해줘야 한다.
scale을 적용하여 수치를 변경할 때 원본의 행렬 값이 0으로 설정되면 0에서 다른 값으로 설정할 수 없는 상황 방지
다른 값으로 행렬을 업데이트하는 클래스를 추가하여 이를 해결한다.
var Node = function(source) {
this.children = [];
this.localMatrix = m4.identity();
this.worldMatrix = m4.identity();
this.source = source;
};
Node.prototype.updateWorldMatrix = function(matrix) {
var source = this.source;
if (source) {
source.getMatrix(this.localMatrix);
}
//...
평행 이동, 회전, 스케일링을 제공하는 소스를 생성하여
var TRS = function() {
this.translation = [0, 0, 0];
this.rotation = [0, 0, 0];
this.scale = [1, 1, 1];
};
TRS.prototype.getMatrix = function(dst) {
dst = dst || new Float32Array(16);
var t = this.translation;
var r = this.rotation;
var s = this.scale;
// 평행 이동, 회전, 스케일링으로 행렬 계산
m4.translation(t[0], t[1], t[2], dst);
matrixMultiply(m4.xRotation(r[0]), dst, dst);
matrixMultiply(m4.yRotation(r[1]), dst, dst);
matrixMultiply(m4.zRotation(r[2]), dst, dst);
matrixMultiply(m4.scaling(s[0], s[1], s[2]), dst, dst);
return dst;
};
노드를 생성할 때 사용한다.
// 초기화할 때 소스로 노드 만들기
var someTRS = new TRS();
var someNode = new Node(someTRS);
// 렌더링할 때
someTRS.rotation[2] += elapsedTime;
이렇게 코드를 구성하면 매번 행렬을 재생성하기 때문에 0에서 다른 값으로 설정할 수 없는 상황을 방지할 수 있다.
https://myoungmin.github.io/WebGL_Fundamentals/
'WebGL' 카테고리의 다른 글
WebGL Fundamentals > Data Textures (0) | 2022.07.08 |
---|---|
WebGL Fundamentals > WebGL Textures (0) | 2022.07.07 |
WebGL Fundamentals > Animation (0) | 2022.07.05 |
WebGL Fundamentals > WebGL 3D : Cameras (0) | 2022.07.03 |
WebGL Fundamentals > WebGL 3D : Perspective (0) | 2022.07.02 |