
요약
Polymer를 사용하여 모바일로 제어되는 고성능 WebGL을 만든 방법 모듈식이고 구성 가능한 광선검입니다. 주요 세부정보 검토 프로젝트 https://lightsaber.withgoogle.com/ 이렇게 하면 다음에 엄청나게 많은 엄청난 양의 동영상을 만들 때 시간을 절약할 수 있습니다. 화가 난 스톰트루퍼스들.
개요
Polymer 또는 WebComponents가 무엇인지 궁금하신다면 먼저 실제 작업 중인 프로젝트에서 발췌한 데이터를 공유하는 것이 가장 좋습니다. 다음은 프로젝트의 방문 페이지에서 발췌한 샘플입니다. https://lightsaber.withgoogle.com. 그것은 일반 HTML 파일이지만 내부에 특별한 기능이 있습니다.
<!-- Element-->
<dom-module id="sw-page-landing">
<!-- Template-->
<template>
<style>
<!-- include elements/sw/pages/sw-page-landing/styles/sw-page-landing.css-->
</style>
<div class="centered content">
<sw-ui-logo></sw-ui-logo>
<div class="connection-url-wrapper">
<sw-t key="landing.type" class="type"></sw-t>
<div id="url" class="connection-url">.</div>
<sw-ui-toast></sw-ui-toast>
</div>
</div>
<div class="disclaimer epilepsy">
<sw-t key="disclaimer.epilepsy" class="type"></sw-t>
</div>
<sw-ui-footer state="extended"></sw-ui-footer>
</template>
<!-- Polymer element script-->
<script src="scripts/sw-page-landing.js"></script>
</dom-module>
따라서 요즘에는 뉴스 앱을 만들고 싶을 때 HTML5 기반 애플리케이션입니다. API, 프레임워크, 라이브러리, 게임 엔진 등 선택지가 너무 많음에도 불구하고 적절한 조합을 잘 조합하기는 어렵습니다. 높은 성능의 그래픽과 깔끔한 모듈식 간의 제어 설계하는 방법도 배웁니다 Polymer를 사용하면 프로젝트를 정리하면서도 하위 수준의 성능 최적화를 계속할 수 있다는 것을 발견했습니다. 이에 따라 Polymer의 기능을 최대한 활용할 수 있도록 프로젝트를 구성요소로 분할하는 방법을 신중하게 고안했습니다.
폴리머를 사용한 모듈성
Polymer는 재사용 가능한 맞춤 요소로부터 프로젝트를 빌드하는 방식에 대한 많은 통제력을 가질 수 있습니다. 이를 통해 단일 HTML 파일에 포함된 독립형의 완전한 기능을 갖춘 모듈을 사용할 수 있습니다. 여기에는 구조 (HTML 마크업)뿐만 아니라 사용할 수 있습니다.
아래 예를 살펴보세요.
<link rel="import" href="bower_components/polymer/polymer.html">
<dom-module id="picture-frame">
<template>
<!-- scoped CSS for this element -->
<style>
div {
display: inline-block;
background-color: #ccc;
border-radius: 8px;
padding: 4px;
}
</style>
<div>
<!-- any children are rendered here -->
<content></content>
</div>
</template>
<script>
Polymer({
is: "picture-frame",
});
</script>
</dom-module>
하지만 대규모 프로젝트에서는 이러한 세 가지 논리적 구성요소(HTML, CSS, JS)를 분리하고 컴파일 시점에만 병합하는 것이 좋습니다. 한 가지 프로젝트의 각 요소에 별도의 폴더를 제공했습니다.
src/elements/
|-- elements.jade
`-- sw
|-- debug
| |-- sw-debug
| |-- sw-debug-performance
| |-- sw-debug-version
| `-- sw-debug-webgl
|-- experience
| |-- effects
| |-- sw-experience
| |-- sw-experience-controller
| |-- sw-experience-engine
| |-- sw-experience-input
| |-- sw-experience-model
| |-- sw-experience-postprocessor
| |-- sw-experience-renderer
| |-- sw-experience-state
| `-- sw-timer
|-- input
| |-- sw-input-keyboard
| `-- sw-input-remote
|-- pages
| |-- sw-page-calibration
| |-- sw-page-connection
| |-- sw-page-connection-error
| |-- sw-page-error
| |-- sw-page-experience
| `-- sw-page-landing
|-- sw-app
| |-- bower.json
| |-- scripts
| |-- styles
| `-- sw-app.jade
|-- system
| |-- sw-routing
| |-- sw-system
| |-- sw-system-audio
| |-- sw-system-config
| |-- sw-system-environment
| |-- sw-system-events
| |-- sw-system-remote
| |-- sw-system-social
| |-- sw-system-tracking
| |-- sw-system-version
| |-- sw-system-webrtc
| `-- sw-system-websocket
|-- ui
| |-- experience
| |-- sw-preloader
| |-- sw-sound
| |-- sw-ui-button
| |-- sw-ui-calibration
| |-- sw-ui-disconnected
| |-- sw-ui-final
| |-- sw-ui-footer
| |-- sw-ui-help
| |-- sw-ui-language
| |-- sw-ui-logo
| |-- sw-ui-mask
| |-- sw-ui-menu
| |-- sw-ui-overlay
| |-- sw-ui-quality
| |-- sw-ui-select
| |-- sw-ui-toast
| |-- sw-ui-toggle-screen
| `-- sw-ui-volume
`-- utils
`-- sw-t
각 요소의 폴더는 동일한 내부 구조로 되어 있으며 디렉터리 및 파일과 로직 (커피 파일), 스타일 (scss 파일) 및 jade 파일).
다음은 sw-ui-logo
요소의 예입니다.
sw-ui-logo/
|-- bower.json
|-- scripts
| `-- sw-ui-logo.coffee
|-- styles
| `-- sw-ui-logo.scss
`-- sw-ui-logo.jade
.jade
파일을 살펴보겠습니다.
// Element
dom-module(id='sw-ui-logo')
// Template
template
style
include elements/sw/ui/sw-ui-logo/styles/sw-ui-logo.css
img(src='[[url]]')
// Polymer element script
script(src='scripts/sw-ui-logo.js')
스타일을 포함하여 항목이 어떻게 정리되어 있는지 확인할 수 있습니다.
및 로직을 분리할 수 있습니다. 폴리머에 스타일을 포함하기 위해
요소는 Jade의 include
문을 사용하므로 실제 인라인 CSS가 있습니다.
파일 콘텐츠의 이름을 지정합니다. sw-ui-logo.js
스크립트 요소는 런타임에 실행됩니다.
Bower를 사용한 모듈식 종속 항목
일반적으로 라이브러리 및 기타 종속 항목은 프로젝트 수준에서 유지합니다.
그러나 위의 설정에서는 요소 폴더에 있는 bower.json
(요소 수준 종속 항목)를 볼 수 있습니다. 이 접근 방식의 이면에 있는 아이디어
서로 다른 색깔의 요소가 많이 있는 상황에서
해당 종속 항목만 로드하도록 하면
확인할 수 있습니다 요소를 삭제하는 경우 이러한 종속 항목을 선언하는 bower.json
파일도 삭제되므로 종속 항목을 삭제할 필요가 없습니다. 각 요소는
종속 항목을 정의합니다
그러나 종속 항목이 중복되지 않도록 .bowerrc
파일을 포함합니다.
각 요소의 폴더에도 있습니다 이렇게 하면 bower에 종속 항목을 저장할 위치를 알려주므로 동일한 디렉터리에 종속 항목이 하나만 남을 수 있습니다.
{
"directory" : "../../../../../bower_components"
}
이렇게 하면 여러 요소가 THREE.js
를 종속 항목으로 선언할 때
첫 번째 요소에 이를 설치하고 두 번째 요소를 파싱하기 시작합니다.
이 종속 항목이 이미 설치되어 있다는 것을 인식하고
다시 다운로드하거나 복제해야 합니다 마찬가지로 종속 항목을
이 파일에 정의된 요소가
bower.json
bash 스크립트는 중첩된 요소 구조에서 모든 bower.json
파일을 찾습니다.
그런 다음 이러한 디렉터리를 하나씩 입력하고 각 디렉터리에서 bower install
를 실행합니다.
echo installing bower components...
modules=$(find /vagrant/app -type f -name "bower.json" -not -path "*node_modules*" -not -path "*bower_components*")
for module in $modules; do
pushd $(dirname $module)
bower install --allow-root -q
popd
done
빠른 새 요소 템플릿
새 요소를 만들 때마다 폴더와 기본 파일 구조를 올바른 이름으로 생성하는 데 약간의 시간이 걸립니다. 그래서 우리는 Slush: 간단한 요소 생성기를 작성합니다.
명령줄에서 스크립트를 호출할 수 있습니다.
$ slush element path/to/your/element-name
그러면 모든 파일 구조와 콘텐츠를 포함한 새 요소가 생성됩니다.
요소 파일의 템플릿을 정의했습니다. 예를 들어 .jade
파일 템플릿은 다음과 같습니다.
// Element
dom-module(id='<%= name %>')
// Template
template
style
include elements/<%= path %>/styles/<%= name %>.css
span This is a '<%= name %>' element.
// Polymer element script
script(src='scripts/<%= name %>.js')
Slush 생성기가 변수를 실제 요소 경로와 이름으로 바꿉니다.
Gulp를 사용하여 요소 빌드
Gulp는 빌드 프로세스를 계속 제어합니다. Google의 구조에서 다음 단계를 따르기 위해 Gulp에 필요한 요소를 추가합니다.
- 요소의
.coffee
파일을.js
로 컴파일 - 요소 컴파일 파일
.scss
개를.css
로 - 요소 컴파일
.jade
파일을.html
로 변환하고.css
파일을 임베딩합니다.
자세한 내용은 다음과 같습니다.
요소 컴파일 파일 .coffee
개를 .js
로
gulp.task('elements-coffee', function () {
return gulp.src(abs(config.paths.app + '/elements/**/*.coffee'))
.pipe($.replaceTask({
patterns: [{json: getVersionData()}]
}))
.pipe($.changed(abs(config.paths.static + '/elements'), {extension: '.js'}))
.pipe($.coffeelint())
.pipe($.coffeelint.reporter())
.pipe($.sourcemaps.init())
.pipe($.coffee({
}))
.on('error', gutil.log)
.pipe($.sourcemaps.write())
.pipe(gulp.dest(abs(config.paths.static + '/elements')));
});
2단계와 3단계에서는 gulp 및 나침반 플러그인을 사용하여 scss
을
위의 2와 유사한 접근 방식으로 .css
및 .jade
를 .html
로 변환합니다.
폴리머 원소 포함
Polymer 요소를 실제로 포함하기 위해 Google에서는 HTML 가져오기를 사용합니다.
<link rel="import" href="elements.html">
<!-- Polymer -->
<link rel="import" href="../bower_components/polymer/polymer.html">
<!-- Custom elements -->
<link rel="import" href="sw/sw-app/sw-app.html">
<link rel="import" href="sw/system/sw-system/sw-system.html">
<link rel="import" href="sw/system/sw-routing/sw-routing.html">
<link rel="import" href="sw/system/sw-system-version/sw-system-version.html">
<link rel="import" href="sw/system/sw-system-environment/sw-system-environment.html">
<link rel="import" href="sw/pages/sw-page-landing/sw-page-landing.html">
<link rel="import" href="sw/pages/sw-page-connection/sw-page-connection.html">
<link rel="import" href="sw/pages/sw-page-calibration/sw-page-calibration.html">
<link rel="import" href="sw/pages/sw-page-experience/sw-page-experience.html">
<link rel="import" href="sw/ui/sw-preloader/sw-preloader.html">
<link rel="import" href="sw/ui/sw-ui-overlay/sw-ui-overlay.html">
<link rel="import" href="sw/ui/sw-ui-button/sw-ui-button.html">
<link rel="import" href="sw/ui/sw-ui-menu/sw-ui-menu.html">
프로덕션용 Polymer 요소 최적화
대규모 프로젝트에는 Polymer 요소가 많을 수 있습니다. Google의
50개가 넘습니다. 각 요소에
별도의 .js
파일과 일부 라이브러리가 참조된 경우
별도의 파일 100개 이것은 브라우저가 만들어야 하는 많은 요청이 있다는 것을 의미합니다.
성능 손실이 발생할 수 있습니다 Angular 빌드에 적용하는 연결 및 축소 프로세스와 마찬가지로 프로덕션을 위해 최종적으로 Polymer 프로젝트를 '발화'합니다.
Vulcanize는 폴리머 도구입니다. 종속 항목 트리를 단일 html 파일로 평면화하여 지정할 수 있습니다 이는 웹 브라우저가 웹 구성요소를 기본적으로 지원합니다.
CSP (Content Security Policy) 및 Polymer
안전한 웹 애플리케이션을 개발할 때는 CSP를 구현해야 합니다. CSP 교차 사이트 스크립팅 (XSS) 공격을 방지하는 규칙 집합입니다. 안전하지 않은 소스에서 스크립트 실행 또는 인라인 스크립트 실행 가져올 수 있습니다.
이제 Vulcanize에서 생성한 최적화되고 연결된 축소된 단일 .html
파일에 CSP를 준수하지 않는 형식으로 모든 JavaScript 코드가 인라인으로 포함되어 있습니다. 이 문제를 해결하기 위해 우리는
Crisper.
Crisper는 HTML 파일에서 인라인 스크립트를 분할하여 CSP 규정을 준수하기 위해 단일 외부 JavaScript 파일에 배치합니다. 그래서 가황 처리된
Crisper를 통해 HTML 파일을 만들고 elements.html
및
elements.js
입니다. elements.html
내부에서 생성된 elements.js
를 로드하는 작업도 처리합니다.
애플리케이션 논리적 구조
Polymer에서 요소는 비시각적 유틸리티부터 작은 재사용 가능한 단일 UI 요소 (예: 버튼)를 '페이지' 전체 응용 프로그램을 작성할 수도 있습니다.

Polymer 및 상위-하위 아키텍처를 사용한 후처리
모든 3D 그래픽 파이프라인에는 마지막 단계에서 효과가 일종의 오버레이로 전체 사진 위에 추가됩니다. 이것은 발광 효과, 신 광선, 초점을 맞출 수 있습니다. 그 효과는 결합되어 장면이 만들어진 방식에 따라 다양한 요소가 표현됩니다. THREE.js에서는 JavaScript에서 후처리를 위한 맞춤 셰이더를 만들거나 상위-하위 구조 덕분에 Polymer를 사용해 이 작업을 수행할 수 있습니다.
후처리기의 요소 HTML 코드를 살펴보면 다음과 같습니다.
<dom-module id="sw-experience-postprocessor">
<!-- Template-->
<template>
<sw-experience-effect-bloom class="effect"></sw-experience-effect-bloom>
<sw-experience-effect-dof class="effect"></sw-experience-effect-dof>
<sw-experience-effect-vignette class="effect"></sw-experience-effect-vignette>
</template>
<!-- Polymer element script-->
<script src="scripts/sw-experience-postprocessor.js"></script>
</dom-module>
효과는 공통 클래스 아래에 중첩된 폴리머 요소로 지정합니다. 그런 다음
sw-experience-postprocessor.js
에서 다음을 실행합니다.
effects = @querySelectorAll '.effect'
@composer.addPass effect.getPass() for effect in effects
HTML 기능과 JavaScript의 querySelectorAll
를 사용하여 포스트 프로세서 내에서 HTML 요소로 중첩된 모든 효과를 지정된 순서대로 찾습니다. 그런 다음 이를 반복하여 컴포저에 추가합니다.
이제 DOF (심도) 효과를 제거하고 블룸 및 비네트 효과의 순서를 변경할 수 있습니다. 편집만 하면 후처리자의 정의를 다음과 같이 변경해야 합니다.
<dom-module id="sw-experience-postprocessor">
<!-- Template-->
<template>
<sw-experience-effect-vignette class="effect"></sw-experience-effect-vignette>
<sw-experience-effect-bloom class="effect"></sw-experience-effect-bloom>
</template>
<!-- Polymer element script-->
<script src="scripts/sw-experience-postprocessor.js"></script>
</dom-module>
실제 코드를 한 줄도 변경하지 않고 장면이 실행됩니다.
Polymer의 렌더링 루프 및 업데이트 루프
Polymer를 사용하면 렌더링 및 엔진 업데이트에도 우아하게 접근할 수 있습니다.
requestAnimationFrame
를 사용하고 계산을 수행하는 timer
요소를 만들었습니다.
값(예: 현재 시간(t
) 및 델타 시간 -
마지막 프레임 (dt
):
Polymer
is: 'sw-timer'
properties:
t:
type: Number
value: 0
readOnly: true
notify: true
dt:
type: Number
value: 0
readOnly: true
notify: true
_isRunning: false
_lastFrameTime: 0
ready: ->
@_isRunning = true
@_update()
_update: ->
if !@_isRunning then return
requestAnimationFrame => @_update()
currentTime = @_getCurrentTime()
@_setT currentTime
@_setDt currentTime - @_lastFrameTime
@_lastFrameTime = @_getCurrentTime()
_getCurrentTime: ->
if window.performance then performance.now() else new Date().getTime()
그런 다음 데이터 결합을 사용하여 t
및 dt
속성을
엔진 (experience.jade
):
sw-timer(
t='{ % templatetag openvariable % }t}}',
dt='{ % templatetag openvariable % }dt}}'
)
sw-experience-engine(
t='[t]',
dt='[dt]'
)
엔진에서 t
및 dt
의 변경사항을 수신 대기하고 값이 변경될 때마다 _update
함수가 호출됩니다.
Polymer
is: 'sw-experience-engine'
properties:
t:
type: Number
dt:
type: Number
observers: [
'_update(t)'
]
_update: (t) ->
dt = @dt
@_physics.update dt, t
@_renderer.render dt, t
FPS에 관심이 있다면 Polymer 데이터를 삭제하는 것이 좋습니다. 알리는 데 필요한 몇 밀리초를 절약하기 위해 렌더링 루프에서 바인딩 요소 다음과 같이 맞춤 관찰자를 구현했습니다.
sw-timer.coffee
:
addUpdateListener: (listener) ->
if @_updateListeners.indexOf(listener) == -1
@_updateListeners.push listener
return
removeUpdateListener: (listener) ->
index = @_updateListeners.indexOf listener
if index != -1
@_updateListeners.splice index, 1
return
_update: ->
# ...
for listener in @_updateListeners
listener @dt, @t
# ...
addUpdateListener
함수는 콜백을 수락하고
콜백 배열입니다. 그런 다음 업데이트 루프에서 모든 콜백을 반복하고
dt
및 t
인수로 직접 실행하며 데이터 결합을 우회하거나
있습니다. 콜백이 더 이상 활성 상태가 아니면 이전에 추가된 콜백을 삭제할 수 있는 removeUpdateListener
함수가 추가되었습니다.
THREE.js의 광선검
THREE.js는 WebGL의 낮은 수준의 세부정보를 추상화하여 문제에 집중할 수 있도록 합니다. 우리의 문제는 스톰트루퍼와 싸우는 것이고 무기입니다. 그럼 광선검을 만들어 보죠.
광선검은 오래된 광선검과는 달리 빛나는 칼날이 도움이 될 수 있습니다. 주로 두 부분으로 구성되는데, 빔과 트레일 이미지를 이동할 때 표시됩니다. 밝은 원통형으로 만들었어. 플레이어가 움직일 때 따라가는 역동적인 흔적이 있습니다.
더 블레이드
블레이드는 2개의 하위 블레이드로 구성되어 있습니다. 안쪽과 바깥쪽입니다. 둘 다 각각의 소재가 있는 THREE.js 메시입니다.
이너 블레이드
내부 블레이드의 경우 맞춤 셰이더가 있는 맞춤 소재를 사용했습니다. 두 점으로 만든 선을 그려 이 두 점 사이에 선을 투영합니다. 하나의 점만 기억합니다. 이 평면은 기본적으로 휴대기로 싸울 때 제어하는 평면으로, 검에 깊이감과 방향을 부여합니다.
둥근 빛나는 물체의 느낌을 주기 위해 주점에서 평면에 있는 모든 점의 직각 점 거리 선을 사용하여 두 점 A와 B를 연결합니다. 점이 더 가까울수록 기본 축이 더 밝아집니다.

아래 소스는 강도를 제어하기 위해 vFactor
를 계산하는 방법을 보여줍니다.
을 사용하여 도형의 장면과 블렌딩할 수 있습니다.
프래그먼트 셰이더.
THREE.LaserShader = {
uniforms: {
"uPointA": {type: "v3", value: new THREE.Vector3(0, -1, 0)},
"uPointB": {type: "v3", value: new THREE.Vector3(0, 1, 0)},
"uColor": {type: "c", value: new THREE.Color(1, 0, 0)},
"uMultiplier": {type: "f", value: 3.0},
"uCoreColor": {type: "c", value: new THREE.Color(1, 1, 1)},
"uCoreOpacity": {type: "f", value: 0.8},
"uLowerBound": {type: "f", value: 0.4},
"uUpperBound": {type: "f", value: 0.8},
"uTransitionPower": {type: "f", value: 2},
"uNearPlaneValue": {type: "f", value: -0.01}
},
vertexShader: [
"uniform vec3 uPointA;",
"uniform vec3 uPointB;",
"uniform float uMultiplier;",
"uniform float uNearPlaneValue;",
"varying float vFactor;",
"float getDistanceFromAB(vec2 a, vec2 b, vec2 p) {",
"vec2 l = b - a;",
"float l2 = dot( l, l );",
"float t = dot( p - a, l ) / l2;",
"if( t < 0.0 ) return distance( p, a );",
"if( t > 1.0 ) return distance( p, b );",
"vec2 projection = a + (l * t);",
"return distance( p, projection );",
"}",
"vec3 getIntersection(vec4 a, vec4 b) {",
"vec3 p = a.xyz;",
"vec3 q = b.xyz;",
"vec3 v = normalize( q - p );",
"float t = ( uNearPlaneValue - p.z ) / v.z;",
"return p + (v * t);",
"}",
"void main() {",
"vec4 a = modelViewMatrix * vec4(uPointA, 1.0);",
"vec4 b = modelViewMatrix * vec4(uPointB, 1.0);",
"if(a.z > uNearPlaneValue) a.xyz = getIntersection(a, b);",
"if(b.z > uNearPlaneValue) b.xyz = getIntersection(a, b);",
"a = projectionMatrix * a; a /= a.w;",
"b = projectionMatrix * b; b /= b.w;",
"vec4 p = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
"gl_Position = p;",
"p /= p.w;",
"float d = getDistanceFromAB(a.xy, b.xy, p.xy) * gl_Position.z;",
"vFactor = 1.0 - clamp(uMultiplier * d, 0.0, 1.0);",
"}"
].join( "\n" ),
fragmentShader: [
"uniform vec3 uColor;",
"uniform vec3 uCoreColor;",
"uniform float uCoreOpacity;",
"uniform float uLowerBound;",
"uniform float uUpperBound;",
"uniform float uTransitionPower;",
"varying float vFactor;",
"void main() {",
"vec4 col = vec4(uColor, vFactor);",
"float factor = smoothstep(uLowerBound, uUpperBound, vFactor);",
"factor = pow(factor, uTransitionPower);",
"vec4 coreCol = vec4(uCoreColor, uCoreOpacity);",
"vec4 finalCol = mix(col, coreCol, factor);",
"gl_FragColor = finalCol;",
"}"
].join( "\n" )
};
아우터 블레이드 발광 효과
외부 발광 효과의 경우 별도의 렌더링 버퍼로 렌더링하고 블룸 효과를 생성하고 최종 이미지와 혼합하여 빛을 조절할 수 있습니다. 아래 이미지는 Google Cloud 콘솔의 괜찮은 검을 원한다면 있어야 합니다. 즉, 흰색 심부는 외부 발광 효과를 볼 수 있습니다.

Lightsaber Trail
광선검의 자국은 원작에서 본 것처럼 스타워즈 시리즈에서 봤습니다. 삼각형 부채로 만든 트레일을 만들었습니다. 광선검의 움직임에 따라 동적으로 변합니다. 이러한 팬들은 시각적 개선을 위해 후처리기로 전달됩니다. 이 부채형 도형은 선 세그먼트가 있으며 이전 변환을 기반으로 합니다. 그리고 현재 변환을 통해 메시에 새로운 삼각형을 생성하여 꼬리 부분을 잘라냅니다.


메시가 있으면 간단한 머티리얼을 할당하고 포스트프로세서를 사용하여 매끄러운 효과를 만듭니다. 블룸 효과를 사용하여 외부 블레이드 발광 효과를 적용하여 다음과 같이 매끄러운 흔적을 얻었습니다.

빛나는 산책로
마지막 조각을 완성하려면 실제 주변을 빛나게 해야 했습니다. 여러 가지 방법으로 생성될 수 있습니다. Google의 솔루션은 여기에서 자세히 설명하지는 않겠습니다. 왜냐하면 성능상의 이유로 커스텀 이 버퍼의 셰이더로, 렌더링 버퍼를 제공합니다. 그런 다음 최종 렌더링에서 이 출력을 결합합니다. 여기서 트레일을 둘러싼 불빛을 확인해 보세요.

결론
Polymer는 강력한 라이브러리이자 개념입니다 (WebComponents가 일반). 그걸로 무엇을 만들지는 여러분에게 달려 있습니다. 데이터 애널리스트가 사용할 수 있는 간단한 UI 버튼을 전체 크기의 WebGL 애플리케이션으로 만들 수 있습니다. 이전 장에서는 Polymer를 효율적으로 사용하는 방법에 대한 도움말 및 유용한 정보를 성능을 향상시키는 더 복잡한 모듈을 구성하는 방법 있습니다. 또한 WebGL에서 멋진 광선검을 얻는 방법도 보여드렸습니다. 이 모든 것을 결합하려면 폴리머 요소를 가황화해야 합니다. 배포 전에 Crisper를 사용해 CSP 규정을 준수하려면 귀사와 협력할 수 있을 것입니다.
