본문 바로가기

WebGL

WebGL Fundamentals > WebGL 3D : Perspective

https://webglfundamentals.org/webgl/lessons/webgl-3d-perspective.html

 

WebGL 3D Perspective

How to display perspective in 3D in WebGL

webglfundamentals.org

https://github.com/Myoungmin/WebGL_Fundamentals

 

GitHub - Myoungmin/WebGL_Fundamentals: WebGL 학습 프로젝트

WebGL 학습 프로젝트. Contribute to Myoungmin/WebGL_Fundamentals development by creating an account on GitHub.

github.com

 

 

WebGL 3D : Perspective

현재 샘플에서 더 멀리 있는 것들이 더 작게 보이도록 만드는 쉬운 방법은 클립 공간의 X와 Y를 Z로 나누는 것이다

Z가 증가할수록, 멀어질수록, 더 작게 그려지게 된다.

 

나누기 전에 Z를 fudgeFactor와 곱하면 주어진 거리에 따라 얼마의 비율로 작게 할지 조정할 수 있다.

 

"fudgeFactor"를 곱한 뒤 Z로 나누도록 정점 셰이더를 수정

<script id="vertex-shader-3d" type="x-shader/x-vertex">
//...
uniform float u_fudgeFactor;
//...
void main() {
  // 위치에 행렬 곱하기
  vec4 position = u_matrix * a_position;
 
  // 나누려는 z 조정
  // Z가 -1에서 +1인 클립 공간에 있기 때문에, 나눌 값을 0에서 +2로 조정하기 위해 1을 더해준다.
  float zToDivideBy = 1.0 + position.z * u_fudgeFactor;
 
  // x와 y를 z로 나누기
  gl_Position = vec4(position.xy / zToDivideBy, position.zw);
}
</script>

 

Z가 -1에서 +1인 클립 공간에 있기 때문에,

나눌 값을 0에서 +2로 조정하기 위해 1을 더해준다.

fudgeFactor로 데이터를 넘길 수 있도록 자바스크립트를 작성

  //...
  var fudgeLocation = gl.getUniformLocation(program, "u_fudgeFactor");
 
  //...
  var fudgeFactor = 1;
  //...
  function drawScene() {
    ...
    // fudgeFactor 설정
    gl.uniform1f(fudgeLocation, fudgeFactor);
 
    // 지오메트리 그리기
    var primitiveType = gl.TRIANGLES;
    var offset = 0;
    var count = 16 * 6;
    gl.drawArrays(primitiveType, offset, count);

 

WebGL은 정점 셰이더의 gl_Position에 할당한 x,y,z,w 값을 가져와 자동으로 w로 나눈다.

zToDivideBy로 나누는 작업을 수행하던 vertex shader에서 나누는 작업을 제거하고  gl_Position.w에 zToDivideBy을 넣어도 동일하게 동작하는 것을 확인할 수 있다.

<script id="vertex-shader-3d" type="x-shader/x-vertex">
//...
uniform float u_fudgeFactor;
//...
void main() {
  // 위치에 행렬 곱하기
  vec4 position = u_matrix * a_position;
 
  // 나누려는 z 조정
  float zToDivideBy = 1.0 + position.z * u_fudgeFactor;
 
  // x와 y를 z로 나누는 것 제거
  //gl_Position = vec4(position.xy / zToDivideBy, position.zw);

  // x, y, z를 "zToDivideBy"로 나누기
  // w를 zToDivideBy 설정하기만 하면 WebGL이 자동으로 나눈다.
  gl_Position = vec4(position.xyz, zToDivideBy);
}
</script>

 

 

WebGL이 자동으로 W로 나눈다는 사실이 유용한 이유.

z를 w에 복사하기 위해 또 다른 행렬을 사용할 수 있기 때문이다.

 

WebGL이 편리하게 Z로 나누는 작업을 해주기 때문에 행렬만 사용하도록 수정할 수 있다.

 

행렬 연산으로 w_out = z_in * fudgeFactor + 1;이 적용되도록 설정한다.

Z → W 행렬을 만드는 함수를 만든다.

function makeZToWMatrix(fudgeFactor) {
  return [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, fudgeFactor,
    0, 0, 0, 1,
  ];
}

그걸 사용하도록 자바스크립트를 설정

    //...
    // 행렬 계산
    var matrix = makeZToWMatrix(fudgeFactor);
    matrix = m4.multiply(matrix, m4.projection(gl.canvas.clientWidth, gl.canvas.clientHeight, 400));
    matrix = m4.translate(matrix, translation[0], translation[1], translation[2]);
    matrix = m4.xRotate(matrix, rotation[0]);
    matrix = m4.yRotate(matrix, rotation[1]);
    matrix = m4.zRotate(matrix, rotation[2]);
    matrix = m4.scale(matrix, scale[0], scale[1], scale[2]);
    //...

 

 

지금까지 내용으로 기본적으로 모든 내용들은 Z로 나누는 게 원근감을 준다는 것과, 
WebGL이 편리하게 Z로 나누는 작업을 해준다는 사실을 알 수 있다.

 

 

 

 

Perspective를 적용한 이후 3D 객체의 Z를 조정하면 일찍 사라지는 현상을 방지하기

WebGL은 X와 Y 혹은 +1에서 -1까지 클리핑하는 것처럼 Z도 클리핑을 한다.

 

Z에 하나의 행렬 연산을 수행하여 원하는 범위를 -1에서 +1사이로 다시 매핑하면 일찍 사라지는 현상을 방지할 수 있다.

 

fudgeFactor 대신에 fieldOfView를 결정하고, 이를 위한 올바른 값을 계산한다.

 

행렬을 만드는 함수

var m4 = {
  perspective: function(fieldOfViewInRadians, aspect, near, far) {
    var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians);
    var rangeInv = 1.0 / (near - far);
 
    return [
      f / aspect, 0, 0, 0,
      0, f, 0, 0,
      0, 0, (near + far) * rangeInv, -1,
      0, 0, near * far * rangeInv * 2, 0
    ];
  },
 
  //...

행렬의 값이 결정되는 과정

  1. zNear와 fieldOfView가 주어지면, zNear의 항목이 Z = -1이 되도록필요한 값을 계산한다.
  2. fieldOfView의 절반인 zNear의 위와 아래는 각각 Y = -1과 Y = 1이 된다.
  3. 일반적으로 디스플레이 영역의 width / height로 aspect가 설정되고,  aspect를 이용하여 Y에서 X에 사용할 값을 계산한다.
  4. 마지막으로 zFar의 항목이 Z = 1이 되도록 하기 위해 Z에서 얼마나 크기를 조정할지 알아낸다.

만든 행렬이 하는 역할

  • 절두체가 클립 공간 안에 있도록 하고 zNear과 zFar를 클립공간의 앞과 뒤로 변환한다.
  • 각도별로 시야각을 선택한다.

 

4면 원뿔 모양을 "절두체(frustum)"라고 한다.

 

행렬절두체 안에 있는 공간을 가져와서 클립 공간으로 변환한다.

 

zNear은 물체의 앞쪽이 잘리는 곳을 정의하고, zFar은 물체의 뒤쪽이 잘리는 곳을 정의한다.

 

 

 

 

 

 

 

https://myoungmin.github.io/WebGL_Fundamentals/

 

WebGL_Fundamentals

WebGL이란? WebGL은 Web Graphics Library의 약자로 웹상에서 2D 및 3D 그래픽을 렌더링하기 위한 로우 레벨 Javascript API.

myoungmin.github.io