├── .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 | 4 | 5 | 6 | Toon Shading 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /dst/tw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnmxmx/toon-shading/cc36cd77f219b8663634c1c25df99a65cc944b22/dst/tw.png -------------------------------------------------------------------------------- /gulp/tasks/browsersync.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var browserSync = require('browser-sync'); 3 | 4 | gulp.task('browserSync', function(){ 5 | browserSync({ 6 | open: 'external', 7 | reloadDebounce: 2000, 8 | ui: false, 9 | notify: false, 10 | startPath: "/", 11 | ghostMode: false, 12 | server: { 13 | baseDir: "dst/" 14 | }, 15 | files: [ 16 | "dst/**/*.obj", 17 | 18 | "dst/**/*.json", 19 | "dst/**/*.xml", 20 | 21 | "dst/**/*.mp4", 22 | "dst/**/*.webm", 23 | "dst/**/*.mp3", 24 | 25 | "dst/**/*.png", 26 | "dst/**/*.jpg", 27 | "dst/**/*.gif", 28 | "dst/**/*.svg", 29 | 30 | "dst/**/*.frag", 31 | "dst/**/*.vert", 32 | 33 | "dst/**/*.html", 34 | "dst/**/*.css", 35 | "dst/**/*.js" 36 | ] 37 | }); 38 | }); -------------------------------------------------------------------------------- /gulp/tasks/glsl.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"); 2 | var glslify = require("gulp-glslify"); 3 | 4 | // var path = "assets/test"; 5 | 6 | 7 | gulp.task("glsl", null, function() { 8 | gulp.src("src/**/*.{vert,frag}") 9 | .pipe(glslify()) 10 | .pipe(gulp.dest("dst")); 11 | }); -------------------------------------------------------------------------------- /gulp/tasks/html.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var plumber = require('gulp-plumber'); 3 | 4 | 5 | gulp.task('html', function() 6 | { 7 | 8 | gulp.src("src/**/*.html") 9 | .pipe(gulp.dest("dst")); 10 | }); -------------------------------------------------------------------------------- /gulp/tasks/javascript.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"); 2 | var changed = require("gulp-changed"); 3 | var concat = require("gulp-concat"); 4 | var runSequence = require('run-sequence'); 5 | var stripDebug = require('gulp-strip-debug'); 6 | var babel = require('gulp-babel'); 7 | var plumber = require('gulp-plumber'); 8 | 9 | 10 | 11 | gulp.task("js", function(){ 12 | return runSequence( 13 | "js.concat" 14 | ); 15 | }); 16 | 17 | 18 | gulp.task("js.concat", function(){ 19 | return gulp.src([ 20 | "src/assets/js/lib-head/*.js", 21 | "src/assets/js/lib/*.js", 22 | "src/assets/js/module/*.js", 23 | "src/assets/js/main.js" 24 | ], 25 | { base: "src" } 26 | ) 27 | .pipe(plumber()) 28 | .pipe(babel()) 29 | .pipe(concat("main.min.js")) 30 | .pipe(gulp.dest("dst/assets/js/")); 31 | }) 32 | -------------------------------------------------------------------------------- /gulp/tasks/sass.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"); 2 | var sass = require("gulp-sass"); 3 | var plumber = require('gulp-plumber'); 4 | var csswring = require('csswring'); 5 | 6 | 7 | gulp.task("css", function () { 8 | 9 | var postcss = require("gulp-postcss"); 10 | var postcssOptions = [ 11 | require('postcss-mixins'), 12 | require("autoprefixer")({ browsers: [ 13 | 'ie >= 9', 14 | 'ie_mob >= 10', 15 | 'ff >= 31', 16 | 'chrome >= 36', 17 | 'safari >= 8', 18 | 'opera >= 23', 19 | 'ios >= 8', 20 | 'android >= 4' 21 | ]}), 22 | require("css-mqpacker")({ sort: function (a, b) { return b.localeCompare(a); } }), 23 | require("postcss-flexbugs-fixes"), 24 | require("postcss-partial-import")() 25 | ]; 26 | 27 | postcssOptions.push(csswring); 28 | 29 | gulp.src("src/assets/css/styles.scss") 30 | .pipe(plumber()) 31 | .pipe(sass({outputStyle: "expanded"})) 32 | .pipe(postcss(postcssOptions)) 33 | .pipe(gulp.dest("dst/assets/css/")); 34 | }); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var requireDir = require('require-dir'); 3 | var runSequence = require('run-sequence'); 4 | 5 | 6 | requireDir('./gulp/tasks'); 7 | 8 | 9 | // 開発watch 10 | gulp.task('watch', function(){ 11 | // gulp.watch('src/assets/img/mobile/topic/*.png', ["sprite"]); 12 | gulp.watch(['src/assets/css/**/*.{scss,css}'], ['css']); 13 | gulp.watch('src/assets/js/**/*.js', ['js']); 14 | gulp.watch('src/assets/glsl/**/*.{vert,frag}', ['glsl']); 15 | gulp.watch('src/**/*.html', ['html']); 16 | // gulp.watch(['src/assets/img/**/*.{png,jpg,jpeg,gif,svg}', 'src/assets/video/**/*.webm'], ['img']); 17 | }); 18 | 19 | 20 | // 開発task 21 | gulp.task('predefault', function(){ 22 | runSequence( 23 | 'css', 24 | 'html', 25 | 'glsl', 26 | 'js', 27 | // 'img', 28 | 'browserSync', 29 | 'watch' 30 | ); 31 | }); 32 | 33 | 34 | // srcからdstへ(開発用)... dstへファイルを上書きする 35 | gulp.task('default', ['predefault'], function(){ 36 | console.log('running default tasks...'); 37 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "toon-shading", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "gulpfile.js", 6 | "scripts": { 7 | "start": "cross-env gulp" 8 | }, 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "autoprefixer": "^6.4.0", 12 | "babel-core": "^5.5.8", 13 | "babel-loader": "^5.1.4", 14 | "browser-sync": "^2.13.0", 15 | "cross-env": "^2.0.0", 16 | "css-loader": "^0.25.0", 17 | "css-mqpacker": "^5.0.1", 18 | "csswring": "^5.1.0", 19 | "del": "^2.2.2", 20 | "ejs": "^2.5.1", 21 | "fs": "0.0.1-security", 22 | "glslify": "^6.0.2", 23 | "glslify-hex": "^2.1.1", 24 | "glslify-import": "^3.0.0", 25 | "gulp": "^3.9.1", 26 | "gulp-babel": "^5.1.0", 27 | "gulp-changed": "^1.3.2", 28 | "gulp-concat": "^2.6.0", 29 | "gulp-ejs": "^2.3.0", 30 | "gulp-glslify": "git+https://github.com/yuichiroharai/gulp-glslify.git", 31 | "gulp-if": "^2.0.1", 32 | "gulp-imagemin": "^3.0.2", 33 | "gulp-load-plugins": "^1.2.4", 34 | "gulp-notify": "^2.2.0", 35 | "gulp-plumber": "^1.1.0", 36 | "gulp-postcss": "^6.1.1", 37 | "gulp-sass": "^2.3.2", 38 | "gulp-strip-debug": "^1.1.0", 39 | "gulp-uglify": "^2.0.0", 40 | "gulp-util": "^3.0.7", 41 | "gulp-watch": "^4.3.9", 42 | "gulp-webpack": "^1.5.0", 43 | "gulp.spritesmith": "^6.2.1", 44 | "html-loader": "^0.4.4", 45 | "imagemin": "^5.2.2", 46 | "imagemin-pngquant": "^5.0.0", 47 | "postcss-flexbugs-fixes": "^2.0.0", 48 | "postcss-mixins": "^5.2.0", 49 | "postcss-partial-import": "^2.0.0", 50 | "require-dir": "^0.3.0", 51 | "run-sequence": "^1.2.2", 52 | "sass-variables-loader": "^0.1.3", 53 | "strip-loader": "^0.1.2", 54 | "style-loader": "^0.13.1", 55 | "through2": "^2.0.3", 56 | "vue-loader": "^9.7.0", 57 | "vue-style-loader": "^1.0.0", 58 | "webpack": "^2.2.1" 59 | }, 60 | "author": "", 61 | "license": "ISC" 62 | } 63 | -------------------------------------------------------------------------------- /screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnmxmx/toon-shading/cc36cd77f219b8663634c1c25df99a65cc944b22/screenshot1.png -------------------------------------------------------------------------------- /screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mnmxmx/toon-shading/cc36cd77f219b8663634c1c25df99a65cc944b22/screenshot2.png -------------------------------------------------------------------------------- /src/assets/css/styles.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | @import "vendor/initial"; 4 | 5 | @import "vendor/easing"; 6 | 7 | body{ 8 | overflow: hidden; 9 | // background: linear-gradient(90deg, #fff, #b7d3dc); 10 | } 11 | 12 | .stats{ 13 | position:absolute; 14 | top: 0; 15 | right: 0; 16 | z-index: 2; 17 | } 18 | 19 | 20 | .file{ 21 | position: absolute; 22 | top: 15px; 23 | left: 15px; 24 | padding: 10px; 25 | box-sizing: border-box; 26 | border: 1px solid #fff; 27 | // background: rgba(255, 255, 255, 0.2); 28 | 29 | letter-spacing: 0.01em; 30 | color: #fff; 31 | cursor: pointer; 32 | transition: opacity .4s; 33 | 34 | font-size: 12px; 35 | padding: 15px 30px; 36 | letter-spacing: 0.15em; 37 | // display: none; 38 | opacity: 0; 39 | 40 | &.isHidden{ 41 | display: none; 42 | } 43 | 44 | // &:hover{ 45 | // opacity: .8; 46 | // } 47 | 48 | input{ 49 | display: none; 50 | } 51 | } 52 | 53 | .tapToStart{ 54 | position: absolute; 55 | top: 0; 56 | left: 0; 57 | width: 100%; 58 | height: 100%; 59 | background: rgba(#000, 0.6); 60 | // transition: opacity .3s; 61 | 62 | .pc &{ 63 | display: none; 64 | } 65 | 66 | &.isHidden{ 67 | display: none; 68 | } 69 | 70 | p{ 71 | position: absolute; 72 | left: 50%; 73 | top: 50%; 74 | padding: 15px 30px; 75 | font-size: 12px; 76 | color: #888; 77 | background-color: #fff; 78 | letter-spacing: 0.15em; 79 | transform: translate(-50%, -50%); 80 | cursor: pointer; 81 | } 82 | 83 | z-index: 100; 84 | } 85 | 86 | .credit{ 87 | position: absolute; 88 | letter-spacing: 0.15em; 89 | line-height: 1.5; 90 | color: #fff; 91 | font-weight: 100; 92 | -webkit-font-smoothing: antialiased; 93 | 94 | &-pc{ 95 | .no-pc &{ 96 | display: none; 97 | } 98 | 99 | bottom: 20px; 100 | right: 20px; 101 | font-size: 14px; 102 | 103 | 104 | a{ 105 | position: relative; 106 | display: inline-block; 107 | vertical-align: middle; 108 | width: 15px; 109 | height: 15px; 110 | top: -1px; 111 | } 112 | 113 | .soundcloud{ 114 | background: url(../img/soundcloud-icon.svg) no-repeat; 115 | background-size: contain; 116 | } 117 | 118 | .facebook{ 119 | background: url(../img/fb-icon.svg) no-repeat; 120 | background-size: contain; 121 | } 122 | } 123 | 124 | 125 | &-sp{ 126 | .pc &{ 127 | display: none; 128 | } 129 | 130 | bottom: 10px; 131 | right: 10px; 132 | font-size: 12px; 133 | color: rgba(#fff, 0.7); 134 | a{ 135 | position: relative; 136 | color: rgba(#fff, 0.7); 137 | text-decoration: none; 138 | &:after{ 139 | content: ""; 140 | position: absolute; 141 | width: 100%; 142 | height: 1px; 143 | background: rgba(#fff, 0.7); 144 | bottom: -1px; 145 | left: 0; 146 | } 147 | } 148 | } 149 | 150 | 151 | } 152 | 153 | // .bg{ 154 | // width: 100%; 155 | // height: 100%; 156 | // position: absolute; 157 | // top: 0; 158 | // left: 0; 159 | // background: #123; 160 | // z-index: 1; 161 | 162 | // } 163 | 164 | // .wrapper > canvas{ 165 | // position: absolute; 166 | // top: 0; 167 | // left: 0; 168 | // z-index: 2; 169 | // } -------------------------------------------------------------------------------- /src/assets/css/vendor/_easing.scss: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- 2 | // Easing 3 | //--------------------------------------------------- 4 | $_1_SineIn: cubic-bezier(0.47, 0, 0.745, 0.715); 5 | $_1_SineOut: cubic-bezier(0.39, 0.575, 0.565, 1); 6 | $_1_SineInOut: cubic-bezier(0.445, 0.05, 0.55, 0.95); 7 | $_2_QuadIn: cubic-bezier(0.55, 0.085, 0.68, 0.53); 8 | $_2_QuadOut: cubic-bezier(0.25, 0.46, 0.45, 0.94); 9 | $_2_QuadInOut: cubic-bezier(0.455, 0.03, 0.515, 0.955); 10 | $_3_CubicIn: cubic-bezier(0.55, 0.055, 0.675, 0.19); 11 | $_3_CubicOut: cubic-bezier(0.215, 0.61, 0.355, 1); 12 | $_3_CubicInOut: cubic-bezier(0.645, 0.045, 0.355, 1); 13 | $_4_QuartIn: cubic-bezier(0.895, 0.03, 0.685, 0.22); 14 | $_4_QuartOut: cubic-bezier(0.165, 0.84, 0.44, 1); 15 | $_4_QuartInOut: cubic-bezier(0.77, 0, 0.175, 1); 16 | $_5_QuintIn: cubic-bezier(0.755, 0.05, 0.855, 0.06); 17 | $_5_QuintOut: cubic-bezier(0.23, 1, 0.32, 1); 18 | $_5_QuintInOut: cubic-bezier(0.86, 0, 0.07, 1); 19 | $_6_ExpoIn: cubic-bezier(0.95, 0.05, 0.795, 0.035); 20 | $_6_ExpoOut: cubic-bezier(0.19, 1, 0.22, 1); 21 | $_6_ExpoInOut: cubic-bezier(1, 0, 0, 1); 22 | $_7_CircIn: cubic-bezier(0.6, 0.04, 0.98, 0.335); 23 | $_7_CircOut: cubic-bezier(0.075, 0.82, 0.165, 1); 24 | $_7_CircInOut: cubic-bezier(0.785, 0.135, 0.15, 0.86); 25 | $_BackIn: cubic-bezier(0.6, -0.28, 0.735, 0.045); 26 | $_BackOut: cubic-bezier(0.175, 0.885, 0.32, 1.275); 27 | $_BackInOut: cubic-bezier(0.68, -0.55, 0.265, 1.55); 28 | 29 | $_BackOutStrong: cubic-bezier(.5, 1.5, .5, 1.5); 30 | $_BackInStrong: cubic-bezier(.5, -0.5, .5, -0.5); 31 | $_BackInOutStrong: cubic-bezier(0.5,-1,0.5,2); 32 | 33 | $_12_SineQuadInOut: cubic-bezier(0.445, 0.05, 0.515, 0.955); 34 | $_21_QuadSineInOut: cubic-bezier(0.455, 0.03, 0.55, 0.95); 35 | 36 | $_23_QuadCubicInOut: cubic-bezier(0.455, 0.03, 0.355, 1); 37 | $_32_CubicQuadInOut: cubic-bezier(0.645, 0.045, 0.515, 0.955); 38 | 39 | $_34_CubicQuartInOut: cubic-bezier(0.645, 0.045, 0.175, 1); 40 | $_43_QuartCubicInOut: cubic-bezier(0.77, 0, 0.355, 1); 41 | 42 | $_45_QuartQuintInOut: cubic-bezier(0.77, 0, 0.07, 1); 43 | $_54_QuintQuartInOut: cubic-bezier(0.86, 0, 0.175, 1); 44 | 45 | $_46_QuartExpoInOut: cubic-bezier(0.77, 0, 1, 0); 46 | $_64_ExpoQuartInOut: cubic-bezier(1, 0,0.175, 1); 47 | $_56_QuintExpoInOut: cubic-bezier(0.86, 0, 1, 0); 48 | $_65_ExpoQuintInOut: cubic-bezier(1, 0, 0.07, 1); 49 | -------------------------------------------------------------------------------- /src/assets/css/vendor/_initial.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | // -------------------- from reset.css 4 | // 必要な要素だけを残して一部削除 5 | html, 6 | body, article, section, nav, aside, 7 | h1, h2, h3, h4, h5, h6, 8 | header, footer, address, 9 | p, ol, ul, li, dl, dt, dd, div, 10 | a, strong, small, sup, sup, span, 11 | img, iframe, embed, object, video, audio, 12 | table, tr, td, th, 13 | canvas, 14 | svg { 15 | margin: 0; 16 | padding: 0; 17 | border: 0; 18 | font-size: 100%; 19 | font: inherit; 20 | vertical-align: baseline; 21 | } 22 | 23 | 24 | 25 | // -------------------- from normalize.css / sanitize.css 26 | // 必要なプロパティだけを残して一部削除 & 一部追加 27 | html { 28 | cursor: default; 29 | line-height: 1; // 行間はいったん1にします、案件ごとにデザインに合わせて調整してください 30 | 31 | // SPでの文字サイズの自動調整を無効 32 | // [参考] 33 | // http://ultimate-ez.com/2014/03/29/3572/ 34 | // http://xxmiz0rexx.tumblr.com/post/73393990520/webkit-text-size-adjust-100%E3%81%8C%E5%8A%B9%E3%81%8B%E3%81%AA%E3%81%8F%E3%81%A6%E7%84%A6%E3%81%A3%E3%81%9F 35 | -ms-text-size-adjust: 100%; 36 | -webkit-text-size-adjust: 100%; 37 | &.windows{ 38 | -webkit-font-smoothing: antialiased; 39 | } 40 | 41 | 42 | // ---------- 以下、よく使うのでココに入れておく 43 | 44 | // iOSのスクロールをルート要素以外もスムーズに 45 | -webkit-overflow-scrolling: touch; 46 | // iOSのタップした時のハイライトカラーを無効化 47 | -webkit-tap-highlight-color: transparent; 48 | 49 | // ゴシック/サンセリフ系の標準 50 | // 日本語環境向けのサイトは日本語名が先の方が良い説を採用 51 | font-family: arial; 52 | } 53 | 54 | 55 | 56 | // -------------------- from sanitize.css 57 | // 色変更 58 | 59 | // テキスト選択の色を統一 60 | // いったんモノトーンで入れてあります 61 | // 案件ごとにデザインにあった色を設定してください 62 | 63 | // 擬似要素のベンダープレフィックスは別々で書かないとダメみたいです 64 | // [参考] 65 | // https://developer.mozilla.org/ja/docs/Web/CSS/::selection 66 | //::-moz-selection, 67 | //::selection { 68 | // background-color: #ccc; 69 | // color: #000; 70 | // text-shadow: none; 71 | //} 72 | 73 | ::-moz-selection { 74 | background-color: #ccc; 75 | color: #000; 76 | text-shadow: none; 77 | } 78 | 79 | ::selection { 80 | background-color: #ccc; 81 | color: #000; 82 | text-shadow: none; 83 | } 84 | 85 | 86 | 87 | // -------------------- from reset.css 88 | // そのまま流用 89 | 90 | // デフォルトのマーカーは広告案件ではあまり使わないので消します 91 | // 必要であれば案件ごとにデザインされているものを再現してください 92 | ol, ul { 93 | list-style: none; 94 | } 95 | 96 | 97 | 98 | // -------------------- from reset.css / sanitize.css 99 | // そのまま流用 100 | 101 | table { 102 | border-collapse: collapse; 103 | border-spacing: 0; 104 | } 105 | 106 | 107 | 108 | // -------------------- from reset.css / normalize.css / sanitize.css 109 | // 必要な要素だけを残して一部削除 110 | article, section, nav, aside, 111 | header, footer { 112 | display: block; 113 | } 114 | 115 | 116 | 117 | // -------------------- from normalize.css / sanitize.css 118 | // 必要な要素だけを残して一部削除 119 | video, audio, 120 | canvas { 121 | display: inline-block; 122 | } 123 | 124 | 125 | 126 | // -------------------- from normalize.css / sanitize.css 127 | // そのまま流用 128 | 129 | // コントローラが無い場合は非表示に 130 | audio:not([controls]) { 131 | display: none; 132 | height: 0; 133 | } 134 | 135 | 136 | 137 | // -------------------- from normalize.css / sanitize.css 138 | // normalize.css の方を流用 139 | 140 | // 各種ブラウザの見た目を統一しているっぽいですが、詳細は不明 141 | hr { 142 | box-sizing: content-box; 143 | height: 0; 144 | overflow: visible; 145 | } 146 | 147 | 148 | 149 | // -------------------- from normalize.css / sanitize.css 150 | // 必要な要素だけを残して一部削除 151 | 152 | // 各種ブラウザの見た目を統一しているっぽいですが、詳細は不明 153 | strong { 154 | font-weight: inherit; 155 | } 156 | strong { 157 | font-weight: bolder; 158 | } 159 | 160 | 161 | 162 | // -------------------- from normalize.css / sanitize.css 163 | // 必要なプロパティだけを残して一部削除 164 | 165 | // 上付き/下付き文字の表示を統一 166 | // 注釈用の*とかでたまに使うので採用 167 | // フォントサイズは案件ごとにデザインに合わせて調整してください 168 | sub, 169 | sup { 170 | line-height: 0; 171 | position: relative; 172 | vertical-align: baseline; 173 | } 174 | sub { 175 | bottom: -.25em; 176 | } 177 | sup { 178 | top: -.5em; 179 | } 180 | 181 | 182 | 183 | // -------------------- from sanitize.css 184 | // そのまま流用 185 | 186 | // SVGにCSSで色を指定できるようにする 187 | // [参考] 188 | // http://www.02320.net/how-to-fill-svg-by-css/ 189 | svg { 190 | fill: currentColor; 191 | } 192 | 193 | 194 | 195 | // -------------------- from normalize.css / sanitize.css 196 | // そのまま流用 197 | 198 | // IEでoverflow:hiddenにならないのを調整 199 | svg:not(:root) { 200 | overflow: hidden; 201 | } 202 | 203 | 204 | 205 | // -------------------- from normalize.css / sanitize.css 206 | // そのまま流用 207 | a { 208 | // IE10でアクティブなリンクの背景がグレーになるのを防ぐ 209 | background-color: transparent; 210 | 211 | // iOS/Mac Safariで text-decoration: underline にすると線が切れるのを防ぐ 212 | // [参考] 213 | // https://css-tricks.com/almanac/properties/t/text-decoration-skip/ 214 | -webkit-text-decoration-skip: objects; 215 | } 216 | 217 | 218 | 219 | // -------------------- from normalize.css / sanitize.css 220 | // normalize.css の方を流用 221 | 222 | // リンクの点線を削除? 223 | a:active, 224 | a:hover { 225 | outline-width: 0; 226 | } 227 | 228 | 229 | 230 | // -------------------- from normalize.css / sanitize.css 231 | // 必要な要素だけを残して一部削除 232 | 233 | // IEのタップの反応速度を早める 234 | // [参考] 235 | // http://lealog.hateblo.jp/entry/2015/02/19/124748 236 | a { 237 | -ms-touch-action: manipulation; 238 | touch-action: manipulation; 239 | } -------------------------------------------------------------------------------- /src/assets/glsl/cube.frag: -------------------------------------------------------------------------------- 1 | uniform bool isEdge; 2 | uniform vec3 uEdgeColor; 3 | uniform bool isShading; 4 | 5 | varying vec3 vPosition; 6 | varying vec3 vColor; 7 | varying vec3 vNormal; 8 | 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 | 25 | const float PI = 3.14159265358979323846264; 26 | 27 | // hemisphere ground color 28 | const vec3 hemiLight_g = vec3(256.0, 246.0, 191.0) / vec3(256.0); 29 | 30 | // hemisphere sky color 31 | const vec3 hemiLight_s_1 = vec3(0.9,0.8,0.6); 32 | const vec3 hemiLight_s_2 = vec3(0.9,0.6,0.7); 33 | 34 | // directional light color 35 | const vec3 dirLight_1 = vec3(0.02, 0.02, 0.0); 36 | const vec3 dirLight_2 = vec3(0.0, 0.1, 0.1); 37 | 38 | const vec3 dirLightPos_1 = vec3(4, 6, 10); 39 | const vec3 dirLightPos_2 = vec3(-5, -5, -5); 40 | 41 | 42 | const vec3 hemiLightPos_1 = vec3(-100.0, -100.0, 100.0); 43 | const vec3 hemiLightPos_2 = vec3(-100.0, 100.0, -100.0); 44 | 45 | void main() { 46 | // vec3 fdx = dFdx( vPosition ); 47 | // vec3 fdy = dFdy( vPosition ); 48 | // vec3 norm = normalize(cross(fdx, fdy)); 49 | 50 | vec3 hemiColor = vec3(0.0); 51 | hemiColor += calcIrradiance_hemi(vNormal, hemiLightPos_1, hemiLight_g, hemiLight_s_1) * 0.56; 52 | hemiColor += calcIrradiance_hemi(vNormal, hemiLightPos_2, hemiLight_g, hemiLight_s_2) * 0.56; 53 | 54 | vec3 dirColor = vec3(0.0); 55 | dirColor += calcIrradiance_dir(vNormal, dirLightPos_1, dirLight_1, 0.3, 0.7); 56 | dirColor += calcIrradiance_dir(vNormal, dirLightPos_2, dirLight_2, 0.5, 0.6); 57 | 58 | 59 | vec3 color = vColor * min(vec3(1.0), hemiColor * 1.06); 60 | color += dirColor; 61 | 62 | // color = min(vColor, color); 63 | 64 | color = (isEdge) ? uEdgeColor : (isShading) ? color : vColor; 65 | 66 | gl_FragColor = vec4(color, 1.0); 67 | } -------------------------------------------------------------------------------- /src/assets/glsl/cube.vert: -------------------------------------------------------------------------------- 1 | attribute float aNum; 2 | attribute vec3 vertNormal; 3 | // attribute float aColorNum; 4 | 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 | 31 | mat3 calcLookAtMatrix(vec3 vector, float roll) { 32 | vec3 rr = vec3(sin(roll), cos(roll), 0.0); 33 | vec3 ww = normalize(vector); 34 | vec3 uu = normalize(cross(ww, rr)); 35 | vec3 vv = normalize(cross(uu, ww)); 36 | 37 | return mat3(uu, ww, vv); 38 | } 39 | 40 | void main() { 41 | float time = uTick * 0.3; 42 | 43 | vec2 posUv; 44 | posUv.x = mod(aNum + 0.5, uSize); 45 | posUv.y = float((aNum + 0.5) / uSize); 46 | posUv /= vec2(uSize); 47 | 48 | vec4 cubePosition = texture2D( posMap, posUv ); 49 | vec4 cubeVelocity = texture2D( velMap, posUv ); 50 | float alpha = cubeVelocity.a / 100.0; 51 | float scale = 0.025 * parabola( alpha); 52 | 53 | vec3 offsetEdgePos = (isEdge) ? vertNormal * uEdgeScale : vec3(0.0); 54 | 55 | vec3 pos = position + offsetEdgePos; 56 | pos.zx = calcRotate2D(time * 0.7) * pos.zx; 57 | 58 | mat4 localRotationMat = mat4( calcLookAtMatrix( cubeVelocity.xyz, 0.0 ) ); 59 | 60 | vec3 modifiedVertex = (localRotationMat * vec4( pos * scale * (vec3(1.0)) * uScale2 * uScale1, 1.0 ) ).xyz; 61 | vec3 modifiedPosition = modifiedVertex + cubePosition.xyz; 62 | 63 | gl_Position = projectionMatrix * modelViewMatrix * vec4( modifiedPosition, 1.0 ); 64 | vPosition = modifiedPosition; 65 | 66 | vNormal = normal; 67 | vNormal.zx = calcRotate2D(time * 0.7) * vNormal.zx; 68 | vNormal = (localRotationMat * vec4(vNormal, 1.0)).xyz; 69 | 70 | vColor = uColorArray[int(mod(aNum, 4.0))]; 71 | } -------------------------------------------------------------------------------- /src/assets/glsl/cube_edge.frag: -------------------------------------------------------------------------------- 1 | void main() { 2 | gl_FragColor = vec4(1.0); 3 | } -------------------------------------------------------------------------------- /src/assets/glsl/cube_edgeFace.frag: -------------------------------------------------------------------------------- 1 | void main() { 2 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 3 | } -------------------------------------------------------------------------------- /src/assets/glsl/noise2D.glsl: -------------------------------------------------------------------------------- 1 | #pragma glslify: export(snoise2D) 2 | 3 | // 4 | // Description : Array and textureless GLSL 2D simplex noise function. 5 | // Author : Ian McEwan, Ashima Arts. 6 | // Maintainer : stegu 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 | // https://github.com/stegu/webgl-noise 12 | // 13 | 14 | vec3 mod289(vec3 x) { 15 | return x - floor(x * (1.0 / 289.0)) * 289.0; 16 | } 17 | 18 | vec2 mod289(vec2 x) { 19 | return x - floor(x * (1.0 / 289.0)) * 289.0; 20 | } 21 | 22 | vec3 permute(vec3 x) { 23 | return mod289(((x*34.0)+1.0)*x); 24 | } 25 | 26 | float snoise(vec2 v) 27 | { 28 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 29 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) 30 | -0.577350269189626, // -1.0 + 2.0 * C.x 31 | 0.024390243902439); // 1.0 / 41.0 32 | // First corner 33 | vec2 i = floor(v + dot(v, C.yy) ); 34 | vec2 x0 = v - i + dot(i, C.xx); 35 | 36 | // Other corners 37 | vec2 i1; 38 | //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 39 | //i1.y = 1.0 - i1.x; 40 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); 41 | // x0 = x0 - 0.0 + 0.0 * C.xx ; 42 | // x1 = x0 - i1 + 1.0 * C.xx ; 43 | // x2 = x0 - 1.0 + 2.0 * C.xx ; 44 | vec4 x12 = x0.xyxy + C.xxzz; 45 | x12.xy -= i1; 46 | 47 | // Permutations 48 | i = mod289(i); // Avoid truncation effects in permutation 49 | vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) 50 | + i.x + vec3(0.0, i1.x, 1.0 )); 51 | 52 | vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0); 53 | m = m*m ; 54 | m = m*m ; 55 | 56 | // Gradients: 41 points uniformly over a line, mapped onto a diamond. 57 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) 58 | 59 | vec3 x = 2.0 * fract(p * C.www) - 1.0; 60 | vec3 h = abs(x) - 0.5; 61 | vec3 ox = floor(x + 0.5); 62 | vec3 a0 = x - ox; 63 | 64 | // Normalise gradients implicitly by scaling m 65 | // Approximation of: m *= inversesqrt( a0*a0 + h*h ); 66 | m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); 67 | 68 | // Compute final noise value at P 69 | vec3 g; 70 | g.x = a0.x * x0.x + h.x * x0.y; 71 | g.yz = a0.yz * x12.xz + h.yz * x12.yw; 72 | return 130.0 * dot(m, g); 73 | } -------------------------------------------------------------------------------- /src/assets/glsl/noise3D.glsl: -------------------------------------------------------------------------------- 1 | #pragma glslify: export(snoise3D) 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 = step(x0.yzx, x0.xyz); 43 | vec3 l = 1.0 - g; 44 | vec3 i1 = min( g.xyz, l.zxy ); 45 | vec3 i2 = max( g.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 | } -------------------------------------------------------------------------------- /src/assets/glsl/noise4D.glsl: -------------------------------------------------------------------------------- 1 | #pragma glslify: export(snoise4D) 2 | 3 | 4 | // 5 | // Description : Array and textureless GLSL 2D/3D/4D simplex 6 | // noise functions. 7 | // Author : Ian McEwan, Ashima Arts. 8 | // Maintainer : stegu 9 | // Lastmod : 20110822 (ijm) 10 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 11 | // Distributed under the MIT License. See LICENSE file. 12 | // https://github.com/ashima/webgl-noise 13 | // https://github.com/stegu/webgl-noise 14 | // 15 | 16 | vec4 mod289(vec4 x) { 17 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 18 | 19 | float mod289(float x) { 20 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 21 | 22 | vec4 permute(vec4 x) { 23 | return mod289(((x*34.0)+1.0)*x); 24 | } 25 | 26 | float permute(float x) { 27 | return mod289(((x*34.0)+1.0)*x); 28 | } 29 | 30 | vec4 taylorInvSqrt(vec4 r) 31 | { 32 | return 1.79284291400159 - 0.85373472095314 * r; 33 | } 34 | 35 | float taylorInvSqrt(float r) 36 | { 37 | return 1.79284291400159 - 0.85373472095314 * r; 38 | } 39 | 40 | vec4 grad4(float j, vec4 ip) 41 | { 42 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); 43 | vec4 p,s; 44 | 45 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 46 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 47 | s = vec4(lessThan(p, vec4(0.0))); 48 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 49 | 50 | return p; 51 | } 52 | 53 | // (sqrt(5) - 1)/4 = F4, used once below 54 | #define F4 0.309016994374947451 55 | 56 | float snoise(vec4 v) 57 | { 58 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 59 | 0.276393202250021, // 2 * G4 60 | 0.414589803375032, // 3 * G4 61 | -0.447213595499958); // -1 + 4 * G4 62 | 63 | // First corner 64 | vec4 i = floor(v + dot(v, vec4(F4)) ); 65 | vec4 x0 = v - i + dot(i, C.xxxx); 66 | 67 | // Other corners 68 | 69 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 70 | vec4 i0; 71 | vec3 isX = step( x0.yzw, x0.xxx ); 72 | vec3 isYZ = step( x0.zww, x0.yyz ); 73 | // i0.x = dot( isX, vec3( 1.0 ) ); 74 | i0.x = isX.x + isX.y + isX.z; 75 | i0.yzw = 1.0 - isX; 76 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 77 | i0.y += isYZ.x + isYZ.y; 78 | i0.zw += 1.0 - isYZ.xy; 79 | i0.z += isYZ.z; 80 | i0.w += 1.0 - isYZ.z; 81 | 82 | // i0 now contains the unique values 0,1,2,3 in each channel 83 | vec4 i3 = clamp( i0, 0.0, 1.0 ); 84 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); 85 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); 86 | 87 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 88 | // x1 = x0 - i1 + 1.0 * C.xxxx 89 | // x2 = x0 - i2 + 2.0 * C.xxxx 90 | // x3 = x0 - i3 + 3.0 * C.xxxx 91 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 92 | vec4 x1 = x0 - i1 + C.xxxx; 93 | vec4 x2 = x0 - i2 + C.yyyy; 94 | vec4 x3 = x0 - i3 + C.zzzz; 95 | vec4 x4 = x0 + C.wwww; 96 | 97 | // Permutations 98 | i = mod289(i); 99 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); 100 | vec4 j1 = permute( permute( permute( permute ( 101 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 102 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 103 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 104 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 105 | 106 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 107 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 108 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 109 | 110 | vec4 p0 = grad4(j0, ip); 111 | vec4 p1 = grad4(j1.x, ip); 112 | vec4 p2 = grad4(j1.y, ip); 113 | vec4 p3 = grad4(j1.z, ip); 114 | vec4 p4 = grad4(j1.w, ip); 115 | 116 | // Normalise gradients 117 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 118 | p0 *= norm.x; 119 | p1 *= norm.y; 120 | p2 *= norm.z; 121 | p3 *= norm.w; 122 | p4 *= taylorInvSqrt(dot(p4,p4)); 123 | 124 | // Mix contributions from the five corners 125 | vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 126 | vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 127 | m0 = m0 * m0; 128 | m1 = m1 * m1; 129 | return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 130 | + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/assets/glsl/output.frag: -------------------------------------------------------------------------------- 1 | #pragma glslify: snoise = require("./noise3D"); 2 | 3 | // varying vec3 vPos; 4 | varying vec2 vUv; 5 | 6 | 7 | 8 | uniform float uTick; 9 | uniform vec2 uSize; 10 | // uniform vec3 uEdgeColor; 11 | uniform vec3 uBgColor; 12 | 13 | 14 | 15 | uniform sampler2D uTex_1; 16 | 17 | 18 | vec3 calcContrast(vec3 _value){ 19 | return _value * 1.1 - 0.1; 20 | } 21 | 22 | float rand(vec2 co){ 23 | float a = fract(dot(co, vec2(2.067390879775102, 12.451168662908249))) - 0.5; 24 | float s = a * (6.182785114200511 + a * a * (-38.026512460676566 + a * a * 53.392573080032137)); 25 | float t = fract(s * 43758.5453); 26 | 27 | return t; 28 | } 29 | 30 | 31 | // color 32 | vec3 normalColor(vec4 c1, vec4 c2){ 33 | return mix(c1.rgb + c2.rgb, c1.rgb, c1.a); 34 | } 35 | 36 | vec3 addColor(vec4 c1, vec4 c2){ 37 | return c2.rgb * c2.a + c1.rgb; 38 | } 39 | 40 | vec3 multiplyColor(vec4 c1, vec4 c2){ 41 | return c2.rgb * c1.rgb; 42 | } 43 | 44 | 45 | vec3 screenColor(vec4 c1, vec4 c2){ 46 | return c2.rgb * (vec3(1.0) - c1.rgb) + c1.rgb; 47 | } 48 | 49 | vec3 overlayColor(vec4 c1, vec4 c2){ 50 | float r = (c1.r < 0.5) ? 2.0 * c1.r * c2.r : 1.0 - 2.0 * (1.0 - c1.r) * (1.0 - c2.r); 51 | float g = (c1.g < 0.5) ? 2.0 * c1.g * c2.g : 1.0 - 2.0 * (1.0 - c1.g) * (1.0 - c2.g); 52 | float b = (c1.b < 0.5) ? 2.0 * c1.b * c2.b : 1.0 - 2.0 * (1.0 - c1.b) * (1.0 - c2.b); 53 | 54 | return vec3(r, g, b); 55 | } 56 | 57 | vec3 rgb2hsv(vec3 c){ 58 | vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); 59 | vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); 60 | vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); 61 | 62 | float d = q.x - min(q.w, q.y); 63 | float e = 1.0e-10; 64 | return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); 65 | } 66 | 67 | 68 | vec3 hsv2rgb(vec3 c){ 69 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 70 | vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 71 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 72 | } 73 | 74 | vec2 noiseUv(vec2 uv){ 75 | float uvNoise = snoise(vec3(uv.x * 200.0, uv.y * 200.0, 0.0)); 76 | float noiseRadius = 1.0; 77 | 78 | float _u = uv.x * uSize.x + rand(uv) * noiseRadius * (1.0 + uvNoise) * 2.0 - noiseRadius; 79 | float _v = uv.y * uSize.y + rand(vec2(uv.y, uv.x)) * noiseRadius * (1.0 + uvNoise) * 2.0 - noiseRadius; 80 | return vec2(_u, _v) / uSize * (1.0 + abs(uvNoise) * 0.001); 81 | } 82 | 83 | 84 | 85 | 86 | void main(){ 87 | 88 | float time = uTick * 0.0001; 89 | 90 | vec4 bgColor = vec4(uBgColor, 1.0); 91 | 92 | vec2 st = vUv * 2.0 - 0.5; 93 | float faceUvOffset = snoise(vec3(st * 6.0, time)); 94 | 95 | vec2 faceColorUv = vUv + vec2(faceUvOffset)* 0.003; 96 | 97 | 98 | vec4 faceColor = texture2D(uTex_1, vUv); 99 | 100 | 101 | vec4 color; 102 | // color = faceColor; 103 | // color.a = edgeColor.a; 104 | // color.rgb = normalColor(color, edgeColor); 105 | // color.rgb = normalColor(color, bgColor); 106 | // color.a = 1.0; 107 | 108 | color = faceColor; 109 | color.rgb = normalColor(color, bgColor); 110 | color.a = 1.0; 111 | 112 | // color.rgb = max(vec3(0.0), min(vec3(1.0), color.rgb)); 113 | 114 | // vec3 hsv = rgb2hsv(color.rgb); 115 | // hsv.r -= 0.62; 116 | // hsv.g -= 0.7; 117 | // hsv.b -= 0.3; 118 | // color.rgb = hsv2rgb(hsv); 119 | 120 | 121 | gl_FragColor = vec4(color); 122 | } -------------------------------------------------------------------------------- /src/assets/glsl/output.vert: -------------------------------------------------------------------------------- 1 | // #pragma glslify: snoise = require("./noise2D") 2 | 3 | // varying vec3 vPos; 4 | varying vec2 vUv; 5 | 6 | // uniform float uTick; 7 | 8 | const float PI = 3.1415926; 9 | 10 | mat2 calcRotate2D(float _time){ 11 | float _sin = sin(_time); 12 | float _cos = cos(_time); 13 | return mat2(_cos, _sin, -_sin, _cos); 14 | } 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 | } -------------------------------------------------------------------------------- /src/assets/glsl/simulation_def.frag: -------------------------------------------------------------------------------- 1 | void main() { 2 | vec2 uv = gl_FragCoord.xy / resolution.xy; 3 | vec4 tmpPos = texture2D( defTex, uv ); 4 | gl_FragColor = vec4( tmpPos.rgb, 0.0 ); 5 | } -------------------------------------------------------------------------------- /src/assets/glsl/simulation_pos.frag: -------------------------------------------------------------------------------- 1 | void main() { 2 | vec2 uv = gl_FragCoord.xy / resolution.xy; 3 | vec4 tmpPos = texture2D( posTex, uv ); 4 | vec3 pos = tmpPos.xyz; 5 | vec4 tmpVel = texture2D( velTex, uv ); 6 | 7 | vec3 vel = tmpVel.xyz; 8 | 9 | pos += vel; 10 | gl_FragColor = vec4( pos, tmpPos.a ); 11 | } -------------------------------------------------------------------------------- /src/assets/glsl/simulation_vel.frag: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Array and textureless GLSL 2D/3D/4D simplex 3 | // noise functions. 4 | // Author : Ian McEwan, Ashima Arts. 5 | // Maintainer : ijm 6 | // Lastmod : 20110822 (ijm) 7 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 8 | // Distributed under the MIT License. See LICENSE file. 9 | // https://github.com/ashima/webgl-noise 10 | // 11 | 12 | vec3 mod289(vec3 x) { 13 | return x - floor(x * (1.0 / 289.0)) * 289.0; 14 | } 15 | 16 | vec4 mod289(vec4 x) { 17 | return x - floor(x * (1.0 / 289.0)) * 289.0; 18 | } 19 | 20 | vec4 permute(vec4 x) { 21 | return mod289(((x*34.0)+1.0)*x); 22 | } 23 | 24 | vec4 taylorInvSqrt(vec4 r){ 25 | return 1.79284291400159 - 0.85373472095314 * r; 26 | } 27 | 28 | float snoise(vec3 v) { 29 | 30 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; 31 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); 32 | 33 | // First corner 34 | vec3 i = floor(v + dot(v, C.yyy) ); 35 | vec3 x0 = v - i + dot(i, C.xxx) ; 36 | 37 | // Other corners 38 | vec3 g = step(x0.yzx, x0.xyz); 39 | vec3 l = 1.0 - g; 40 | vec3 i1 = min( g.xyz, l.zxy ); 41 | vec3 i2 = max( g.xyz, l.zxy ); 42 | 43 | // x0 = x0 - 0.0 + 0.0 * C.xxx; 44 | // x1 = x0 - i1 + 1.0 * C.xxx; 45 | // x2 = x0 - i2 + 2.0 * C.xxx; 46 | // x3 = x0 - 1.0 + 3.0 * C.xxx; 47 | vec3 x1 = x0 - i1 + C.xxx; 48 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y 49 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y 50 | 51 | // Permutations 52 | i = mod289(i); 53 | vec4 p = permute( permute( permute( 54 | i.z + vec4(0.0, i1.z, i2.z, 1.0 )) 55 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 56 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); 57 | 58 | // Gradients: 7x7 points over a square, mapped onto an octahedron. 59 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) 60 | float n_ = 0.142857142857; // 1.0/7.0 61 | vec3 ns = n_ * D.wyz - D.xzx; 62 | 63 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) 64 | 65 | vec4 x_ = floor(j * ns.z); 66 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) 67 | 68 | vec4 x = x_ *ns.x + ns.yyyy; 69 | vec4 y = y_ *ns.x + ns.yyyy; 70 | vec4 h = 1.0 - abs(x) - abs(y); 71 | 72 | vec4 b0 = vec4( x.xy, y.xy ); 73 | vec4 b1 = vec4( x.zw, y.zw ); 74 | 75 | //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; 76 | //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; 77 | vec4 s0 = floor(b0)*2.0 + 1.0; 78 | vec4 s1 = floor(b1)*2.0 + 1.0; 79 | vec4 sh = -step(h, vec4(0.0)); 80 | 81 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; 82 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; 83 | 84 | vec3 p0 = vec3(a0.xy,h.x); 85 | vec3 p1 = vec3(a0.zw,h.y); 86 | vec3 p2 = vec3(a1.xy,h.z); 87 | vec3 p3 = vec3(a1.zw,h.w); 88 | 89 | //Normalise gradients 90 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 91 | p0 *= norm.x; 92 | p1 *= norm.y; 93 | p2 *= norm.z; 94 | p3 *= norm.w; 95 | 96 | // Mix final noise value 97 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); 98 | m = m * m; 99 | return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3) ) ); 100 | 101 | } 102 | 103 | vec3 snoiseVec3( vec3 x ){ 104 | 105 | float s = snoise(vec3( x )); 106 | float s1 = snoise(vec3( x.y - 19.1 , x.z + 33.4 , x.x + 47.2 )); 107 | float s2 = snoise(vec3( x.z + 74.2 , x.x - 124.5 , x.y + 99.4 )); 108 | vec3 c = vec3( s , s1 , s2 ); 109 | return c; 110 | 111 | } 112 | 113 | vec3 curlNoise( vec3 p ){ 114 | 115 | const float e = .1; 116 | // vec3 dx = vec3( e , 0.0 , 0.0 ); 117 | // vec3 dy = vec3( 0.0 , e , 0.0 ); 118 | // vec3 dz = vec3( 0.0 , 0.0 , e ); 119 | 120 | // vec3 p_x0 = snoiseVec3( p - dx ); 121 | // vec3 p_x1 = snoiseVec3( p + dx ); 122 | // vec3 p_y0 = snoiseVec3( p - dy ); 123 | // vec3 p_y1 = snoiseVec3( p + dy ); 124 | // vec3 p_z0 = snoiseVec3( p - dz ); 125 | // vec3 p_z1 = snoiseVec3( p + dz ); 126 | 127 | // float x = p_y1.z - p_y0.z - p_z1.y + p_z0.y; 128 | // float y = p_z1.x - p_z0.x - p_x1.z + p_x0.z; 129 | // float z = p_x1.y - p_x0.y - p_y1.x + p_y0.x; 130 | 131 | float n1 = snoise(vec3(p.x, p.y + e, p.z)); 132 | float n2 = snoise(vec3(p.x, p.y - e, p.z)); 133 | float n3 = snoise(vec3(p.x, p.y, p.z + e)); 134 | float n4 = snoise(vec3(p.x, p.y, p.z - e)); 135 | float n5 = snoise(vec3(p.x + e, p.y, p.z)); 136 | float n6 = snoise(vec3(p.x - e, p.y, p.z)); 137 | 138 | float x = n2 - n1 - n4 + n3; 139 | float y = n4 - n3 - n6 + n5; 140 | float z = n6 - n5 - n2 + n1; 141 | 142 | 143 | const float divisor = 1.0 / ( 2.0 * e ); 144 | return normalize( vec3( x , y , z ) * divisor ); 145 | } 146 | 147 | 148 | // mat4 rotationMatrix(vec3 axis, float angle) { 149 | 150 | // axis = normalize(axis); 151 | // float s = sin(angle); 152 | // float c = cos(angle); 153 | // float oc = 1.0 - c; 154 | 155 | // 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, 156 | // oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, 157 | // oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, 158 | // 0.0, 0.0, 0.0, 1.0); 159 | // } 160 | 161 | // float rand(vec2 co){ 162 | // return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 163 | // } 164 | 165 | 166 | uniform float timer; 167 | uniform float delta; 168 | uniform float speed; 169 | // uniform float genScale; 170 | uniform float factor; 171 | uniform float evolution; 172 | uniform float radius; 173 | 174 | 175 | void main() { 176 | vec2 uv = gl_FragCoord.xy / resolution.xy; 177 | vec4 c = texture2D( posTex, uv ); 178 | vec4 oldVel = texture2D( velTex, uv ); 179 | 180 | vec3 pos = c.xyz; 181 | float life = oldVel.a; 182 | 183 | float s = life / 100.0; 184 | float speedInc = 1.0; 185 | 186 | vec3 v = factor * speedInc * delta * speed * ( curlNoise( .2 * pos) ) * (c.a + 1.0); 187 | 188 | pos += v; 189 | life -= 0.2; 190 | 191 | if( life <= 0.0) { 192 | 193 | pos = texture2D( defTex, uv ).xyz; 194 | life = 100.0; 195 | 196 | } 197 | 198 | 199 | gl_FragColor = vec4( pos - c.xyz, life ); 200 | } -------------------------------------------------------------------------------- /src/assets/js/lib-head/modernizr-custom.js: -------------------------------------------------------------------------------- 1 | /*! modernizr 3.3.1 (Custom Build) | MIT * 2 | * https://modernizr.com/download/?-webgl-setclasses !*/ 3 | !function(e,n,t){function o(e,n){return typeof e===n}function s(){var e,n,t,s,a,i,f;for(var c in l)if(l.hasOwnProperty(c)){if(e=[],n=l[c],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(t=0;t0?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}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,t,s,l=a._classPrefix="",p=o.documentElement,c=p.className;for(t=a._detects.length,d=0;d EPS 189 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8 190 | 191 | if ( zoomChanged || 192 | lastPosition.distanceToSquared( scope.object.position ) > EPS || 193 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) { 194 | 195 | scope.dispatchEvent( changeEvent ); 196 | 197 | lastPosition.copy( scope.object.position ); 198 | lastQuaternion.copy( scope.object.quaternion ); 199 | zoomChanged = false; 200 | 201 | return true; 202 | 203 | } 204 | 205 | return false; 206 | 207 | }; 208 | 209 | }(); 210 | 211 | this.dispose = function () { 212 | 213 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); 214 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); 215 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false ); 216 | 217 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false ); 218 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false ); 219 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false ); 220 | 221 | document.removeEventListener( 'mousemove', onMouseMove, false ); 222 | document.removeEventListener( 'mouseup', onMouseUp, false ); 223 | 224 | window.removeEventListener( 'keydown', onKeyDown, false ); 225 | 226 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? 227 | 228 | }; 229 | 230 | // 231 | // internals 232 | // 233 | 234 | var scope = this; 235 | 236 | var changeEvent = { type: 'change' }; 237 | var startEvent = { type: 'start' }; 238 | var endEvent = { type: 'end' }; 239 | 240 | var STATE = { NONE: - 1, ROTATE: 0, DOLLY: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_DOLLY: 4, TOUCH_PAN: 5 }; 241 | 242 | var state = STATE.NONE; 243 | 244 | var EPS = 0.000001; 245 | 246 | // current position in spherical coordinates 247 | var spherical = new THREE.Spherical(); 248 | var sphericalDelta = new THREE.Spherical(); 249 | 250 | var scale = 1; 251 | var panOffset = new THREE.Vector3(); 252 | var zoomChanged = false; 253 | 254 | var rotateStart = new THREE.Vector2(); 255 | var rotateEnd = new THREE.Vector2(); 256 | var rotateDelta = new THREE.Vector2(); 257 | 258 | var panStart = new THREE.Vector2(); 259 | var panEnd = new THREE.Vector2(); 260 | var panDelta = new THREE.Vector2(); 261 | 262 | var dollyStart = new THREE.Vector2(); 263 | var dollyEnd = new THREE.Vector2(); 264 | var dollyDelta = new THREE.Vector2(); 265 | 266 | function getAutoRotationAngle() { 267 | 268 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 269 | 270 | } 271 | 272 | function getZoomScale() { 273 | 274 | return Math.pow( 0.95, scope.zoomSpeed ); 275 | 276 | } 277 | 278 | function rotateLeft( angle ) { 279 | 280 | sphericalDelta.theta -= angle; 281 | 282 | } 283 | 284 | function rotateUp( angle ) { 285 | 286 | sphericalDelta.phi -= angle; 287 | 288 | } 289 | 290 | var panLeft = function () { 291 | 292 | var v = new THREE.Vector3(); 293 | 294 | return function panLeft( distance, objectMatrix ) { 295 | 296 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix 297 | v.multiplyScalar( - distance ); 298 | 299 | panOffset.add( v ); 300 | 301 | }; 302 | 303 | }(); 304 | 305 | var panUp = function () { 306 | 307 | var v = new THREE.Vector3(); 308 | 309 | return function panUp( distance, objectMatrix ) { 310 | 311 | v.setFromMatrixColumn( objectMatrix, 1 ); // get Y column of objectMatrix 312 | v.multiplyScalar( distance ); 313 | 314 | panOffset.add( v ); 315 | 316 | }; 317 | 318 | }(); 319 | 320 | // deltaX and deltaY are in pixels; right and down are positive 321 | var pan = function () { 322 | 323 | var offset = new THREE.Vector3(); 324 | 325 | return function pan( deltaX, deltaY ) { 326 | 327 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 328 | 329 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 330 | 331 | // perspective 332 | var position = scope.object.position; 333 | offset.copy( position ).sub( scope.target ); 334 | var targetDistance = offset.length(); 335 | 336 | // half of the fov is center to top of screen 337 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); 338 | 339 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 340 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); 341 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); 342 | 343 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 344 | 345 | // orthographic 346 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); 347 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); 348 | 349 | } else { 350 | 351 | // camera neither orthographic nor perspective 352 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 353 | scope.enablePan = false; 354 | 355 | } 356 | 357 | }; 358 | 359 | }(); 360 | 361 | function dollyIn( dollyScale ) { 362 | 363 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 364 | 365 | scale /= dollyScale; 366 | 367 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 368 | 369 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) ); 370 | scope.object.updateProjectionMatrix(); 371 | zoomChanged = true; 372 | 373 | } else { 374 | 375 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 376 | scope.enableZoom = false; 377 | 378 | } 379 | 380 | } 381 | 382 | function dollyOut( dollyScale ) { 383 | 384 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 385 | 386 | scale *= dollyScale; 387 | 388 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 389 | 390 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) ); 391 | scope.object.updateProjectionMatrix(); 392 | zoomChanged = true; 393 | 394 | } else { 395 | 396 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 397 | scope.enableZoom = false; 398 | 399 | } 400 | 401 | } 402 | 403 | // 404 | // event callbacks - update the object state 405 | // 406 | 407 | function handleMouseDownRotate( event ) { 408 | 409 | //console.log( 'handleMouseDownRotate' ); 410 | 411 | rotateStart.set( event.clientX, event.clientY ); 412 | 413 | } 414 | 415 | function handleMouseDownDolly( event ) { 416 | 417 | //console.log( 'handleMouseDownDolly' ); 418 | 419 | dollyStart.set( event.clientX, event.clientY ); 420 | 421 | } 422 | 423 | function handleMouseDownPan( event ) { 424 | 425 | //console.log( 'handleMouseDownPan' ); 426 | 427 | panStart.set( event.clientX, event.clientY ); 428 | 429 | } 430 | 431 | function handleMouseMoveRotate( event ) { 432 | 433 | //console.log( 'handleMouseMoveRotate' ); 434 | 435 | rotateEnd.set( event.clientX, event.clientY ); 436 | rotateDelta.subVectors( rotateEnd, rotateStart ); 437 | 438 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 439 | 440 | // rotating across whole screen goes 360 degrees around 441 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 442 | 443 | // rotating up and down along whole screen attempts to go 360, but limited to 180 444 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 445 | 446 | rotateStart.copy( rotateEnd ); 447 | 448 | scope.update(); 449 | 450 | } 451 | 452 | function handleMouseMoveDolly( event ) { 453 | 454 | //console.log( 'handleMouseMoveDolly' ); 455 | 456 | dollyEnd.set( event.clientX, event.clientY ); 457 | 458 | dollyDelta.subVectors( dollyEnd, dollyStart ); 459 | 460 | if ( dollyDelta.y > 0 ) { 461 | 462 | dollyIn( getZoomScale() ); 463 | 464 | } else if ( dollyDelta.y < 0 ) { 465 | 466 | dollyOut( getZoomScale() ); 467 | 468 | } 469 | 470 | dollyStart.copy( dollyEnd ); 471 | 472 | scope.update(); 473 | 474 | } 475 | 476 | function handleMouseMovePan( event ) { 477 | 478 | //console.log( 'handleMouseMovePan' ); 479 | 480 | panEnd.set( event.clientX, event.clientY ); 481 | 482 | panDelta.subVectors( panEnd, panStart ); 483 | 484 | pan( panDelta.x, panDelta.y ); 485 | 486 | panStart.copy( panEnd ); 487 | 488 | scope.update(); 489 | 490 | } 491 | 492 | function handleMouseUp( event ) { 493 | 494 | // console.log( 'handleMouseUp' ); 495 | 496 | } 497 | 498 | function handleMouseWheel( event ) { 499 | 500 | // console.log( 'handleMouseWheel' ); 501 | 502 | if ( event.deltaY < 0 ) { 503 | 504 | dollyOut( getZoomScale() ); 505 | 506 | } else if ( event.deltaY > 0 ) { 507 | 508 | dollyIn( getZoomScale() ); 509 | 510 | } 511 | 512 | scope.update(); 513 | 514 | } 515 | 516 | function handleKeyDown( event ) { 517 | 518 | //console.log( 'handleKeyDown' ); 519 | 520 | switch ( event.keyCode ) { 521 | 522 | case scope.keys.UP: 523 | pan( 0, scope.keyPanSpeed ); 524 | scope.update(); 525 | break; 526 | 527 | case scope.keys.BOTTOM: 528 | pan( 0, - scope.keyPanSpeed ); 529 | scope.update(); 530 | break; 531 | 532 | case scope.keys.LEFT: 533 | pan( scope.keyPanSpeed, 0 ); 534 | scope.update(); 535 | break; 536 | 537 | case scope.keys.RIGHT: 538 | pan( - scope.keyPanSpeed, 0 ); 539 | scope.update(); 540 | break; 541 | 542 | } 543 | 544 | } 545 | 546 | function handleTouchStartRotate( event ) { 547 | 548 | //console.log( 'handleTouchStartRotate' ); 549 | 550 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 551 | 552 | } 553 | 554 | function handleTouchStartDolly( event ) { 555 | 556 | //console.log( 'handleTouchStartDolly' ); 557 | 558 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 559 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 560 | 561 | var distance = Math.sqrt( dx * dx + dy * dy ); 562 | 563 | dollyStart.set( 0, distance ); 564 | 565 | } 566 | 567 | function handleTouchStartPan( event ) { 568 | 569 | //console.log( 'handleTouchStartPan' ); 570 | 571 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 572 | 573 | } 574 | 575 | function handleTouchMoveRotate( event ) { 576 | 577 | //console.log( 'handleTouchMoveRotate' ); 578 | 579 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 580 | rotateDelta.subVectors( rotateEnd, rotateStart ); 581 | 582 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 583 | 584 | // rotating across whole screen goes 360 degrees around 585 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 586 | 587 | // rotating up and down along whole screen attempts to go 360, but limited to 180 588 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 589 | 590 | rotateStart.copy( rotateEnd ); 591 | 592 | scope.update(); 593 | 594 | } 595 | 596 | function handleTouchMoveDolly( event ) { 597 | 598 | //console.log( 'handleTouchMoveDolly' ); 599 | 600 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 601 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 602 | 603 | var distance = Math.sqrt( dx * dx + dy * dy ); 604 | 605 | dollyEnd.set( 0, distance ); 606 | 607 | dollyDelta.subVectors( dollyEnd, dollyStart ); 608 | 609 | if ( dollyDelta.y > 0 ) { 610 | 611 | dollyOut( getZoomScale() ); 612 | 613 | } else if ( dollyDelta.y < 0 ) { 614 | 615 | dollyIn( getZoomScale() ); 616 | 617 | } 618 | 619 | dollyStart.copy( dollyEnd ); 620 | 621 | scope.update(); 622 | 623 | } 624 | 625 | function handleTouchMovePan( event ) { 626 | 627 | //console.log( 'handleTouchMovePan' ); 628 | 629 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 630 | 631 | panDelta.subVectors( panEnd, panStart ); 632 | 633 | pan( panDelta.x, panDelta.y ); 634 | 635 | panStart.copy( panEnd ); 636 | 637 | scope.update(); 638 | 639 | } 640 | 641 | function handleTouchEnd( event ) { 642 | 643 | //console.log( 'handleTouchEnd' ); 644 | 645 | } 646 | 647 | // 648 | // event handlers - FSM: listen for events and reset state 649 | // 650 | 651 | function onMouseDown( event ) { 652 | 653 | if ( scope.enabled === false ) return; 654 | 655 | event.preventDefault(); 656 | 657 | if ( event.button === scope.mouseButtons.ORBIT ) { 658 | 659 | if ( scope.enableRotate === false ) return; 660 | 661 | handleMouseDownRotate( event ); 662 | 663 | state = STATE.ROTATE; 664 | 665 | } else if ( event.button === scope.mouseButtons.ZOOM ) { 666 | 667 | if ( scope.enableZoom === false ) return; 668 | 669 | handleMouseDownDolly( event ); 670 | 671 | state = STATE.DOLLY; 672 | 673 | } else if ( event.button === scope.mouseButtons.PAN ) { 674 | 675 | if ( scope.enablePan === false ) return; 676 | 677 | handleMouseDownPan( event ); 678 | 679 | state = STATE.PAN; 680 | 681 | } 682 | 683 | if ( state !== STATE.NONE ) { 684 | 685 | document.addEventListener( 'mousemove', onMouseMove, false ); 686 | document.addEventListener( 'mouseup', onMouseUp, false ); 687 | 688 | scope.dispatchEvent( startEvent ); 689 | 690 | } 691 | 692 | } 693 | 694 | function onMouseMove( event ) { 695 | 696 | if ( scope.enabled === false ) return; 697 | 698 | event.preventDefault(); 699 | 700 | if ( state === STATE.ROTATE ) { 701 | 702 | if ( scope.enableRotate === false ) return; 703 | 704 | handleMouseMoveRotate( event ); 705 | 706 | } else if ( state === STATE.DOLLY ) { 707 | 708 | if ( scope.enableZoom === false ) return; 709 | 710 | handleMouseMoveDolly( event ); 711 | 712 | } else if ( state === STATE.PAN ) { 713 | 714 | if ( scope.enablePan === false ) return; 715 | 716 | handleMouseMovePan( event ); 717 | 718 | } 719 | 720 | } 721 | 722 | function onMouseUp( event ) { 723 | 724 | if ( scope.enabled === false ) return; 725 | 726 | handleMouseUp( event ); 727 | 728 | document.removeEventListener( 'mousemove', onMouseMove, false ); 729 | document.removeEventListener( 'mouseup', onMouseUp, false ); 730 | 731 | scope.dispatchEvent( endEvent ); 732 | 733 | state = STATE.NONE; 734 | 735 | } 736 | 737 | function onMouseWheel( event ) { 738 | 739 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return; 740 | 741 | event.preventDefault(); 742 | event.stopPropagation(); 743 | 744 | handleMouseWheel( event ); 745 | 746 | scope.dispatchEvent( startEvent ); // not sure why these are here... 747 | scope.dispatchEvent( endEvent ); 748 | 749 | } 750 | 751 | function onKeyDown( event ) { 752 | 753 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return; 754 | 755 | handleKeyDown( event ); 756 | 757 | } 758 | 759 | function onTouchStart( event ) { 760 | 761 | if ( scope.enabled === false ) return; 762 | 763 | switch ( event.touches.length ) { 764 | 765 | case 1: // one-fingered touch: rotate 766 | 767 | if ( scope.enableRotate === false ) return; 768 | 769 | handleTouchStartRotate( event ); 770 | 771 | state = STATE.TOUCH_ROTATE; 772 | 773 | break; 774 | 775 | case 2: // two-fingered touch: dolly 776 | 777 | if ( scope.enableZoom === false ) return; 778 | 779 | handleTouchStartDolly( event ); 780 | 781 | state = STATE.TOUCH_DOLLY; 782 | 783 | break; 784 | 785 | case 3: // three-fingered touch: pan 786 | 787 | if ( scope.enablePan === false ) return; 788 | 789 | handleTouchStartPan( event ); 790 | 791 | state = STATE.TOUCH_PAN; 792 | 793 | break; 794 | 795 | default: 796 | 797 | state = STATE.NONE; 798 | 799 | } 800 | 801 | if ( state !== STATE.NONE ) { 802 | 803 | scope.dispatchEvent( startEvent ); 804 | 805 | } 806 | 807 | } 808 | 809 | function onTouchMove( event ) { 810 | 811 | if ( scope.enabled === false ) return; 812 | 813 | event.preventDefault(); 814 | event.stopPropagation(); 815 | 816 | switch ( event.touches.length ) { 817 | 818 | case 1: // one-fingered touch: rotate 819 | 820 | if ( scope.enableRotate === false ) return; 821 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?... 822 | 823 | handleTouchMoveRotate( event ); 824 | 825 | break; 826 | 827 | case 2: // two-fingered touch: dolly 828 | 829 | if ( scope.enableZoom === false ) return; 830 | if ( state !== STATE.TOUCH_DOLLY ) return; // is this needed?... 831 | 832 | handleTouchMoveDolly( event ); 833 | 834 | break; 835 | 836 | case 3: // three-fingered touch: pan 837 | 838 | if ( scope.enablePan === false ) return; 839 | if ( state !== STATE.TOUCH_PAN ) return; // is this needed?... 840 | 841 | handleTouchMovePan( event ); 842 | 843 | break; 844 | 845 | default: 846 | 847 | state = STATE.NONE; 848 | 849 | } 850 | 851 | } 852 | 853 | function onTouchEnd( event ) { 854 | 855 | if ( scope.enabled === false ) return; 856 | 857 | handleTouchEnd( event ); 858 | 859 | scope.dispatchEvent( endEvent ); 860 | 861 | state = STATE.NONE; 862 | 863 | } 864 | 865 | function onContextMenu( event ) { 866 | 867 | event.preventDefault(); 868 | 869 | } 870 | 871 | // 872 | 873 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); 874 | 875 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); 876 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false ); 877 | 878 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false ); 879 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false ); 880 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false ); 881 | 882 | window.addEventListener( 'keydown', onKeyDown, false ); 883 | 884 | // force an update at start 885 | 886 | this.update(); 887 | 888 | }; 889 | 890 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 891 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; 892 | 893 | Object.defineProperties( THREE.OrbitControls.prototype, { 894 | 895 | center: { 896 | 897 | get: function () { 898 | 899 | console.warn( 'THREE.OrbitControls: .center has been renamed to .target' ); 900 | return this.target; 901 | 902 | } 903 | 904 | }, 905 | 906 | // backward compatibility 907 | 908 | noZoom: { 909 | 910 | get: function () { 911 | 912 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); 913 | return ! this.enableZoom; 914 | 915 | }, 916 | 917 | set: function ( value ) { 918 | 919 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' ); 920 | this.enableZoom = ! value; 921 | 922 | } 923 | 924 | }, 925 | 926 | noRotate: { 927 | 928 | get: function () { 929 | 930 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); 931 | return ! this.enableRotate; 932 | 933 | }, 934 | 935 | set: function ( value ) { 936 | 937 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' ); 938 | this.enableRotate = ! value; 939 | 940 | } 941 | 942 | }, 943 | 944 | noPan: { 945 | 946 | get: function () { 947 | 948 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); 949 | return ! this.enablePan; 950 | 951 | }, 952 | 953 | set: function ( value ) { 954 | 955 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' ); 956 | this.enablePan = ! value; 957 | 958 | } 959 | 960 | }, 961 | 962 | noKeys: { 963 | 964 | get: function () { 965 | 966 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); 967 | return ! this.enableKeys; 968 | 969 | }, 970 | 971 | set: function ( value ) { 972 | 973 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' ); 974 | this.enableKeys = ! value; 975 | 976 | } 977 | 978 | }, 979 | 980 | staticMoving: { 981 | 982 | get: function () { 983 | 984 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); 985 | return ! this.enableDamping; 986 | 987 | }, 988 | 989 | set: function ( value ) { 990 | 991 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' ); 992 | this.enableDamping = ! value; 993 | 994 | } 995 | 996 | }, 997 | 998 | dynamicDampingFactor: { 999 | 1000 | get: function () { 1001 | 1002 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); 1003 | return this.dampingFactor; 1004 | 1005 | }, 1006 | 1007 | set: function ( value ) { 1008 | 1009 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' ); 1010 | this.dampingFactor = value; 1011 | 1012 | } 1013 | 1014 | } 1015 | 1016 | } ); 1017 | -------------------------------------------------------------------------------- /src/assets/js/main.js: -------------------------------------------------------------------------------- 1 | window.onload = () => { 2 | var webgl = new Webgl(); 3 | }; 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/js/module/color-texture.js: -------------------------------------------------------------------------------- 1 | class ColorTex{ 2 | constructor(webgl){ 3 | this.webgl = webgl; 4 | this.controls = this.webgl.controls; 5 | this.size = this.webgl.size; 6 | 7 | this.colorPallete = []; 8 | 9 | const _colorPallete = this.controls.props.pallete; 10 | 11 | 12 | for(let i = 0; i < _colorPallete.length; i++){ 13 | this.colorPallete[i] = new THREE.Color(_colorPallete[i]); 14 | } 15 | 16 | this.objType = [ 17 | new THREE.TorusBufferGeometry(8, 4, 18, 32), 18 | new THREE.TorusBufferGeometry(14, 3, 5, 3), 19 | new THREE.BoxBufferGeometry(12, 12, 12), 20 | new THREE.TorusBufferGeometry(16, 2, 5, 6), 21 | ]; 22 | 23 | this.objTypeName = [ 24 | 'ring', 'triangle', 'box', 'hexagon' 25 | ]; 26 | 27 | this.objNum = 0 28 | this.init(); 29 | } 30 | 31 | init(){ 32 | this.width = this.webgl.width; 33 | this.height = this.webgl.height; 34 | this.scene = new THREE.Scene(); 35 | 36 | this.camera = new THREE.PerspectiveCamera( 70, this.width / this.height, .01, 10000 ); 37 | this.scene.add( this.camera ); 38 | this.camera.position.set(-1, 3, 1); 39 | this.camera.lookAt(this.scene.position); 40 | 41 | var renderTargetParameters = { 42 | minFilter: THREE.LinearFilter, 43 | magFilter: THREE.LinearFilter, 44 | format: THREE.RGBAFormat 45 | }; 46 | 47 | this.fbo = new THREE.WebGLRenderTarget( this.width, this.height, renderTargetParameters ); 48 | this.fbo.texture.format = THREE.RGBAFormat; 49 | 50 | this.group = new THREE.Group(); 51 | this.scene.add(this.group); 52 | 53 | this.orbitControls = new THREE.OrbitControls( this.camera, this.webgl.renderer.domElement ); 54 | 55 | this.sim = new Simulation(this.webgl, this.size); 56 | 57 | var scale = { 58 | x: 1, 59 | y: 1, 60 | z: 1 61 | } 62 | 63 | this.material = new THREE.ShaderMaterial( { 64 | uniforms: { 65 | posMap: { type: "t", value: this.sim.gpuCompute.getCurrentRenderTarget(this.sim.pos).texture }, 66 | velMap: { type: "t", value: this.sim.gpuCompute.getCurrentRenderTarget(this.sim.vel).texture }, 67 | uSize: { type: "f", value: this.sim.size }, 68 | uTick: { type: 'f', value: 0 }, 69 | uScale2: { type: 'v3', value: new THREE.Vector3(scale.x, scale.y, scale.z) }, 70 | uScale1: { type: 'f', value: 0.7 }, 71 | uColorArray: {type: "v3v", value: this.colorPallete}, 72 | isEdge: {type: 'i', value: true}, 73 | uEdgeScale: {type: 'f', value: this.controls.props.edgeSize}, 74 | uEdgeColor: {type: 'vec3', value: new THREE.Color(this.controls.props.edgeColor)}, 75 | isShading: {type: 'i', value: this.controls.props.shading} 76 | }, 77 | 78 | vertexShader: this.webgl.vertShader[1], 79 | fragmentShader: this.webgl.fragShader[4], 80 | side: THREE.DoubleSide, 81 | flatShading: true, 82 | transparent: true, 83 | }); 84 | 85 | for(let i = 0; i < this.objType.length; i++){ 86 | var originalG = this.objType[i]; 87 | var edgesOriginalG = new THREE.EdgesGeometry(originalG); 88 | var EdgeVertices = edgesOriginalG.attributes.position.array; 89 | 90 | const posOrder = this.calcSameVert(originalG); 91 | originalG = this.calcVertNormal(originalG, posOrder, EdgeVertices); 92 | 93 | var mesh = this.createObj(originalG); 94 | 95 | this.objType[i] = mesh; 96 | } 97 | }; 98 | 99 | 100 | calcVertNormal(originalG, posOrder, edgeVertices){ 101 | const length = posOrder.array.length; 102 | const vertNormal = []; 103 | const faceNormal = originalG.attributes.normal.array; 104 | const originalVertices = originalG.attributes.position.array; 105 | 106 | for(let i = 0; i <= posOrder.maxCount; i++){ 107 | const orderNumArray = []; 108 | const normalSum = {x: 0, y: 0, z: 0}; 109 | var count = 0; 110 | 111 | for(let j = 0; j < length; j++){ 112 | if(posOrder.array[j] === i){ 113 | var pos = { 114 | x: originalVertices[j * 3 + 0], 115 | y: originalVertices[j * 3 + 1], 116 | z: originalVertices[j * 3 + 2] 117 | } 118 | 119 | var detectEdge = this.detectEdge(pos, edgeVertices); 120 | 121 | orderNumArray.push(j); 122 | 123 | if(detectEdge){ 124 | normalSum.x += faceNormal[j * 3 + 0]; 125 | normalSum.y += faceNormal[j * 3 + 1]; 126 | normalSum.z += faceNormal[j * 3 + 2]; 127 | 128 | 129 | } else { 130 | } 131 | 132 | count++; 133 | } 134 | } 135 | 136 | normalSum.x /= count; 137 | normalSum.y /= count; 138 | normalSum.z /= count; 139 | 140 | 141 | 142 | const vecLength = Math.sqrt(normalSum.x * normalSum.x + normalSum.y * normalSum.y + normalSum.z * normalSum.z); 143 | 144 | if(vecLength !== 0){ 145 | normalSum.x /= vecLength; 146 | normalSum.y /= vecLength; 147 | normalSum.z /= vecLength; 148 | } 149 | 150 | for(let k = 0; k < count; k++){ 151 | const num = orderNumArray[k]; 152 | vertNormal[num * 3 + 0] = normalSum.x; 153 | vertNormal[num * 3 + 1] = normalSum.y; 154 | vertNormal[num * 3 + 2] = normalSum.z; 155 | } 156 | 157 | } 158 | 159 | const vertNormals = new THREE.Float32BufferAttribute( vertNormal , 3 ); 160 | originalG.addAttribute("vertNormal", vertNormals); 161 | return originalG; 162 | } 163 | 164 | detectEdge(vec3, edgeVertices){ 165 | var isSame = false; 166 | for(var i = 0, len = edgeVertices.length; i < len; i+=3){ 167 | var edgeVert = {}; 168 | edgeVert.x = edgeVertices[i + 0]; 169 | edgeVert.y = edgeVertices[i + 1]; 170 | edgeVert.z = edgeVertices[i + 2]; 171 | 172 | 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; 173 | if(isSame) break; 174 | } 175 | 176 | return isSame; 177 | } 178 | 179 | 180 | calcSameVert(geometry){ 181 | this.verticesArray = geometry.attributes.position.array; 182 | var arrayLength = this.verticesArray.length; 183 | 184 | this.vecCount = 0; 185 | this.indexCount = 0; 186 | this.vec3Array = []; 187 | 188 | this.vertexArray = []; 189 | const posOrderArray = []; 190 | 191 | for(var i = 0; i < arrayLength; i+= 3){ 192 | var vec3 = {}; 193 | vec3.x = this.verticesArray[i]; 194 | vec3.y = this.verticesArray[i + 1]; 195 | vec3.z = this.verticesArray[i + 2]; 196 | var detect = this.detectVec(vec3); 197 | 198 | if(detect === 0 || detect > 0){ 199 | posOrderArray[this.indexCount] = detect; 200 | 201 | } else { 202 | this.vec3Array[this.vecCount] = vec3; 203 | this.vertexArray.push(vec3.x, vec3.y, vec3.z); 204 | 205 | posOrderArray[this.indexCount] = this.vecCount; 206 | 207 | this.vecCount++; 208 | } 209 | 210 | this.indexCount++; 211 | } 212 | 213 | return {array: posOrderArray, maxCount: this.vecCount - 1}; 214 | } 215 | 216 | detectVec(vec3){ 217 | if(this.vecCount === 0) return false; 218 | 219 | for(var i = 0, len = this.vec3Array.length; i < len; i++){ 220 | var _vec3 = this.vec3Array[i]; 221 | 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; 222 | if(isSame) { 223 | return i; 224 | } 225 | } 226 | 227 | return false; 228 | }; 229 | 230 | 231 | createObj(originalG){ 232 | var geometry = new THREE.InstancedBufferGeometry(); 233 | var vertices = originalG.attributes.position.clone(); 234 | 235 | geometry.addAttribute("position", vertices); 236 | 237 | var normals = originalG.attributes.normal.clone(); 238 | geometry.addAttribute("normal", normals); 239 | 240 | var vertNormals = originalG.attributes.vertNormal.clone(); 241 | geometry.addAttribute("vertNormal", vertNormals); 242 | 243 | // uv 244 | var uvs = originalG.attributes.uv.clone(); 245 | geometry.addAttribute("uv", uvs); 246 | 247 | // index 248 | if(originalG.index){ 249 | var indices = originalG.index.clone(); 250 | geometry.setIndex(indices); 251 | } 252 | 253 | 254 | geometry.maxInstancedCount = this.sim.size * this.sim.size; 255 | 256 | var nums = new THREE.InstancedBufferAttribute(new Float32Array(this.sim.size * this.sim.size * 1), 1, 1); 257 | var numRatios = new THREE.InstancedBufferAttribute(new Float32Array(this.sim.size * this.sim.size * 1), 1, 1); 258 | 259 | for(var i = 0; i < nums.count; i++){ 260 | nums.setX(i, i); 261 | numRatios.setX(i, i / (nums.count - 1)); 262 | } 263 | 264 | 265 | geometry.addAttribute("aNum", nums); 266 | geometry.addAttribute("aNumRatio", numRatios); 267 | 268 | 269 | 270 | const mesh = new THREE.Mesh( geometry, this.material ); 271 | 272 | mesh.visible = false; 273 | this.group.add( mesh ); 274 | 275 | return mesh; 276 | }; 277 | 278 | 279 | render(time, delta){ 280 | this.webgl.renderer.clearTarget(this.fbo); 281 | 282 | var sin = (Math.sin(time) * 0.5 + 0.5) * 0.5; 283 | 284 | const mesh = this.objType[this.objNum]; 285 | 286 | this.group.rotation.x += delta * 0.1; 287 | this.group.rotation.y -= delta * 0.08; 288 | 289 | 290 | this.sim.velUniforms.timer.value = time; 291 | this.sim.velUniforms.delta.value = delta; 292 | 293 | this.sim.gpuCompute.compute(); 294 | 295 | mesh.material.uniforms.posMap.value = this.sim.gpuCompute.getCurrentRenderTarget(this.sim.pos).texture; 296 | mesh.material.uniforms.velMap.value = this.sim.gpuCompute.getCurrentRenderTarget(this.sim.vel).texture; 297 | 298 | // timer 299 | mesh.material.uniforms.uTick.value = time; 300 | 301 | this.objType[this.objNum].visible = true; 302 | 303 | if(this.webgl.controls.props.backSide){ 304 | mesh.material.uniforms.isEdge.value = true; 305 | mesh.material.side = THREE.BackSide; 306 | this.webgl.renderer.render( this.scene, this.camera, this.fbo); 307 | } 308 | 309 | if(this.webgl.controls.props.frontSide){ 310 | mesh.material.uniforms.isEdge.value = false; 311 | mesh.material.side = THREE.FrontSide; 312 | this.webgl.renderer.render( this.scene, this.camera, this.fbo); 313 | } 314 | 315 | 316 | 317 | this.objType[this.objNum].visible = false; 318 | }; 319 | 320 | } -------------------------------------------------------------------------------- /src/assets/js/module/controls.js: -------------------------------------------------------------------------------- 1 | class Controls{ 2 | constructor(webgl){ 3 | this.webgl = webgl; 4 | 5 | 6 | this.props = { 7 | pallete: [ 8 | '#ffe100', 9 | '#44dee8', 10 | '#fa2c98', 11 | '#ffffff' 12 | ], 13 | edgeColor: '#69145a', 14 | bgColor: '#ff2f6c', 15 | objType: null, 16 | backSide: true, 17 | frontSide: true, 18 | edgeSize: 0.38, 19 | shading: true, 20 | }; 21 | } 22 | 23 | init(){ 24 | this.colorTex = this.webgl.colorTex; 25 | this.props.objType = this.colorTex.objTypeName[0]; 26 | this.uColorArray = this.colorTex.material.uniforms.uColorArray; 27 | 28 | this.gui = new dat.GUI({width: 300}); 29 | 30 | this.gui_objColor = this.gui.addFolder('obj color'); 31 | this.gui_objColor.open(); 32 | 33 | this.gui_objColor.addColor(this.props.pallete, 0).name('obj color 1').onChange(this.colorFunc1.bind(this)); 34 | this.gui_objColor.addColor(this.props.pallete, 1).name('obj color 2').onChange(this.colorFunc2.bind(this)); 35 | this.gui_objColor.addColor(this.props.pallete, 2).name('obj color 3').onChange(this.colorFunc3.bind(this)); 36 | this.gui_objColor.addColor(this.props.pallete, 3).name('obj color 4').onChange(this.colorFunc4.bind(this)); 37 | 38 | this.gui.addColor(this.props, 'edgeColor').name('edge color').onChange(this.colorFunc_edge.bind(this)); 39 | this.gui.addColor(this.props, 'bgColor').name('bg color').onChange(this.colorFunc_bg.bind(this)); 40 | 41 | this.gui.add(this.props, 'objType', this.colorTex.objTypeName).name('obj type').onFinishChange(this.objTypeFunc.bind(this)); 42 | 43 | this.gui.add(this.props, 'backSide').name('outline color'); 44 | this.gui.add(this.props, 'frontSide').name('obj color'); 45 | this.gui.add(this.props, 'edgeSize', 0.1, 0.6).name('edge size').onChange(this.edgeSize.bind(this)); 46 | this.gui.add(this.props, 'shading').onChange(this.shading.bind(this)); 47 | 48 | 49 | } 50 | 51 | colorFunc1(value){ 52 | var color = new THREE.Color(value); 53 | this.uColorArray.value[0] = color; 54 | } 55 | 56 | colorFunc2(value){ 57 | var color = new THREE.Color(value); 58 | this.uColorArray.value[1] = color; 59 | } 60 | 61 | colorFunc3(value){ 62 | var color = new THREE.Color(value); 63 | this.uColorArray.value[2] = color; 64 | } 65 | 66 | colorFunc4(value){ 67 | var color = new THREE.Color(value); 68 | this.uColorArray.value[3] = color; 69 | } 70 | 71 | colorFunc_edge(value){ 72 | var color = new THREE.Color(value); 73 | // this.webgl.uniforms.uEdgeColor.value = color; 74 | this.colorTex.material.uniforms.uEdgeColor.value = color; 75 | } 76 | 77 | colorFunc_bg(value){ 78 | var color = new THREE.Color(value); 79 | this.webgl.uniforms.uBgColor.value = color; 80 | } 81 | 82 | objTypeFunc(value){ 83 | for(var i = 0, len = this.colorTex.objTypeName.length; i < len; i++){ 84 | if(value === this.colorTex.objTypeName[i]){ 85 | break; 86 | } 87 | } 88 | 89 | this.colorTex.objNum = i; 90 | } 91 | 92 | edgeSize(value){ 93 | this.colorTex.material.uniforms.uEdgeScale.value = value; 94 | } 95 | 96 | shading(value){ 97 | this.colorTex.material.uniforms.isShading.value = value; 98 | } 99 | } -------------------------------------------------------------------------------- /src/assets/js/module/resize-watch.js: -------------------------------------------------------------------------------- 1 | class ResizeWatch{ 2 | constructor(){ 3 | this.instances = []; 4 | 5 | this.width = this._width = document.body.clientWidth; 6 | this.height = this._height = window.innerHeight; 7 | this.aspect = this.width / this.height; 8 | 9 | 10 | window.onresize = () => { 11 | if(this.instances.length === 0) return; 12 | 13 | this.width = document.body.clientWidth; 14 | this.height = window.innerHeight; 15 | this.aspect = this.width / this.height; 16 | 17 | for(var i = 0; i < this.instances.length; i++){ 18 | this.instances[i].resizeUpdate(); 19 | } 20 | }.bind(this) 21 | } 22 | 23 | register(instance){ 24 | this.instances.push(instance); 25 | } 26 | 27 | } 28 | 29 | window.ResizeWatch = new ResizeWatch(); -------------------------------------------------------------------------------- /src/assets/js/module/simulation.js: -------------------------------------------------------------------------------- 1 | class Simulation{ 2 | constructor(basic, size){ 3 | this.basic = basic; 4 | this.renderer = this.basic.renderer; 5 | this.size = size; 6 | this.init(); 7 | } 8 | 9 | init(){ 10 | this.gpuCompute = new GPUComputationRenderer( this.size, this.size, this.renderer ); 11 | 12 | this.dataPos = this.gpuCompute.createTexture(); 13 | this.dataVel = this.gpuCompute.createTexture(); 14 | this.dataDef = this.gpuCompute.createTexture(); 15 | 16 | 17 | var posArray = this.dataPos.image.data; 18 | var velArray = this.dataVel.image.data; 19 | var defArray = this.dataDef.image.data; 20 | 21 | 22 | for ( var i = 0, il = posArray.length; i < il; i += 4 ) { 23 | 24 | var phi = Math.random() * 2 * Math.PI; 25 | var theta = Math.random() * Math.PI; 26 | var r = (1.2 + Math.random() * 2) * 1.2; 27 | 28 | defArray[ i + 0 ] = posArray[ i + 0 ] = r * Math.sin( theta) * Math.cos( phi ); 29 | defArray[ i + 1 ] = posArray[ i + 1 ] = r * Math.sin( theta) * Math.sin( phi ) * 1.4; 30 | defArray[ i + 2 ] = posArray[ i + 2 ] = r * Math.cos( theta ); 31 | defArray[ i + 3 ] = posArray[ i + 3 ] = Math.random() * 0.5; 32 | 33 | 34 | velArray[ i + 3 ] = Math.random() * 100; // frames life 35 | } 36 | 37 | this.def = this.gpuCompute.addVariable( "defTex", this.basic.fragShader[1], this.dataDef ); 38 | this.vel = this.gpuCompute.addVariable( "velTex", this.basic.fragShader[2], this.dataVel ); 39 | this.pos = this.gpuCompute.addVariable( "posTex", this.basic.fragShader[3], this.dataPos ); 40 | 41 | this.gpuCompute.setVariableDependencies( this.def, [ this.pos, this.vel, this.def ] ); 42 | this.gpuCompute.setVariableDependencies( this.vel, [ this.pos, this.vel, this.def ] ); 43 | this.gpuCompute.setVariableDependencies( this.pos, [ this.pos, this.vel, this.def ] ); 44 | 45 | 46 | // var posUniforms = this.pos.material.uniforms; 47 | this.velUniforms = this.vel.material.uniforms; 48 | 49 | this.velUniforms.timer = { value: 0.0 }; 50 | this.velUniforms.delta = { value: 0.0 }; 51 | this.velUniforms.speed = { value: 0.3 }; 52 | this.velUniforms.factor = { value: 0.5 }; 53 | this.velUniforms.evolution = { value: 0.5 }; 54 | this.velUniforms.radius = { value: 2.0 }; 55 | 56 | var error = this.gpuCompute.init(); 57 | if ( error !== null ) { 58 | console.error( error ); 59 | } 60 | }; 61 | 62 | } -------------------------------------------------------------------------------- /src/assets/js/module/webgl.js: -------------------------------------------------------------------------------- 1 | class Webgl{ 2 | constructor(){ 3 | this.size = 32; 4 | 5 | this.vertShader = [ 6 | "assets/glsl/output.vert", 7 | "assets/glsl/cube.vert" 8 | ]; 9 | 10 | this.fragShader = [ 11 | "assets/glsl/output.frag", 12 | "assets/glsl/simulation_def.frag", 13 | "assets/glsl/simulation_vel.frag", 14 | "assets/glsl/simulation_pos.frag", 15 | "assets/glsl/cube.frag", 16 | ]; 17 | 18 | 19 | this.shaderLength = this.vertShader.length + this.fragShader.length; 20 | this.shaderCount = 0; 21 | 22 | for(var i = 0; i < this.vertShader.length; i++){ 23 | this.importShader_vert(i); 24 | } 25 | 26 | for(var i = 0; i < this.fragShader.length; i++){ 27 | this.importShader_frag(i); 28 | } 29 | } 30 | 31 | importShader_vert(i){ 32 | 33 | var myRequest = new XMLHttpRequest(); 34 | 35 | var _this = this; 36 | myRequest.onreadystatechange = () =>{ 37 | if ( myRequest.readyState === 4 ) { 38 | _this.vertShader[i] = myRequest.response; 39 | _this.completeShaderLoad(); 40 | } 41 | }; 42 | 43 | 44 | myRequest.open("GET", this.vertShader[i], true); 45 | myRequest.send(); 46 | }; 47 | 48 | 49 | importShader_frag(i){ 50 | 51 | var myRequest = new XMLHttpRequest(); 52 | // ハンドラの登録 53 | var _this = this; 54 | myRequest.onreadystatechange = () => { 55 | if ( myRequest.readyState === 4 ) { 56 | _this.fragShader[i] = myRequest.response; 57 | 58 | 59 | _this.completeShaderLoad(); 60 | } 61 | }; 62 | 63 | myRequest.open("GET", this.fragShader[i], true); 64 | myRequest.send(); 65 | }; 66 | 67 | 68 | 69 | completeShaderLoad(){ 70 | this.shaderCount++; 71 | 72 | if(this.shaderCount === this.shaderLength) { 73 | this.isShaderComplete = true; 74 | this.init(); 75 | } 76 | }; 77 | 78 | 79 | init(){ 80 | this.width = 2048; 81 | this.height = 2048; 82 | this.aspect = this.width / this.height; 83 | this.setProps(); 84 | this.container = document.getElementById( "wrapper" ); 85 | 86 | this.renderer = new THREE.WebGLRenderer( { 87 | antialias: true, 88 | alpha: true, 89 | } ); 90 | 91 | this.renderer.autoClear = false; 92 | // renderer.setPixelRatio( window.devicePixelRatio ); 93 | this.renderer.setSize( ResizeWatch.width, ResizeWatch.height ); 94 | this.renderer.setClearColor( 0xffffff, 0.0 ); 95 | this.container.appendChild( this.renderer.domElement ); 96 | 97 | var ratio = (Useragnt.pc) ? 1.0 : 2.0; 98 | 99 | this.renderer.setPixelRatio(ratio); 100 | 101 | 102 | this.scene = new THREE.Scene(); 103 | 104 | this.camera = new THREE.PerspectiveCamera(this.props.fov, this.props.aspect, this.props.near, this.props.far); 105 | var cameraZ = (this.props.height / 2) / Math.tan((this.props.fov * Math.PI / 180) / 2); 106 | this.camera.position.set(0, 0, cameraZ); 107 | this.camera.lookAt(this.scene.position); 108 | 109 | this.controls = new Controls(this); 110 | 111 | this.colorTex = new ColorTex(this); 112 | 113 | this.createPlane(); 114 | 115 | this.controls.init(); 116 | 117 | 118 | 119 | 120 | this.time = new THREE.Clock(); 121 | this.render(); 122 | 123 | ResizeWatch.register(this); 124 | }; 125 | 126 | 127 | setProps(){ 128 | var width = ResizeWatch.width; 129 | var height = ResizeWatch.height; 130 | var aspect = width / height; 131 | 132 | this.props = { 133 | width: width, 134 | height: height, 135 | aspect: aspect, 136 | fov: 45, 137 | left: -width / 2, 138 | right: width / 2, 139 | top: height / 2, 140 | bottom: -height / 2, 141 | near: 0.1, 142 | far: 10000, 143 | parent: document.getElementById("wrapper") 144 | }; 145 | }; 146 | 147 | 148 | createPlane(){ 149 | var g = new THREE.PlaneBufferGeometry(this.width, this.height); 150 | 151 | 152 | 153 | this.uniforms = { 154 | uTex_1: {type: "t", value: this.colorTex.fbo.texture}, 155 | uTick: {type: "f", value: 0}, 156 | uSize: {type: "v2", value: new THREE.Vector2(this.width, this.height)}, 157 | // uEdgeColor: {type: "v3", value: new THREE.Color(this.edgeColor)}, 158 | uBgColor: {type: "v3", value: new THREE.Color(this.controls.props.bgColor)}, 159 | }; 160 | 161 | var m = new THREE.ShaderMaterial({ 162 | vertexShader: this.vertShader[0], 163 | fragmentShader: this.fragShader[0], 164 | uniforms: this.uniforms 165 | }); 166 | 167 | 168 | 169 | var mesh = new THREE.Mesh(g, m); 170 | 171 | mesh.position.z = 10; 172 | this.scene.add(mesh); 173 | 174 | this.plane = mesh; 175 | 176 | console.log(this.plane); 177 | 178 | if(ResizeWatch.aspect > this.aspect){ 179 | var scale = ResizeWatch.width / this.width; 180 | } else { 181 | var scale = ResizeWatch.height / this.height; 182 | } 183 | 184 | this.plane.scale.x = scale; 185 | this.plane.scale.y = scale; 186 | }; 187 | 188 | 189 | render(){ 190 | var delta = this.time.getDelta() * 5; 191 | var time = this.time.elapsedTime; 192 | 193 | this.renderer.clear(); 194 | 195 | this.colorTex.render(time, delta); 196 | this.uniforms.uTick.value = time; 197 | 198 | this.renderer.render( this.scene, this.camera ); 199 | 200 | requestAnimationFrame(this.render.bind(this)); 201 | }; 202 | 203 | 204 | resizeUpdate(){ 205 | this.setProps(); 206 | this.renderer.setSize(this.props.width, this.props.height); 207 | 208 | this.camera.aspect = this.props.aspect; 209 | 210 | var cameraZ = (this.props.height / 2) / Math.tan((this.props.fov * Math.PI / 180) / 2); 211 | 212 | this.camera.position.set(0, 0, cameraZ); 213 | this.camera.lookAt(this.scene.position); 214 | 215 | this.camera.updateProjectionMatrix(); 216 | 217 | if(ResizeWatch.aspect > this.aspect){ 218 | var scale = ResizeWatch.width / this.width; 219 | } else { 220 | var scale = ResizeWatch.height / this.height; 221 | } 222 | 223 | this.plane.scale.x = scale; 224 | this.plane.scale.y = scale; 225 | } 226 | 227 | } -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Toon Shading 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | --------------------------------------------------------------------------------