게임에서 웹으로 확장된 Frosted Glass 기술
(tyleo.com)- Forza Horizon 3와 Forza Motorsport 7의 frosted acrylic UI에서 출발해, 웹에서도 비슷한 유리 패널 효과를 HTML/CSS/JavaScript로 재현하는 과정을 정리함
- 효과의 중심은 Gaussian blur이며, CSS
backdrop-filter와 iOS용-webkit-backdrop-filter를 함께 써야 브라우저별 차이를 줄일 수 있음 - 단순한 흐림만으로는 평평해 보이기 때문에
box-shadow: inset으로 가장자리 깊이와 subsurface scattering을 더하고, 일반 그림자로 배경 위에 떠 있는 느낌을 만듦 - 표면 반사는 빛줄기 이미지를 별도 레이어에 깔아 구현하며,
background-attachment: fixed나 JavaScript로 유리 이동에 맞춘 동적 반사를 흉내 냄 - 최종 UI는
.glass,.light,.drag-me레이어를 조합하고 둥근 모서리, clipping, 반투명 배경색을 더해 복사 가능한 유리 컴포넌트 형태로 완성됨
게임 UI에서 가져온 유리 패널 효과
- Forza Horizon 3와 Forza Motorsport 7의 UI 개발 경험에서 본 frosted acrylic 디자인을 HTML로 재현하는 것이 목표임
- 결과물은 드래그 가능한 유리 패널이며, 배경 흐림, 가장자리 깊이, 빛 반사, 둥근 모서리, 반투명 색상 레이어가 함께 들어감
- 초반에는 바로 복사해 쓸 수 있는 최종 레시피를 제공하고, 이후 각 효과를 단계별로 쌓아 올림
배경 흐림을 만드는 backdrop-filter
- 좋은 frosted glass 효과의 핵심은 배경을 흐리게 해 반투명 스크린처럼 보이게 만드는 Gaussian blur임
- CSS에서는
backdrop-filter에blur를 적용해 구현함 - 기본 유리 요소는 단순한
div와.glass클래스에서 시작함backdrop-filter: blur(10px)-webkit-backdrop-filter: blur(10px)
- iOS에서는 별도 설정을 깊게 바꾸지 않는 한
-webkit-backdrop-filter가 필요함 backdrop-filter는 최근에야 브라우저 지원이 이뤄졌기 때문에 Internet Explorer 사용자는 데모를 경험할 수 없음
가장자리와 그림자로 깊이 만들기
backdrop-filter: blur(10px)만 적용하면 유리가 평평하고 단조롭게 보일 수 있음- 가장자리는
border: 1px solid대신box-shadow: inset으로 구현함border는 요소 크기와 자식 요소 배치에 영향을 줌box-shadow: inset을 쓰면 자식 요소가 음수 마진 같은 조정 없이 유리의 콘텐츠 영역을 자연스럽게 채울 수 있음
- 두 개의 반투명 흰색 inset shadow로 유리 가장자리를 표현함
- 첫 번째 shadow는 바깥에서 보이는 유리 모서리를 나타내며, 예시에서는 아래쪽과 왼쪽 가장자리에 나타남
- 두 번째 shadow는 유리를 통과해 보이는 가장자리를 나타내며, 예시에서는 위쪽과 오른쪽 가장자리에 나타남
- 유리를 통과해 보이는 가장자리는 빛이 줄어들기 때문에 더 부드럽고 투명하게 설정함
- 일반 어두운
box-shadow를 추가하면 유리가 배경에서 물리적으로 떠 있는 듯한 느낌을 줄 수 있음
빛의 산란과 표면 반사
- 유리 내부로 빛이 퍼지는 느낌은
subsurface scattering을 단순화해 표현함 - 구현에는 다시
box-shadow: inset을 사용함- 짧은 산란 효과:
inset 0px 0px 10px 5px rgba(255, 255, 255, 0.025) - 더 깊은 산란 효과:
inset 0px 0px 40px 5px rgba(255, 255, 255, 0.025)
- 짧은 산란 효과:
- 이 효과는 어두운 배경 위로 유리를 드래그할 때 더 잘 보임
- 배경이 흐려져 굴절되는 느낌에 더해, 유리 표면에서 빛이 반사되는 특성도 추가함
- 빛 반사는 빛줄기 이미지를 배경으로 섞어 구현함
- 유사한 이미지를 직접 만들 때 참고할 수 있는 YouTube 튜토리얼과 텍스트 설명이 있음
- 이미지를
.glass요소의background-image로 직접 넣으면backdrop-filter가 이미지를 흐리게 만들기 때문에::before같은 별도 레이어를 사용함
정적인 반사를 동적으로 바꾸기
- 빛줄기 이미지만 올리면 반사는 생기지만, 유리를 움직여도 빛이 유리 표면에 고정된 것처럼 보일 수 있음
- 순수 CSS 방식은
::before에background-attachment: fixed를 추가함- 구현은 간단하지만 모든 플랫폼에서 동작하지 않을 수 있음
- 모든 플랫폼에서 동작하게 하려면 JavaScript로
background-attachment: fixed를 흉내 냄::before를 JavaScript에서 직접 다루는 방식은 비효율적이므로 별도div.light요소를 사용함- 대상 요소에는
data-js-background-attachment-fixed속성을 붙임
- JavaScript는 매 애니메이션 프레임마다 대상 요소의
getBoundingClientRect()값을 읽고, 뷰포트 내 위치의 반대 방향으로background-position을 갱신함backgroundPositionX = -clientRect.xbackgroundPositionY = -clientRect.y
- 반복 루프는
requestAnimationFrame으로 실행됨
최종 HTML/CSS 구성
- JavaScript 버전의 최종 HTML은
.glass안에 빛 반사용.light와 텍스트/색상 레이어인.drag-me를 넣는 구조임
<div class="glass">
<div class="light" data-js-background-attachment-fixed/>
<div class="drag-me">Drag Me</div>
</div>
.glass는 유리 패널의 기본 형태와 시각 효과를 담당함backdrop-filter와-webkit-backdrop-filter로 배경 흐림 적용- 여러
box-shadow로 가장자리, 그림자, 산란 효과 적용 position: relative로 자식 레이어가 부모 영역을 채울 수 있게 함border-radius: 5px로 둥근 모서리 적용overflow: hidden으로 자식 요소가 둥근 모서리 밖으로 튀어나가지 않게 자름
.light는 빛줄기 이미지를 반복 배경으로 사용함background-image: url(path/to/light.png)background-repeat: repeatbackground-size: 750pxopacity: 0.075position: absolute와 네 방향 오프셋0으로 부모 전체를 채움z-index: -1로 다른 자식 뒤에 렌더링함
.drag-me는 콘텐츠를 가운데 정렬하고 색이 있는 반투명 유리 레이어를 만듦display: flexalign-items: centerjustify-content: centerheight: 30pxbackground-color: rgba(12, 13, 14, 0.75)
JavaScript 없는 버전의 차이
- JavaScript 없는 버전은 별도
.light요소 대신.glass::before를 사용함 ::before에 빛줄기 이미지를 적용하고background-attachment: fixed로 화면에 고정된 반사를 구현함- 이 방식은 모든 플랫폼에서 동작하지 않으며, JavaScript 버전은 이를 보완하기 위해
background-position을 직접 갱신함
댓글과 토론
Hacker News 의견들
-
미학적으로는 정말 마음에 들지만, 가장자리에서는 물리적으로 맞지 않는 부분이 있음
임베디드 개발자 관점에서는 좀 낭비적으로 느껴짐. 모든 클라이언트가 배경 이미지의 흐릿한 버전과 섞기 위해 매번 “비싼” 블러 필터를 계산하는 셈 아닌가 싶음
GPU를 쓰고 최적화되어 있어서 결국 큰 비용은 아닐 수 있지만, 요즘 웹 개발은 전반적으로 너무 비대해지는 흐름처럼 보임. 단순한 사이트도 5초씩 로딩되고, 무거운 처리를 클라이언트에 떠넘기는 식인데, 물론 이 글 작성자의 잘못은 아님- 무엇을 낭비로 보는지는 각자의 선입견이 있는 듯함
14.4k 모뎀 시대에 자라서, 네트워크 대역폭이 CPU 시간보다 훨씬 더 희소하고 비싸다고 생각하는 데 익숙함
클라이언트에서 쉽게 계산할 수 있다면, 인터넷으로 이미지 전체를 내려받는 쪽이 더 낭비라고 봄. 그 이미지를 받기 위해 라우터, 서버, 멀리 있는 디스크까지 작동시키는 셈인데, 이미 클라이언트 RAM에 있는 데이터에 한 번 처리하는 걸 피하려고 그 모든 걸 쓰는 게 더 아까움 - 기술적으로 어떤 면에서 “낭비적”인 웹사이트들도, 실제 용도를 보면 더 크게 보면 대부분 순수 오락에 쓰임
그러니 오락 자체가 낭비이거나, 아니라면 오락을 더 좋게 만들기 위해 계산을 더 쓰는 것도 괜찮다고 봄 - 사람 입장에서는 “렌더링된 그래픽을 크게 다시 계산한다”처럼 보여 낭비처럼 느껴지지만, 기술적으로는 꼭 그렇지 않다고 봄
UI에서 바뀌는 것을 다시 그리려면 어차피 GPU 계산이 필요하고, 단순한 블러는 꽤 효율적으로 추가 가능함. GPU 레이어로 최적화되지 않은 DOM 객체 애니메이션보다 비용이 낮을 가능성이 큼
요즘 가장 단순한 사이트도 1MB 이상 JavaScript와 추적기를 잔뜩 로드해 CPU를 먹는 걸 보면, 미적 효과용 블러는 낭비 목록에서 한참 아래라고 봄 - 블러된 이미지와 원본 이미지를 둘 다 네트워크로 보내는 방식이 지난 20년간 웹 개발에서 해오던 방식 아닌가 싶음
고해상도 이미지가 많으면, 실제 네트워크 대역폭이 유한하고 특히 무선 커버리지가 나쁜 모바일 환경에서는 로컬 계산보다 확실히 성능이 떨어짐. 요즘은 CSS/WebGL만으로 가능한 일이 놀랍고, 예전에는 이런 효과를 위해 해킹과 우회가 많이 필요했음 - 이제는 “오 멋져 보이네, 분명 나/우리에게 필요해” 같은 기술 상당수는 이 세상에 설 자리가 없다는 걸 진지하게 받아들일 때라고 봄
행위 자체가 낭비인지와 별개로, 이런 사고방식 자체가 사회의 더 큰 문제를 보여줌. 왜 이게 필요하고, 왜 이게 없는 상태를 나쁘다고 보는가?
오늘날 사회 문제의 규모를 생각하면, UI 패널이 흐릿하든 아니든 누가 신경 쓰겠나 싶음
- 무엇을 낭비로 보는지는 각자의 선입견이 있는 듯함
-
Android에서 새 셰이더 API로 무엇을 할 수 있는지 보려고 Windows 7 Aero Glass 효과를 재현하다가 비슷한 “빛줄기” 텍스처를 추출한 적 있음
결과적으로 이 글과 매우 비슷해졌는데, 내부 그림자는 넣지 않았고 흐릿한 배경의 채도는 조금 올렸음
https://mastodon.social/@grishka/110524476584503201- 아주 멋짐. 빛줄기 텍스처가 이 효과에서 실제로 많은 역할을 해줄 수 있음
솔직히 튜토리얼용으로 만든 건 그렇게 훌륭하지도 않음. Windows 7 쪽의 더 선명한 선들이 마음에 듦
채도 조정도 생각했고 다른 예시에서도 봤지만, 글에서는 쓰지 않기로 했음. 사용한 달 이미지에 색이 많지 않아서 큰 차이도 없었을 듯함. 직접 유리 효과를 만드는 사람들은 채도도 실험해보면 좋겠음
- 아주 멋짐. 빛줄기 텍스처가 이 효과에서 실제로 많은 역할을 해줄 수 있음
-
작업 자체는 훌륭하고, 대화형 데모로 보여주는 방식도 좋음. 창을 달 위로 끌어보는 재미가 있음
다만 사용자 인터페이스 디자인에서 서리 낀 유리 효과를 쓰는 건 싫고, Mac OS에 추가됐을 때는 후퇴였다고 느낌. 디자이너들은 이 효과에 너무 쉽게 매혹되지만, 실제로는 결코 이상적이지 않음. 특히 패널 뒤 콘텐츠가 동적이거나 사용자가 만든 것일 때 더 그렇음
2010년대의 브러시드 메탈 같은 존재인데, Windows Vista에서 새어 나와 모든 곳으로 퍼진 게 놀라움- 왜 “이상적이지” 않은지 모르겠음. 시각 디자인은 실용적 목적보다는 무엇이 보기 좋은지에 대한 개인 취향이 큰 비중을 차지함
물론 디자인은 기능에도 영향을 주지만, 주된 목적은 사용자 경험을 더 즐겁게 만드는 데 있음
스큐어모피즘은 지난 10년 넘게 이어진 모든 것을 납작하게 만드는 미니멀 경향보다 인터페이스를 더 즐겁게 만든다고 봄. 현실 세계의 표면과 재질을 따라 한 시각적 장식이 익숙함과 접근성을 더해줌. 실용적으로도 플랫 디자인은 요소와 상태를 구분하기 어렵게 만드는 반면, 3D 버튼, 브러시드 메탈, 서리 낀 유리가 있던 시절에는 훨씬 명확했음
이제 추가 다시 돌아가는 중이라고 생각함. 더 많은 디자이너가 평평한 외형을 피하고 있으니, 더 인간 친화적인 UI가 부활하길 기대함 - UI 디자이너는 아니라서 진지하게 묻고 싶은데, 문제는 서리 낀 유리나 브러시드 메탈 같은 미적 효과를 UI 디자인에 쓰는 것 자체인가, 아니면 그런 인터페이스를 만들려다 일부 디자이너가 컨트롤의 위치와 역할을 알려주는 시각적 단서를 잃어버린 것인가?
- 터미널 창에 수년간 약간의 투명도를 넣어왔는데, 이제 다시 생각해보게 됨
- 브러시드 메탈은 정보 맥락에 영향을 주지 않음. 블러는 조절 가능한 맥락을 제공함
블러는 대개 정답이 아니지만, 가끔은 맞는 선택임 - 아이러니하게도 이전 macOS 버전들도 브러시드 메탈을 많이 썼음
- 왜 “이상적이지” 않은지 모르겠음. 시각 디자인은 실용적 목적보다는 무엇이 보기 좋은지에 대한 개인 취향이 큰 비중을 차지함
-
2008년에 CSS가
backdrop-blur를 지원하기 전, 비슷한 효과를 내는 CSS 실험을 한 적 있음: https://webdev.andersriggelsen.dk/aero/
고정 위치의 미리 흐리게 처리된 배경 이미지에 유리 효과를 넣는 방식임: https://webdev.andersriggelsen.dk/aero/bgl.jpg
실시간 가우시안 블러보다 훨씬 성능이 좋지만, 동적 배경 이미지를 허용하지 못하는 단점은 그대로 있음- 더 이른 시기에 Flash 파일을 배경으로 써서 한 단계 더 나아간 적 있음
동적으로 움직일 수 있는 프레임 안에서도 유리 효과를 낼 수 있었음. iframe을 썼는지는 기억나지 않아서 코드를 찾아봐야 하지만, 당시 시대를 생각하면 아마 썼을 가능성이 큼 - 떠 있는 창이 초기 위치 이후로 업데이트되지 않음
Windows 10 19045.5131이고, Chrome은 최신 Version 131.0.6778.86 (Official Build) (64-bit)임
- 더 이른 시기에 Flash 파일을 배경으로 써서 한 단계 더 나아간 적 있음
-
기술적 성취는 멋지지만 나쁜 UX라고 봄
반투명 패널 안의 내용은 일관된 단색 배경 위보다 읽거나 이해하기 어렵고, 배경과의 대비가 낮고 들쭉날쭉함. 게다가 아래에서 무언가 움직이면 산만하고 방향 감각을 흐릴 수 있음- 단순한 시각 효과 하나를 두고 “나쁜 UX”라고 일반화하기는 어려움. 통합 방식에 크게 좌우됨
좋은 통합이라면 UI를 과하게 만들지 않도록 효과 강도를 조절하고, 블러를 지원하지 않을 때 추가 장식을 피하도록 기능 지원을 확인하며, GPU가 충분하지 않을 가능성이 있는 장치에서는 렌더링하지 않도록 디스플레이와 영상 기능을 확인해야 함
또 사용자 선호를 확인해 복잡한 배경이 어려운 사용자에게 대비 좋은 단색을 제공하고, 사용 가능한 공간에 따라 효과와 그림자 범위를 제한하며, 흐릿한 영역 아래 배경의 복잡도와 대비를 고려해 블러와 불투명도를 조절해야 함
타이포그래피에 맞춰 블러와 그림자가 적응형으로 스케일되도록 보장하는 것도 필요함. UX는 본질적으로 사용자가 전체 앱과 상호작용을 어떻게 경험하는지의 설계이지, 개별 스타일 선택 하나로 성패가 갈리지는 않음 - Windows Vista는 서리 낀 유리 위의 검은 글자에 흰색 그림자 같은 외곽선을 주는 방식으로 이 문제를 해결했음
배경이 검은색인 극단적인 경우에도 텍스트 가독성을 보장함. CSS에서는text-shadow속성으로 재현 가능함 - 그래서 이 효과를 내 사이트 전반에 쓰지는 않음. 좋은 UX를 만들기 더 어렵기 때문임
다만 게임에서는 더 쓸 자리가 있음. 특히 사용자가 상호작용하는 주 영역인 배경을 가리고 싶지 않은 HUD에서는 적합할 수 있음 - 대비가 낮은 게 문제라면 배경이나 유리 색조를 더 어둡게 하고 전경 콘텐츠를 더 밝게 하거나, 반대로 하면 됨
좋은 대비는 단색이든 유리 효과든 어떤 디자인 요소에서도 도전 과제임
배경에서 일어나는 일이 산만하고 방향 감각을 흐린다는 것도 매우 주관적임. 단색의 불투명도를 낮추듯 유리의 반투명도도 낮출 수 있음
이 비판은 객관적으로 참이라고 보기 어렵고, 그런 문제를 피하면서 이 디자인 요소를 통합하는 방법은 있음. Forza 스크린샷은 괜찮아 보이고, 실제로 봤을 때도 배경이 산만하지 않았음. 대신 단색 배경보다 더 흥미롭고 시각적으로 즐거운 UI를 얻음. 비디오 게임에서 더 중요할 수 있지만, OS와 웹 디자인에서도 세련되게 쓸 수 있음 - 블러는 맥락을 조절 가능한 만큼 유지하면서 정보를 숨기는 방식임
많은 맥락이 필요하면 블러는 좋지 않은 선택이고, 맥락이 전혀 필요 없어도 좋지 않은 선택임
하지만 어떤 때는 훌륭한 선택이 됨
- 단순한 시각 효과 하나를 두고 “나쁜 UX”라고 일반화하기는 어려움. 통합 방식에 크게 좌우됨
-
대화형 예시는 좋지만, 이전 단계와 나란히 비교하거나 전환할 수 있는 토글이 있었으면 더 좋았을 듯함
대부분의 단계에서 개선이 너무 미묘해서 차이를 알아보기 어려웠음- 동의함. 모바일에서는 두 번째 단계 이후로는 차이가 거의 보이지 않거나 아예 모르겠음
-
효과가 10개쯤 겹쳐 있는데, 가장 까다로운 미학을 노리는 경우가 아니라면 7개쯤은 과함
배경색, 블러, 그림자나 테두리 정도만 쓰면 됨- 덜 무거운 유리 효과를 원하는 사람들은 튜토리얼에서 골라 쓰면 됨
세 번째 단계만 가도 블러와 그림자만으로 꽤 괜찮은 유리 조각이 나옴. 개인적으로는 거기에 정적인 빛줄기 이미지를 섞는 걸 추천함. 대부분의 애플리케이션에는 전체 JavaScript 효과가 필요 없을 것 같음. 재미는 있지만 산만할 수 있음
블러, 그림자, 빛줄기만으로도 맨 위의 Forza 이미지에 정말 가깝게 갈 수 있음 - 상위 댓글 대부분이 낭비에 대해 말하는 게 흥미로움
모두가 플랫 디자인으로 간 이유 중 하나가 그 때문이라고 봄. Windows Vista와 유리 효과는 멋졌지만 계산 비용이 컸음
플랫 디자인은 미학적으로 논쟁적이고, 특히 나이 든 사용자에게는 스큐어모픽 디자인보다 쓰기 더 어려울 수 있음 [0][1]
현실감이 사용성을 도울 수 있다면, 이런 효과를 웹에서 쓰는 건 충분히 타당하다고 봄
[0]: https://www.tandfonline.com/doi/abs/10.1080/0144929X.2020.18...
[1]: https://www.sciencedirect.com/science/article/abs/pii/S01419... - 겹친 효과의 한계를
n <= 3으로 보는 이유가 뭔지 궁금함
접근성 문제인지, 성능 문제인지, 유지보수 문제인지, 아니면 다른 이유인지 모르겠음
개발 역량 중 CSS가 압도적으로 가장 약해서, 모범 사례와 나쁜 사례를 전혀 모르는 상태임 - 작성자가 가장 까다로운 미학을 목표로 한다고 설명한 부분을 놓친 것 아닌가?
- 덜 무거운 유리 효과를 원하는 사람들은 튜토리얼에서 골라 쓰면 됨
-
완전히 한 바퀴 돌아온 느낌이라 흥미로움
초기 CSS 데모 중 하나, CSS 1용 데모가 서리 낀 유리 효과였음: https://meyerweb.com/eric/css/edge/complexspiral/glassy.html -
솔직히 꽤 인상적임. CSS가 얼마나 창의적이고 강력할 수 있는지 보는 건 언제나 즐거움
다만 HTML/CSS에는 영원히 이해하지 못할 어떤 수준이 있는 것처럼 느껴짐. 이 데모가 딱 그렇고, 이 사람은 서리 낀 창을 만들고 있는데 나는 아직도div하나 가운데 정렬을 못함- 웹 개발이 어떻게 돌아가는지 익히던 때,
div를 하나 더 추가하는 게 종종 좋은 생각이라는 걸 어렵게 배웠음
최신 웹사이트 대부분을 보면div가 거대한 더미처럼 쌓여 있음. 가운데 정렬된div를 원하면, 그 안에서 가운데 정렬할 공간 전체를 차지하는div를 하나 더 만들면 비교적 쉬워짐
많은 시각적 트릭도 비슷하지만, 이 예시는 매우 깔끔해서 더 인상적임
- 웹 개발이 어떻게 돌아가는지 익히던 때,
-
정확히 서리 낀 유리는 아니지만, https://www.gyan.dev/ffmpeg/builds/의 사이드바에 유리창 효과를 넣어봤음
- PC에서 보면 사이트가 아주 편안하고 예쁨