GLSL과 THREE.js로 불가능한 상자 만들기

14570 단어 glsljavascriptthreejs
제가 가장 좋아하는 시각화 유형 중 하나는 물리적으로 불가능한 것입니다. 그리고 그 중 가장 재미있는 것 중 하나는 내부가 더 큰 상자입니다!

최종 제품



직접 가지고 놀고 싶다면 볼 수 있습니다on my website .

더 많은 정보를 얻으려면 완전히 주석이 달린GitGub repository을 확인하십시오.



구축하자!



셰이더 및 WebGL 작동 방식의 기본 사항에 익숙하지 않은 경우 주제에 대한 이 문서MDN article를 확인하는 것이 좋습니다.

이제 제가 이것을 어떻게 구축했는지 자세히 살펴보겠습니다!

먼저 이 모든 것을 작동하게 만든 기능discard에 대해 이야기해 보겠습니다.

프래그먼트 셰이더에는 일반 프로그래밍 언어의 return와 유사하게 작동하는 특수 키워드가 있습니다. discard GPU가 현재 조각을 렌더링하지 않도록 지시하여 그 뒤에 있는 것이 무엇이든 보여줍니다. 이에 대한 자세한 내용을 읽을 수 있습니다here.

이 기능을 사용하면 지루한 일반 큐브를 매우 멋진 투명 큐브로 바꿀 수 있습니다!

Source: outer_fragment.glsl

// Check if the fragment is far enough along any axis
bool x_edge = abs(worldPosition.x) > 0.4;
bool y_edge = abs(worldPosition.y) > 0.64;
bool z_edge = abs(worldPosition.z) > 0.4;

// Check that the fragment is at the edge of at least two axis'
if (!y_edge && !z_edge) {
    discard;
}

if (!y_edge && !x_edge) {
    discard;
}




이제 우리는 우리가 보고 있는 얼굴을 알 수 있는 방법을 찾아야 합니다. 이 부분이 가장 어려웠습니다. 해결 방법이 너무 어려워서가 아니라 대부분 제가 수학을 잘하지 못하기 때문입니다.

이제 이것을 구현하는 방법에 대해 살펴보겠습니다.

첫째, 우리 상자에는 위와 아래가 있기 때문에 이를 위해 3D로 작업할 필요가 없습니다. 3D 상자를 2D 상자로 생각해 봅시다.



이제 상자 안에 있는 3D 형상(빨간색)을 가져와서 2D로 평면화할 수 있습니다.



다음으로 카메라(파란색)와 렌더링하려는 일부 예제 조각(녹색)을 추가해 보겠습니다.



이 설정을 사용하면 프래그먼트와 카메라 사이에 선을 만들고 어떤 면을 통과하는지 확인할 수 있습니다.



이 방법을 상자에 적용하고 각 얼굴에 색상을 지정하면 재미있는 효과를 얻을 수 있습니다!

Source: inner_fragment.glsl

// Define all the corners of our box
const vec2 corners[4] = vec2[](vec2(0.5, 0.5), vec2(-0.5, 0.5), vec2(-0.5, -0.5), vec2(0.5, -0.5));

// Define a line from the fragment's position (A) to the camera (B)
vec2 a = worldPosition.xz;
vec2 b = cameraPosition.xz;

int intersectedFace = -1;

// Iterate over each face
for (int i = 0; i < 4; i++) {
    // Get the second point for our face
    int next = int(mod(float(i + 1), 4.0));

    // Create a line from 2 corners based on the face
    vec2 c = corners[i];
    vec2 d = corners[next];

    // Does line 1 and 2 intersect? If so, assign the intersected face
    if (intersect(a, b, c, d)) {
        intersectedFace = i;
        break;
    }
}

// Color the fragment based on the face
switch (intersectedFace) {
    case -1:
        gl_FragColor = vec4(1, 0, 1, 1);
        break;
    case 0:
        gl_FragColor = vec4(1, 0, 0, 1);
        break;
    case 1:
        gl_FragColor = vec4(0, 1, 0, 1);
        break;
    case 2:
        gl_FragColor = vec4(0, 0, 1, 1);
        break;
    case 3:
        gl_FragColor = vec4(0, 1, 1, 1);
        break;
}




여기에서 내부에 원하는 각 개체에 얼굴을 할당하고 지정된 얼굴을 통과하지 않는 조각discard을 할당할 수 있습니다.

Source: inner_fragment.glsl

// Define a line from the fragment's position (A) to the camera (B)
vec2 a = worldPosition.xz;
vec2 b = cameraPosition.xz;

// Get the second point to define the face
int next = int(mod(float(face + 1), 4.0));

// Define a line at the given face
vec2 c = corners[face];
vec2 d = corners[next];

// If the defined lines do NOT intersect, then discard the fragment
if (!intersect(a, b, c, d)) {
    discard;
}


그런 다음 몇 가지 흥미로운 애니메이션 개체, 깊이를 위한 약간의 방향성 조명을 추가하면 완료됩니다!



읽어 주셔서 감사합니다! 제가 만든 만큼 즐거우셨기를 바랍니다!

좋은 웹페이지 즐겨찾기