[THREE.JS] Blooming Earth

원하는 결과는 다음과 같습니다.

장면에 추가된 모델은 3개입니다. 지구 본체, 지구 주변의 푸른 빛(Blooming Light), 지구 주위의 하얀 작은 무수한 별들.

먼저 지구 본체에 대한 코드입니다.

const sphere = new THREE.Mesh(
    new THREE.SphereGeometry(5, 50, 50), 
    new THREE.ShaderMaterial({
        uniforms: {
            globeTexture: {
                value: new THREE.TextureLoader().load("data/earth.jpg")
            }
        },
        vertexShader: `
            varying vec2 vertexUV;
            varying vec3 vertexNormal;

            void main() {
                vertexUV = uv;
                vertexNormal = normalize(normalMatrix * normal);

                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
        `,
        fragmentShader: `
            uniform sampler2D globeTexture;

            varying vec2 vertexUV;

            void main() {
                vec4 color = texture2D(globeTexture, vertexUV);
                gl_FragColor = vec4(color.xyz, 1.);
            }
        `
    })
);

결과는 다음과 같습니다.

지구 가장자리가 어두워서 가장자리를 밝게 만들기 위해 위의 코드에서 fragmentShader 코드를 다음처럼 변경합니다.

fragmentShader: `
    uniform sampler2D globeTexture;

    varying vec2 vertexUV;
    varying vec3 vertexNormal;

    void main() {
        float intensity = 1.05 - dot(vertexNormal, vec3(0.,0.,1.));
        vec3 atmosphere = vec3(0.3, 0.6, 1.0) * pow(intensity, 1.5);

        vec4 color = texture2D(globeTexture, vertexUV);
        gl_FragColor = vec4(atmosphere + color.xyz, 1.);
    }
`,

결과는 다음과 같습니다.

지구 주변의 푸른 빛(Blooming Light)에 대한 코드입니다.

const atmosphere = new THREE.Mesh(
    new THREE.SphereGeometry(5, 50, 50),
    new THREE.ShaderMaterial({
        vertexShader: `
            varying vec3 vertexNormal;

            void main() {
                vertexNormal = normalize(normalMatrix * normal);
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
        `,
        fragmentShader: `
            varying vec3 vertexNormal;

            void main() {
                float intensity = pow(0.76 - dot(vertexNormal, vec3(0,0,1.)), 2.0);
                gl_FragColor = vec4(0.3, 0.6, 1.0, 1) * intensity;
            }
        `,
        transparent: true,
        blending: THREE.AdditiveBlending,
        side: THREE.BackSide
    })
);
atmosphere.scale.set(1.2, 1.2, 1.2);

결과는 다음과 같습니다.

이제 지구 주위의 하얀 작은 무수한 별들에 대한 코드입니다.

const starGeometry = new THREE.BufferGeometry();

const starVertices = [];
for(let i=0; i<10000; i++) {
    const x = (Math.random() - 0.5) * 1000; 
    const y = (Math.random() - 0.5) * 1000; 
    const z = (Math.random() - 0.5) * 1000; 
    starVertices.push(x, y, z);
}

starGeometry.setAttribute("position", new THREE.Float32BufferAttribute(starVertices, 3));

const starMaterial = new THREE.PointsMaterial({color: 0xffffff});
const stars = new THREE.Points(starGeometry, starMaterial);
this._scene.add(stars);

결과는 이 글의 가장 첫번째 이미지와 같습니다.

gwc-resizable-panel를 이용해 사이드 패널 구성

크기 조절이 가능한 gwc-resizable-panel 태그를 이용한 사이드 패널을 구성하기 위한 코드를 정리합니다. 아래의 이미지에서 우측의 초록색과 빨간색 외곽선을 내용으로 가지는 것이 이 글의 주인공입니다.

이제 웹 개발도 클래스 기반으로 개발을 하는게 당연한데요. 사이드 패널에 대한 JS 코드는 다음과 같습니다.

class SearchResultUI {
    constructor() {
        const domLayout = document.createElement("div");
        domLayout.classList.add("search-result-ui");

        domLayout.innerHTML = `
            
                
`; document.body.appendChild(domLayout); GeoServiceWebComponentManager.instance.update(); } }

7번의 gwc-resizable-panel의 resizable-left와 min-width는 각각 패널의 왼쪽 모서리를 이용해 크기를 조절할 수 있고, 패널의 가로 크기는 최소 200px를 유지해야 한다는 것입니다. min-width 값의 단위는 px이며 값을 지정할 때는 단위를 지정하지 않습니다./p>

해당되는 CSS는 다음과 같습니다.

.search-result-ui > gwc-resizable-panel {
    box-shadow:  0 0 2px rgb(0 0 0 / 50%), 0 0 10px rgb(0 0 0 / 50%);;
    left: calc(100% - 20em);
    right: 0;
    top: 3em;
    width: 20em;
    height: calc(100vh - 3em);
}

.search-result-ui > gwc-resizable-panel > .search-result-header {
    border: 2px solid green;
    background-color: #202020;
    height: 2.6em;
    display: flex;
}

.search-result-ui > gwc-resizable-panel > .search-result-content {
    border: 2px solid red;
    min-height: calc(100% - 2.6em);
    display: flex;
    flex-direction: column;
    gap: 0.1em;
    background: #0f0f0f;
}

중요한 부분은 gwc-resizable-panel의 처음 크기를 지정하기 위해 width를 20em으로 지정했다면 left의 값을 지정할 때 초기 크기의 width 만큼 빼줘야 한다는 것입니다. 크기 조정을 위해 반드시 고려해야 할 부분입니다.