본문 바로가기

WebGL

WebGL Fundamentals > Directional Lighting

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

 

WebGL 3D - Directional Lighting

How to implement directional lighting 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 : Directional Lighting

방향성 조명은 빛이 한 방향에서 균일하게 들어온다고 가정한다.

태양 빛이 너무 멀리 있어서 빛살이 물체의 표면 모두를 평행하게 비추는 상황을 방향성 조명이라고 할 수 있다.

 

빛이 어떤 방향으로 가고 있는지 알고 물체의 표면이 향하는 방향을 알면,

두 방향의 스칼라곱을 구할 수 있고, 

두 방향 사이의 각도에 대한 코사인을 얻을 수 있다.

 

두 방향이 정확히 반대라면 스칼라곱은 -1이고, 

정확히 같은 지점에 있다면 스칼라곱은 1이다.

 

3D 객체의 표면이 향하는 방향과 빛이 비추는 방향을 안다면,

그 스칼라곱을 구할 수 있기 때문에, 

색상에 해당 스칼라곱으로 곱하여 조명을 만들 수 있게된다.

 

조명을 만드는데 필요한 스칼라곱을 구할 수 있는 전제 조건은 빛을 받는 3D 객체의 표면이 향하는 방향을 알아야 한다.

 

3D 객체의 표면이 향하는 방향은 법선을 통해 구할 수 있다.

 

법선

법선은 선이나 표면에 대해 직각을 이룬다.

3D 그래픽에서 법선은 표면이 향하는 방향을 설명하는 단위 벡터를 뜻한다.

 

큐브를 예로 들면 큐브의 각 면이 향하는 방향을 나타내려면 3개의 다른 법선이 필요하기 때문에,

각 모서리에 3개의 법선을 가진다.

 

법선에 대한 정보를 Float32Array로 생성하고, 버퍼에 넣어주고, Vertex Shader에 Attribute로 넘겨줘서 조명을 만들어줘야 한다.

 

Directional 조명 적용하기

먼저 정점 셰이더를 통해 프래그먼트 셰이더로 법선을 전달한다.

 

객체의 방향이 변경될 때 법선의 방향을 변경줘야 객체가 변경될 때 조명이 바뀌는 것을 적용할 수 있다.

 

법선에 world 행렬을 곱하여 법선의 방향을 변경해줄 수 있다.

하지만 월드 행렬에 스케일링을 적용하면, 

월드 행렬을 곱하게 될 때 잘못된 법선을 얻게 되는데, 

이를 방지하기 위해서는 월드 행렬에 조작이 필요하다.

 

월드 행렬의 역을 구하고, 열을 행으로 바꾸는 전치를 한 행렬을 법선의 방향을 구하는데 사용한다.

이러한 u_worldInverseTranspose라는 행렬을 곱하여 법선의 방향을 구하면 올바른 방향을 구할 수 있게된다.

행렬을 전치하는 코드

var m4 = {
  transpose: function(m) {
    return [
      m[0], m[4], m[8], m[12],
      m[1], m[5], m[9], m[13],
      m[2], m[6], m[10], m[14],
      m[3], m[7], m[11], m[15],
    ];
  },
//...

 

법선이 방향이기 때문에 평행 이동을 고려하지 않는다.

a_normal를 mat3(u_worldInverseTranspose)로 곱한다.

행렬의 오리엔테이션 부분은 행렬의 상단 3x3 영역에만 있기 때문에 mat3로 곱하게 된다.

정점 셰이더를 통해 프래그먼트 셰이더로 법선을 전달한다.

<!-- vertex shader -->
<script  id="vertex-shader-3d" type="x-shader/x-vertex">
attribute vec4 a_position;
attribute vec3 a_normal;

uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;

varying vec3 v_normal;

void main() {
  // 위치에 행렬 곱하기
  gl_Position = u_worldViewProjection * a_position;

  // 법선의 방향을 정하고 프래그먼트 셰이더로 전달
  // 법선이 방향이기 때문에 평행 이동을 고려하지 않는다.
  // 행렬의 오리엔테이션 부분은 행렬의 상단 3x3 영역에만 있기 때문에 mat3로 곱하게 된다.
  v_normal = mat3(u_worldInverseTranspose) * a_normal;
}
</script>

프래그먼트 셰이더는 빛의 방향과 법선의 스칼라곱을 이용한 수식을 수행한다.

<!-- fragment shader -->
<script  id="fragment-shader-3d" type="x-shader/x-fragment">
precision mediump float;

// 정점 셰이더에서 전달됩니다.
varying vec3 v_normal;

uniform vec3 u_reverseLightDirection;
uniform vec4 u_color;

void main() {
  // v_normal이 베링이기 때문에 보간되므로 단위 벡터가 아닙니다.
  // 정규화하면 다시 단위 벡터가 됩니다.
  vec3 normal = normalize(v_normal);

  float light = dot(normal, u_reverseLightDirection);

  gl_FragColor = u_color;

  // 색상 부분(알파 제외)에만 "light" 곱하기
  gl_FragColor.rgb *= light;
}
</script>

그런 다음 u_worldViewProjection,  u_worldInverseTranspose, u_color,  u_reverseLightDirection의 위치를 찾아야 한다.

  // 유니폼 탐색
  var worldViewProjectionLocation = gl.getUniformLocation(program, "u_worldViewProjection");
  var worldInverseTransposeLocation = gl.getUniformLocation(program, "u_worldInverseTranspose");
  var colorLocation = gl.getUniformLocation(program, "u_color");
  var reverseLightDirectionLocation = gl.getUniformLocation(program, "u_reverseLightDirection");

그리고 u_worldViewProjection,  u_worldInverseTranspose 행렬을 설정하고 u_color와 u_reverseLightDirection를 설정한다.

 // 행렬 설정
 gl.uniformMatrix4fv(worldViewProjectionLocation, false, worldViewProjectionMatrix);
 gl.uniformMatrix4fv(worldInverseTransposeLocation, false, worldInverseTransposeMatrix);
 
 // 사용할 색상 설정
 gl.uniform4fv(colorLocation, [0.2, 1, 0.2, 1]); // 초록색
 
 // 빛의 방향 설정
 gl.uniform3fv(reverseLightDirectionLocation, m4.normalize([0.5, 0.7, 1]));

 

 

 

 

 

 

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

 

WebGL_Fundamentals

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

myoungmin.github.io