ewdlop commited on
Commit
b26b98b
·
verified ·
1 Parent(s): 0ad0cec

Create index.js (#2)

Browse files

- Create index.js (5ff24d76e55e503c147ead44acec8632ffc7ce8b)

Files changed (1) hide show
  1. index.js +187 -0
index.js ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const canvas = document.getElementById('gl');
2
+ const gl = canvas.getContext('webgl');
3
+ if (!gl) { alert('此瀏覽器不支援 WebGL'); }
4
+
5
+ function resize() {
6
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
7
+ const w = Math.floor(window.innerWidth * dpr);
8
+ const h = Math.floor((window.innerHeight - document.querySelector('.ui').offsetHeight) * dpr);
9
+ canvas.width = w; canvas.height = Math.max(h, 2);
10
+ canvas.style.width = '100%'; canvas.style.height = '100%';
11
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
12
+ }
13
+ window.addEventListener('resize', resize);
14
+
15
+ const vertSrc = `
16
+ attribute vec2 aPos;
17
+ attribute vec2 aUV;
18
+ varying vec2 vUV;
19
+ void main(){
20
+ vUV = aUV;
21
+ gl_Position = vec4(aPos, 0.0, 1.0);
22
+ }`;
23
+
24
+ // fragment shader(點質量透鏡公式 β = θ - θ_E² θ / |θ|²)
25
+ const fragSrc = `
26
+ precision highp float;
27
+ varying vec2 vUV;
28
+ uniform sampler2D uTex;
29
+ uniform vec2 uPosition;
30
+ uniform float uThetaE2;
31
+ uniform float uRatio;
32
+ uniform float uShadowScale;
33
+
34
+ void main(){
35
+ float eps = 1e-6;
36
+ vec2 d = vUV - uPosition;
37
+ vec2 d_n = d / vec2(uRatio, 1.0);
38
+ float r2 = dot(d_n, d_n) + eps;
39
+ vec2 beta_n = d_n - (uThetaE2 * d_n / r2);
40
+ vec2 sampleUV = beta_n * vec2(uRatio, 1.0) + uPosition;
41
+ vec4 res = texture2D(uTex, clamp(sampleUV, 0.0, 1.0));
42
+ if (length(d_n) < uShadowScale * sqrt(uThetaE2)) {
43
+ res.rgb = vec3(0.0);
44
+ }
45
+ gl_FragColor = res;
46
+ }`;
47
+
48
+ function compile(type, src){
49
+ const sh = gl.createShader(type);
50
+ gl.shaderSource(sh, src);
51
+ gl.compileShader(sh);
52
+ if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
53
+ throw new Error(gl.getShaderInfoLog(sh) || 'shader compile error');
54
+ }
55
+ return sh;
56
+ }
57
+
58
+ const vs = compile(gl.VERTEX_SHADER, vertSrc);
59
+ const fs = compile(gl.FRAGMENT_SHADER, fragSrc);
60
+ const prog = gl.createProgram();
61
+ gl.attachShader(prog, vs);
62
+ gl.attachShader(prog, fs);
63
+ gl.linkProgram(prog);
64
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
65
+ throw new Error(gl.getProgramInfoLog(prog) || 'program link error');
66
+ }
67
+ gl.useProgram(prog);
68
+
69
+ const quad = new Float32Array([
70
+ -1, -1, 0, 0,
71
+ 1, -1, 1, 0,
72
+ -1, 1, 0, 1,
73
+ 1, 1, 1, 1,
74
+ ]);
75
+ const ibo = new Uint16Array([0,1,2,2,1,3]);
76
+ const vbo = gl.createBuffer();
77
+ const ebo = gl.createBuffer();
78
+
79
+ gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
80
+ gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);
81
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo);
82
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, ibo, gl.STATIC_DRAW);
83
+
84
+ const aPos = gl.getAttribLocation(prog, 'aPos');
85
+ const aUV = gl.getAttribLocation(prog, 'aUV');
86
+ gl.enableVertexAttribArray(aPos);
87
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 16, 0);
88
+ gl.enableVertexAttribArray(aUV);
89
+ gl.vertexAttribPointer(aUV, 2, gl.FLOAT, false, 16, 8);
90
+
91
+ const uTex = gl.getUniformLocation(prog, 'uTex');
92
+ const uPosition = gl.getUniformLocation(prog, 'uPosition');
93
+ const uThetaE2 = gl.getUniformLocation(prog, 'uThetaE2');
94
+ const uRatio = gl.getUniformLocation(prog, 'uRatio');
95
+ const uShadowScale = gl.getUniformLocation(prog, 'uShadowScale');
96
+
97
+ gl.uniform1i(uTex, 0);
98
+
99
+ let state = {
100
+ position: { x: 0.5, y: 0.5 },
101
+ thetaE2: parseFloat(document.getElementById('thetaE').value),
102
+ shadowScale: parseFloat(document.getElementById('shadowScale').value)
103
+ };
104
+
105
+ function updateUniforms(){
106
+ const ratio = gl.drawingBufferWidth / gl.drawingBufferHeight;
107
+ gl.uniform2f(uPosition, state.position.x, state.position.y);
108
+ gl.uniform1f(uThetaE2, state.thetaE2);
109
+ gl.uniform1f(uRatio, ratio);
110
+ gl.uniform1f(uShadowScale, state.shadowScale);
111
+ }
112
+
113
+ let texture = gl.createTexture();
114
+ function useTextureFromImage(img){
115
+ gl.bindTexture(gl.TEXTURE_2D, texture);
116
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
117
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
118
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
119
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
120
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
121
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
122
+ }
123
+
124
+ function genStarfield(w=1024, h=512){
125
+ const cvs = document.createElement('canvas');
126
+ cvs.width = w; cvs.height = h; const ctx = cvs.getContext('2d');
127
+ ctx.fillStyle = '#000'; ctx.fillRect(0,0,w,h);
128
+ for(let i=0;i<5000;i++){
129
+ const x=Math.random()*w, y=Math.random()*h; const s=Math.random()*1.6+0.2;
130
+ const a=Math.random()*0.9+0.1; ctx.fillStyle = `rgba(255,255,255,${a})`;
131
+ ctx.fillRect(x, y, s, s);
132
+ }
133
+ return cvs;
134
+ }
135
+
136
+ useTextureFromImage(genStarfield());
137
+
138
+ const imgInput = document.getElementById('img');
139
+ imgInput.addEventListener('change', e => {
140
+ const file = e.target.files[0]; if (!file) return;
141
+ const img = new Image();
142
+ img.onload = () => useTextureFromImage(img);
143
+ img.src = URL.createObjectURL(file);
144
+ });
145
+
146
+ let dragging = false;
147
+ let animate = true;
148
+ const chkAnimate = document.getElementById('animate');
149
+ chkAnimate.addEventListener('change', () => animate = chkAnimate.checked);
150
+
151
+ canvas.addEventListener('mousedown', (e)=>{ dragging = true; move(e); });
152
+ window.addEventListener('mouseup', ()=> dragging = false);
153
+ window.addEventListener('mousemove', (e)=>{ if (dragging) move(e); });
154
+
155
+ function move(e){
156
+ const rect = canvas.getBoundingClientRect();
157
+ const x = (e.clientX - rect.left) / rect.width;
158
+ const y = (e.clientY - rect.top) / rect.height;
159
+ state.position.x = x; state.position.y = 1.0 - y;
160
+ }
161
+
162
+ const thetaEEl = document.getElementById('thetaE');
163
+ const shadowEl = document.getElementById('shadowScale');
164
+ thetaEEl.addEventListener('input', ()=> state.thetaE2 = parseFloat(thetaEEl.value));
165
+ shadowEl.addEventListener('input', ()=> state.shadowScale = parseFloat(shadowEl.value));
166
+
167
+ document.getElementById('reset').addEventListener('click', ()=>{
168
+ state = { position: {x:0.5, y:0.5}, thetaE2: 0.01, shadowScale: 2.598 };
169
+ thetaEEl.value = state.thetaE2; shadowEl.value = state.shadowScale; chkAnimate.checked = false; animate = false;
170
+ });
171
+
172
+ resize();
173
+ let t0 = performance.now();
174
+ function render(now){
175
+ if (animate){
176
+ const t = (now - t0) * 0.001;
177
+ const r = 0.25; const cx = 0.5, cy = 0.55;
178
+ state.position.x = cx + r * Math.cos(t*0.3);
179
+ state.position.y = cy + r * Math.sin(t*0.3);
180
+ state.thetaE2 = 0.01 + 0.005*Math.sin(t*0.7);
181
+ }
182
+ updateUniforms();
183
+ gl.clearColor(0,0,0,1); gl.clear(gl.COLOR_BUFFER_BIT);
184
+ gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
185
+ requestAnimationFrame(render);
186
+ }
187
+ requestAnimationFrame(render);