three.js에 정의된 Material 클래스를 상속 받고 있는 재질 클래스들
3차원 객체의 3요소
점: PointMaterial
선: LineBasicMaterial
면: MeshBasicMaterial
물리 기반 렌더링으로 Drei의 Material 중에서는 meshStandardMaterial
과 meshPhysicalMaterial
이 있음.
두가지를 가장 많이 사용하며, 계산량이 높아 렌더링 속도는 비교적 느리지만 고품질의 렌더링 결과를 얻을 수 있음.
물리 기반 렌더링(PBR)을 지원하기 때문에 광원과의 상호 작용에 따라 물체의 재질이 현실적으로 보임. 하지만 계산량이 많아 성능 요구가 높음.
...
const { roughness, metalness } = useControls({
roughness: { value: 0, min: 0, max: 1, step: 0.1 },
metalness: { value: 0, min: 0, max: 1, step: 0.1 },
});
...
<mesh ref={mesh1} position={[0.7, 0, 0]}>
<torusKnotGeometry args={[0.5, 0.15, 256, 128]} />
<meshStandardMaterial
visible={true} // 렌더링 여부
transparent={false} // opacity 사용 여부
opacity={1} // 재질 불투명도 0 ~ 1
depthTest={true} // 렌더링 시에 depth buffer 사용하여 검사할 것인지 여부
depthWrite={true} // 렌더링 되고 있는 mesh의 z값을 depth buffer 에 기록할 것인지 여부
side={THREE.FrontSide} // mesh의 어떤면을 렌더링할 것인지 (FrontSide / BackSide / DoubleSide)
color={0xff0000} // 재질 색상
emissive={0x00000}
roughness={roughness} // 거칠기 0 ~ 1
metalness={metalness} // 금속성 0 ~ 1 (거칠기와 금속성은 절절한 값 조절이 필요함)
flatShading={false} // 재질의 면을 평평하게 할 것인지
wireframe={false}
/>
</mesh>
물리 기반 렌더링(PBR)을 지원하지 않으므로 현실적인 재질 표현에 제한이 있음. 하지만 계산량이 적어 렌더링 속도가 빠름.
<meshLambertMaterial
visible={true} // 렌더링 여부
transparent={false} // opacity 사용 여부
opacity={1} // 재질 불투명도 0 ~ 1
depthTest={true} // 렌더링 시에 depth buffer 사용하여 검사할 것인지 여부
depthWrite={true} // 렌더링 되고 있는 mesh의 z값을 depth buffer 에 기록할 것인지 여부
side={THREE.FrontSide} // mesh의 어떤면을 렌더링할 것인지 (FrontSide / BackSide / DoubleSide)
color='#b25383'
wireframe={true}
emissive='#d5d500'
/>
픽셀 단위로 광원의 영향을 계산하는 재질이며, meshLambertMaterial 보다 정교한 쉐이딩 효과를 얻을 수 있음. MeshStandardMaterial
보다 덜 복잡하고 가볍지만 PBR 기술보다는 성능 및 현실적인 효과 면에서 떨어질 수 있음.
<meshPhongMaterial
visible={true} // 렌더링 여부
transparent={false} // opacity 사용 여부
opacity={1} // 재질 불투명도 0 ~ 1
depthTest={true} // 렌더링 시에 depth buffer 사용하여 검사할 것인지 여부
depthWrite={true} // 렌더링 되고 있는 mesh의 z값을 depth buffer 에 기록할 것인지 여부
side={THREE.FrontSide} // mesh의 어떤면을 렌더링할 것인지 (FrontSide / BackSide / DoubleSide)
color={0xff0000} // 재질 색상
emissive={0x000000} // 재질에서 방출하는 색상
specular={0xffff00} // 광원에 의해 반사되는 색상
shininess={100} // 반사의 정도
flatShading={true} // 재질의 면을 평평하게 할 것인지
wireframe={false}
/>
PBR 기술을 사용하여 현실적인 광택, 음영 및 광투과를 표현함. 빛이 투과하고 물체 내부를 밝혀주는 효과를 제공하여 유리같은 면을 표현할 수 있음. 거칠기와 금속도를 설정할 수 있음
...
const { roughness, metalness } = useControls({
roughness: { value: 0, min: 0, max: 1, step: 0.1 },
metalness: { value: 0, min: 0, max: 1, step: 0.1 },
clearcoat: { value: 0, min: 0, max: 1, step: 0.01 },
clearcoatRoughness: { value: 0, min: 0, max: 1, step: 0.01 },
});
...
<mesh ref={mesh1} position={[0.7, 0, 0]}>
<boxGeometry />
<meshPhysicalMaterial
visible={true} // 렌더링 여부
transparent={false} // opacity 사용 여부
opacity={1} // 재질 불투명도 0 ~ 1
depthTest={true} // 렌더링 시에 depth buffer 사용하여 검사할 것인지 여부
depthWrite={true} // 렌더링 되고 있는 mesh의 z값을 depth buffer 에 기록할 것인지 여부
side={THREE.FrontSide} // mesh의 어떤면을 렌더링할 것인지 (FrontSide / BackSide / DoubleSide)
color={0xff0000} // 재질 색상
emissive={0x00000}
roughness={roughness} // 거칠기 0 ~ 1
metalness={metalness} // 금속성 0 ~ 1 (거칠기와 금속성은 절절한 값 조절이 필요함)
flatShading={false} // 재질의 면을 평평하게 할 것인지
wireframe={false}
clearcoat={clearcoat} // 코팅 0 ~ 1
clearcoatRoughness={clearcoatRoughness} // 코팅 거칠기 0 ~ 1
/>
</mesh>
물체의 깊이(depth) 정보에 기반하여 렌더링함. 즉, 카메라로부터 얼마나 떨어져 있는지를 나타내는 값에 따라 그림자 및 표면 디테일을 표현함. 주로 그림자 생성 및 거리에 따른 시각적 효과를 조절하는 데 사용함.
{/* camera 로부터 2.5 떨어진 지점은 0을 할당하고, 6인 지점은 1을 할당하여 렌더링한 재질의 표현 */}
<Canvas camera={{ near: 3.5, far: 6 }}>
...
<mesh ref={mesh1} position={[0.7, 0, 0]}>
<torusKnotGeometry args={[0.5, 0.15, 256, 128]} />
<meshDepthMaterial />
</mesh>
...
</Canvas>
Matcap(Material Capture) 텍스처 기반 렌더링.
Matcap은 실제 광원의 반사를 이미지로 캡처한 텍스처로, 이 텍스처를 사용하여 물체의 표면이 실제 광원을 반사하는 것처럼 렌더링함. 아래의 코드를 보면 조명 코드를 지워도 해당 결과물이 나옴.
import { useRef, useEffect } from 'react';
import { OrbitControls, useTexture } from '@react-three/drei';
const Matcap = () => {
const mesh1 = useRef();
const mesh2 = useRef();
// useTexture를 사용하여 이미지를 로드하고, 로드된 텍스처 객체를 반환
const matcap = useTexture('./images/matcap/gold.jpg');
useEffect(() => {
mesh2.current.material = mesh1.current.material;
}, []);
return (
<>
{/* 컨트롤러 */}
<OrbitControls />
<mesh ref={mesh1} position={[0.7, 0, 0]}>
<torusKnotGeometry args={[0.5, 0.15, 256, 128]} />
<meshMatcapMaterial matcap={matcap} flatShading={true} />
</mesh>
<mesh ref={mesh2} position={[-0.7, 0, 0]}>
<torusGeometry args={[0.5, 0.2]} />
</mesh>
</>
);
};
export default Matcap;
매쉬 표면에 대한 법선 벡터의 x, y, z 값을 색상 요소값인 RGB 값으로 사용하여 표현함.
import { useRef, useEffect } from 'react';
import { OrbitControls, useTexture } from '@react-three/drei';
const Normal = () => {
const mesh1 = useRef();
const mesh2 = useRef();
useEffect(() => {
mesh2.current.material = mesh1.current.material;
}, []);
return (
<>
{/* 컨트롤러 */}
<OrbitControls />
<mesh ref={mesh1} position={[0.7, 0, 0]}>
<torusKnotGeometry args={[0.5, 0.15, 256, 128]} />
<meshNormalMaterial />
</mesh>
<mesh ref={mesh2} position={[-0.7, 0, 0]}>
<torusGeometry args={[0.5, 0.2]} />
</mesh>
</>
);
};
export default Normal;
매쉬 표면에 대한 법선 벡터의 x, y, z 값을 색상 요소값인 RGB 값으로 사용하여 표현함.
(좌: threeTone.jpg 중: fourTone.jpg 우: fiveTone.jpg)
import { useRef, useEffect } from 'react';
import * as THREE from 'three';
import { OrbitControls, useTexture } from '@react-three/drei';
const Toon = () => {
const mesh1 = useRef();
const mesh2 = useRef();
const texture = useTexture('./images/tone/fiveTone.jpg');
// 텍스처의 픽셀화된 모습을 유지하도록 함.
texture.minFilter = THREE.NearestFilter; // 텍스처가 축소될 때 => 가장 가까운 픽셀의 값을 사용하여 텍스처를 축소하는 필터링 방법
texture.magFilter = THREE.NearestFilter; // 텍스처가 확대될 때 => 가장 가까운 픽셀의 값을 사용하여 텍스처를 확대하는 필터링 방법
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]}>
<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 Toon;
주로 어떤 물체의 표면을 반사하는데 사용함. 거울이나 유리와 같은 반사적인 표면을 표현할 수 있음.
import { MeshReflectorMaterial, OrbitControls } from '@react-three/drei';
const Reflector = () => {
return (
<>
<OrbitControls />
<ambientLight intensity={0.2} />
<directionalLight position={[0, 1, 0]} />
<directionalLight position={[1, 2, 8]} intensity={0.7} />
<mesh position={[0, -0.6, 0]} rotation={[-Math.PI / 2, 0, 0]}>
<planeGeometry args={[10, 10]} />
<MeshReflectorMaterial
blur={[300, 100]}
resolution={2048}
mixBlur={1}
mixStrength={30}
roughness={1}
depthScale={0.7}
minDepthThreshold={0.4}
maxDepthThreshold={1.4}
color='#777777'
metalness={0.5}
/>
</mesh>
<mesh position={[0, 0, 0]}>
<boxGeometry />
<meshStandardMaterial color='cyan' />
</mesh>
</>
);
};
export default Reflector;
import { useLoader } from '@react-three/fiber';
import { CubeCamera, MeshRefractionMaterial, OrbitControls } from '@react-three/drei';
import { RGBELoader } from 'three-stdlib';
const Refraction = () => {
const texture = useLoader(RGBELoader, './images/hdr/shanghai_bund_1k.hdr');
return (
<>
<OrbitControls />
<ambientLight intensity={0.2} />
<directionalLight position={[0, 1, 0]} />
<directionalLight position={[1, 2, 8]} intensity={0.7} />
<CubeCamera resolution={1024} frames={1} envMap={texture}>
{(texture) => (
<mesh position={[0, -0.6, 0]} rotation={[-Math.PI / 2, 0, 0]}>
<dodecahedronGeometry />
<MeshRefractionMaterial
envMap={texture}
toneMapped={false}
bounces={2}
aberrationsStrength={0.03}
ior={2.75}
fresnel={1}
color='white'
fastChroma={true}
/>
</mesh>
)}
</CubeCamera>
</>
);
};
export default Refraction;
주로 투명한 물체를 렌더링하는 데 사용함. 유리, 투명한 플라스틱, 물 등의 재질을 표현할 때 사용함.
import * as THREE from 'three';
import { MeshTransmissionMaterial, OrbitControls } from '@react-three/drei';
import { useControls } from 'leva';
const Transmission = () => {
const config = useControls({
transmissionSampler: false,
backside: false,
samples: { value: 10, min: 1, max: 32, step: 1 },
resolution: { value: 2048, min: 256, max: 2048, step: 256 },
transmission: { value: 1, min: 0, max: 1 },
roughness: { value: 0.0, min: 0, max: 1, step: 0.01 },
thickness: { value: 3.5, min: 0, max: 10, step: 0.01 },
ior: { value: 1.5, min: 1, max: 5, step: 0.01 },
chromaticAberration: { value: 0.06, min: 0, max: 1 },
anisotropy: { value: 0.1, min: 0, max: 1, step: 0.01 },
distortion: { value: 0.0, min: 0, max: 1, step: 0.01 },
distortionScale: { value: 0.3, min: 0.01, max: 1, step: 0.01 },
temporalDistortion: { value: 0.5, min: 0, max: 1, step: 0.01 },
clearcoat: { value: 1, min: 0, max: 1 },
attenuationDistance: { value: 0.5, min: 0, max: 10, step: 0.01 },
attenuationColor: '#ffffff',
color: '#c9ffa1',
bg: '#839681',
});
return (
<>
<OrbitControls />
<ambientLight intensity={0.2} />
<directionalLight position={[0, 1, 0]} />
<directionalLight position={[1, 2, 8]} intensity={0.7} />
<mesh>
<sphereGeometry args={[1.4, 128, 128]} />
<MeshTransmissionMaterial {...config} background={new THREE.Color(config.bg)} />
</mesh>
<mesh scale={0.3}>
<torusGeometry args={[0.5, 0.2, 32]} />
<meshStandardMaterial />
</mesh>
</>
);
};
export default Transmission;
웨이블(wobble) 효과: 물체의 표면이나 전체적인 형태가 일정한 주기로 변형되는 효과.
따라서 물체의 표면이나 전체적인 형태가 일정한 주기로 변형되도록 할
때 사용함.
import { MeshWobbleMaterial, OrbitControls } from '@react-three/drei';
const Wobble = () => {
return (
<>
<OrbitControls />
<ambientLight intensity={0.2} />
<directionalLight position={[0, 1, 0]} />
<directionalLight position={[1, 2, 8]} intensity={0.7} />
<mesh>
<torusGeometry />
<MeshWobbleMaterial
factor={1} // 흔들림 정도
speed={10} // 흔들림 속도
/>
</mesh>
</>
);
};
export default Wobble;
물체의 표면을 왜곡시키는 효과를 줄 때 사용함.
import { MeshDistortMaterial, OrbitControls } from '@react-three/drei';
const Distort = () => {
return (
<>
<OrbitControls />
<ambientLight intensity={0.2} />
<directionalLight position={[0, 1, 0]} />
<directionalLight position={[1, 2, 8]} intensity={0.7} />
<mesh>
<torusGeometry />
<MeshDistortMaterial
distort={0.9} // 왜곡 정도
speed={3} // 왜곡 속도
/>
</mesh>
</>
);
};
export default Distort;
재질이 적용된 메시를 화면에 표시하지 않도록 함.
일반적인 mesh에
visible={false}
속성을 주는 것과의 차이점
MeshDiscardMaterial
은 화면에 표시하지는 않지만 그림자는 표현함.
import { MeshDiscardMaterial, OrbitControls } from '@react-three/drei';
const Discard = () => {
return (
<>
<OrbitControls />
<ambientLight intensity={0.2} />
<directionalLight position={[0, 1, 0]} />
<directionalLight position={[1, 2, 8]} intensity={0.7} />
<mesh>
<torusGeometry />
<MeshDiscardMaterial />
</mesh>
</>
);
};
export default Discard;
사용자 정의 쉐이더(Shader)를 통해 물체의 렌더링을 제어하는 데 사용하며, 비교적 저수준의 재질이지만 내장된 재질외에 사용자가 커스텀하여 고급 렌더링으로 사용할 수 있음.
import * as THREE from 'three';
import { extend } from '@react-three/fiber';
import { OrbitControls, shaderMaterial } from '@react-three/drei';
const SimpleMaterial = new shaderMaterial(
// [첫번째 인자] uColor라는 uniform을 정의하여 색상정보를 GPU에 전달
{
uColor: new THREE.Color(1, 0, 0),
},
// [두번째 인자] Vertex(꼭짓점) Shader: Geometry의 좌표값을 화면에 출력하기 위한 좌표로 변경하는 목적
`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
// [세번째 인자] Fragment shader: mesh가 화면에 픽셀 단위로 표시될 때 각 픽셀에 생상 값을 결정하는 목적
// 버텍스 쉐이더에서 계산된 정보를 사용하여 각 픽셀의 색상을 결정함.
`
uniform vec3 uColor;
varying vec2 vUv;
void main() {
gl_FragColor = vec4(vUv.y * uColor, 1.0);
}
`
);
// SimpleMaterial를 태그 형태로 사용하기 위해서 R3F의 extend 함수를 사용함.
extend({ SimpleMaterial });
const Shader = () => {
return (
<>
<OrbitControls />
<ambientLight intensity={0.2} />
<directionalLight position={[0, 1, 0]} />
<directionalLight position={[1, 2, 8]} intensity={0.7} />
<mesh>
<boxGeometry />
<simpleMaterial uColor={'red'} /> {/* jsx 에서는 소문자로 사용 */} {/* uColor를 전달할 수 있음 */}
</mesh>
</>
);
};
export default Shader;