Three.js로 3D 플랫폼 개발 [Material]

Material과 관련된 10가지 종류의 mesh에 대해 알아본다.

Featured image

Three.js를 이용한 3D 플랫폼 개발 개념공부

Material

Mesh는 Geometry와 Material로 이루어져 있는 것으로 알고 있다. 우리는 Geometry는 Shape을 구성하는 요소라는 걸 알고 있고, 배웠기 때문에, Geometry를 잘 생각해보면 좋을 것 같다.

기본적인 Material의 종류는 10가지 정도로 정리해볼 수 있을 것 같다.

meshBasicMaterial

wireframe 자체 속성은 meshBasicMaterial에만 있는 것은 아니다, 다만 다른 속성들과 다르게 우리가 생각하느 그 뼈대의 느낌으로 생각하는 wireframe의 형태는 meshBasicMaterial이기 때문에 이것을 메인으로 생각하면 될 것 같다.

특히, 신경써야하는 부분은, transparent 속성을 false로 해둔다면, opacity조정을 못하는 상황이 생기기도 한다고 한다.

기본적으로 meshBasicMaterial과 같은 속성은, 빛의 영향을 받지 않는 재질을 의미하기 때문에 기본적으로 명암 같은 것들이 보여지지 않는다.

<fog attach={"fog"} args={['blue', 3, 10]}/>
<mesh>
    <meshBasicMaterial 
        color="red"
        visible = {true}
        transparent = {false}
        opacity={0.7}
        side={THREE.BackSide}
        alphaTest={0.6}
        depthWrite={true}
        fog={true}
    /> 
</mesh>

depth buffer

이 depth buffer(또는 Z-Buffer)는 깊이 정보를 저장하는 데 사용한다. 이는 Object3D 화면에 어떤 순서로 그려지는지 결정된다.

그래서 depthtest랑 depthwrite랑 뭐가 다른건데?

Reference

LambertMaterial

<mesh>
    <meshLambertMaterial    //매트한 재질
        color="red"
        visible = {true}
        transparent = {false}
        opacity={0.7}
        side={THREE.BackSide}
        alphaTest={0.6}
        depthWrite={true}
        fog={true}

        // emissive={'yellow'} //재질보다는 방충하는 색깔 값이라고 생각하면 된다.
    /> 
</mesh>

반짝이지 않는 표면을 구현할 때 주로 쓰이는 material. 살짝 매트한 재질을 의미한다고 생각하면 되겠다.

PhongMaterial

<mesh>
    <meshPhongMaterial    //쨍한 재질(반사광)
        color="red"
        visible = {true}
        transparent = {false}
        opacity={0.7}
        side={THREE.BackSide}
        alphaTest={0.6}
        depthWrite={true}
        fog={true}

        emissive={'black'} //재질보다는 방충하는 색깔 값이라고 생각하면 된다.
        specular={"#fff"}
        shininess={40}
        flatShading={true}
    /> 
</mesh>

반사광이 있는 표면을 구현할 때 쓰는 material.

물리 기반 렌더링(PBR. Physical Based Rendering)이 적용된 material

물리적인 원리에 기반하여 물체의 실제 광학적 특성을 더 정확하게 시뮬레이션하여 현실감 있는 결과를 나타내는 렌더링 기법. 더 복잡하고 정밀하여, 계산량이 더 많고 렌더링 엔진에 높은 성능을 요구

StandardMaterial

{/* 물리기반 렌더링 */}
<mesh>
    <meshStandardMaterial
        color="red"
        visible = {true}
        transparent = {false}
        opacity={0.7}
        side={THREE.BackSide}
        alphaTest={0.6}
        depthWrite={true}
        fog={true}

        emissive={'black'} //재질보다는 방충하는 색깔 값이라고 생각하면 된다.
        roughness={1}
        metalness={0.9}
        flatShading={true} 
    /> 
</mesh>

StandardMaterial에서 Clearcoat, Physically-based transparency,Advanced reflectivity, Sheen property를 추가한 것이 바로 PhysicalMaterial이 된다.

<mesh>
    <meshPhysicalMaterial
        color="white"
        visible = {true}
        transparent = {true}
        opacity={0.7}
        side={THREE.BackSide}
        alphaTest={0.6}
        depthWrite={true}
        fog={true}

        emissive={'black'} //재질보다는 방충하는 색깔 값이라고 생각하면 된다.
        roughness={1}
        metalness={0.9}
        flatShading={false}
        clearcoat={0}
        clearcoatRoughness={0}

        transmission={0.5}
        thickness={controls.thickness}
        ior = {2.33}
    /> 
</mesh>

normal과 physical의 차이점을 확인할 수 있을 것 같다.

NormalMaterial

<mesh>
    <meshNormalMaterial/> 
    {/* Normal 값들을 RGB값으로 변환 시킨 것이다. */}
</mesh>

normal vector(법선 벡터) 값을 RGB로 나타낸 material. 객체의 표면에서 수직으로 나아가는 벡터를 RGB로 그대로 옮겨 해당 vertex의 color가 지정되는 것이다.

DepthMaterial

<mesh>
    <meshDepthMaterial/>
</mesh>

빛과는 상관없이 Depth 값에 의해 그려지는 material. depth는 카메라와 객체가 렌더되는 위치와의 거리를 나타낸 값이므로, 카메라의 zoom 정도에 따라서 밝기가 달라진다.

MatcapMaterial

const matcap = useTexture('./img/matcap2.jpg');

return(
    <mesh>
        <meshMatcapMaterial matcap={matcap} flatShading = {false}/>
        {/* 빛이 없어도 그대로 나타내짐 */}
    </mesh>
)

원하는 표면을 덮어씌울 수 있는 material.

ToonMaterial

const tone = useTexture('./img/fiveTone.jpg');
tone.minFilter = THREE.NearestFilter;
tone.magFilter = THREE.NearestFilter;

return(
    <mesh>
        <meshToonMaterial gradientMap={tone} color = "brown"/>
    </mesh>
)

만화 느낌이 나도록 만드는 material.

group으로 자동화

const meshRef = useRef<THREE.Mesh>(null);
const groupRef = useRef<THREE.Mesh>(null);

useEffect(()=>{
    const meshLength = groupRef.current!.children.length;
    for(let i = 0; i<groupRef.current!.children.length; i++){
        console.log(groupRef);
        const mesh = groupRef.current!.children[i] as THREE.Mesh;
        mesh.geometry = meshRef.current!.geometry;
        mesh.position.x = i % (meshLength/2) *2 -4;
        mesh.position.z = 0;
        if(i >= meshLength /2){
            mesh.position.z = 2;
        }
    }
    // meshCopyRef1.current!.geometry = meshRef.current!.geometry;
    // meshCopyRef2.current!.geometry = meshRef.current!.geometry;
},[])