[R3F] 재질 (Material) 1

박세윤·2023년 10월 27일
0
post-thumbnail

📖 재질 (Material) 1


📌 Material 종류


✅ Material 종류 (three.js)

  • Point / Line / Mesh

  • Three.js의 모든 재질은 material 클래스를 상속받음



✅ Material 종류 (R3F - Drei)



📌 실습 : Material (표면의 시각적 특성을 정의)


✅ meshBasicMaterial

  • 그래픽 객체의 표면 처리 방식을 정의

  • 기본 재질로, 조명에 반응하지 않고 설정된 색상 그대로 표시한다.

import { OrbitControls } from "@react-three/drei";
import { useEffect, useRef } from "react";
import * as THREE from "three"

function MyElement3D() {
    const mesh1 = useRef()
    const mesh2 = useRef()

    useEffect(() => {
        mesh2.current.material = mesh1.current.material
    }, [])

    return (
        <>
            <OrbitControls />

            <ambientLight intensity = {0.2} />

            <directionalLight position = {[0, 1, 0]} />
            <directionalLight position = {[1, 2, 8]} intensity = {0.7} />

            <mesh 
                ref = {mesh1}
                position = {[0.7, 0, 0]}
            >
                <boxGeometry />

                {/* 
                visible : 보이는지 안보이는지 true/false
                transparent : 재질 투명 효과 true/false
                opacity : 투명도 (1 : 불투명, 0 : 투명) - transparent가 true일때만 작동
                depthTest : 렌더링 되고 있는 Mesh의 픽셀을 depthBuffer 값을 활용하여 검사할 것인지
                depthWrite : 렌더링 되고 있는 Mesh의 픽셀에 대한 Z 값을 depthBuffer에 기록할 것인지
                side : Mesh를 구성하고 있는 면 중, 어느 면을 렌더링 할 것인지 (기본값 : FrontSide)
                */}
                <meshBasicMaterial 
                    color = "yellow" 
                    wireframe = {false}
                    visible={true}
                    transparent = {true}
                    opacity = {0.5}
                    depthTest = {true}
                    depthWrite = {true}
                    side = {THREE.DoubleSide}
                />
            </mesh>

            <mesh 
                ref = {mesh2}
                position = {[-0.7, 0, 0]}
            >
                <torusGeometry args={[0.5, 0.2]} />
            </mesh>
        </>
    )
}

export default MyElement3D

  • Z-Buffer는 0 ~ 1 사이 값 : 화면과 멀 수록 크고 가까울 수록 작다.
    • 멀수록 밝고, 가까울수록 어둡다.
      - 동일한 픽셀 위치에서 그려질 때, Z-Buffer를 비교하여, 멀리있는 것과 가까이 있는 것이 서로 버퍼가 덮혀지지 않게 함.
  • 면이 앞면인지, 뒷면인지는 면을 구성하는 삼각형의 좌표가 시계 방향 구성인지, 반시계 방향 구성인지에 따라 결정
    • 반시계 방향 구성 : 앞면
    • 시계 방향 구성 : 뒷면



✅ meshLambertMaterial

  • 물체의 표면이 광원에 어떻게 반응하는지 정의

    	- 고유 속성 : emissive
  • 물체가 빛을 모든 방향으로 균등하게 분산시켜 매트한 외관이 생성된다.

    • 하이라이트나 광택은 표현하지 않는다.
import { OrbitControls } from "@react-three/drei";
import { useEffect, useRef } from "react";
import * as THREE from "three"

function MyElement3D() {
    const mesh1 = useRef()
    const mesh2 = useRef()

    useEffect(() => {
        mesh2.current.material = mesh1.current.material
    }, [])

    return (
        <>
            <OrbitControls />

            <ambientLight intensity = {0.2} />

            <directionalLight position = {[0, 1, 0]} />
            <directionalLight position = {[1, 2, 8]} intensity = {0.7} />

            <mesh 
                ref = {mesh1}
                position = {[0.7, 0, 0]}
            >
                <boxGeometry />

                {/* 
                visible : 보이는지 안보이는지 true/false
                transparent : 재질 투명 효과 true/false
                opacity : 투명도 (1 : 불투명, 0 : 투명) - transparent가 true일때만 작동
                depthTest : 렌더링 되고 있는 Mesh의 픽셀을 depthBuffer 값을 활용하여 검사할 것인지
                depthWrite : 렌더링 되고 있는 Mesh의 픽셀에 대한 Z 값을 depthBuffer에 기록할 것인지
                side : Mesh를 구성하고 있는 면 중, 어느 면을 렌더링 할 것인지 (기본값 : FrontSide)
                */}
                <meshLambertMaterial
                    color = "#d25383" 
                    wireframe = {false}
                    visible={true}
                    transparent = {true}
                    opacity = {1}
                    depthTest = {true}
                    depthWrite = {true}
                    side = {THREE.FrontSide}

                    // 재질 자체에서 검출하는 속성값 (기본 : 검정색 - 어떤 색상도 검출 X)
                    // 위 색이랑 섞임
                    emissive={0x666600}
                />
            </mesh>

            <mesh 
                ref = {mesh2}
                position = {[-0.7, 0, 0]}
            >
                <torusGeometry args={[0.5, 0.2]} />
            </mesh>
        </>
    )
}

export default MyElement3D



✅ meshPhongMaterial

  • Mesh가 렌더링되는 픽셀 단위로 광원의 영향을 계산

    • MeshLambertMaterial 보다 좀 더 정교한 Shading 효과를 얻을 수 있다.
      - 고유 속성 : specular, shininess, flatShading
  • 표면의 반사 부분을 계산하여 광택이 있는 외관을 만드는데 적합하다.

import { OrbitControls } from "@react-three/drei";
import { useEffect, useRef } from "react";
import * as THREE from "three"

function MyElement3D() {
    const mesh1 = useRef()
    const mesh2 = useRef()

    useEffect(() => {
        mesh2.current.material = mesh1.current.material
    }, [])

    return (
        <>
            <OrbitControls />

            <ambientLight intensity = {0.2} />

            <directionalLight position = {[0, 1, 0]} />
            <directionalLight position = {[1, 2, 8]} intensity = {0.7} />

            <mesh 
                ref = {mesh1}
                position = {[0.7, 0, 0]}
            >
                <boxGeometry />

                {/* 
                visible : 보이는지 안보이는지 true/false
                transparent : 재질 투명 효과 true/false
                opacity : 투명도 (1 : 불투명, 0 : 투명) - transparent가 true일때만 작동
                depthTest : 렌더링 되고 있는 Mesh의 픽셀을 depthBuffer 값을 활용하여 검사할 것인지
                depthWrite : 렌더링 되고 있는 Mesh의 픽셀에 대한 Z 값을 depthBuffer에 기록할 것인지
                side : Mesh를 구성하고 있는 면 중, 어느 면을 렌더링 할 것인지 (기본값 : FrontSide)
                emissive : 재질 자체에서 검출하는 속성값 (기본 : 검정색 - 어떤 색상도 검출 X)
                 color 색이랑 섞임
                */}
                <meshPhongMaterial
                    wireframe = {false}
                    visible={true}
                    transparent = {true}
                    opacity = {1}
                    depthTest = {true}
                    depthWrite = {true}
                    side = {THREE.FrontSide}

                    emissive = {0x666600}
                    color = {0xff0000} 

                    // specular : 광원에 의해 반사되는 색상 값
                    // shininess : specular의 반사 정도 조절
                    specular = {0xffff00}
                    shininess = {100}

                    // flatShading : mesh를 평평하게 렌더링할 지 여부
                    flatShading = {true}
                />
            </mesh>

            <mesh 
                ref = {mesh2}
                position = {[-0.7, 0, 0]}
            >
                <torusGeometry args={[0.5, 0.2]} />
            </mesh>
        </>
    )
}

export default MyElement3D



📌 실습 : 물리기반 렌더링(PBR, Physically Based Rendering)


✅ meshStandardMaterial

  • PBR 방식 채택, 현실 세계의 물리적 특성에 근거하여 빛과 상호작용

    • 더 복잡하고 현실적인 조명 효과와 물질감 표현
  • 고유 속성

    • roughness(거칠기)
    • metalness(금속성)
import { OrbitControls } from "@react-three/drei";
import { useControls } from "leva";
import { useEffect, useRef } from "react";
import * as THREE from "three"

function MyElement3D() {
    const mesh1 = useRef()
    const mesh2 = useRef()

    useEffect(() => {
        mesh2.current.material = mesh1.current.material
    }, [])

    const {roughness, metalness} = useControls({
        roughness: {value: 0, min: 0, max: 1, step: 0.01},
        metalness: {value: 0, min: 0, max: 1, step: 0.01}
    })

    return (
        <>
            <OrbitControls />

            <ambientLight intensity = {0.2} />

            <directionalLight position = {[0, 1, 0]} />
            <directionalLight position = {[1, 2, 8]} intensity = {0.7} />

            <mesh 
                ref = {mesh1}
                position = {[0.7, 0, 0]}
            >
                <boxGeometry />

                <meshStandardMaterial 
                    visible={true}
                    transparent = {true}
                    opacity = {0.5}
                    depthTest = {true}
                    depthWrite = {true}
                    side = {THREE.FrontSide}

                    emissive = {0x00000}
                    flatShading = {false}

                    color = {0xff0000} 
                    wireframe = {false}

                    // meshStandardMaterial 고유 특성
                    roughness = {roughness}
                    metalness = {metalness}
                />
            </mesh>

            <mesh 
                ref = {mesh2}
                position = {[-0.7, 0, 0]}
            >
                <torusGeometry args={[0.5, 0.2]} />
            </mesh>
        </>
    )
}

export default MyElement3D



✅ meshPhysicalMaterial

  • meshStandardMaterial에서 한 단계 더 나아가 더 정교한 제어와 현실성을 제공한다.

  • 고유 속성

    • clearcoat : 코팅된 광택 있는 층
    • clearcoatRoughness : clearcoat 표면의 거칠기
  • 유리 재질 전용 속성

    • transmission : 유리 투명도
    • thickness : 유리 두께
    • ior : 유리 굴절률
import { OrbitControls } from "@react-three/drei";
import { useEffect, useRef } from "react";
import { useControls } from "leva";
import * as THREE from "three"

function MyElement3D() {
    const mesh1 = useRef()
    const mesh2 = useRef()

    useEffect(() => {
        mesh2.current.material = mesh1.current.material
    }, [])

    const {roughness, metalness, clearcoat, clearcoatRoughness, transmission, thickness, ior} = useControls({
        roughness: {value: 0, min: 0, max: 1, step: 0.01},
        metalness: {value: 0, min: 0, max: 1, step: 0.01},
        clearcoat: {value: 0, min: 0, max: 1, step: 0.01}, 
        clearcoatRoughness: {value: 0, min: 0, max: 1, step: 0.01},
        transmission: {value: 0, min: 0, max: 1, step: 0.01},
        thickness: {value: 0, min: 0, max: 1, step: 0.01},
        ior: {value: 1.5, min: 0, max: 2.333, step: 0.01}
    })

    return (
        <>
            <OrbitControls />

            <ambientLight intensity = {0.2} />

            <directionalLight position = {[0, 1, 0]} />
            <directionalLight position = {[1, 2, 8]} intensity = {0.7} />

            <mesh 
                ref = {mesh1}
                position = {[0.7, 0, 0]}
            >
                <torusKnotGeometry args = {[0.5, 0.15, 256, 128]} />
              
                <meshPhysicalMaterial 
                    visible={true}
                    transparent = {true}
                    opacity = {0.5}
                    depthTest = {true}
                    depthWrite = {true}
                    side = {THREE.DoubleSide}
                    emissive = {0x00000}
                    flatShading = {false}
                    roughness = {roughness}
                    metalness = {metalness}

                    color = {0xffffff} 
                    wireframe = {false}

                    // meshPhysicalMaterial 고유 속성
                    // clearcoat : 코팅된 광택 있는 층
                    // clearcoatRoughness :  clearcoat 표면의 거칠기
                    clearcoat = {clearcoat}
                    clearcoatRoughness = {clearcoatRoughness}

                    // meshPhysicalMaterial 유리에 사용되는 고유 속성
                    // transmission : 유리투명도
                    // thickness : 유리 두께
                    // ior : 유리 굴절률
                    transmission = {transmission}
                    thickness = {thickness}
                    ior = {ior}
                />
            </mesh>

            <mesh 
                ref = {mesh2}
                position = {[-0.7, 0, 0]}
            >
                <torusGeometry args={[0.5, 0.2]} />
            </mesh>
        </>
    )
}

export default MyElement3D



✅ meshDepthMaterial

  • Scene의 특정 물체의 깊이 정보를 이용하여 렌더링

    • 깊이 정보 : 카메라의 시점에서 물체의 각 부분까지의 거리를 기반으로 함.
  • 물체를 흑백 또는 단색 그레이 스케일로 나타내며, 카메라에 가까운 부분은 밝게, 멀리 떨어진 부분은 어둡게 보통 표현한다.

import { OrbitControls } from "@react-three/drei";
import { useEffect, useRef } from "react";
import { useControls } from "leva";
import * as THREE from "three"

function MyElement3D() {
    const mesh1 = useRef()
    const mesh2 = useRef()

    useEffect(() => {
        mesh2.current.material = mesh1.current.material
    }, [])

    const {roughness, metalness, clearcoat, clearcoatRoughness, transmission, thickness, ior} = useControls({
        roughness: {value: 0, min: 0, max: 1, step: 0.01},
        metalness: {value: 0, min: 0, max: 1, step: 0.01},
        clearcoat: {value: 0, min: 0, max: 1, step: 0.01}, 
        clearcoatRoughness: {value: 0, min: 0, max: 1, step: 0.01},
        transmission: {value: 0, min: 0, max: 1, step: 0.01},
        thickness: {value: 0, min: 0, max: 1, step: 0.01},
        ior: {value: 1.5, min: 0, max: 2.333, step: 0.01}
    })

    return (
        <>
            <OrbitControls />

            <ambientLight intensity = {0.2} />

            <directionalLight position = {[0, 1, 0]} />
            <directionalLight position = {[1, 2, 8]} intensity = {0.7} />

            <mesh 
                ref = {mesh1}
                position = {[0.7, 0, 0]}
            >
                <torusKnotGeometry args = {[0.5, 0.15, 256, 128]} />
              
                <meshDepthMaterial />
            </mesh>

            <mesh 
                ref = {mesh2}
                position = {[-0.7, 0, 0]}
            >
                <torusGeometry args={[0.5, 0.2]} />
            </mesh>
        </>
    )
}

export default MyElement3D

  • App.jsx
import './App.css'
import { Canvas } from '@react-three/fiber'
import MyElement3D from './Component/MyElement3D'

function App() {
  return (
    <>
      {/* camera로부터 거리가 3.5인 픽셀지점은 그 값을 0으로 할당, 
          camera로부터 거리가 6인 지점은 2를 할당 */}
      <Canvas camera = {{near: 3.5, far: 6}} >
        <MyElement3D/>
      </Canvas>   
    </>
  )
}

export default App



✅ meshMatcapMaterial

  • matcaps 사이트 : https://github.com/nidorx/matcaps

  • 특정 반사된 빛의 모습을 가진 텍스처를 이용하여 물체를 렌더링한다.

    • 조명, 반사, 그림자 등 복잡한 상호작용을 단일 이미지로 단순화하여 그 이미지를 기반으로 객체의 외관을 렌더링한다.
  • 고유 특성

    • flatShading : 각진 면으로 표현
import { OrbitControls, useTexture } from "@react-three/drei";
import { useEffect, useRef } from "react";
import { useControls } from "leva";
import * as THREE from "three"

function MyElement3D() {
    const mesh1 = useRef()
    const mesh2 = useRef()

    useEffect(() => {
        mesh2.current.material = mesh1.current.material
    }, [])

    // matcap 이미지 불러오기
    const matcap = useTexture("././public/images/matcap.jpg")

    return (
        <>
            <OrbitControls />

            <ambientLight intensity = {0.2} />

            {/* meshMatcapMaterial은 광원을 필요로 하지 않는다. */}
            {/* <directionalLight position = {[0, 1, 0]} />
            <directionalLight position = {[1, 2, 8]} intensity = {0.7} /> */}

            <mesh 
                ref = {mesh1}
                position = {[0.7, 0, 0]}
            >
                <torusKnotGeometry args = {[0.5, 0.15, 256, 128]} />

                {/* <meshDepthMaterial/> */}
                {/* flatShading : 각진 면으로 표현 */}
                <meshMatcapMaterial matcap={matcap} 
                    flatShading = {true}
                />
                
            </mesh>

            <mesh 
                ref = {mesh2}
                position = {[-0.7, 0, 0]}
            >
                <torusGeometry args={[0.5, 0.2]} />
            </mesh>
        </>
    )
}

export default MyElement3D
  • meshMatcapMaterial은 광원을 필요로 하지 않음. (directionalLight)
  • matcap 이미지를 불러와서 활용함.



✅ meshToonMaterial

  • 색상 톤을 나타내는 이미지가 필요하다.
  • 3D 객체에 대한 만화스러운 외관을 제공
import { OrbitControls, useTexture } from "@react-three/drei";
import { useEffect, useRef } from "react";
import { useControls } from "leva";
import * as THREE from "three"

function MyElement3D() {
    const mesh1 = useRef()
    const mesh2 = useRef()

    useEffect(() => {
        mesh2.current.material = mesh1.current.material
    }, [])

    // tone 불러오기
    const texture = useTexture("././public/images/threeTone.jpg")
    // 색상 보관이 이루어지지 않도록 함. 
    texture.minFilter = THREE.NearestFilter
    texture.magFilter = THREE.NearestFilter

    return (
        <>
            <OrbitControls />

            <ambientLight intensity = {0.2} />

            {/* meshMatcapMaterial은 광원을 필요로 하지 않는다. */}
            {/* <directionalLight position = {[0, 1, 0]} />
            <directionalLight position = {[1, 2, 8]} intensity = {0.7} /> */}

            <mesh 
                ref = {mesh1}
                position = {[0.7, 0, 0]}
            >
                <torusKnotGeometry args = {[0.5, 0.15, 256, 128]} />

                <meshToonMaterial gradientMap={texture} color="cyan" />
                
            </mesh>

            <mesh 
                ref = {mesh2}
                position = {[-0.7, 0, 0]}
            >
                <torusGeometry args={[0.5, 0.2]} />
            </mesh>
        </>
    )
}

export default MyElement3D



profile
개발 공부!

0개의 댓글

관련 채용 정보