๐Ÿ”ง OPENRNDR 3D ์•„ํŠธ์›Œํฌ ๋งŒ๋“ค๊ธฐ

BamgasiJMยท4์ผ ์ „

OPENRNDR

๋ชฉ๋ก ๋ณด๊ธฐ
14/17
post-thumbnail

์ด ๊ธ€์€ OPENRNDR 0.5.0 ๊ธฐ์ค€ 3D ์•„ํŠธ์›Œํฌ๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ๊ณต์‹ ๊ฐ€์ด๋“œ(3D graphics, Shade styles, Transformations, Render targets)์™€ ORX(orx-mesh-generators, orx-camera)๋ฅผ ๊ทผ๊ฑฐ๋กœ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

OPENRNDR์€ Blender๋‚˜ Three.js์ฒ˜๋Ÿผ ์™„์„ฑ๋œ 3D ์—”์ง„ยท์”ฌ ๊ทธ๋ž˜ํ”„ยทGLTF ๋กœ๋”๋ฅผ ๊ธฐ๋ณธ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ๋‹ค์Œ ํŒŒ์ดํ”„๋ผ์ธ์„ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ ๊ตฌ์„ฑํ•˜์—ฌ ์ œ๋„ˆ๋ ˆ์ดํ‹ฐ๋ธŒ ์•„ํŠธยท์ ˆ์ฐจ์  3D ๊ทธ๋ž˜ํ”ฝ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Kotlin ์ˆ˜ํ•™ โ†’ Mesh / VertexBuffer โ†’ 3D transform โ†’ Camera โ†’ GLSL ShadeStyle โ†’ (RenderTarget) โ†’ ํ™”๋ฉด

๊ธฐ๋ณธ ๋ฉ”์‹œ ์ƒ์„ฑ ยท ์นด๋ฉ”๋ผ ยท VertexBuffer ยท ๋ฒ„ํ…์Šค ๋ณ€ํ˜• ยท ShadeStyle ์กฐ๋ช…/๋””์Šคํ† ์…˜ ยท ๊นŠ์ด ๋ฒ„ํผ ยท ์ œ๋„ˆ๋ ˆ์ดํ‹ฐ๋ธŒ 3D ํŒจํ„ด์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.


1. ๊ณตํ†ต import

import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.math.Vector3
import org.openrndr.math.transforms.transform
import org.openrndr.extra.camera.Orbital
import org.openrndr.extra.camera.OrbitalCamera
import org.openrndr.extra.meshgenerators.boxMesh
import org.openrndr.extra.meshgenerators.sphereMesh
import kotlin.math.cos
import kotlin.math.sin

2. 3D ๋ฉ”์‹œ ์ƒ์„ฑ

2.1 orx-mesh-generators ๊ธฐ๋ณธ primitive

orx-mesh-generators๋Š” positionยทnormalยทtexture coordinate๊ฐ€ ํฌํ•จ๋œ VertexBuffer๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€ํ‘œ ํ•จ์ˆ˜:

ํ•จ์ˆ˜์„ค๋ช…
boxMesh(width, height, depth)์ง์œก๋ฉด์ฒด
sphereMesh(sides, segments, radius)UV ๊ตฌ
cylinderMesh(sides, length, radius, ...)์›๊ธฐ๋‘ฅ
dodecahedronMesh(radius)์ •์‹ญ์ด๋ฉด์ฒด
planeMesh(...)ํ‰๋ฉด
buildTriangleMesh { ... }DSL๋กœ ์—ฌ๋Ÿฌ ๋ฉ”์‹œ ์กฐํ•ฉ
val cube = boxMesh(140.0, 70.0, 10.0)
val sphere = sphereMesh(32, 32, 0.6)

2.2 ๊ทธ๋ฆฌ๊ธฐ: vertexBuffer

drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES)
drawer.vertexBuffer(sphere, DrawPrimitive.LINES)   // ์™€์ด์–ดํ”„๋ ˆ์ž„
  • DrawPrimitive.TRIANGLES, LINES, POINTS ๋“ฑ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ™์€ ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ๊ทธ๋ฆด ๋•Œ๋Š” ์ธ์Šคํ„ด์‹ฑ(vertexBufferInstances)์„ ๊ณ ๋ คํ•ฉ๋‹ˆ๋‹ค.

2.3 ์ง์ ‘ VertexBuffer ๊ตฌ์„ฑ

์ˆ˜ํ•™์œผ๋กœ ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ๋ฅผ ๋งŒ๋“ค ๋•Œ vertexFormat + vertexBuffer๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

val geometry = vertexBuffer(vertexFormat {
    position(3)
    normal(3)
    // textureCoordinate(2)  // ํ•„์š” ์‹œ
}, vertexCount)

geometry.put {
    // write(Vector3(...)) ๋“ฑ์œผ๋กœ ์ฑ„์›€
}

ํ๋ฆ„์€ ์ˆ˜ํ•™์  geometry ์ƒ์„ฑ โ†’ VertexBuffer โ†’ GPU ๋ Œ๋”๋ง์ž…๋‹ˆ๋‹ค. ๊ตฌ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ๋’ค ๋ฒ„ํ…์Šค ์œ„์น˜๋ฅผ ํ•จ์ˆ˜๋กœ ๋ณ€ํ˜•ํ•˜๊ฑฐ๋‚˜, ShadeStyle์˜ vertexTransform์—์„œ ๋ณ€ํ˜•ํ•˜๋Š” ๋ฐฉ์‹์ด ์ œ๋„ˆ๋ ˆ์ดํ‹ฐ๋ธŒ 3D์™€ ์ž˜ ๋งž์Šต๋‹ˆ๋‹ค.

2.4 buildTriangleMesh DSL (์š”์•ฝ)

์—ฌ๋Ÿฌ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋ฅผ ํ•˜๋‚˜์˜ ๋ฒ„ํผ๋กœ ํ•ฉ์น  ๋•Œ:

val mesh = buildTriangleMesh {
    // rotate / translate ํ›„
    box(2.0, 2.0, 2.0)
    // translate ํ›„
    sphere(16, 16, 1.0)
}

(์ •ํ™•ํ•œ ์‹œ๊ทธ๋‹ˆ์ฒ˜ยทํ—ฌํผ๋Š” orx-mesh-generators ์†Œ์Šค/๋ฐ๋ชจ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.)


3. 3D ์นด๋ฉ”๋ผ์™€ ํˆฌ์˜

3.1 perspective ํˆฌ์˜ (์ €์ˆ˜์ค€)

drawer.perspective(60.0, width * 1.0 / height, 0.01, 1000.0)
drawer.depthWrite = true
drawer.depthTestPass = DepthTestPass.LESS_OR_EQUAL
  • FOV(๋„), ์ข…ํšก๋น„, near, far.
  • 3D์—์„œ๋Š” ๊นŠ์ด ํ…Œ์ŠคํŠธ/์“ฐ๊ธฐ๋ฅผ ์ผœ์•ผ ์•ž๋’ค ๊ด€๊ณ„๊ฐ€ ์˜ฌ๋ฐ”๋ฆ…๋‹ˆ๋‹ค.

3.2 ๋ชจ๋ธ ๋ณ€ํ™˜

drawer.translate(0.0, 0.0, -150.0)
drawer.rotate(Vector3.UNIT_X, seconds * 15.0 + 30.0)
drawer.rotate(Vector3.UNIT_Y, seconds * 5.0 + 60.0)
drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES)
  • translate / rotate / scale์€ model ํ–‰๋ ฌ์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ๋ณ€ํ™˜์€ ์•„๋ž˜์—์„œ ์œ„๋กœ ์ฝํžˆ๋Š” ์ˆœ์„œ์— ์ฃผ์˜ํ•ฉ๋‹ˆ๋‹ค. isolated { }๋กœ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

3.3 Orbital ์นด๋ฉ”๋ผ (ORX)

๋งˆ์šฐ์Šค/ํ‚ค๋ณด๋“œ๋กœ ๊ถค๋„๋ฅผ ๋„๋Š” ์นด๋ฉ”๋ผ๊ฐ€ ํ•„์š”ํ•  ๋•Œ:

val cam = Orbital()
cam.eye = -Vector3.UNIT_Z * 150.0
extend(cam)

extend {
    drawer.fill = ColorRGBa.PINK
    // shadeStyle ...
    drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES)
}

๋˜๋Š” OrbitalCamera๋ฅผ ์ง์ ‘ ์“ฐ๋Š” ์˜ˆ (๊ณต์‹ Shade styles ์˜ˆ์ œ):

val camera = OrbitalCamera(Vector3.UNIT_Z, Vector3.ZERO)
extend(camera)
extend {
    camera.rotate(0.2, 0.0)
    // ...
}
  • Orbital์€ OrbitalCamera + ์ปจํŠธ๋กค์„ ๋ฌถ์€ ํŽธ์˜ ํ™•์žฅ์ž…๋‹ˆ๋‹ค.
  • view/projection์„ ์ง์ ‘ ๊ด€๋ฆฌํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

3.4 ๋ณ€ํ™˜ ํŒŒ์ดํ”„๋ผ์ธ ์š”์•ฝ

ํ–‰๋ ฌDrawer ์—ฐ์‚ฐ ์˜ˆ
modeltranslate, rotate, scale
viewlookAt, ์นด๋ฉ”๋ผ ํ™•์žฅ
projectionperspective, ortho

4. ๊นŠ์ด ๋ฒ„ํผ์™€ RenderTarget

4.1 ์™œ depth๊ฐ€ ํ•„์š”ํ•œ๊ฐ€

  • 3D ๊ฐ์ฒด์˜ ์•ž๋’ค ๊ฐ€๋ฆผ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด depth buffer๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • Shape/ShapeContour๋ฅผ RenderTarget์— ๊ทธ๋ฆด ๋•Œ๋„ depth(์Šคํ…์‹ค)๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ํ™”๋ฉด ๊ธฐ๋ณธ ํƒ€๊นƒ์—๋„ 3D๋ฅผ ๊ทธ๋ฆด ๋•Œ๋Š” depthWrite / depthTestPass๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

4.2 ์˜คํ”„์Šคํฌ๋ฆฐ 3D

val rt = renderTarget(width, height) {
    colorBuffer()
    depthBuffer()   // 3D ํ•„์ˆ˜
}

drawer.isolatedWithTarget(rt) {
    drawer.clear(ColorRGBa.BLACK)
    drawer.perspective(60.0, width * 1.0 / height, 0.01, 1000.0)
    drawer.depthWrite = true
    drawer.depthTestPass = DepthTestPass.LESS_OR_EQUAL
    // ๋ฉ”์‹œ ๊ทธ๋ฆฌ๊ธฐ
}
drawer.image(rt.colorBuffer(0))
  • ๋ Œ๋” ํƒ€๊นƒ ํฌ๊ธฐ๊ฐ€ ์ฐฝ๊ณผ ๋‹ค๋ฅด๋ฉด ํˆฌ์˜์„ ํƒ€๊นƒ์— ๋งž์ถฐ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

5. ShadeStyle๋กœ ์กฐ๋ช…๊ณผ ๋ฒ„ํ…์Šค ๋ณ€ํ˜•

5.1 ๊ฐœ๋…

  • ShadeStyle์€ ํ…œํ”Œ๋ฆฟ ์…ฐ์ด๋”์— GLSL ์กฐ๊ฐ์„ ๋ผ์›Œ ๋„ฃ๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
  • vertexTransform: ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ ๋ณ€ํ˜• (x_position, x_normal ๋“ฑ).
  • fragmentTransform: ํ”ฝ์…€ ์ƒ‰ (x_fill, x_stroke).
  • 3D์—์„œ๋Š” va_normal, v_worldPosition, v_worldNormal ๋“ฑ์ด ์ž์ฃผ ์“ฐ์ž…๋‹ˆ๋‹ค.

5.2 ๊ฐ„๋‹จํ•œ ๋””ํ“จ์ฆˆ ์กฐ๋ช… (๊ณต์‹ 3D ์˜ˆ์ œ ๊ธฐ๋ฐ˜)

fun main() = application {
    configure {
        multisample = WindowMultisample.SampleCount(4)
    }
    program {
        val cube = boxMesh(140.0, 70.0, 10.0)
        extend {
            drawer.perspective(60.0, width * 1.0 / height, 0.01, 1000.0)
            drawer.depthWrite = true
            drawer.depthTestPass = DepthTestPass.LESS_OR_EQUAL
            drawer.fill = ColorRGBa.PINK
            drawer.shadeStyle = shadeStyle {
                fragmentTransform = """
                    vec3 lightDir = normalize(vec3(0.3, 1.0, 0.5));
                    float l = dot(va_normal, lightDir) * 0.4 + 0.5;
                    x_fill.rgb *= l;
                """.trimIndent()
            }
            drawer.translate(0.0, 0.0, -150.0)
            drawer.rotate(Vector3.UNIT_X, seconds * 15 + 30)
            drawer.rotate(Vector3.UNIT_Y, seconds * 5 + 60)
            drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES)
        }
    }
}

5.3 ๋ฒ„ํ…์Šค ๋””์Šคํ† ์…˜ (๊ณต์‹ Shade styles โ€œ3D mesh distortionโ€)

๋ฒ„ํ…์Šค ์œ„์น˜๋ฅผ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ํ”๋“œ๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค. ์ œ๋„ˆ๋ ˆ์ดํ‹ฐ๋ธŒ 3D์˜ ํ•ต์‹ฌ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

fun main() = application {
    program {
        val sphere = sphereMesh(32, 32, 0.6)
        val style = shadeStyle {
            vertexTransform = """
                vec3 p = x_position * 8.0 + p_seconds;
                x_position.x += sin(p.y) * 0.1;
                x_position.y += sin(p.z) * 0.1;
                x_position.z += sin(p.x) * 0.1;
            """.trimIndent()
            fragmentTransform = """
                vec3 c = sin(v_worldPosition) * 0.5 + 0.5;
                x_fill = vec4(c, 1.0);
            """.trimIndent()
        }
        val camera = OrbitalCamera(Vector3.UNIT_Z, Vector3.ZERO)
        extend(camera)
        extend {
            camera.rotate(0.2, 0.0)
            style.parameter("seconds", seconds)
            drawer.shadeStyle = style
            drawer.vertexBuffer(sphere, DrawPrimitive.LINES)
        }
    }
}
  • x_position์„ ๋ฐ”๊พธ๋ฉด ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ๊ฐ€ ๋ณ€ํ˜•๋ฉ๋‹ˆ๋‹ค.
  • v_worldPosition์œผ๋กœ ์›”๋“œ ์ขŒํ‘œ ๊ธฐ๋ฐ˜ ์ƒ‰์„ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” Kotlin์—์„œ parameter("seconds", seconds) โ†’ GLSL p_seconds.

5.4 ๋ฒ„ํ…์Šค ๊ด€๋ จ ์ฃผ์š” ์‹ฌ๋ณผ

์ด๋ฆ„๋‹จ๊ณ„์˜๋ฏธ
x_positionvertex๋ณ€ํ˜• ๊ฐ€๋Šฅํ•œ ์œ„์น˜ (์ดˆ๊ธฐ๊ฐ’ a_position)
x_normalvertex๋ณ€ํ˜• ๊ฐ€๋Šฅํ•œ ๋ฒ•์„ 
a_position / a_normalvertex์›๋ณธ ์†์„ฑ
va_normalfragment๋ณด๊ฐ„๋œ ๋ฒ•์„ 
v_worldPositionfragment์›”๋“œ ์ขŒํ‘œ ์œ„์น˜
v_worldNormalfragment์›”๋“œ ์ขŒํ‘œ ๋ฒ•์„ 
p_*์–‘์ชฝ์‚ฌ์šฉ์ž ํŒŒ๋ผ๋ฏธํ„ฐ

๋ฒ•์„ ์„ ํฌ๊ฒŒ ๋ฐ”๊พธ๋ฉด ์กฐ๋ช…์ด ๊นจ์งˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์œ„์น˜๋งŒ ์›€์ง์ผ ๋•Œ๋„ ํ•„์š”ํ•˜๋ฉด ๋ฒ•์„ ์„ ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜๋Š” ๋ฐฉ์‹์„ ๊ณ ๋ คํ•ฉ๋‹ˆ๋‹ค.

5.5 CPU ์ชฝ ๋ฒ„ํ…์Šค ์ˆ˜์ •

ShadeStyle ๋Œ€์‹  ๋ฒ„ํผ๋ฅผ ๋‹ค์‹œ ์ฑ„์šธ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

geometry.put {
    // ๋งค ํ”„๋ ˆ์ž„ ๋˜๋Š” ํ•„์š”ํ•  ๋•Œ๋งŒ
    for (i in 0 until vertexCount) {
        write(modifiedPosition)
        // normal ๋“ฑ
    }
}
  • ์ •์ ์ด ์ˆ˜์ฒœ~์ˆ˜๋งŒ ๊ฐœ์ผ ๋•Œ๋Š” GPU vertexTransform์ด ์œ ๋ฆฌํ•œ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.
  • ํ•œ ๋ฒˆ๋งŒ ๋งŒ๋“ค๊ณ  ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ธฐ๋ณธ์ž…๋‹ˆ๋‹ค.

6. ์ธ์Šคํ„ด์‹ฑ๊ณผ ๋ฐฐ์น˜

๊ฐ™์€ ๋ฉ”์‹œ๋ฅผ ์—ฌ๋Ÿฌ ์œ„์น˜/์ƒ‰์œผ๋กœ ๊ทธ๋ฆด ๋•Œ:

val transforms = vertexBuffer(vertexFormat {
    attribute("transform", VertexElementType.MATRIX44_FLOAT32)
    // attribute("color", VertexElementType.VECTOR4_FLOAT32)
}, instanceCount)

// transforms.put { write(matrix) ... }

drawer.shadeStyle = shadeStyle {
    vertexTransform = "x_modelMatrix *= i_transform;"
    // fragmentTransform = "x_fill = vi_color;"
}
drawer.vertexBufferInstances(
    listOf(mesh),
    listOf(transforms),
    DrawPrimitive.TRIANGLES,
    transforms.vertexCount
)
  • i_ / vi_ ์ ‘๋‘์‚ฌ๋กœ ์ธ์Šคํ„ด์Šค ์†์„ฑ์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.
  • ์ˆ˜์ฒœ ๊ฐœ์˜ ์ž‘์€ ๋ฉ”์‹œ๋ฅผ ๊ฐœ๋ณ„ vertexBuffer ํ˜ธ์ถœ๋กœ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ๋ณด๋‹ค ํ›จ์”ฌ ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.

7. ์ข…ํ•ฉ ์˜ˆ์ œ: ํšŒ์ „ํ•˜๋Š” ์กฐ๋ช… ํ๋ธŒ + ๋””์Šคํ† ์…˜ ๊ตฌ

fun main() = application {
    configure {
        width = 960
        height = 540
        multisample = WindowMultisample.SampleCount(4)
    }
    program {
        val cube = boxMesh(80.0, 80.0, 80.0)
        val sphere = sphereMesh(48, 48, 1.0)

        val lightStyle = shadeStyle {
            fragmentTransform = """
                vec3 lightDir = normalize(vec3(0.4, 1.0, 0.3));
                float l = max(dot(va_normal, lightDir), 0.0) * 0.6 + 0.4;
                x_fill.rgb *= l;
            """.trimIndent()
        }

        val distortStyle = shadeStyle {
            vertexTransform = """
                vec3 p = x_position * 6.0 + p_time;
                x_position += 0.08 * vec3(sin(p.y), sin(p.z), sin(p.x));
            """.trimIndent()
            fragmentTransform = """
                vec3 c = 0.5 + 0.5 * sin(v_worldPosition * 2.0 + p_time);
                x_fill = vec4(c, 1.0);
            """.trimIndent()
        }

        val cam = Orbital()
        cam.eye = Vector3(0.0, 40.0, 220.0)
        extend(cam)

        extend {
            lightStyle.parameter("time", seconds)   // ํ•„์š” ์‹œ
            distortStyle.parameter("time", seconds)

            drawer.clear(ColorRGBa.fromHex("#0d0d12"))
            drawer.depthWrite = true
            drawer.depthTestPass = DepthTestPass.LESS_OR_EQUAL

            drawer.isolated {
                drawer.shadeStyle = lightStyle
                drawer.fill = ColorRGBa.fromHex("#FFC0CB")
                drawer.translate(-90.0, 0.0, 0.0)
                drawer.rotate(Vector3.UNIT_Y, seconds * 25.0)
                drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES)
            }

            drawer.isolated {
                drawer.shadeStyle = distortStyle
                drawer.fill = ColorRGBa.WHITE
                drawer.translate(90.0, 0.0, 0.0)
                drawer.scale(50.0)
                drawer.vertexBuffer(sphere, DrawPrimitive.TRIANGLES)
            }
        }
    }
}

8. ์ž์ฃผ ์“ฐ๋Š” ํŒจํ„ด ์š”์•ฝ

ํ•˜๊ณ  ์‹ถ์€ ์ผ์‚ฌ์šฉํ•  ๊ธฐ๋Šฅ
๊ธฐ๋ณธ ๋ฐ•์Šค/๊ตฌ ๋ฉ”์‹œboxMesh, sphereMesh โ†’ drawer.vertexBuffer
๊ถค๋„ ์นด๋ฉ”๋ผOrbital() ๋˜๋Š” OrbitalCamera + extend
์•ž๋’ค ๊ฐ€๋ฆผdepthWrite = true, depthTestPass = LESS_OR_EQUAL
๊ฐ„๋‹จํ•œ ์กฐ๋ช…fragmentTransform์—์„œ va_normal ๋‹ท ํ”„๋กœ๋•ํŠธ
๋ฒ„ํ…์Šค ํ”๋“ค๊ธฐ/ํŒŒ๋™vertexTransform์—์„œ x_position ์ˆ˜์ • + p_time
์›”๋“œ ์ขŒํ‘œ ๊ธฐ๋ฐ˜ ์ƒ‰v_worldPosition
๊ฐ™์€ ๋ฉ”์‹œ ๋‹ค์ˆ˜vertexBufferInstances + ์ธ์Šคํ„ด์Šค ์†์„ฑ
์˜คํ”„์Šคํฌ๋ฆฐ 3DrenderTarget { colorBuffer(); depthBuffer() }
์ƒํƒœ ๊ฒฉ๋ฆฌdrawer.isolated { }

9. ์„ฑ๋Šฅ ๊ด€์ ์˜ ์ •๋ฆฌ

  1. ๋ฉ”์‹œยท์Šคํƒ€์ผยทํƒ€๊นƒ ์ƒ์„ฑ ์œ„์น˜: boxMesh/sphereMesh, ShadeStyle, RenderTarget, ์ธ์Šคํ„ด์Šค ๋ฒ„ํผ๋Š” ์ž…๋ ฅ์ด ๊ณ ์ •์ด๋ฉด extend ๋ฐ”๊นฅ์—์„œ ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  2. ๋“œ๋กœ์šฐ ์ฝœ: ๋™์ผ ์ง€์˜ค๋ฉ”ํŠธ๋ฆฌ๋Š” ์ธ์Šคํ„ด์‹ฑ์„ ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค.
  3. ๋ฒ„ํ…์Šค ๋ณ€ํ˜• ์œ„์น˜: ๋งค ํ”„๋ ˆ์ž„ CPU put๋ณด๋‹ค GPU vertexTransform์ด ์œ ๋ฆฌํ•œ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ •์  ์ˆ˜๊ฐ€ ๋งŽ์„์ˆ˜๋ก ์ฐจ์ด๊ฐ€ ํฝ๋‹ˆ๋‹ค.
  4. ์…ฐ์ด๋” ๋ณต์žก๋„: fBmยท๋‹ค์ค‘ ๋…ธ์ด์ฆˆ๋Š” ํ”ฝ์…€ ์ˆ˜์— ๋น„๋ก€ํ•ด ๋น„์šฉ์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ํฐ ํ™”๋ฉดยท๊ณ ํ•ด์ƒ๋„์—์„œ๋Š” ๋ฐ˜๋ณต ํšŸ์ˆ˜๋ฅผ ์ค„์ž…๋‹ˆ๋‹ค.
  5. ์ƒํƒœ ๋ˆ„์ˆ˜: shadeStyle, depth ์„ค์ •, blendMode๋Š” ๋‹ค์Œ ๊ทธ๋ฆฌ๊ธฐ์— ๋‚จ์Šต๋‹ˆ๋‹ค. isolated๋กœ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.
  6. ๋ฉ€ํ‹ฐ์ƒ˜ํ”Œ: WindowMultisample.SampleCount(4) ๋“ฑ์œผ๋กœ ๊ฐ€์žฅ์ž๋ฆฌ๋ฅผ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์„ฑ๋Šฅ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„).

10. ๋ฌธ์„œ์—์„œ ๋‹ค๋ฃจ์ง€ ์•Š์€ ํ•ญ๋ชฉ

  • GLTF/GLB ๋กœ๋” ๋ฐ ์”ฌ ๊ทธ๋ž˜ํ”„ (์ฝ”์–ด์— ์—†์Œ. ์ง์ ‘ ๊ตฌํ˜„ ๋˜๋Š” ์™ธ๋ถ€ ๋„๊ตฌ)
  • PBRยท๋ฌผ๋ฆฌ ๊ธฐ๋ฐ˜ ์žฌ์งˆ ์‹œ์Šคํ…œ
  • ์Šคํ‚จ๋“œ ๋ฉ”์‹œยท๋ณธ ์• ๋‹ˆ๋ฉ”์ด์…˜
  • Compute shader๋กœ VertexBuffer ์ฑ„์šฐ๊ธฐ (๊ณ ๊ธ‰)
  • orx-fx ํ›„์ฒ˜๋ฆฌ ์ฒด์ธ ์ƒ์„ธ
  • 3D ํ…์ŠคํŠธยท๋ณต์žกํ•œ CSG

11. ๊ฒ€์ฆ ๋ฉ”๋ชจ

ํ•ญ๋ชฉ๋‚ด์šฉ
์ฝ”๋“œ ์‹คํ–‰ ์—ฌ๋ถ€๊ณต์‹ ๊ฐ€์ด๋“œ 3D graphicsยทShade styles 3D mesh distortion ์˜ˆ์ œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•จ. ์ข…ํ•ฉ ์˜ˆ์ œยท์ธ์Šคํ„ด์‹ฑ ์กฐํ•ฉ ์ฝ”๋“œ๋Š” 0.5.0์—์„œ ์ง์ ‘ ์‹คํ–‰ ๊ฒ€์ฆํ•˜์ง€ ์•Š์Œ
ORX importboxMesh, sphereMesh, Orbital, OrbitalCamera ํŒจํ‚ค์ง€๋Š” IDE ์ž๋™ import๋กœ ํ™•์ธ
depth ๊ธฐ๋ณธ๊ฐ’3D ์‹œ depthWrite/depthTestPass๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •ํ•  ๊ฒƒ
์นด๋ฉ”๋ผ eyeOrbital์˜ ์ดˆ๊ธฐ eye๋Š” ์žฅ๋ฉด์— ๋งž๊ฒŒ ์กฐ์ • ํ•„์š”
c_element / ์ธ์Šคํ„ด์Šค๋ฐฐ์น˜ยท์ธ์Šคํ„ด์Šค ์ธ๋ฑ์Šค๋Š” ๊ฐ€์ด๋“œ ์ƒ์ˆ˜ ์ •์˜์— ๊ทผ๊ฑฐ. ์‹คํ–‰์œผ๋กœ ํ™•์ธ ๊ถŒ์žฅ
API ๋ฌธ์„œ ๋ฒ„์ „API ์‚ฌ์ดํŠธ ํ‘œ๊ธฐ๊ฐ€ 0.5.0-dev...์—ฌ๋„ v0.5.0 ๋ฆด๋ฆฌ์ฆˆ์™€ ์ปค๋ฐ‹์ด ์ผ์น˜ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Œ

12. ์ฐธ๊ณ  ์ž๋ฃŒ

๊ณต์‹ ๊ฐ€์ด๋“œ

ORX

  • orx-mesh-generators: 3D ๋ฉ”์‹œ ์ƒ์„ฑ
  • orx-camera: Orbital, OrbitalCamera

๋ฆด๋ฆฌ์ฆˆยทAPI


profile
Coding Art with Blender / Three.js / p5.js / GLSL Shader / Processing / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€