├── .gitignore
├── README.md
├── dst
├── assets
│ ├── css
│ │ └── styles.css
│ ├── glsl
│ │ ├── cube.frag
│ │ ├── cube.vert
│ │ ├── cube_edge.frag
│ │ ├── cube_edgeFace.frag
│ │ ├── output.frag
│ │ ├── output.vert
│ │ ├── simulation_def.frag
│ │ ├── simulation_pos.frag
│ │ └── simulation_vel.frag
│ └── js
│ │ └── main.min.js
├── fb.png
├── index.html
└── tw.png
├── gulp
└── tasks
│ ├── browsersync.js
│ ├── glsl.js
│ ├── html.js
│ ├── javascript.js
│ └── sass.js
├── gulpfile.js
├── package.json
├── screenshot1.png
├── screenshot2.png
└── src
├── assets
├── css
│ ├── styles.scss
│ └── vendor
│ │ ├── _easing.scss
│ │ └── _initial.scss
├── glsl
│ ├── cube.frag
│ ├── cube.vert
│ ├── cube_edge.frag
│ ├── cube_edgeFace.frag
│ ├── noise2D.glsl
│ ├── noise3D.glsl
│ ├── noise4D.glsl
│ ├── output.frag
│ ├── output.vert
│ ├── simulation_def.frag
│ ├── simulation_pos.frag
│ └── simulation_vel.frag
└── js
│ ├── lib-head
│ ├── modernizr-custom.js
│ └── useragnt-all.min.js
│ ├── lib
│ ├── GPUComputationRenderer.js
│ └── OrbitControls.js
│ ├── main.js
│ └── module
│ ├── color-texture.js
│ ├── controls.js
│ ├── resize-watch.js
│ ├── simulation.js
│ └── webgl.js
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # MacOS
2 | .DS_Store
3 | .AppleDouble
4 | .LSOverride
5 |
6 |
7 | # Thumbnails
8 | ._*
9 |
10 | # Files that might appear on external disk
11 | .Spotlight-V100
12 | .Trashes
13 |
14 |
15 | # node.js
16 | lib-cov
17 | *.seed
18 | *.log
19 | *.csv
20 | *.dat
21 | *.out
22 | *.pid
23 | *.gz
24 | pids
25 | logs
26 | results
27 | npm-debug.log
28 | node_modules
29 |
30 | # Sass
31 | .sass-cache
32 |
33 | # SublimeText
34 | *.sublime-project
35 | *.sublime-workspace
36 | sftp-config.json
37 |
38 | #vim
39 | .*.s[a-w][a-z]
40 | *.un~
41 | Session.vim
42 | .netrwhist
43 | *~
44 |
45 | .idea
46 | .htaccess
47 | .htpasswd
48 | pull.php
49 |
50 | #dev
51 | #dst
52 | build
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Toon Shading
2 |
3 | * [Demo](https://mnmxmx.github.io/toon-shading/dst/index.html)
4 | * [How To Implement Toon Shading With Webgl (in Japanese)](https://wgld.org/d/webgl/w048.html)
5 |
6 |
7 | [](./screenshot1.png)
8 |
9 | [
](./screenshot2.png)
10 |
11 | ## Usage
12 | * Clone repository
13 | * Install Node.js
14 | * Run following commands
15 | ```
16 | npm install
17 | npm start
18 | ```
19 |
20 |
--------------------------------------------------------------------------------
/dst/assets/css/styles.css:
--------------------------------------------------------------------------------
1 | html,body,article,section,nav,aside,h1,h2,h3,h4,h5,h6,header,footer,address,p,ol,ul,li,dl,dt,dd,div,a,strong,small,sup,span,img,iframe,embed,object,video,audio,table,tr,td,th,canvas,svg{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}html{cursor:default;line-height:1;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-overflow-scrolling:touch;-webkit-tap-highlight-color:transparent;font-family:arial}html.windows{-webkit-font-smoothing:antialiased}::-moz-selection{background-color:#ccc;color:#000;text-shadow:none}::selection{background-color:#ccc;color:#000;text-shadow:none}ol,ul{list-style:none}table{border-collapse:collapse;border-spacing:0}article,section,nav,aside,header,footer{display:block}video,audio,canvas{display:inline-block}audio:not([controls]){display:none;height:0}hr{box-sizing:content-box;height:0;overflow:visible}strong{font-weight:inherit}strong{font-weight:bolder}sub,sup{line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}svg{fill:currentColor}svg:not(:root){overflow:hidden}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}a{-ms-touch-action:manipulation;touch-action:manipulation}body{overflow:hidden}.stats{position:absolute;top:0;right:0;z-index:2}.file{position:absolute;top:15px;left:15px;padding:10px;box-sizing:border-box;border:1px solid #fff;letter-spacing:.01em;color:#fff;cursor:pointer;-webkit-transition:opacity .4s;transition:opacity .4s;font-size:12px;padding:15px 30px;letter-spacing:.15em;opacity:0}.file.isHidden{display:none}.file input{display:none}.tapToStart{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.6);z-index:100}.pc .tapToStart{display:none}.tapToStart.isHidden{display:none}.tapToStart p{position:absolute;left:50%;top:50%;padding:15px 30px;font-size:12px;color:#888;background-color:#fff;letter-spacing:.15em;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%);cursor:pointer}.credit{position:absolute;letter-spacing:.15em;line-height:1.5;color:#fff;font-weight:100;-webkit-font-smoothing:antialiased}.credit-pc{bottom:20px;right:20px;font-size:14px}.no-pc .credit-pc{display:none}.credit-pc a{position:relative;display:inline-block;vertical-align:middle;width:15px;height:15px;top:-1px}.credit-pc .soundcloud{background:url(../img/soundcloud-icon.svg) no-repeat;background-size:contain}.credit-pc .facebook{background:url(../img/fb-icon.svg) no-repeat;background-size:contain}.credit-sp{bottom:10px;right:10px;font-size:12px;color:rgba(255,255,255,.7)}.pc .credit-sp{display:none}.credit-sp a{position:relative;color:rgba(255,255,255,.7);text-decoration:none}.credit-sp a:after{content:"";position:absolute;width:100%;height:1px;background:rgba(255,255,255,.7);bottom:-1px;left:0}
--------------------------------------------------------------------------------
/dst/assets/glsl/cube.frag:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | uniform bool isEdge;
3 | uniform vec3 uEdgeColor;
4 | uniform bool isShading;
5 |
6 | varying vec3 vPosition;
7 | varying vec3 vColor;
8 | varying vec3 vNormal;
9 |
10 | vec3 calcIrradiance_hemi(vec3 newNormal, vec3 lightPos, vec3 grd, vec3 sky){
11 | float dotNL = clamp(dot(newNormal, normalize(lightPos)), 0.0, 1.0);
12 |
13 | return mix(grd, sky, dotNL);
14 | }
15 |
16 | vec3 calcIrradiance_dir(vec3 newNormal, vec3 lightPos, vec3 light, float t1, float t2){
17 | float dotNL = clamp(dot(newNormal, normalize(lightPos)) , 0.0, 1.0);
18 |
19 | vec3 diffuse = (dotNL < t1) ? vec3(0.0, -0.1, 0.1) : (dotNL < t2) ? vec3(-0.1, -0.2, 0.1) : vec3(1.0);
20 |
21 | return light * diffuse;
22 | }
23 |
24 | const float PI = 3.14159265358979323846264;
25 |
26 | // hemisphere ground color
27 | const vec3 hemiLight_g = vec3(256.0, 246.0, 191.0) / vec3(256.0);
28 |
29 | // hemisphere sky color
30 | const vec3 hemiLight_s_1 = vec3(0.9,0.8,0.6);
31 | const vec3 hemiLight_s_2 = vec3(0.9,0.6,0.7);
32 |
33 | // directional light color
34 | const vec3 dirLight_1 = vec3(0.02, 0.02, 0.0);
35 | const vec3 dirLight_2 = vec3(0.0, 0.1, 0.1);
36 |
37 | const vec3 dirLightPos_1 = vec3(4, 6, 10);
38 | const vec3 dirLightPos_2 = vec3(-5, -5, -5);
39 |
40 | const vec3 hemiLightPos_1 = vec3(-100.0, -100.0, 100.0);
41 | const vec3 hemiLightPos_2 = vec3(-100.0, 100.0, -100.0);
42 |
43 | void main() {
44 | // vec3 fdx = dFdx( vPosition );
45 | // vec3 fdy = dFdy( vPosition );
46 | // vec3 norm = normalize(cross(fdx, fdy));
47 |
48 | vec3 hemiColor = vec3(0.0);
49 | hemiColor += calcIrradiance_hemi(vNormal, hemiLightPos_1, hemiLight_g, hemiLight_s_1) * 0.56;
50 | hemiColor += calcIrradiance_hemi(vNormal, hemiLightPos_2, hemiLight_g, hemiLight_s_2) * 0.56;
51 |
52 | vec3 dirColor = vec3(0.0);
53 | dirColor += calcIrradiance_dir(vNormal, dirLightPos_1, dirLight_1, 0.3, 0.7);
54 | dirColor += calcIrradiance_dir(vNormal, dirLightPos_2, dirLight_2, 0.5, 0.6);
55 |
56 | vec3 color = vColor * min(vec3(1.0), hemiColor * 1.06);
57 | color += dirColor;
58 |
59 | // color = min(vColor, color);
60 |
61 | color = (isEdge) ? uEdgeColor : (isShading) ? color : vColor;
62 |
63 | gl_FragColor = vec4(color, 1.0);
64 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/cube.vert:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | attribute float aNum;
3 | attribute vec3 vertNormal;
4 | // attribute float aColorNum;
5 |
6 | uniform sampler2D posMap;
7 | uniform sampler2D velMap;
8 | uniform float uSize;
9 | uniform float uTick;
10 | uniform float uScale1;
11 | uniform vec3 uScale2;
12 | uniform vec3 uColorArray[4];
13 | uniform bool isEdge;
14 | uniform float uEdgeScale;
15 |
16 | varying vec3 vPosition;
17 | varying vec3 vColor;
18 | varying vec3 vNormal;
19 |
20 | float parabola( float x) {
21 | return 4.0 * (1.0 - x) * x;
22 | }
23 |
24 | mat2 calcRotate2D(float _deg){
25 | float _sin = sin(_deg);
26 | float _cos = cos(_deg);
27 | return mat2(_cos, _sin, -_sin, _cos);
28 | }
29 |
30 | mat3 calcLookAtMatrix(vec3 vector, float roll) {
31 | vec3 rr = vec3(sin(roll), cos(roll), 0.0);
32 | vec3 ww = normalize(vector);
33 | vec3 uu = normalize(cross(ww, rr));
34 | vec3 vv = normalize(cross(uu, ww));
35 |
36 | return mat3(uu, ww, vv);
37 | }
38 |
39 | void main() {
40 | float time = uTick * 0.3;
41 |
42 | vec2 posUv;
43 | posUv.x = mod(aNum + 0.5, uSize);
44 | posUv.y = float((aNum + 0.5) / uSize);
45 | posUv /= vec2(uSize);
46 |
47 | vec4 cubePosition = texture2D( posMap, posUv );
48 | vec4 cubeVelocity = texture2D( velMap, posUv );
49 | float alpha = cubeVelocity.a / 100.0;
50 | float scale = 0.025 * parabola( alpha);
51 |
52 | vec3 offsetEdgePos = (isEdge) ? vertNormal * uEdgeScale : vec3(0.0);
53 |
54 | vec3 pos = position + offsetEdgePos;
55 | pos.zx = calcRotate2D(time * 0.7) * pos.zx;
56 |
57 | mat4 localRotationMat = mat4( calcLookAtMatrix( cubeVelocity.xyz, 0.0 ) );
58 |
59 | vec3 modifiedVertex = (localRotationMat * vec4( pos * scale * (vec3(1.0)) * uScale2 * uScale1, 1.0 ) ).xyz;
60 | vec3 modifiedPosition = modifiedVertex + cubePosition.xyz;
61 |
62 | gl_Position = projectionMatrix * modelViewMatrix * vec4( modifiedPosition, 1.0 );
63 | vPosition = modifiedPosition;
64 |
65 | vNormal = normal;
66 | vNormal.zx = calcRotate2D(time * 0.7) * vNormal.zx;
67 | vNormal = (localRotationMat * vec4(vNormal, 1.0)).xyz;
68 |
69 | vColor = uColorArray[int(mod(aNum, 4.0))];
70 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/cube_edge.frag:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | void main() {
3 | gl_FragColor = vec4(1.0);
4 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/cube_edgeFace.frag:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | void main() {
3 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
4 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/output.frag:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 |
3 | //
4 | // Description : Array and textureless GLSL 2D/3D/4D simplex
5 | // noise functions.
6 | // Author : Ian McEwan, Ashima Arts.
7 | // Maintainer : stegu
8 | // Lastmod : 20110822 (ijm)
9 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved.
10 | // Distributed under the MIT License. See LICENSE file.
11 | // https://github.com/ashima/webgl-noise
12 | // https://github.com/stegu/webgl-noise
13 | //
14 |
15 | vec3 mod289(vec3 x) {
16 | return x - floor(x * (1.0 / 289.0)) * 289.0;
17 | }
18 |
19 | vec4 mod289(vec4 x) {
20 | return x - floor(x * (1.0 / 289.0)) * 289.0;
21 | }
22 |
23 | vec4 permute(vec4 x) {
24 | return mod289(((x*34.0)+1.0)*x);
25 | }
26 |
27 | vec4 taylorInvSqrt(vec4 r)
28 | {
29 | return 1.79284291400159 - 0.85373472095314 * r;
30 | }
31 |
32 | float snoise3D(vec3 v)
33 | {
34 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
35 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
36 |
37 | // First corner
38 | vec3 i = floor(v + dot(v, C.yyy) );
39 | vec3 x0 = v - i + dot(i, C.xxx) ;
40 |
41 | // Other corners
42 | vec3 g_0 = step(x0.yzx, x0.xyz);
43 | vec3 l = 1.0 - g_0;
44 | vec3 i1 = min( g_0.xyz, l.zxy );
45 | vec3 i2 = max( g_0.xyz, l.zxy );
46 |
47 | // x0 = x0 - 0.0 + 0.0 * C.xxx;
48 | // x1 = x0 - i1 + 1.0 * C.xxx;
49 | // x2 = x0 - i2 + 2.0 * C.xxx;
50 | // x3 = x0 - 1.0 + 3.0 * C.xxx;
51 | vec3 x1 = x0 - i1 + C.xxx;
52 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
53 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
54 |
55 | // Permutations
56 | i = mod289(i);
57 | vec4 p = permute( permute( permute(
58 | i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
59 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
60 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
61 |
62 | // Gradients: 7x7 points over a square, mapped onto an octahedron.
63 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
64 | float n_ = 0.142857142857; // 1.0/7.0
65 | vec3 ns = n_ * D.wyz - D.xzx;
66 |
67 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
68 |
69 | vec4 x_ = floor(j * ns.z);
70 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
71 |
72 | vec4 x = x_ *ns.x + ns.yyyy;
73 | vec4 y = y_ *ns.x + ns.yyyy;
74 | vec4 h = 1.0 - abs(x) - abs(y);
75 |
76 | vec4 b0 = vec4( x.xy, y.xy );
77 | vec4 b1 = vec4( x.zw, y.zw );
78 |
79 | //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
80 | //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
81 | vec4 s0 = floor(b0)*2.0 + 1.0;
82 | vec4 s1 = floor(b1)*2.0 + 1.0;
83 | vec4 sh = -step(h, vec4(0.0));
84 |
85 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
86 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
87 |
88 | vec3 p0 = vec3(a0.xy,h.x);
89 | vec3 p1 = vec3(a0.zw,h.y);
90 | vec3 p2 = vec3(a1.xy,h.z);
91 | vec3 p3 = vec3(a1.zw,h.w);
92 |
93 | //Normalise gradients
94 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
95 | p0 *= norm.x;
96 | p1 *= norm.y;
97 | p2 *= norm.z;
98 | p3 *= norm.w;
99 |
100 | // Mix final noise value
101 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
102 | m = m * m;
103 | return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
104 | dot(p2,x2), dot(p3,x3) ) );
105 | }
106 |
107 | // varying vec3 vPos;
108 | varying vec2 vUv;
109 |
110 | uniform float uTick;
111 | uniform vec2 uSize;
112 | // uniform vec3 uEdgeColor;
113 | uniform vec3 uBgColor;
114 |
115 | uniform sampler2D uTex_1;
116 |
117 | vec3 calcContrast(vec3 _value){
118 | return _value * 1.1 - 0.1;
119 | }
120 |
121 | float rand(vec2 co){
122 | float a = fract(dot(co, vec2(2.067390879775102, 12.451168662908249))) - 0.5;
123 | float s = a * (6.182785114200511 + a * a * (-38.026512460676566 + a * a * 53.392573080032137));
124 | float t = fract(s * 43758.5453);
125 |
126 | return t;
127 | }
128 |
129 | // color
130 | vec3 normalColor(vec4 c1, vec4 c2){
131 | return mix(c1.rgb + c2.rgb, c1.rgb, c1.a);
132 | }
133 |
134 | vec3 addColor(vec4 c1, vec4 c2){
135 | return c2.rgb * c2.a + c1.rgb;
136 | }
137 |
138 | vec3 multiplyColor(vec4 c1, vec4 c2){
139 | return c2.rgb * c1.rgb;
140 | }
141 |
142 | vec3 screenColor(vec4 c1, vec4 c2){
143 | return c2.rgb * (vec3(1.0) - c1.rgb) + c1.rgb;
144 | }
145 |
146 | vec3 overlayColor(vec4 c1, vec4 c2){
147 | float r = (c1.r < 0.5) ? 2.0 * c1.r * c2.r : 1.0 - 2.0 * (1.0 - c1.r) * (1.0 - c2.r);
148 | float g = (c1.g < 0.5) ? 2.0 * c1.g * c2.g : 1.0 - 2.0 * (1.0 - c1.g) * (1.0 - c2.g);
149 | float b = (c1.b < 0.5) ? 2.0 * c1.b * c2.b : 1.0 - 2.0 * (1.0 - c1.b) * (1.0 - c2.b);
150 |
151 | return vec3(r, g, b);
152 | }
153 |
154 | vec3 rgb2hsv(vec3 c){
155 | vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
156 | vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
157 | vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
158 |
159 | float d = q.x - min(q.w, q.y);
160 | float e = 1.0e-10;
161 | return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
162 | }
163 |
164 | vec3 hsv2rgb(vec3 c){
165 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
166 | vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
167 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
168 | }
169 |
170 | vec2 noiseUv(vec2 uv){
171 | float uvNoise = snoise3D(vec3(uv.x * 200.0, uv.y * 200.0, 0.0));
172 | float noiseRadius = 1.0;
173 |
174 | float _u = uv.x * uSize.x + rand(uv) * noiseRadius * (1.0 + uvNoise) * 2.0 - noiseRadius;
175 | float _v = uv.y * uSize.y + rand(vec2(uv.y, uv.x)) * noiseRadius * (1.0 + uvNoise) * 2.0 - noiseRadius;
176 | return vec2(_u, _v) / uSize * (1.0 + abs(uvNoise) * 0.001);
177 | }
178 |
179 | void main(){
180 |
181 | float time = uTick * 0.0001;
182 |
183 | vec4 bgColor = vec4(uBgColor, 1.0);
184 |
185 | vec2 st = vUv * 2.0 - 0.5;
186 | float faceUvOffset = snoise3D(vec3(st * 6.0, time));
187 |
188 | vec2 faceColorUv = vUv + vec2(faceUvOffset)* 0.003;
189 |
190 | vec4 faceColor = texture2D(uTex_1, vUv);
191 |
192 | vec4 color;
193 | // color = faceColor;
194 | // color.a = edgeColor.a;
195 | // color.rgb = normalColor(color, edgeColor);
196 | // color.rgb = normalColor(color, bgColor);
197 | // color.a = 1.0;
198 |
199 | color = faceColor;
200 | color.rgb = normalColor(color, bgColor);
201 | color.a = 1.0;
202 |
203 | // color.rgb = max(vec3(0.0), min(vec3(1.0), color.rgb));
204 |
205 | // vec3 hsv = rgb2hsv(color.rgb);
206 | // hsv.r -= 0.62;
207 | // hsv.g -= 0.7;
208 | // hsv.b -= 0.3;
209 | // color.rgb = hsv2rgb(hsv);
210 |
211 | gl_FragColor = vec4(color);
212 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/output.vert:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | // #pragma glslify: snoise = require("./noise2D")
3 |
4 | // varying vec3 vPos;
5 | varying vec2 vUv;
6 |
7 | // uniform float uTick;
8 |
9 | const float PI = 3.1415926;
10 |
11 | mat2 calcRotate2D(float _time){
12 | float _sin = sin(_time);
13 | float _cos = cos(_time);
14 | return mat2(_cos, _sin, -_sin, _cos);
15 | }
16 |
17 | void main(){
18 |
19 | vUv = uv;
20 |
21 | vec4 mvPos = vec4(position, 1.0);
22 |
23 | // vPos = mvPos.xyz;
24 |
25 | gl_Position =projectionMatrix * modelViewMatrix * mvPos;
26 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/simulation_def.frag:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | void main() {
3 | vec2 uv = gl_FragCoord.xy / resolution.xy;
4 | vec4 tmpPos = texture2D( defTex, uv );
5 | gl_FragColor = vec4( tmpPos.rgb, 0.0 );
6 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/simulation_pos.frag:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | void main() {
3 | vec2 uv = gl_FragCoord.xy / resolution.xy;
4 | vec4 tmpPos = texture2D( posTex, uv );
5 | vec3 pos = tmpPos.xyz;
6 | vec4 tmpVel = texture2D( velTex, uv );
7 |
8 | vec3 vel = tmpVel.xyz;
9 |
10 | pos += vel;
11 | gl_FragColor = vec4( pos, tmpPos.a );
12 | }
--------------------------------------------------------------------------------
/dst/assets/glsl/simulation_vel.frag:
--------------------------------------------------------------------------------
1 | #define GLSLIFY 1
2 | //
3 | // Description : Array and textureless GLSL 2D/3D/4D simplex
4 | // noise functions.
5 | // Author : Ian McEwan, Ashima Arts.
6 | // Maintainer : ijm
7 | // Lastmod : 20110822 (ijm)
8 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved.
9 | // Distributed under the MIT License. See LICENSE file.
10 | // https://github.com/ashima/webgl-noise
11 | //
12 |
13 | vec3 mod289(vec3 x) {
14 | return x - floor(x * (1.0 / 289.0)) * 289.0;
15 | }
16 |
17 | vec4 mod289(vec4 x) {
18 | return x - floor(x * (1.0 / 289.0)) * 289.0;
19 | }
20 |
21 | vec4 permute(vec4 x) {
22 | return mod289(((x*34.0)+1.0)*x);
23 | }
24 |
25 | vec4 taylorInvSqrt(vec4 r){
26 | return 1.79284291400159 - 0.85373472095314 * r;
27 | }
28 |
29 | float snoise(vec3 v) {
30 |
31 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
32 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
33 |
34 | // First corner
35 | vec3 i = floor(v + dot(v, C.yyy) );
36 | vec3 x0 = v - i + dot(i, C.xxx) ;
37 |
38 | // Other corners
39 | vec3 g = step(x0.yzx, x0.xyz);
40 | vec3 l = 1.0 - g;
41 | vec3 i1 = min( g.xyz, l.zxy );
42 | vec3 i2 = max( g.xyz, l.zxy );
43 |
44 | // x0 = x0 - 0.0 + 0.0 * C.xxx;
45 | // x1 = x0 - i1 + 1.0 * C.xxx;
46 | // x2 = x0 - i2 + 2.0 * C.xxx;
47 | // x3 = x0 - 1.0 + 3.0 * C.xxx;
48 | vec3 x1 = x0 - i1 + C.xxx;
49 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
50 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
51 |
52 | // Permutations
53 | i = mod289(i);
54 | vec4 p = permute( permute( permute(
55 | i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
56 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
57 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
58 |
59 | // Gradients: 7x7 points over a square, mapped onto an octahedron.
60 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
61 | float n_ = 0.142857142857; // 1.0/7.0
62 | vec3 ns = n_ * D.wyz - D.xzx;
63 |
64 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
65 |
66 | vec4 x_ = floor(j * ns.z);
67 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
68 |
69 | vec4 x = x_ *ns.x + ns.yyyy;
70 | vec4 y = y_ *ns.x + ns.yyyy;
71 | vec4 h = 1.0 - abs(x) - abs(y);
72 |
73 | vec4 b0 = vec4( x.xy, y.xy );
74 | vec4 b1 = vec4( x.zw, y.zw );
75 |
76 | //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
77 | //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
78 | vec4 s0 = floor(b0)*2.0 + 1.0;
79 | vec4 s1 = floor(b1)*2.0 + 1.0;
80 | vec4 sh = -step(h, vec4(0.0));
81 |
82 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
83 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
84 |
85 | vec3 p0 = vec3(a0.xy,h.x);
86 | vec3 p1 = vec3(a0.zw,h.y);
87 | vec3 p2 = vec3(a1.xy,h.z);
88 | vec3 p3 = vec3(a1.zw,h.w);
89 |
90 | //Normalise gradients
91 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
92 | p0 *= norm.x;
93 | p1 *= norm.y;
94 | p2 *= norm.z;
95 | p3 *= norm.w;
96 |
97 | // Mix final noise value
98 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
99 | m = m * m;
100 | return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3) ) );
101 |
102 | }
103 |
104 | vec3 snoiseVec3( vec3 x ){
105 |
106 | float s = snoise(vec3( x ));
107 | float s1 = snoise(vec3( x.y - 19.1 , x.z + 33.4 , x.x + 47.2 ));
108 | float s2 = snoise(vec3( x.z + 74.2 , x.x - 124.5 , x.y + 99.4 ));
109 | vec3 c = vec3( s , s1 , s2 );
110 | return c;
111 |
112 | }
113 |
114 | vec3 curlNoise( vec3 p ){
115 |
116 | const float e = .1;
117 | // vec3 dx = vec3( e , 0.0 , 0.0 );
118 | // vec3 dy = vec3( 0.0 , e , 0.0 );
119 | // vec3 dz = vec3( 0.0 , 0.0 , e );
120 |
121 | // vec3 p_x0 = snoiseVec3( p - dx );
122 | // vec3 p_x1 = snoiseVec3( p + dx );
123 | // vec3 p_y0 = snoiseVec3( p - dy );
124 | // vec3 p_y1 = snoiseVec3( p + dy );
125 | // vec3 p_z0 = snoiseVec3( p - dz );
126 | // vec3 p_z1 = snoiseVec3( p + dz );
127 |
128 | // float x = p_y1.z - p_y0.z - p_z1.y + p_z0.y;
129 | // float y = p_z1.x - p_z0.x - p_x1.z + p_x0.z;
130 | // float z = p_x1.y - p_x0.y - p_y1.x + p_y0.x;
131 |
132 | float n1 = snoise(vec3(p.x, p.y + e, p.z));
133 | float n2 = snoise(vec3(p.x, p.y - e, p.z));
134 | float n3 = snoise(vec3(p.x, p.y, p.z + e));
135 | float n4 = snoise(vec3(p.x, p.y, p.z - e));
136 | float n5 = snoise(vec3(p.x + e, p.y, p.z));
137 | float n6 = snoise(vec3(p.x - e, p.y, p.z));
138 |
139 | float x = n2 - n1 - n4 + n3;
140 | float y = n4 - n3 - n6 + n5;
141 | float z = n6 - n5 - n2 + n1;
142 |
143 | const float divisor = 1.0 / ( 2.0 * e );
144 | return normalize( vec3( x , y , z ) * divisor );
145 | }
146 |
147 | // mat4 rotationMatrix(vec3 axis, float angle) {
148 |
149 | // axis = normalize(axis);
150 | // float s = sin(angle);
151 | // float c = cos(angle);
152 | // float oc = 1.0 - c;
153 |
154 | // return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,
155 | // oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,
156 | // oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,
157 | // 0.0, 0.0, 0.0, 1.0);
158 | // }
159 |
160 | // float rand(vec2 co){
161 | // return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
162 | // }
163 |
164 | uniform float timer;
165 | uniform float delta;
166 | uniform float speed;
167 | // uniform float genScale;
168 | uniform float factor;
169 | uniform float evolution;
170 | uniform float radius;
171 |
172 | void main() {
173 | vec2 uv = gl_FragCoord.xy / resolution.xy;
174 | vec4 c = texture2D( posTex, uv );
175 | vec4 oldVel = texture2D( velTex, uv );
176 |
177 | vec3 pos = c.xyz;
178 | float life = oldVel.a;
179 |
180 | float s = life / 100.0;
181 | float speedInc = 1.0;
182 |
183 | vec3 v = factor * speedInc * delta * speed * ( curlNoise( .2 * pos) ) * (c.a + 1.0);
184 |
185 | pos += v;
186 | life -= 0.2;
187 |
188 | if( life <= 0.0) {
189 |
190 | pos = texture2D( defTex, uv ).xyz;
191 | life = 100.0;
192 |
193 | }
194 |
195 | gl_FragColor = vec4( pos - c.xyz, life );
196 | }
--------------------------------------------------------------------------------
/dst/assets/js/main.min.js:
--------------------------------------------------------------------------------
1 | /*! modernizr 3.3.1 (Custom Build) | MIT *
2 | * https://modernizr.com/download/?-webgl-setclasses !*/
3 | "use strict";
4 |
5 | !(function (e, n, t) {
6 | function o(e, n) {
7 | return typeof e === n;
8 | }function s() {
9 | var e, n, t, s, a, i, f;for (var c in l) if (l.hasOwnProperty(c)) {
10 | if ((e = [], n = l[c], n.name && (e.push(n.name.toLowerCase()), n.options && n.options.aliases && n.options.aliases.length))) for (t = 0; t < n.options.aliases.length; t++) e.push(n.options.aliases[t].toLowerCase());for (s = o(n.fn, "function") ? n.fn() : n.fn, a = 0; a < e.length; a++) i = e[a], f = i.split("."), 1 === f.length ? Modernizr[f[0]] = s : (!Modernizr[f[0]] || Modernizr[f[0]] instanceof Boolean || (Modernizr[f[0]] = new Boolean(Modernizr[f[0]])), Modernizr[f[0]][f[1]] = s), r.push((s ? "" : "no-") + f.join("-"));
11 | }
12 | }function a(e) {
13 | var n = c.className,
14 | t = Modernizr._config.classPrefix || "";if ((u && (n = n.baseVal), Modernizr._config.enableJSClass)) {
15 | var o = new RegExp("(^|\\s)" + t + "no-js(\\s|$)");n = n.replace(o, "$1" + t + "js$2");
16 | }Modernizr._config.enableClasses && (n += " " + t + e.join(" " + t), u ? c.className.baseVal = n : c.className = n);
17 | }function i() {
18 | return "function" != typeof n.createElement ? n.createElement(arguments[0]) : u ? n.createElementNS.call(n, "http://www.w3.org/2000/svg", arguments[0]) : n.createElement.apply(n, arguments);
19 | }var r = [],
20 | l = [],
21 | f = { _version: "3.3.1", _config: { classPrefix: "", enableClasses: !0, enableJSClass: !0, usePrefixes: !0 }, _q: [], on: function on(e, n) {
22 | var t = this;setTimeout(function () {
23 | n(t[e]);
24 | }, 0);
25 | }, addTest: function addTest(e, n, t) {
26 | l.push({ name: e, fn: n, options: t });
27 | }, addAsyncTest: function addAsyncTest(e) {
28 | l.push({ name: null, fn: e });
29 | } },
30 | Modernizr = function Modernizr() {};Modernizr.prototype = f, Modernizr = new Modernizr();var c = n.documentElement,
31 | u = "svg" === c.nodeName.toLowerCase();Modernizr.addTest("webgl", function () {
32 | var n = i("canvas"),
33 | t = "probablySupportsContext" in n ? "probablySupportsContext" : "supportsContext";return t in n ? n[t]("webgl") || n[t]("experimental-webgl") : "WebGLRenderingContext" in e;
34 | }), s(), a(r), delete f.addTest, delete f.addAsyncTest;for (var p = 0; p < Modernizr._q.length; p++) Modernizr._q[p]();e.Modernizr = Modernizr;
35 | })(window, document);
36 | /*!
37 | * Useragnt
38 | * v0.3.1
39 | *
40 | * Copyright (c) 2016 Yuichiroh Arai
41 | * Released under the MIT license
42 | * http://opensource.org/licenses/mit-license.php
43 | *
44 | * detects: mobile, tablet, pc, windows, mac, linux, ios, android, edge, ie, safari, chrome, firefox, opera
45 | !*/
46 | "use strict";
47 |
48 | !(function (e, o) {
49 | function i(e) {
50 | return n.indexOf(e) != -1;
51 | }function r(e) {
52 | var o = e.split("."),
53 | i = {};return i.str = e, i.float = parseFloat(e) || 0, i.major = o.length > 0 ? parseInt(o[0]) || 0 : 0, i.minor = o.length > 1 ? parseInt(o[1]) || 0 : 0, i.build = o.length > 2 ? parseInt(o[2]) || 0 : 0, i.revision = o.length > 3 ? parseInt(o[3]) || 0 : 0, i;
54 | }var a = {};a._detects = ["mobile", "tablet", "pc", "windows", "mac", "linux", "ios", "android", "edge", "ie", "safari", "chrome", "firefox", "opera"];var n = a.userAgent = e.navigator.userAgent.toLowerCase();a.mobile = i("iphone") || i("ipod") || i("android") && i("mobile") || i("windows") && i("phone") || i("firefox") && i("mobile") || i("blackberry"), a.tablet = i("ipad") || i("android") && !i("mobile") || i("windows") && i("touch") && !i("tablet pc") || i("firefox") && i("tablet") || i("kindle") || i("silk") || i("playbook"), a.pc = !i("iphone") && !i("ipod") && !i("ipad") && !i("android") && (!i("windows") || !i("phone") && (!i("touch") || i("tablet pc"))) && (!i("firefox") || !i("mobile") && !i("tablet")) && !i("blackberry") && !i("kindle") && !i("silk") && !i("playbook"), a.windows = i("windows"), a.mac = i("mac os x") && !i("iphone") && !i("ipad") && !i("ipod"), a.linux = i("linux") && !i("android"), a.ios = i("iphone") || i("ipad") || i("ipod"), a.ios && (a.ios = new Boolean(!0), n.match(/ os ([\d_]+)/g), a.ios.version = r(RegExp.$1.replace("_", "."))), a.android = i("android"), a.android && (a.android = new Boolean(!0), n.match(/android ([\d\.]+)/g), a.android.version = r(RegExp.$1)), a.edge = i("edge"), a.ie = i("trident") || i("msie"), a.safari = i("safari") && !i("android") && !i("edge") && !i("opera") && !i("opr") && !i("chrome"), a.chrome = i("chrome") && !i("edge") && !i("opera") && !i("opr"), a.chrome && (a.chrome = new Boolean(!0), n.match(/chrome\/([\d.]+)/g), a.chrome.version = r(RegExp.$1)), a.firefox = i("firefox") && !i("edge"), a.opera = i("opera") || i("opr");var d,
55 | t,
56 | s,
57 | l = a._classPrefix = "",
58 | p = o.documentElement,
59 | c = p.className;for (t = a._detects.length, d = 0; d < t; d++) s = a._detects[d], c += a[s] ? " " + l + s : " " + l + "no-" + s;p.className = c, e.Useragnt = a;
60 | })(window, document);
61 | /**
62 | * @author yomboprime https://github.com/yomboprime
63 | *
64 | * GPUComputationRenderer, based on SimulationRenderer by zz85
65 | *
66 | * The GPUComputationRenderer uses the concept of variables. These variables are RGBA float textures that hold 4 floats
67 | * for each compute element (texel)
68 | *
69 | * Each variable has a fragment shader that defines the computation made to obtain the variable in question.
70 | * You can use as many variables you need, and make dependencies so you can use textures of other variables in the shader
71 | * (the sampler uniforms are added automatically) Most of the variables will need themselves as dependency.
72 | *
73 | * The renderer has actually two render targets per variable, to make ping-pong. Textures from the current frame are used
74 | * as inputs to render the textures of the next frame.
75 | *
76 | * The render targets of the variables can be used as input textures for your visualization shaders.
77 | *
78 | * Variable names should be valid identifiers and should not collide with THREE GLSL used identifiers.
79 | * a common approach could be to use 'texture' prefixing the variable name; i.e texturePosition, textureVelocity...
80 | *
81 | * The size of the computation (sizeX * sizeY) is defined as 'resolution' automatically in the shader. For example:
82 | * #DEFINE resolution vec2( 1024.0, 1024.0 )
83 | *
84 | * -------------
85 | *
86 | * Basic use:
87 | *
88 | * // Initialization...
89 | *
90 | * // Create computation renderer
91 | * var gpuCompute = new GPUComputationRenderer( 1024, 1024, renderer );
92 | *
93 | * // Create initial state float textures
94 | * var pos0 = gpuCompute.createTexture();
95 | * var vel0 = gpuCompute.createTexture();
96 | * // and fill in here the texture data...
97 | *
98 | * // Add texture variables
99 | * var velVar = gpuCompute.addVariable( "textureVelocity", fragmentShaderVel, pos0 );
100 | * var posVar = gpuCompute.addVariable( "texturePosition", fragmentShaderPos, vel0 );
101 | *
102 | * // Add variable dependencies
103 | * gpuCompute.setVariableDependencies( velVar, [ velVar, posVar ] );
104 | * gpuCompute.setVariableDependencies( posVar, [ velVar, posVar ] );
105 | *
106 | * // Add custom uniforms
107 | * velVar.material.uniforms.time = { value: 0.0 };
108 | *
109 | * // Check for completeness
110 | * var error = gpuCompute.init();
111 | * if ( error !== null ) {
112 | * console.error( error );
113 | * }
114 | *
115 | *
116 | * // In each frame...
117 | *
118 | * // Compute!
119 | * gpuCompute.compute();
120 | *
121 | * // Update texture uniforms in your visualization materials with the gpu renderer output
122 | * myMaterial.uniforms.myTexture.value = gpuCompute.getCurrentRenderTarget( posVar ).texture;
123 | *
124 | * // Do your rendering
125 | * renderer.render( myScene, myCamera );
126 | *
127 | * -------------
128 | *
129 | * Also, you can use utility functions to create ShaderMaterial and perform computations (rendering between textures)
130 | * Note that the shaders can have multiple input textures.
131 | *
132 | * var myFilter1 = gpuCompute.createShaderMaterial( myFilterFragmentShader1, { theTexture: { value: null } } );
133 | * var myFilter2 = gpuCompute.createShaderMaterial( myFilterFragmentShader2, { theTexture: { value: null } } );
134 | *
135 | * var inputTexture = gpuCompute.createTexture();
136 | *
137 | * // Fill in here inputTexture...
138 | *
139 | * myFilter1.uniforms.theTexture.value = inputTexture;
140 | *
141 | * var myRenderTarget = gpuCompute.createRenderTarget();
142 | * myFilter2.uniforms.theTexture.value = myRenderTarget.texture;
143 | *
144 | * var outputRenderTarget = gpuCompute.createRenderTarget();
145 | *
146 | * // Now use the output texture where you want:
147 | * myMaterial.uniforms.map.value = outputRenderTarget.texture;
148 | *
149 | * // And compute each frame, before rendering to screen:
150 | * gpuCompute.doRenderTarget( myFilter1, myRenderTarget );
151 | * gpuCompute.doRenderTarget( myFilter2, outputRenderTarget );
152 | *
153 | *
154 | *
155 | * @param {int} sizeX Computation problem size is always 2d: sizeX * sizeY elements.
156 | * @param {int} sizeY Computation problem size is always 2d: sizeX * sizeY elements.
157 | * @param {WebGLRenderer} renderer The renderer
158 | */
159 |
160 | "use strict";
161 |
162 | function GPUComputationRenderer(sizeX, sizeY, renderer) {
163 |
164 | this.variables = [];
165 |
166 | this.currentTextureIndex = 0;
167 |
168 | var scene = new THREE.Scene();
169 |
170 | var camera = new THREE.Camera();
171 | camera.position.z = 1;
172 |
173 | var passThruUniforms = {
174 | texture: { value: null }
175 | };
176 |
177 | var passThruShader = createShaderMaterial(getPassThroughFragmentShader(), passThruUniforms);
178 |
179 | var mesh = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2), passThruShader);
180 | scene.add(mesh);
181 |
182 | this.addVariable = function (variableName, computeFragmentShader, initialValueTexture) {
183 |
184 | var material = this.createShaderMaterial(computeFragmentShader);
185 |
186 | var variable = {
187 | name: variableName,
188 | initialValueTexture: initialValueTexture,
189 | material: material,
190 | dependencies: null,
191 | renderTargets: [],
192 | wrapS: null,
193 | wrapT: null,
194 | minFilter: THREE.NearestFilter,
195 | magFilter: THREE.NearestFilter
196 | };
197 |
198 | this.variables.push(variable);
199 |
200 | return variable;
201 | };
202 |
203 | this.setVariableDependencies = function (variable, dependencies) {
204 |
205 | variable.dependencies = dependencies;
206 | };
207 |
208 | this.init = function () {
209 |
210 | if (!renderer.extensions.get("OES_texture_float")) {
211 |
212 | return "No OES_texture_float support for float textures.";
213 | }
214 |
215 | if (renderer.capabilities.maxVertexTextures === 0) {
216 |
217 | return "No support for vertex shader textures.";
218 | }
219 |
220 | for (var i = 0; i < this.variables.length; i++) {
221 |
222 | var variable = this.variables[i];
223 |
224 | // Creates rendertargets and initialize them with input texture
225 | variable.renderTargets[0] = this.createRenderTarget(sizeX, sizeY, variable.wrapS, variable.wrapT, variable.minFilter, variable.magFilter);
226 | variable.renderTargets[1] = this.createRenderTarget(sizeX, sizeY, variable.wrapS, variable.wrapT, variable.minFilter, variable.magFilter);
227 | this.renderTexture(variable.initialValueTexture, variable.renderTargets[0]);
228 | this.renderTexture(variable.initialValueTexture, variable.renderTargets[1]);
229 |
230 | // Adds dependencies uniforms to the ShaderMaterial
231 | var material = variable.material;
232 | var uniforms = material.uniforms;
233 | if (variable.dependencies !== null) {
234 |
235 | for (var d = 0; d < variable.dependencies.length; d++) {
236 |
237 | var depVar = variable.dependencies[d];
238 |
239 | if (depVar.name !== variable.name) {
240 |
241 | // Checks if variable exists
242 | var found = false;
243 | for (var j = 0; j < this.variables.length; j++) {
244 |
245 | if (depVar.name === this.variables[j].name) {
246 | found = true;
247 | break;
248 | }
249 | }
250 | if (!found) {
251 | return "Variable dependency not found. Variable=" + variable.name + ", dependency=" + depVar.name;
252 | }
253 | }
254 |
255 | uniforms[depVar.name] = { value: null };
256 |
257 | material.fragmentShader = "\nuniform sampler2D " + depVar.name + ";\n" + material.fragmentShader;
258 | }
259 | }
260 | }
261 |
262 | this.currentTextureIndex = 0;
263 |
264 | return null;
265 | };
266 |
267 | this.compute = function () {
268 |
269 | var currentTextureIndex = this.currentTextureIndex;
270 | var nextTextureIndex = this.currentTextureIndex === 0 ? 1 : 0;
271 |
272 | for (var i = 0, il = this.variables.length; i < il; i++) {
273 |
274 | var variable = this.variables[i];
275 |
276 | // Sets texture dependencies uniforms
277 | if (variable.dependencies !== null) {
278 |
279 | var uniforms = variable.material.uniforms;
280 | for (var d = 0, dl = variable.dependencies.length; d < dl; d++) {
281 |
282 | var depVar = variable.dependencies[d];
283 | // console.log(depVar);
284 |
285 | uniforms[depVar.name].value = depVar.renderTargets[currentTextureIndex].texture;
286 | }
287 | }
288 |
289 | // Performs the computation for this variable
290 | this.doRenderTarget(variable.material, variable.renderTargets[nextTextureIndex]);
291 | }
292 |
293 | this.currentTextureIndex = nextTextureIndex;
294 | };
295 |
296 | this.getCurrentRenderTarget = function (variable) {
297 |
298 | return variable.renderTargets[this.currentTextureIndex];
299 | };
300 |
301 | this.getAlternateRenderTarget = function (variable) {
302 |
303 | return variable.renderTargets[this.currentTextureIndex === 0 ? 1 : 0];
304 | };
305 |
306 | function addResolutionDefine(materialShader) {
307 |
308 | materialShader.defines.resolution = 'vec2( ' + sizeX.toFixed(1) + ', ' + sizeY.toFixed(1) + " )";
309 | };
310 | this.addResolutionDefine = addResolutionDefine;
311 |
312 | // The following functions can be used to compute things manually
313 |
314 | function createShaderMaterial(computeFragmentShader, uniforms) {
315 |
316 | uniforms = uniforms || {};
317 |
318 | var material = new THREE.ShaderMaterial({
319 | uniforms: uniforms,
320 | vertexShader: getPassThroughVertexShader(),
321 | fragmentShader: computeFragmentShader
322 | });
323 |
324 | addResolutionDefine(material);
325 |
326 | return material;
327 | };
328 | this.createShaderMaterial = createShaderMaterial;
329 |
330 | this.createRenderTarget = function (sizeXTexture, sizeYTexture, wrapS, wrapT, minFilter, magFilter) {
331 |
332 | sizeXTexture = sizeXTexture || sizeX;
333 | sizeYTexture = sizeYTexture || sizeY;
334 |
335 | wrapS = wrapS || THREE.ClampToEdgeWrapping;
336 | wrapT = wrapT || THREE.ClampToEdgeWrapping;
337 |
338 | minFilter = minFilter || THREE.NearestFilter;
339 | magFilter = magFilter || THREE.NearestFilter;
340 |
341 | var floatType = Useragnt.ios ? THREE.HalfFloatType : THREE.FloatType;
342 |
343 | var renderTarget = new THREE.WebGLRenderTarget(sizeXTexture, sizeYTexture, {
344 | wrapS: wrapS,
345 | wrapT: wrapT,
346 | minFilter: minFilter,
347 | magFilter: magFilter,
348 | format: THREE.RGBAFormat,
349 | type: floatType,
350 | stencilBuffer: false
351 | });
352 |
353 | return renderTarget;
354 | };
355 |
356 | this.createTexture = function (sizeXTexture, sizeYTexture) {
357 |
358 | sizeXTexture = sizeXTexture || sizeX;
359 | sizeYTexture = sizeYTexture || sizeY;
360 |
361 | var floatType = Useragnt.ios ? THREE.HalfFloatType : THREE.FloatType;
362 |
363 | var a = new Float32Array(sizeXTexture * sizeYTexture * 4);
364 | var texture = new THREE.DataTexture(a, sizeX, sizeY, THREE.RGBAFormat, THREE.FloatType);
365 | texture.needsUpdate = true;
366 |
367 | return texture;
368 | };
369 |
370 | this.renderTexture = function (input, output) {
371 |
372 | // Takes a texture, and render out in rendertarget
373 | // input = Texture
374 | // output = RenderTarget
375 |
376 | passThruUniforms.texture.value = input;
377 |
378 | this.doRenderTarget(passThruShader, output);
379 |
380 | passThruUniforms.texture.value = null;
381 | };
382 |
383 | this.doRenderTarget = function (material, output) {
384 |
385 | mesh.material = material;
386 | renderer.render(scene, camera, output);
387 | mesh.material = passThruShader;
388 | };
389 |
390 | // Shaders
391 |
392 | function getPassThroughVertexShader() {
393 |
394 | return "void main() {\n" + "\n" + " gl_Position = vec4( position, 1.0 );\n" + "\n" + "}\n";
395 | }
396 |
397 | function getPassThroughFragmentShader() {
398 |
399 | return "uniform sampler2D texture;\n" + "\n" + "void main() {\n" + "\n" + " vec2 uv = gl_FragCoord.xy / resolution.xy;\n" + "\n" + " gl_FragColor = texture2D( texture, uv );\n" + "\n" + "}\n";
400 | }
401 | }
402 | /**
403 | * @author qiao / https://github.com/qiao
404 | * @author mrdoob / http://mrdoob.com
405 | * @author alteredq / http://alteredqualia.com/
406 | * @author WestLangley / http://github.com/WestLangley
407 | * @author erich666 / http://erichaines.com
408 | */
409 |
410 | // This set of controls performs orbiting, dollying (zooming), and panning.
411 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
412 | //
413 | // Orbit - left mouse / touch: one finger move
414 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
415 | // Pan - right mouse, or arrow keys / touch: three finger swipe
416 |
417 | 'use strict';
418 |
419 | THREE.OrbitControls = function (object, domElement) {
420 |
421 | this.object = object;
422 |
423 | this.domElement = domElement !== undefined ? domElement : document;
424 |
425 | // Set to false to disable this control
426 | this.enabled = true;
427 |
428 | // "target" sets the location of focus, where the object orbits around
429 | this.target = new THREE.Vector3();
430 |
431 | // How far you can dolly in and out ( PerspectiveCamera only )
432 | this.minDistance = 0;
433 | this.maxDistance = Infinity;
434 |
435 | // How far you can zoom in and out ( OrthographicCamera only )
436 | this.minZoom = 0;
437 | this.maxZoom = Infinity;
438 |
439 | // How far you can orbit vertically, upper and lower limits.
440 | // Range is 0 to Math.PI radians.
441 | this.minPolarAngle = 0; // radians
442 | this.maxPolarAngle = Math.PI; // radians
443 |
444 | // How far you can orbit horizontally, upper and lower limits.
445 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
446 | this.minAzimuthAngle = -Infinity; // radians
447 | this.maxAzimuthAngle = Infinity; // radians
448 |
449 | // Set to true to enable damping (inertia)
450 | // If damping is enabled, you must call controls.update() in your animation loop
451 | this.enableDamping = false;
452 | this.dampingFactor = 0.25;
453 |
454 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
455 | // Set to false to disable zooming
456 | this.enableZoom = true;
457 | this.zoomSpeed = 1.0;
458 |
459 | // Set to false to disable rotating
460 | this.enableRotate = true;
461 | this.rotateSpeed = 1.0;
462 |
463 | // Set to false to disable panning
464 | this.enablePan = true;
465 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
466 |
467 | // Set to true to automatically rotate around the target
468 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
469 | this.autoRotate = false;
470 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
471 |
472 | // Set to false to disable use of the keys
473 | this.enableKeys = true;
474 |
475 | // The four arrow keys
476 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
477 |
478 | // Mouse buttons
479 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
480 |
481 | // for reset
482 | this.target0 = this.target.clone();
483 | this.position0 = this.object.position.clone();
484 | this.zoom0 = this.object.zoom;
485 |
486 | //
487 | // public methods
488 | //
489 |
490 | this.getPolarAngle = function () {
491 |
492 | return spherical.phi;
493 | };
494 |
495 | this.getAzimuthalAngle = function () {
496 |
497 | return spherical.theta;
498 | };
499 |
500 | this.reset = function () {
501 |
502 | scope.target.copy(scope.target0);
503 | scope.object.position.copy(scope.position0);
504 | scope.object.zoom = scope.zoom0;
505 |
506 | scope.object.updateProjectionMatrix();
507 | scope.dispatchEvent(changeEvent);
508 |
509 | scope.update();
510 |
511 | state = STATE.NONE;
512 | };
513 |
514 | // this method is exposed, but perhaps it would be better if we can make it private...
515 | this.update = (function () {
516 |
517 | var offset = new THREE.Vector3();
518 |
519 | // so camera.up is the orbit axis
520 | var quat = new THREE.Quaternion().setFromUnitVectors(object.up, new THREE.Vector3(0, 1, 0));
521 | var quatInverse = quat.clone().inverse();
522 |
523 | var lastPosition = new THREE.Vector3();
524 | var lastQuaternion = new THREE.Quaternion();
525 |
526 | return function update() {
527 |
528 | var position = scope.object.position;
529 |
530 | offset.copy(position).sub(scope.target);
531 |
532 | // rotate offset to "y-axis-is-up" space
533 | offset.applyQuaternion(quat);
534 |
535 | // angle from z-axis around y-axis
536 | spherical.setFromVector3(offset);
537 |
538 | if (scope.autoRotate && state === STATE.NONE) {
539 |
540 | rotateLeft(getAutoRotationAngle());
541 | }
542 |
543 | spherical.theta += sphericalDelta.theta;
544 | spherical.phi += sphericalDelta.phi;
545 |
546 | // restrict theta to be between desired limits
547 | spherical.theta = Math.max(scope.minAzimuthAngle, Math.min(scope.maxAzimuthAngle, spherical.theta));
548 |
549 | // restrict phi to be between desired limits
550 | spherical.phi = Math.max(scope.minPolarAngle, Math.min(scope.maxPolarAngle, spherical.phi));
551 |
552 | spherical.makeSafe();
553 |
554 | spherical.radius *= scale;
555 |
556 | // restrict radius to be between desired limits
557 | spherical.radius = Math.max(scope.minDistance, Math.min(scope.maxDistance, spherical.radius));
558 |
559 | // move target to panned location
560 | scope.target.add(panOffset);
561 |
562 | offset.setFromSpherical(spherical);
563 |
564 | // rotate offset back to "camera-up-vector-is-up" space
565 | offset.applyQuaternion(quatInverse);
566 |
567 | position.copy(scope.target).add(offset);
568 |
569 | scope.object.lookAt(scope.target);
570 |
571 | if (scope.enableDamping === true) {
572 |
573 | sphericalDelta.theta *= 1 - scope.dampingFactor;
574 | sphericalDelta.phi *= 1 - scope.dampingFactor;
575 | } else {
576 |
577 | sphericalDelta.set(0, 0, 0);
578 | }
579 |
580 | scale = 1;
581 | panOffset.set(0, 0, 0);
582 |
583 | // update condition is:
584 | // min(camera displacement, camera rotation in radians)^2 > EPS
585 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
586 |
587 | if (zoomChanged || lastPosition.distanceToSquared(scope.object.position) > EPS || 8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS) {
588 |
589 | scope.dispatchEvent(changeEvent);
590 |
591 | lastPosition.copy(scope.object.position);
592 | lastQuaternion.copy(scope.object.quaternion);
593 | zoomChanged = false;
594 |
595 | return true;
596 | }
597 |
598 | return false;
599 | };
600 | })();
601 |
602 | this.dispose = function () {
603 |
604 | scope.domElement.removeEventListener('contextmenu', onContextMenu, false);
605 | scope.domElement.removeEventListener('mousedown', onMouseDown, false);
606 | scope.domElement.removeEventListener('wheel', onMouseWheel, false);
607 |
608 | scope.domElement.removeEventListener('touchstart', onTouchStart, false);
609 | scope.domElement.removeEventListener('touchend', onTouchEnd, false);
610 | scope.domElement.removeEventListener('touchmove', onTouchMove, false);
611 |
612 | document.removeEventListener('mousemove', onMouseMove, false);
613 | document.removeEventListener('mouseup', onMouseUp, false);
614 |
615 | window.removeEventListener('keydown', onKeyDown, false);
616 |
617 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
618 | };
619 |
620 | //
621 | // internals
622 | //
623 |
624 | var scope = this;
625 |
626 | var changeEvent = { type: 'change' };
627 | var startEvent = { type: 'start' };
628 | var endEvent = { type: 'end' };
629 |
630 | var STATE = { NONE: -1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY: 4, TOUCH_PAN: 5 };
631 |
632 | var state = STATE.NONE;
633 |
634 | var EPS = 0.000001;
635 |
636 | // current position in spherical coordinates
637 | var spherical = new THREE.Spherical();
638 | var sphericalDelta = new THREE.Spherical();
639 |
640 | var scale = 1;
641 | var panOffset = new THREE.Vector3();
642 | var zoomChanged = false;
643 |
644 | var rotateStart = new THREE.Vector2();
645 | var rotateEnd = new THREE.Vector2();
646 | var rotateDelta = new THREE.Vector2();
647 |
648 | var panStart = new THREE.Vector2();
649 | var panEnd = new THREE.Vector2();
650 | var panDelta = new THREE.Vector2();
651 |
652 | var dollyStart = new THREE.Vector2();
653 | var dollyEnd = new THREE.Vector2();
654 | var dollyDelta = new THREE.Vector2();
655 |
656 | function getAutoRotationAngle() {
657 |
658 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
659 | }
660 |
661 | function getZoomScale() {
662 |
663 | return Math.pow(0.95, scope.zoomSpeed);
664 | }
665 |
666 | function rotateLeft(angle) {
667 |
668 | sphericalDelta.theta -= angle;
669 | }
670 |
671 | function rotateUp(angle) {
672 |
673 | sphericalDelta.phi -= angle;
674 | }
675 |
676 | var panLeft = (function () {
677 |
678 | var v = new THREE.Vector3();
679 |
680 | return function panLeft(distance, objectMatrix) {
681 |
682 | v.setFromMatrixColumn(objectMatrix, 0); // get X column of objectMatrix
683 | v.multiplyScalar(-distance);
684 |
685 | panOffset.add(v);
686 | };
687 | })();
688 |
689 | var panUp = (function () {
690 |
691 | var v = new THREE.Vector3();
692 |
693 | return function panUp(distance, objectMatrix) {
694 |
695 | v.setFromMatrixColumn(objectMatrix, 1); // get Y column of objectMatrix
696 | v.multiplyScalar(distance);
697 |
698 | panOffset.add(v);
699 | };
700 | })();
701 |
702 | // deltaX and deltaY are in pixels; right and down are positive
703 | var pan = (function () {
704 |
705 | var offset = new THREE.Vector3();
706 |
707 | return function pan(deltaX, deltaY) {
708 |
709 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
710 |
711 | if (scope.object instanceof THREE.PerspectiveCamera) {
712 |
713 | // perspective
714 | var position = scope.object.position;
715 | offset.copy(position).sub(scope.target);
716 | var targetDistance = offset.length();
717 |
718 | // half of the fov is center to top of screen
719 | targetDistance *= Math.tan(scope.object.fov / 2 * Math.PI / 180.0);
720 |
721 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
722 | panLeft(2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix);
723 | panUp(2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix);
724 | } else if (scope.object instanceof THREE.OrthographicCamera) {
725 |
726 | // orthographic
727 | panLeft(deltaX * (scope.object.right - scope.object.left) / scope.object.zoom / element.clientWidth, scope.object.matrix);
728 | panUp(deltaY * (scope.object.top - scope.object.bottom) / scope.object.zoom / element.clientHeight, scope.object.matrix);
729 | } else {
730 |
731 | // camera neither orthographic nor perspective
732 | console.warn('WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.');
733 | scope.enablePan = false;
734 | }
735 | };
736 | })();
737 |
738 | function dollyIn(dollyScale) {
739 |
740 | if (scope.object instanceof THREE.PerspectiveCamera) {
741 |
742 | scale /= dollyScale;
743 | } else if (scope.object instanceof THREE.OrthographicCamera) {
744 |
745 | scope.object.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope.object.zoom * dollyScale));
746 | scope.object.updateProjectionMatrix();
747 | zoomChanged = true;
748 | } else {
749 |
750 | console.warn('WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.');
751 | scope.enableZoom = false;
752 | }
753 | }
754 |
755 | function dollyOut(dollyScale) {
756 |
757 | if (scope.object instanceof THREE.PerspectiveCamera) {
758 |
759 | scale *= dollyScale;
760 | } else if (scope.object instanceof THREE.OrthographicCamera) {
761 |
762 | scope.object.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope.object.zoom / dollyScale));
763 | scope.object.updateProjectionMatrix();
764 | zoomChanged = true;
765 | } else {
766 |
767 | console.warn('WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.');
768 | scope.enableZoom = false;
769 | }
770 | }
771 |
772 | //
773 | // event callbacks - update the object state
774 | //
775 |
776 | function handleMouseDownRotate(event) {
777 |
778 | //console.log( 'handleMouseDownRotate' );
779 |
780 | rotateStart.set(event.clientX, event.clientY);
781 | }
782 |
783 | function handleMouseDownDolly(event) {
784 |
785 | //console.log( 'handleMouseDownDolly' );
786 |
787 | dollyStart.set(event.clientX, event.clientY);
788 | }
789 |
790 | function handleMouseDownPan(event) {
791 |
792 | //console.log( 'handleMouseDownPan' );
793 |
794 | panStart.set(event.clientX, event.clientY);
795 | }
796 |
797 | function handleMouseMoveRotate(event) {
798 |
799 | //console.log( 'handleMouseMoveRotate' );
800 |
801 | rotateEnd.set(event.clientX, event.clientY);
802 | rotateDelta.subVectors(rotateEnd, rotateStart);
803 |
804 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
805 |
806 | // rotating across whole screen goes 360 degrees around
807 | rotateLeft(2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed);
808 |
809 | // rotating up and down along whole screen attempts to go 360, but limited to 180
810 | rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed);
811 |
812 | rotateStart.copy(rotateEnd);
813 |
814 | scope.update();
815 | }
816 |
817 | function handleMouseMoveDolly(event) {
818 |
819 | //console.log( 'handleMouseMoveDolly' );
820 |
821 | dollyEnd.set(event.clientX, event.clientY);
822 |
823 | dollyDelta.subVectors(dollyEnd, dollyStart);
824 |
825 | if (dollyDelta.y > 0) {
826 |
827 | dollyIn(getZoomScale());
828 | } else if (dollyDelta.y < 0) {
829 |
830 | dollyOut(getZoomScale());
831 | }
832 |
833 | dollyStart.copy(dollyEnd);
834 |
835 | scope.update();
836 | }
837 |
838 | function handleMouseMovePan(event) {
839 |
840 | //console.log( 'handleMouseMovePan' );
841 |
842 | panEnd.set(event.clientX, event.clientY);
843 |
844 | panDelta.subVectors(panEnd, panStart);
845 |
846 | pan(panDelta.x, panDelta.y);
847 |
848 | panStart.copy(panEnd);
849 |
850 | scope.update();
851 | }
852 |
853 | function handleMouseUp(event) {
854 |
855 | // console.log( 'handleMouseUp' );
856 |
857 | }
858 |
859 | function handleMouseWheel(event) {
860 |
861 | // console.log( 'handleMouseWheel' );
862 |
863 | if (event.deltaY < 0) {
864 |
865 | dollyOut(getZoomScale());
866 | } else if (event.deltaY > 0) {
867 |
868 | dollyIn(getZoomScale());
869 | }
870 |
871 | scope.update();
872 | }
873 |
874 | function handleKeyDown(event) {
875 |
876 | //console.log( 'handleKeyDown' );
877 |
878 | switch (event.keyCode) {
879 |
880 | case scope.keys.UP:
881 | pan(0, scope.keyPanSpeed);
882 | scope.update();
883 | break;
884 |
885 | case scope.keys.BOTTOM:
886 | pan(0, -scope.keyPanSpeed);
887 | scope.update();
888 | break;
889 |
890 | case scope.keys.LEFT:
891 | pan(scope.keyPanSpeed, 0);
892 | scope.update();
893 | break;
894 |
895 | case scope.keys.RIGHT:
896 | pan(-scope.keyPanSpeed, 0);
897 | scope.update();
898 | break;
899 |
900 | }
901 | }
902 |
903 | function handleTouchStartRotate(event) {
904 |
905 | //console.log( 'handleTouchStartRotate' );
906 |
907 | rotateStart.set(event.touches[0].pageX, event.touches[0].pageY);
908 | }
909 |
910 | function handleTouchStartDolly(event) {
911 |
912 | //console.log( 'handleTouchStartDolly' );
913 |
914 | var dx = event.touches[0].pageX - event.touches[1].pageX;
915 | var dy = event.touches[0].pageY - event.touches[1].pageY;
916 |
917 | var distance = Math.sqrt(dx * dx + dy * dy);
918 |
919 | dollyStart.set(0, distance);
920 | }
921 |
922 | function handleTouchStartPan(event) {
923 |
924 | //console.log( 'handleTouchStartPan' );
925 |
926 | panStart.set(event.touches[0].pageX, event.touches[0].pageY);
927 | }
928 |
929 | function handleTouchMoveRotate(event) {
930 |
931 | //console.log( 'handleTouchMoveRotate' );
932 |
933 | rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY);
934 | rotateDelta.subVectors(rotateEnd, rotateStart);
935 |
936 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
937 |
938 | // rotating across whole screen goes 360 degrees around
939 | rotateLeft(2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed);
940 |
941 | // rotating up and down along whole screen attempts to go 360, but limited to 180
942 | rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed);
943 |
944 | rotateStart.copy(rotateEnd);
945 |
946 | scope.update();
947 | }
948 |
949 | function handleTouchMoveDolly(event) {
950 |
951 | //console.log( 'handleTouchMoveDolly' );
952 |
953 | var dx = event.touches[0].pageX - event.touches[1].pageX;
954 | var dy = event.touches[0].pageY - event.touches[1].pageY;
955 |
956 | var distance = Math.sqrt(dx * dx + dy * dy);
957 |
958 | dollyEnd.set(0, distance);
959 |
960 | dollyDelta.subVectors(dollyEnd, dollyStart);
961 |
962 | if (dollyDelta.y > 0) {
963 |
964 | dollyOut(getZoomScale());
965 | } else if (dollyDelta.y < 0) {
966 |
967 | dollyIn(getZoomScale());
968 | }
969 |
970 | dollyStart.copy(dollyEnd);
971 |
972 | scope.update();
973 | }
974 |
975 | function handleTouchMovePan(event) {
976 |
977 | //console.log( 'handleTouchMovePan' );
978 |
979 | panEnd.set(event.touches[0].pageX, event.touches[0].pageY);
980 |
981 | panDelta.subVectors(panEnd, panStart);
982 |
983 | pan(panDelta.x, panDelta.y);
984 |
985 | panStart.copy(panEnd);
986 |
987 | scope.update();
988 | }
989 |
990 | function handleTouchEnd(event) {}
991 |
992 | //console.log( 'handleTouchEnd' );
993 |
994 | //
995 | // event handlers - FSM: listen for events and reset state
996 | //
997 |
998 | function onMouseDown(event) {
999 |
1000 | if (scope.enabled === false) return;
1001 |
1002 | event.preventDefault();
1003 |
1004 | if (event.button === scope.mouseButtons.ORBIT) {
1005 |
1006 | if (scope.enableRotate === false) return;
1007 |
1008 | handleMouseDownRotate(event);
1009 |
1010 | state = STATE.ROTATE;
1011 | } else if (event.button === scope.mouseButtons.ZOOM) {
1012 |
1013 | if (scope.enableZoom === false) return;
1014 |
1015 | handleMouseDownDolly(event);
1016 |
1017 | state = STATE.DOLLY;
1018 | } else if (event.button === scope.mouseButtons.PAN) {
1019 |
1020 | if (scope.enablePan === false) return;
1021 |
1022 | handleMouseDownPan(event);
1023 |
1024 | state = STATE.PAN;
1025 | }
1026 |
1027 | if (state !== STATE.NONE) {
1028 |
1029 | document.addEventListener('mousemove', onMouseMove, false);
1030 | document.addEventListener('mouseup', onMouseUp, false);
1031 |
1032 | scope.dispatchEvent(startEvent);
1033 | }
1034 | }
1035 |
1036 | function onMouseMove(event) {
1037 |
1038 | if (scope.enabled === false) return;
1039 |
1040 | event.preventDefault();
1041 |
1042 | if (state === STATE.ROTATE) {
1043 |
1044 | if (scope.enableRotate === false) return;
1045 |
1046 | handleMouseMoveRotate(event);
1047 | } else if (state === STATE.DOLLY) {
1048 |
1049 | if (scope.enableZoom === false) return;
1050 |
1051 | handleMouseMoveDolly(event);
1052 | } else if (state === STATE.PAN) {
1053 |
1054 | if (scope.enablePan === false) return;
1055 |
1056 | handleMouseMovePan(event);
1057 | }
1058 | }
1059 |
1060 | function onMouseUp(event) {
1061 |
1062 | if (scope.enabled === false) return;
1063 |
1064 | handleMouseUp(event);
1065 |
1066 | document.removeEventListener('mousemove', onMouseMove, false);
1067 | document.removeEventListener('mouseup', onMouseUp, false);
1068 |
1069 | scope.dispatchEvent(endEvent);
1070 |
1071 | state = STATE.NONE;
1072 | }
1073 |
1074 | function onMouseWheel(event) {
1075 |
1076 | if (scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE && state !== STATE.ROTATE) return;
1077 |
1078 | event.preventDefault();
1079 | event.stopPropagation();
1080 |
1081 | handleMouseWheel(event);
1082 |
1083 | scope.dispatchEvent(startEvent); // not sure why these are here...
1084 | scope.dispatchEvent(endEvent);
1085 | }
1086 |
1087 | function onKeyDown(event) {
1088 |
1089 | if (scope.enabled === false || scope.enableKeys === false || scope.enablePan === false) return;
1090 |
1091 | handleKeyDown(event);
1092 | }
1093 |
1094 | function onTouchStart(event) {
1095 |
1096 | if (scope.enabled === false) return;
1097 |
1098 | switch (event.touches.length) {
1099 |
1100 | case 1:
1101 | // one-fingered touch: rotate
1102 |
1103 | if (scope.enableRotate === false) return;
1104 |
1105 | handleTouchStartRotate(event);
1106 |
1107 | state = STATE.TOUCH_ROTATE;
1108 |
1109 | break;
1110 |
1111 | case 2:
1112 | // two-fingered touch: dolly
1113 |
1114 | if (scope.enableZoom === false) return;
1115 |
1116 | handleTouchStartDolly(event);
1117 |
1118 | state = STATE.TOUCH_DOLLY;
1119 |
1120 | break;
1121 |
1122 | case 3:
1123 | // three-fingered touch: pan
1124 |
1125 | if (scope.enablePan === false) return;
1126 |
1127 | handleTouchStartPan(event);
1128 |
1129 | state = STATE.TOUCH_PAN;
1130 |
1131 | break;
1132 |
1133 | default:
1134 |
1135 | state = STATE.NONE;
1136 |
1137 | }
1138 |
1139 | if (state !== STATE.NONE) {
1140 |
1141 | scope.dispatchEvent(startEvent);
1142 | }
1143 | }
1144 |
1145 | function onTouchMove(event) {
1146 |
1147 | if (scope.enabled === false) return;
1148 |
1149 | event.preventDefault();
1150 | event.stopPropagation();
1151 |
1152 | switch (event.touches.length) {
1153 |
1154 | case 1:
1155 | // one-fingered touch: rotate
1156 |
1157 | if (scope.enableRotate === false) return;
1158 | if (state !== STATE.TOUCH_ROTATE) return; // is this needed?...
1159 |
1160 | handleTouchMoveRotate(event);
1161 |
1162 | break;
1163 |
1164 | case 2:
1165 | // two-fingered touch: dolly
1166 |
1167 | if (scope.enableZoom === false) return;
1168 | if (state !== STATE.TOUCH_DOLLY) return; // is this needed?...
1169 |
1170 | handleTouchMoveDolly(event);
1171 |
1172 | break;
1173 |
1174 | case 3:
1175 | // three-fingered touch: pan
1176 |
1177 | if (scope.enablePan === false) return;
1178 | if (state !== STATE.TOUCH_PAN) return; // is this needed?...
1179 |
1180 | handleTouchMovePan(event);
1181 |
1182 | break;
1183 |
1184 | default:
1185 |
1186 | state = STATE.NONE;
1187 |
1188 | }
1189 | }
1190 |
1191 | function onTouchEnd(event) {
1192 |
1193 | if (scope.enabled === false) return;
1194 |
1195 | handleTouchEnd(event);
1196 |
1197 | scope.dispatchEvent(endEvent);
1198 |
1199 | state = STATE.NONE;
1200 | }
1201 |
1202 | function onContextMenu(event) {
1203 |
1204 | event.preventDefault();
1205 | }
1206 |
1207 | //
1208 |
1209 | scope.domElement.addEventListener('contextmenu', onContextMenu, false);
1210 |
1211 | scope.domElement.addEventListener('mousedown', onMouseDown, false);
1212 | scope.domElement.addEventListener('wheel', onMouseWheel, false);
1213 |
1214 | scope.domElement.addEventListener('touchstart', onTouchStart, false);
1215 | scope.domElement.addEventListener('touchend', onTouchEnd, false);
1216 | scope.domElement.addEventListener('touchmove', onTouchMove, false);
1217 |
1218 | window.addEventListener('keydown', onKeyDown, false);
1219 |
1220 | // force an update at start
1221 |
1222 | this.update();
1223 | };
1224 |
1225 | THREE.OrbitControls.prototype = Object.create(THREE.EventDispatcher.prototype);
1226 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
1227 |
1228 | Object.defineProperties(THREE.OrbitControls.prototype, {
1229 |
1230 | center: {
1231 |
1232 | get: function get() {
1233 |
1234 | console.warn('THREE.OrbitControls: .center has been renamed to .target');
1235 | return this.target;
1236 | }
1237 |
1238 | },
1239 |
1240 | // backward compatibility
1241 |
1242 | noZoom: {
1243 |
1244 | get: function get() {
1245 |
1246 | console.warn('THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.');
1247 | return !this.enableZoom;
1248 | },
1249 |
1250 | set: function set(value) {
1251 |
1252 | console.warn('THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.');
1253 | this.enableZoom = !value;
1254 | }
1255 |
1256 | },
1257 |
1258 | noRotate: {
1259 |
1260 | get: function get() {
1261 |
1262 | console.warn('THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.');
1263 | return !this.enableRotate;
1264 | },
1265 |
1266 | set: function set(value) {
1267 |
1268 | console.warn('THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.');
1269 | this.enableRotate = !value;
1270 | }
1271 |
1272 | },
1273 |
1274 | noPan: {
1275 |
1276 | get: function get() {
1277 |
1278 | console.warn('THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.');
1279 | return !this.enablePan;
1280 | },
1281 |
1282 | set: function set(value) {
1283 |
1284 | console.warn('THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.');
1285 | this.enablePan = !value;
1286 | }
1287 |
1288 | },
1289 |
1290 | noKeys: {
1291 |
1292 | get: function get() {
1293 |
1294 | console.warn('THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.');
1295 | return !this.enableKeys;
1296 | },
1297 |
1298 | set: function set(value) {
1299 |
1300 | console.warn('THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.');
1301 | this.enableKeys = !value;
1302 | }
1303 |
1304 | },
1305 |
1306 | staticMoving: {
1307 |
1308 | get: function get() {
1309 |
1310 | console.warn('THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.');
1311 | return !this.enableDamping;
1312 | },
1313 |
1314 | set: function set(value) {
1315 |
1316 | console.warn('THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.');
1317 | this.enableDamping = !value;
1318 | }
1319 |
1320 | },
1321 |
1322 | dynamicDampingFactor: {
1323 |
1324 | get: function get() {
1325 |
1326 | console.warn('THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.');
1327 | return this.dampingFactor;
1328 | },
1329 |
1330 | set: function set(value) {
1331 |
1332 | console.warn('THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.');
1333 | this.dampingFactor = value;
1334 | }
1335 |
1336 | }
1337 |
1338 | });
1339 | 'use strict';
1340 |
1341 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
1342 |
1343 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
1344 |
1345 | var ColorTex = (function () {
1346 | function ColorTex(webgl) {
1347 | _classCallCheck(this, ColorTex);
1348 |
1349 | this.webgl = webgl;
1350 | this.controls = this.webgl.controls;
1351 | this.size = this.webgl.size;
1352 |
1353 | this.colorPallete = [];
1354 |
1355 | var _colorPallete = this.controls.props.pallete;
1356 |
1357 | for (var i = 0; i < _colorPallete.length; i++) {
1358 | this.colorPallete[i] = new THREE.Color(_colorPallete[i]);
1359 | }
1360 |
1361 | this.objType = [new THREE.TorusBufferGeometry(8, 4, 18, 32), new THREE.TorusBufferGeometry(14, 3, 5, 3), new THREE.BoxBufferGeometry(12, 12, 12), new THREE.TorusBufferGeometry(16, 2, 5, 6)];
1362 |
1363 | this.objTypeName = ['ring', 'triangle', 'box', 'hexagon'];
1364 |
1365 | this.objNum = 0;
1366 | this.init();
1367 | }
1368 |
1369 | _createClass(ColorTex, [{
1370 | key: 'init',
1371 | value: function init() {
1372 | this.width = this.webgl.width;
1373 | this.height = this.webgl.height;
1374 | this.scene = new THREE.Scene();
1375 |
1376 | this.camera = new THREE.PerspectiveCamera(70, this.width / this.height, .01, 10000);
1377 | this.scene.add(this.camera);
1378 | this.camera.position.set(-1, 3, 1);
1379 | this.camera.lookAt(this.scene.position);
1380 |
1381 | var renderTargetParameters = {
1382 | minFilter: THREE.LinearFilter,
1383 | magFilter: THREE.LinearFilter,
1384 | format: THREE.RGBAFormat
1385 | };
1386 |
1387 | this.fbo = new THREE.WebGLRenderTarget(this.width, this.height, renderTargetParameters);
1388 | this.fbo.texture.format = THREE.RGBAFormat;
1389 |
1390 | this.group = new THREE.Group();
1391 | this.scene.add(this.group);
1392 |
1393 | this.orbitControls = new THREE.OrbitControls(this.camera, this.webgl.renderer.domElement);
1394 |
1395 | this.sim = new Simulation(this.webgl, this.size);
1396 |
1397 | var scale = {
1398 | x: 1,
1399 | y: 1,
1400 | z: 1
1401 | };
1402 |
1403 | this.material = new THREE.ShaderMaterial({
1404 | uniforms: {
1405 | posMap: { type: "t", value: this.sim.gpuCompute.getCurrentRenderTarget(this.sim.pos).texture },
1406 | velMap: { type: "t", value: this.sim.gpuCompute.getCurrentRenderTarget(this.sim.vel).texture },
1407 | uSize: { type: "f", value: this.sim.size },
1408 | uTick: { type: 'f', value: 0 },
1409 | uScale2: { type: 'v3', value: new THREE.Vector3(scale.x, scale.y, scale.z) },
1410 | uScale1: { type: 'f', value: 0.7 },
1411 | uColorArray: { type: "v3v", value: this.colorPallete },
1412 | isEdge: { type: 'i', value: true },
1413 | uEdgeScale: { type: 'f', value: this.controls.props.edgeSize },
1414 | uEdgeColor: { type: 'vec3', value: new THREE.Color(this.controls.props.edgeColor) },
1415 | isShading: { type: 'i', value: this.controls.props.shading }
1416 | },
1417 |
1418 | vertexShader: this.webgl.vertShader[1],
1419 | fragmentShader: this.webgl.fragShader[4],
1420 | side: THREE.DoubleSide,
1421 | flatShading: true,
1422 | transparent: true
1423 | });
1424 |
1425 | for (var i = 0; i < this.objType.length; i++) {
1426 | var originalG = this.objType[i];
1427 | var edgesOriginalG = new THREE.EdgesGeometry(originalG);
1428 | var EdgeVertices = edgesOriginalG.attributes.position.array;
1429 |
1430 | var posOrder = this.calcSameVert(originalG);
1431 | originalG = this.calcVertNormal(originalG, posOrder, EdgeVertices);
1432 |
1433 | var mesh = this.createObj(originalG);
1434 |
1435 | this.objType[i] = mesh;
1436 | }
1437 | }
1438 | }, {
1439 | key: 'calcVertNormal',
1440 | value: function calcVertNormal(originalG, posOrder, edgeVertices) {
1441 | var length = posOrder.array.length;
1442 | var vertNormal = [];
1443 | var faceNormal = originalG.attributes.normal.array;
1444 | var originalVertices = originalG.attributes.position.array;
1445 |
1446 | for (var i = 0; i <= posOrder.maxCount; i++) {
1447 | var orderNumArray = [];
1448 | var normalSum = { x: 0, y: 0, z: 0 };
1449 | var count = 0;
1450 |
1451 | for (var j = 0; j < length; j++) {
1452 | if (posOrder.array[j] === i) {
1453 | var pos = {
1454 | x: originalVertices[j * 3 + 0],
1455 | y: originalVertices[j * 3 + 1],
1456 | z: originalVertices[j * 3 + 2]
1457 | };
1458 |
1459 | var detectEdge = this.detectEdge(pos, edgeVertices);
1460 |
1461 | orderNumArray.push(j);
1462 |
1463 | if (detectEdge) {
1464 | normalSum.x += faceNormal[j * 3 + 0];
1465 | normalSum.y += faceNormal[j * 3 + 1];
1466 | normalSum.z += faceNormal[j * 3 + 2];
1467 | } else {}
1468 |
1469 | count++;
1470 | }
1471 | }
1472 |
1473 | normalSum.x /= count;
1474 | normalSum.y /= count;
1475 | normalSum.z /= count;
1476 |
1477 | var vecLength = Math.sqrt(normalSum.x * normalSum.x + normalSum.y * normalSum.y + normalSum.z * normalSum.z);
1478 |
1479 | if (vecLength !== 0) {
1480 | normalSum.x /= vecLength;
1481 | normalSum.y /= vecLength;
1482 | normalSum.z /= vecLength;
1483 | }
1484 |
1485 | for (var k = 0; k < count; k++) {
1486 | var num = orderNumArray[k];
1487 | vertNormal[num * 3 + 0] = normalSum.x;
1488 | vertNormal[num * 3 + 1] = normalSum.y;
1489 | vertNormal[num * 3 + 2] = normalSum.z;
1490 | }
1491 | }
1492 |
1493 | var vertNormals = new THREE.Float32BufferAttribute(vertNormal, 3);
1494 | originalG.addAttribute("vertNormal", vertNormals);
1495 | return originalG;
1496 | }
1497 | }, {
1498 | key: 'detectEdge',
1499 | value: function detectEdge(vec3, edgeVertices) {
1500 | var isSame = false;
1501 | for (var i = 0, len = edgeVertices.length; i < len; i += 3) {
1502 | var edgeVert = {};
1503 | edgeVert.x = edgeVertices[i + 0];
1504 | edgeVert.y = edgeVertices[i + 1];
1505 | edgeVert.z = edgeVertices[i + 2];
1506 |
1507 | isSame = Math.abs(vec3.x - edgeVert.x) < 0.001 && Math.abs(vec3.y - edgeVert.y) < 0.001 && Math.abs(vec3.z - edgeVert.z) < 0.001;
1508 | if (isSame) break;
1509 | }
1510 |
1511 | return isSame;
1512 | }
1513 | }, {
1514 | key: 'calcSameVert',
1515 | value: function calcSameVert(geometry) {
1516 | this.verticesArray = geometry.attributes.position.array;
1517 | var arrayLength = this.verticesArray.length;
1518 |
1519 | this.vecCount = 0;
1520 | this.indexCount = 0;
1521 | this.vec3Array = [];
1522 |
1523 | this.vertexArray = [];
1524 | var posOrderArray = [];
1525 |
1526 | for (var i = 0; i < arrayLength; i += 3) {
1527 | var vec3 = {};
1528 | vec3.x = this.verticesArray[i];
1529 | vec3.y = this.verticesArray[i + 1];
1530 | vec3.z = this.verticesArray[i + 2];
1531 | var detect = this.detectVec(vec3);
1532 |
1533 | if (detect === 0 || detect > 0) {
1534 | posOrderArray[this.indexCount] = detect;
1535 | } else {
1536 | this.vec3Array[this.vecCount] = vec3;
1537 | this.vertexArray.push(vec3.x, vec3.y, vec3.z);
1538 |
1539 | posOrderArray[this.indexCount] = this.vecCount;
1540 |
1541 | this.vecCount++;
1542 | }
1543 |
1544 | this.indexCount++;
1545 | }
1546 |
1547 | return { array: posOrderArray, maxCount: this.vecCount - 1 };
1548 | }
1549 | }, {
1550 | key: 'detectVec',
1551 | value: function detectVec(vec3) {
1552 | if (this.vecCount === 0) return false;
1553 |
1554 | for (var i = 0, len = this.vec3Array.length; i < len; i++) {
1555 | var _vec3 = this.vec3Array[i];
1556 | var isSame = Math.abs(vec3.x - _vec3.x) < 0.001 && Math.abs(vec3.y - _vec3.y) < 0.001 && Math.abs(vec3.z - _vec3.z) < 0.001;
1557 | if (isSame) {
1558 | return i;
1559 | }
1560 | }
1561 |
1562 | return false;
1563 | }
1564 | }, {
1565 | key: 'createObj',
1566 | value: function createObj(originalG) {
1567 | var geometry = new THREE.InstancedBufferGeometry();
1568 | var vertices = originalG.attributes.position.clone();
1569 |
1570 | geometry.addAttribute("position", vertices);
1571 |
1572 | var normals = originalG.attributes.normal.clone();
1573 | geometry.addAttribute("normal", normals);
1574 |
1575 | var vertNormals = originalG.attributes.vertNormal.clone();
1576 | geometry.addAttribute("vertNormal", vertNormals);
1577 |
1578 | // uv
1579 | var uvs = originalG.attributes.uv.clone();
1580 | geometry.addAttribute("uv", uvs);
1581 |
1582 | // index
1583 | if (originalG.index) {
1584 | var indices = originalG.index.clone();
1585 | geometry.setIndex(indices);
1586 | }
1587 |
1588 | geometry.maxInstancedCount = this.sim.size * this.sim.size;
1589 |
1590 | var nums = new THREE.InstancedBufferAttribute(new Float32Array(this.sim.size * this.sim.size * 1), 1, 1);
1591 | var numRatios = new THREE.InstancedBufferAttribute(new Float32Array(this.sim.size * this.sim.size * 1), 1, 1);
1592 |
1593 | for (var i = 0; i < nums.count; i++) {
1594 | nums.setX(i, i);
1595 | numRatios.setX(i, i / (nums.count - 1));
1596 | }
1597 |
1598 | geometry.addAttribute("aNum", nums);
1599 | geometry.addAttribute("aNumRatio", numRatios);
1600 |
1601 | var mesh = new THREE.Mesh(geometry, this.material);
1602 |
1603 | mesh.visible = false;
1604 | this.group.add(mesh);
1605 |
1606 | return mesh;
1607 | }
1608 | }, {
1609 | key: 'render',
1610 | value: function render(time, delta) {
1611 | this.webgl.renderer.clearTarget(this.fbo);
1612 |
1613 | var sin = (Math.sin(time) * 0.5 + 0.5) * 0.5;
1614 |
1615 | var mesh = this.objType[this.objNum];
1616 |
1617 | this.group.rotation.x += delta * 0.1;
1618 | this.group.rotation.y -= delta * 0.08;
1619 |
1620 | this.sim.velUniforms.timer.value = time;
1621 | this.sim.velUniforms.delta.value = delta;
1622 |
1623 | this.sim.gpuCompute.compute();
1624 |
1625 | mesh.material.uniforms.posMap.value = this.sim.gpuCompute.getCurrentRenderTarget(this.sim.pos).texture;
1626 | mesh.material.uniforms.velMap.value = this.sim.gpuCompute.getCurrentRenderTarget(this.sim.vel).texture;
1627 |
1628 | // timer
1629 | mesh.material.uniforms.uTick.value = time;
1630 |
1631 | this.objType[this.objNum].visible = true;
1632 |
1633 | if (this.webgl.controls.props.backSide) {
1634 | mesh.material.uniforms.isEdge.value = true;
1635 | mesh.material.side = THREE.BackSide;
1636 | this.webgl.renderer.render(this.scene, this.camera, this.fbo);
1637 | }
1638 |
1639 | if (this.webgl.controls.props.frontSide) {
1640 | mesh.material.uniforms.isEdge.value = false;
1641 | mesh.material.side = THREE.FrontSide;
1642 | this.webgl.renderer.render(this.scene, this.camera, this.fbo);
1643 | }
1644 |
1645 | this.objType[this.objNum].visible = false;
1646 | }
1647 | }]);
1648 |
1649 | return ColorTex;
1650 | })();
1651 | 'use strict';
1652 |
1653 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
1654 |
1655 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
1656 |
1657 | var Controls = (function () {
1658 | function Controls(webgl) {
1659 | _classCallCheck(this, Controls);
1660 |
1661 | this.webgl = webgl;
1662 |
1663 | this.props = {
1664 | pallete: ['#ffe100', '#44dee8', '#fa2c98', '#ffffff'],
1665 | edgeColor: '#69145a',
1666 | bgColor: '#ff2f6c',
1667 | objType: null,
1668 | backSide: true,
1669 | frontSide: true,
1670 | edgeSize: 0.38,
1671 | shading: true
1672 | };
1673 | }
1674 |
1675 | _createClass(Controls, [{
1676 | key: 'init',
1677 | value: function init() {
1678 | this.colorTex = this.webgl.colorTex;
1679 | this.props.objType = this.colorTex.objTypeName[0];
1680 | this.uColorArray = this.colorTex.material.uniforms.uColorArray;
1681 |
1682 | this.gui = new dat.GUI({ width: 300 });
1683 |
1684 | this.gui_objColor = this.gui.addFolder('obj color');
1685 | this.gui_objColor.open();
1686 |
1687 | this.gui_objColor.addColor(this.props.pallete, 0).name('obj color 1').onChange(this.colorFunc1.bind(this));
1688 | this.gui_objColor.addColor(this.props.pallete, 1).name('obj color 2').onChange(this.colorFunc2.bind(this));
1689 | this.gui_objColor.addColor(this.props.pallete, 2).name('obj color 3').onChange(this.colorFunc3.bind(this));
1690 | this.gui_objColor.addColor(this.props.pallete, 3).name('obj color 4').onChange(this.colorFunc4.bind(this));
1691 |
1692 | this.gui.addColor(this.props, 'edgeColor').name('edge color').onChange(this.colorFunc_edge.bind(this));
1693 | this.gui.addColor(this.props, 'bgColor').name('bg color').onChange(this.colorFunc_bg.bind(this));
1694 |
1695 | this.gui.add(this.props, 'objType', this.colorTex.objTypeName).name('obj type').onFinishChange(this.objTypeFunc.bind(this));
1696 |
1697 | this.gui.add(this.props, 'backSide').name('outline color');
1698 | this.gui.add(this.props, 'frontSide').name('obj color');
1699 | this.gui.add(this.props, 'edgeSize', 0.1, 0.6).name('edge size').onChange(this.edgeSize.bind(this));
1700 | this.gui.add(this.props, 'shading').onChange(this.shading.bind(this));
1701 | }
1702 | }, {
1703 | key: 'colorFunc1',
1704 | value: function colorFunc1(value) {
1705 | var color = new THREE.Color(value);
1706 | this.uColorArray.value[0] = color;
1707 | }
1708 | }, {
1709 | key: 'colorFunc2',
1710 | value: function colorFunc2(value) {
1711 | var color = new THREE.Color(value);
1712 | this.uColorArray.value[1] = color;
1713 | }
1714 | }, {
1715 | key: 'colorFunc3',
1716 | value: function colorFunc3(value) {
1717 | var color = new THREE.Color(value);
1718 | this.uColorArray.value[2] = color;
1719 | }
1720 | }, {
1721 | key: 'colorFunc4',
1722 | value: function colorFunc4(value) {
1723 | var color = new THREE.Color(value);
1724 | this.uColorArray.value[3] = color;
1725 | }
1726 | }, {
1727 | key: 'colorFunc_edge',
1728 | value: function colorFunc_edge(value) {
1729 | var color = new THREE.Color(value);
1730 | // this.webgl.uniforms.uEdgeColor.value = color;
1731 | this.colorTex.material.uniforms.uEdgeColor.value = color;
1732 | }
1733 | }, {
1734 | key: 'colorFunc_bg',
1735 | value: function colorFunc_bg(value) {
1736 | var color = new THREE.Color(value);
1737 | this.webgl.uniforms.uBgColor.value = color;
1738 | }
1739 | }, {
1740 | key: 'objTypeFunc',
1741 | value: function objTypeFunc(value) {
1742 | for (var i = 0, len = this.colorTex.objTypeName.length; i < len; i++) {
1743 | if (value === this.colorTex.objTypeName[i]) {
1744 | break;
1745 | }
1746 | }
1747 |
1748 | this.colorTex.objNum = i;
1749 | }
1750 | }, {
1751 | key: 'edgeSize',
1752 | value: function edgeSize(value) {
1753 | this.colorTex.material.uniforms.uEdgeScale.value = value;
1754 | }
1755 | }, {
1756 | key: 'shading',
1757 | value: function shading(value) {
1758 | this.colorTex.material.uniforms.isShading.value = value;
1759 | }
1760 | }]);
1761 |
1762 | return Controls;
1763 | })();
1764 | "use strict";
1765 |
1766 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
1767 |
1768 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
1769 |
1770 | var ResizeWatch = (function () {
1771 | function ResizeWatch() {
1772 | var _this = this;
1773 |
1774 | _classCallCheck(this, ResizeWatch);
1775 |
1776 | this.instances = [];
1777 |
1778 | this.width = this._width = document.body.clientWidth;
1779 | this.height = this._height = window.innerHeight;
1780 | this.aspect = this.width / this.height;
1781 |
1782 | window.onresize = (function () {
1783 | if (_this.instances.length === 0) return;
1784 |
1785 | _this.width = document.body.clientWidth;
1786 | _this.height = window.innerHeight;
1787 | _this.aspect = _this.width / _this.height;
1788 |
1789 | for (var i = 0; i < _this.instances.length; i++) {
1790 | _this.instances[i].resizeUpdate();
1791 | }
1792 | }).bind(this);
1793 | }
1794 |
1795 | _createClass(ResizeWatch, [{
1796 | key: "register",
1797 | value: function register(instance) {
1798 | this.instances.push(instance);
1799 | }
1800 | }]);
1801 |
1802 | return ResizeWatch;
1803 | })();
1804 |
1805 | window.ResizeWatch = new ResizeWatch();
1806 | "use strict";
1807 |
1808 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
1809 |
1810 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
1811 |
1812 | var Simulation = (function () {
1813 | function Simulation(basic, size) {
1814 | _classCallCheck(this, Simulation);
1815 |
1816 | this.basic = basic;
1817 | this.renderer = this.basic.renderer;
1818 | this.size = size;
1819 | this.init();
1820 | }
1821 |
1822 | _createClass(Simulation, [{
1823 | key: "init",
1824 | value: function init() {
1825 | this.gpuCompute = new GPUComputationRenderer(this.size, this.size, this.renderer);
1826 |
1827 | this.dataPos = this.gpuCompute.createTexture();
1828 | this.dataVel = this.gpuCompute.createTexture();
1829 | this.dataDef = this.gpuCompute.createTexture();
1830 |
1831 | var posArray = this.dataPos.image.data;
1832 | var velArray = this.dataVel.image.data;
1833 | var defArray = this.dataDef.image.data;
1834 |
1835 | for (var i = 0, il = posArray.length; i < il; i += 4) {
1836 |
1837 | var phi = Math.random() * 2 * Math.PI;
1838 | var theta = Math.random() * Math.PI;
1839 | var r = (1.2 + Math.random() * 2) * 1.2;
1840 |
1841 | defArray[i + 0] = posArray[i + 0] = r * Math.sin(theta) * Math.cos(phi);
1842 | defArray[i + 1] = posArray[i + 1] = r * Math.sin(theta) * Math.sin(phi) * 1.4;
1843 | defArray[i + 2] = posArray[i + 2] = r * Math.cos(theta);
1844 | defArray[i + 3] = posArray[i + 3] = Math.random() * 0.5;
1845 |
1846 | velArray[i + 3] = Math.random() * 100; // frames life
1847 | }
1848 |
1849 | this.def = this.gpuCompute.addVariable("defTex", this.basic.fragShader[1], this.dataDef);
1850 | this.vel = this.gpuCompute.addVariable("velTex", this.basic.fragShader[2], this.dataVel);
1851 | this.pos = this.gpuCompute.addVariable("posTex", this.basic.fragShader[3], this.dataPos);
1852 |
1853 | this.gpuCompute.setVariableDependencies(this.def, [this.pos, this.vel, this.def]);
1854 | this.gpuCompute.setVariableDependencies(this.vel, [this.pos, this.vel, this.def]);
1855 | this.gpuCompute.setVariableDependencies(this.pos, [this.pos, this.vel, this.def]);
1856 |
1857 | // var posUniforms = this.pos.material.uniforms;
1858 | this.velUniforms = this.vel.material.uniforms;
1859 |
1860 | this.velUniforms.timer = { value: 0.0 };
1861 | this.velUniforms.delta = { value: 0.0 };
1862 | this.velUniforms.speed = { value: 0.3 };
1863 | this.velUniforms.factor = { value: 0.5 };
1864 | this.velUniforms.evolution = { value: 0.5 };
1865 | this.velUniforms.radius = { value: 2.0 };
1866 |
1867 | var error = this.gpuCompute.init();
1868 | if (error !== null) {
1869 | console.error(error);
1870 | }
1871 | }
1872 | }]);
1873 |
1874 | return Simulation;
1875 | })();
1876 | "use strict";
1877 |
1878 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
1879 |
1880 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
1881 |
1882 | var Webgl = (function () {
1883 | function Webgl() {
1884 | _classCallCheck(this, Webgl);
1885 |
1886 | this.size = 32;
1887 |
1888 | this.vertShader = ["assets/glsl/output.vert", "assets/glsl/cube.vert"];
1889 |
1890 | this.fragShader = ["assets/glsl/output.frag", "assets/glsl/simulation_def.frag", "assets/glsl/simulation_vel.frag", "assets/glsl/simulation_pos.frag", "assets/glsl/cube.frag"];
1891 |
1892 | this.shaderLength = this.vertShader.length + this.fragShader.length;
1893 | this.shaderCount = 0;
1894 |
1895 | for (var i = 0; i < this.vertShader.length; i++) {
1896 | this.importShader_vert(i);
1897 | }
1898 |
1899 | for (var i = 0; i < this.fragShader.length; i++) {
1900 | this.importShader_frag(i);
1901 | }
1902 | }
1903 |
1904 | _createClass(Webgl, [{
1905 | key: "importShader_vert",
1906 | value: function importShader_vert(i) {
1907 |
1908 | var myRequest = new XMLHttpRequest();
1909 |
1910 | var _this = this;
1911 | myRequest.onreadystatechange = function () {
1912 | if (myRequest.readyState === 4) {
1913 | _this.vertShader[i] = myRequest.response;
1914 | _this.completeShaderLoad();
1915 | }
1916 | };
1917 |
1918 | myRequest.open("GET", this.vertShader[i], true);
1919 | myRequest.send();
1920 | }
1921 | }, {
1922 | key: "importShader_frag",
1923 | value: function importShader_frag(i) {
1924 |
1925 | var myRequest = new XMLHttpRequest();
1926 | // ハンドラの登録
1927 | var _this = this;
1928 | myRequest.onreadystatechange = function () {
1929 | if (myRequest.readyState === 4) {
1930 | _this.fragShader[i] = myRequest.response;
1931 |
1932 | _this.completeShaderLoad();
1933 | }
1934 | };
1935 |
1936 | myRequest.open("GET", this.fragShader[i], true);
1937 | myRequest.send();
1938 | }
1939 | }, {
1940 | key: "completeShaderLoad",
1941 | value: function completeShaderLoad() {
1942 | this.shaderCount++;
1943 |
1944 | if (this.shaderCount === this.shaderLength) {
1945 | this.isShaderComplete = true;
1946 | this.init();
1947 | }
1948 | }
1949 | }, {
1950 | key: "init",
1951 | value: function init() {
1952 | this.width = 2048;
1953 | this.height = 2048;
1954 | this.aspect = this.width / this.height;
1955 | this.setProps();
1956 | this.container = document.getElementById("wrapper");
1957 |
1958 | this.renderer = new THREE.WebGLRenderer({
1959 | antialias: true,
1960 | alpha: true
1961 | });
1962 |
1963 | this.renderer.autoClear = false;
1964 | // renderer.setPixelRatio( window.devicePixelRatio );
1965 | this.renderer.setSize(ResizeWatch.width, ResizeWatch.height);
1966 | this.renderer.setClearColor(0xffffff, 0.0);
1967 | this.container.appendChild(this.renderer.domElement);
1968 |
1969 | var ratio = Useragnt.pc ? 1.0 : 2.0;
1970 |
1971 | this.renderer.setPixelRatio(ratio);
1972 |
1973 | this.scene = new THREE.Scene();
1974 |
1975 | this.camera = new THREE.PerspectiveCamera(this.props.fov, this.props.aspect, this.props.near, this.props.far);
1976 | var cameraZ = this.props.height / 2 / Math.tan(this.props.fov * Math.PI / 180 / 2);
1977 | this.camera.position.set(0, 0, cameraZ);
1978 | this.camera.lookAt(this.scene.position);
1979 |
1980 | this.controls = new Controls(this);
1981 |
1982 | this.colorTex = new ColorTex(this);
1983 |
1984 | this.createPlane();
1985 |
1986 | this.controls.init();
1987 |
1988 | this.time = new THREE.Clock();
1989 | this.render();
1990 |
1991 | ResizeWatch.register(this);
1992 | }
1993 | }, {
1994 | key: "setProps",
1995 | value: function setProps() {
1996 | var width = ResizeWatch.width;
1997 | var height = ResizeWatch.height;
1998 | var aspect = width / height;
1999 |
2000 | this.props = {
2001 | width: width,
2002 | height: height,
2003 | aspect: aspect,
2004 | fov: 45,
2005 | left: -width / 2,
2006 | right: width / 2,
2007 | top: height / 2,
2008 | bottom: -height / 2,
2009 | near: 0.1,
2010 | far: 10000,
2011 | parent: document.getElementById("wrapper")
2012 | };
2013 | }
2014 | }, {
2015 | key: "createPlane",
2016 | value: function createPlane() {
2017 | var g = new THREE.PlaneBufferGeometry(this.width, this.height);
2018 |
2019 | this.uniforms = {
2020 | uTex_1: { type: "t", value: this.colorTex.fbo.texture },
2021 | uTick: { type: "f", value: 0 },
2022 | uSize: { type: "v2", value: new THREE.Vector2(this.width, this.height) },
2023 | // uEdgeColor: {type: "v3", value: new THREE.Color(this.edgeColor)},
2024 | uBgColor: { type: "v3", value: new THREE.Color(this.controls.props.bgColor) }
2025 | };
2026 |
2027 | var m = new THREE.ShaderMaterial({
2028 | vertexShader: this.vertShader[0],
2029 | fragmentShader: this.fragShader[0],
2030 | uniforms: this.uniforms
2031 | });
2032 |
2033 | var mesh = new THREE.Mesh(g, m);
2034 |
2035 | mesh.position.z = 10;
2036 | this.scene.add(mesh);
2037 |
2038 | this.plane = mesh;
2039 |
2040 | console.log(this.plane);
2041 |
2042 | if (ResizeWatch.aspect > this.aspect) {
2043 | var scale = ResizeWatch.width / this.width;
2044 | } else {
2045 | var scale = ResizeWatch.height / this.height;
2046 | }
2047 |
2048 | this.plane.scale.x = scale;
2049 | this.plane.scale.y = scale;
2050 | }
2051 | }, {
2052 | key: "render",
2053 | value: function render() {
2054 | var delta = this.time.getDelta() * 5;
2055 | var time = this.time.elapsedTime;
2056 |
2057 | this.renderer.clear();
2058 |
2059 | this.colorTex.render(time, delta);
2060 | this.uniforms.uTick.value = time;
2061 |
2062 | this.renderer.render(this.scene, this.camera);
2063 |
2064 | requestAnimationFrame(this.render.bind(this));
2065 | }
2066 | }, {
2067 | key: "resizeUpdate",
2068 | value: function resizeUpdate() {
2069 | this.setProps();
2070 | this.renderer.setSize(this.props.width, this.props.height);
2071 |
2072 | this.camera.aspect = this.props.aspect;
2073 |
2074 | var cameraZ = this.props.height / 2 / Math.tan(this.props.fov * Math.PI / 180 / 2);
2075 |
2076 | this.camera.position.set(0, 0, cameraZ);
2077 | this.camera.lookAt(this.scene.position);
2078 |
2079 | this.camera.updateProjectionMatrix();
2080 |
2081 | if (ResizeWatch.aspect > this.aspect) {
2082 | var scale = ResizeWatch.width / this.width;
2083 | } else {
2084 | var scale = ResizeWatch.height / this.height;
2085 | }
2086 |
2087 | this.plane.scale.x = scale;
2088 | this.plane.scale.y = scale;
2089 | }
2090 | }]);
2091 |
2092 | return Webgl;
2093 | })();
2094 | "use strict";
2095 |
2096 | window.onload = function () {
2097 | var webgl = new Webgl();
2098 | };
--------------------------------------------------------------------------------
/dst/fb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mnmxmx/toon-shading/cc36cd77f219b8663634c1c25df99a65cc944b22/dst/fb.png
--------------------------------------------------------------------------------
/dst/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |