Three.js Chapter 4 section 27 Shaders

황상진·2024년 11월 12일
0

Three.js

목록 보기
14/15
post-thumbnail

Shaders

  • shader란?
  • simple shader 만들기
  • syntax

Shader란

  • WebGL에서 중요한 속성
  • native WebGL 할 때 반드시 알아야함
  • GLSL 언어로 쓰여진 프로그램
  • GPU에 보내진다.
  • geometry의 각 vertex를 위치 시킨다.
  • geometry의 각 pixel의 색을 입힌다.

Shader에 보내는 data들

  • Vertecies coordinates
  • Mesh transformation
  • Camera 정보
  • Colors
  • Textures
  • Lights
  • Fog
  • Etc

GPU가 해당 data를 process한다.

Vertex Shader

  • geometry의 vertex를 위치시킨다.

  • vertex의 coordinates, mesh transformation, camera 정보 등을 shader를 통해서 GPU에 낸다.

  • GPU는 해당 지시에 따라서 vertex를 배치하고 render한다.

  • 각 vertex의 위치 같이 모든 vertex들 다 다른 data를 attribute라고 한다.

  • mesh의 위치와 같이 모든 vertex들이 공유하는 data를 uniform이라고 한다.

  • vertex shader가 먼저 실행되고, vertex들이 배치되면 GPU가 geometry의 보여지는 pixel을 판단하고 fragment shader가 실행된다.

Fragment Shader

  • color each visible fragment of geometry
  • uniform data를 활용해서 모든 보이는 fragment에 color같은 data를 GPU로 보낸다.
  • GPU는 해당 지시를 따라서 color를 입힌다.
  • vertex shader에서 fragment shader로 오는 data는 varying이다.

요약

  • vertex shader - vertex들을 어디에 위치 시킬지 결정
  • fragment shader - 보여지는 fragment에 어떤 색을 칠할지 결정
  • attribute - vertex들 마다 다른 값(위치)을 가진 data들, vertex shader에서만 사용
  • uniform - vertex들 마다 같은 값(mesh position, color)을 가진 data들, vertex shader/ fragment shader에서 사용 가능
  • varing - vertex shader에서 fragment shader로 보내져서 사용되는 data들
  • vertex shader 이후에 fragment shader가 실행된다.

왜 shader를 작성해야하는가?

  • Three.js material은 매우 제한적이다.
  • 직접 작성한 shader는 간단하고, 효율적이다.
  • custom post-processing 가능

shader 작성하는 법

  • 기존 MeshBasicMaterial을 RawShaderMaterial로 변경한다.
const material = new THREE.RawShaderMaterial()
  • shader 폴더 생성 후, vertex shader, fragment shader 파일을 만든다.
  • vertex.glsl / fragment.glsl
// vertex shader
uniform mat4 projectionMatrix;
        uniform mat4 viewMatrix;
        uniform mat4 modelMatrix;

        attribute vec3 position;

        void main()
        {
            gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
        }
// fragment shader
precision mediump float;

        void main()
        {
            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
        }
  • shader 파일을 import 해오기
  • glsl 파일을 import하려면 plugin을 활용한다.
  • vite-plugin-glsl을 설치한다.
  • vite config를 수정한다.
import glsl from 'vite-plugin-glsl'

// ...

export default {
    // ...
    plugins:
    [
        restart({ restart: [ '../static/**', ] }), // Restart server on static file change
        glsl() // Handle shader files
    ]
}
  • import 한 이후에 RawShaderMaterial에 적용한다.
import testVertexShader from './shaders/test/vertex.glsl'
import testFragmentShader from './shaders/test/fragment.glsl'

const material = new THREE.RawShaderMaterial({
    vertexShader: testVertexShader,
    fragmentShader: testFragmentShader
})

RawShaderMaterial의 properties

  • vertexShader
  • fragmentShader
  • wireframe
  • map, alphaMap, opacity, color 등은 glsl shader 파일에서 직접 조작해야한다.

GLSL

  • OpenGL Shading Language
  • C 언어랑 비슷
  • log 찍는건 불가능
  • indent는 상관 없음
  • ; 세미 콜론 필수
  • type language

Float

  • 10진법
      • / * 가능
  • .을 반드시 사용
float a = 1.0;
float b = 2.0;
float c = a/b;

Integer

  • 10진법
      • / * 가능
  • float과 연산하려면 형변환해야한다.
float a = 1.0;
int b = 2;
float c = a* float(b);

Boolean

  • true, false
bool foo = true;
bool bar = flase;

Vector2

  • 2개의 coordinates를 저장할때 사용 (x,y)
vec2 foo = vec2(1.0 , 2.0);

Vector3

  • 3개의 coordinates 를 저장할 때 사용(x,y,z)
  • 3D coordinates 표현할 때 사용
  • r,g,b로 color도 표현이 가능
  • vector2를 vector3화 할 수 있다.
  • vector3를 vector2로도 변경 가능하다. (swizzle이라고 부른다.)
vec3 bar = vec3(1.0,2.0,3.0);

vec3 purpleColor = vec3(0.0);
purpleColor.r = 0.5;
purpleColor.b = 1.0;

vec2 foo = vec2(1.0,2.0);
vec3 bar = vec3(foo,3.0);

vec3 bar = vec3(foo,3.0);
vec2 temp = bar.xy;
temp = bar.xz;

Vector4

  • 4개의 coordinates 를 저장할 때 사용(x,y,z,w)
vec4 foo = vec4(1.0,2.0,3.0,4.0);
float bar = foo.w;
float bar = foo.a;(alpha)

Function

  • function에는 type이 있어야한다.
  • return하지 않으면 void를 사용한다.
  • parameter를 전달할 수 있다.
  • 다수의 built-in 함수를 사용가능하다. (sin,cos,max,min,pow...)
float loremIpsum(){
	float a = 1.0;
    float b = 2.0;
    return a+b;
}

shader 함수들을 찾아볼 수 있다.

https://shaderific.com/glsl.html
https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/indexflat.php
https://thebookofshaders.com/

Vertex Shader

main function

  • 자동적으로 불러짐
  • void 타입
  • gl_Position이 자동적으로 있으며, 이들의 위치를 할당해주어야한다.
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;

attribute vec3 position;

void main()
{
    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
}

position

  • vertex들의 x,y,z 값을 가진 vec3
  • x,y,z는 vec3이므로 1.0을 추가해서 vec4화해주어야한다.(gl_Position은 vec4이므로)

Matrices uniforms

  • uniforms
  • gl_Position에 적용하기 위해서 곱해야한다.
  • vec4로 만들어주기 위해서 mat4로 계산한다.

modelMatrix

  • mesh의 position, rotation, sclae의 변화를 적용한다.

viewMatrix

  • camera의 position, rotation, fov, near,far의 변화를 적용한다.

projectionMatrix

  • clip space의 coordinates의 변화를 적용한다.

Fragment Shader

main function

  • 자동적으로 불러짐
  • void 타입
  • gl_FragColor 자동적으로 있으며, 이들의 색상를 할당해주어야한다.

Precision

  • float의 정확도를 결정해주는 설정
  • highp, mediump,lowp가 있다.
  • 의무적이다.
  • 주로 meiump를 사용한다.

gl_FragColor

  • 이미 존재한다.
  • 할당을 해주어야한다.
  • vec4로 구성된다. r,g,b,a
precision mediump float;

    void main()
    {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }

Attributes

  • 각각의 vertex 마다 다른 값을 가진다. (position과 같은)
  • geometry에 attribute를 추가해서 vertex shader에서 접근할 수 있다.
  • varying을 활용해서 vertex shader에서 fragment shader로 보낼 수 있다.
const count = geometry.attributes.position.count
const randoms = new Float32Array(count)

for(let i = 0; i < count; i++)
{
    randoms[i] = Math.random()
}

geometry.setAttribute('aRandom', new THREE.BufferAttribute(randoms, 1))
// ...
attribute float aRandom;

void main()
{
    // ...
    modelPosition.z += aRandom * 0.1;

    // ...
}

precision mediump float;

varying float vRandom;

void main()
{
    gl_FragColor = vec4(0.5, vRandom, 1.0, 1.0);
}

Uniforms

  • 같은 shader를 사용하면서 다른 결과를 야기할 수 있다.
  • 값을 비틀 수 있다.
  • 값을 애니메이션화할 수 있다.
profile
Web FrontEnd Developer

0개의 댓글