OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 // | |
5 // This sample is based upon the Ray Tracer sample by Jonas Sicking at: | |
6 // http://www.khronos.org/webgl/wiki/Demo_Repository | |
7 | |
8 /** | |
9 * A sample GL application. | |
10 */ | |
11 library openglui_raytrace; | |
12 | |
13 import 'gl.dart'; | |
14 import 'dart:math' as Math; | |
15 | |
16 WebGLRenderingContext gl = null; | |
17 | |
18 // Note: The first line of the fragment shader ("precision mediump float") | |
19 // is not portable. It is required for WebGL and OpenGL ES. Desktop OpenGL | |
20 // does not use precision specifiers. Some desktop systems treat this as a | |
21 // no-op and some treat it as a syntax error. In particular, this line needs | |
22 // to be removed to this this shader on a MAc. | |
23 const FRAGMENT_PROGRAM = """ | |
24 // TODO(vsm): Remove this ifdef. It's a temporary workaround to get | |
25 // this sample running on the Mac desktop emulator. | |
26 #ifdef GL_ES | |
27 precision mediump float; | |
28 #endif | |
29 | |
30 const vec3 lightDir = vec3(0.577350269, 0.577350269, -0.577350269); | |
31 varying vec3 vPosition; | |
32 uniform vec3 cameraPos; | |
33 uniform vec3 sphere1Center; | |
34 uniform vec3 sphere2Center; | |
35 uniform vec3 sphere3Center; | |
36 | |
37 bool intersectSphere(vec3 center, vec3 lStart, vec3 lDir, | |
38 out float dist) { | |
39 vec3 c = center - lStart; | |
40 float b = dot(lDir, c); | |
41 float d = b*b - dot(c, c) + 1.0; | |
42 if (d < 0.0) { | |
43 dist = 10000.0; | |
44 return false; | |
45 } | |
46 | |
47 dist = b - sqrt(d); | |
48 if (dist < 0.0) { | |
49 dist = 10000.0; | |
50 return false; | |
51 } | |
52 | |
53 return true; | |
54 } | |
55 | |
56 vec3 lightAt(vec3 N, vec3 V, vec3 color) { | |
57 vec3 L = lightDir; | |
58 vec3 R = reflect(-L, N); | |
59 | |
60 float c = 0.3 + 0.4 * pow(max(dot(R, V), 0.0), 30.0) + 0.7 * dot(L, N); | |
61 | |
62 if (c > 1.0) { | |
63 return mix(color, vec3(1.6, 1.6, 1.6), c - 1.0); | |
64 } | |
65 | |
66 return c * color; | |
67 } | |
68 | |
69 bool intersectWorld(vec3 lStart, vec3 lDir, out vec3 pos, | |
70 out vec3 normal, out vec3 color) { | |
71 float d1, d2, d3; | |
72 bool h1, h2, h3; | |
73 | |
74 h1 = intersectSphere(sphere1Center, lStart, lDir, d1); | |
75 h2 = intersectSphere(sphere2Center, lStart, lDir, d2); | |
76 h3 = intersectSphere(sphere3Center, lStart, lDir, d3); | |
77 | |
78 if (h1 && d1 < d2 && d1 < d3) { | |
79 pos = lStart + d1 * lDir; | |
80 normal = pos - sphere1Center; | |
81 color = vec3(0.0, 0.0, 0.9); | |
82 if (fract(pos.x / 1.5) > 0.5 ^^ | |
83 fract(pos.y / 1.5) > 0.5 ^^ | |
84 fract(pos.z / 1.5) > 0.5) { | |
85 color = vec3(1.0, 0.0, 1.0); | |
86 } | |
87 else { | |
88 color = vec3(1.0, 1.0, 0.0); | |
89 } | |
90 } | |
91 else if (h2 && d2 < d3) { | |
92 pos = lStart + d2 * lDir; | |
93 normal = pos - sphere2Center; | |
94 color = vec3(0.9, mod(normal.y * 2.5, 1.0), 0.0); | |
95 } | |
96 else if (h3) { | |
97 pos = lStart + d3 * lDir; | |
98 normal = pos - sphere3Center; | |
99 color = vec3(0.0, clamp(sphere3Center.y/1.5, 0.0, 0.9), | |
100 clamp(0.9 - sphere3Center.y/1.5, 0.0, 0.9)); | |
101 } | |
102 else if (lDir.y < -0.01) { | |
103 pos = lStart + ((lStart.y + 2.7) / -lDir.y) * lDir; | |
104 if (pos.x*pos.x + pos.z*pos.z > 30.0) { | |
105 return false; | |
106 } | |
107 normal = vec3(0.0, 1.0, 0.0); | |
108 if (fract(pos.x / 5.0) > 0.5 == fract(pos.z / 5.0) > 0.5) { | |
109 color = vec3(1.0); | |
110 } | |
111 else { | |
112 color = vec3(0.0); | |
113 } | |
114 } | |
115 else { | |
116 return false; | |
117 } | |
118 | |
119 return true; | |
120 } | |
121 | |
122 void main(void) | |
123 { | |
124 vec3 cameraDir = normalize(vPosition - cameraPos); | |
125 | |
126 vec3 p1, norm, p2; | |
127 vec3 col, colT, colM, col3; | |
128 if (intersectWorld(cameraPos, cameraDir, p1, | |
129 norm, colT)) { | |
130 col = lightAt(norm, -cameraDir, colT); | |
131 colM = (colT + vec3(0.2)) / 1.2; | |
132 cameraDir = reflect(cameraDir, norm); | |
133 if (intersectWorld(p1, cameraDir, p2, norm, colT)) { | |
134 col += lightAt(norm, -cameraDir, colT) * colM; | |
135 colM *= (colT + vec3(0.2)) / 1.2; | |
136 cameraDir = reflect(cameraDir, norm); | |
137 if (intersectWorld(p2, cameraDir, p1, norm, colT)) { | |
138 col += lightAt(norm, -cameraDir, colT) * colM; | |
139 } | |
140 } | |
141 | |
142 gl_FragColor = vec4(col, 1.0); | |
143 } | |
144 else { | |
145 gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); | |
146 } | |
147 } | |
148 """; | |
149 | |
150 const VERTEX_PROGRAM = """ | |
151 attribute vec2 aVertexPosition; | |
152 attribute vec3 aPlotPosition; | |
153 | |
154 varying vec3 vPosition; | |
155 | |
156 void main(void) | |
157 { | |
158 gl_Position = vec4(aVertexPosition, 1.0, 1.0); | |
159 vPosition = aPlotPosition; | |
160 } | |
161 """; | |
162 | |
163 loadShader(final type, final program) { | |
164 final shader = gl.createShader(type); | |
165 gl.shaderSource(shader, program); | |
166 gl.compileShader(shader); | |
167 if (gl.getShaderParameter(shader, WebGLRenderingContext.COMPILE_STATUS) != tru
e) { | |
168 final error = gl.getShaderInfoLog(shader); | |
169 log(error); | |
170 throw new Exception("Shader compilation error: $error"); | |
171 } | |
172 | |
173 return shader; | |
174 } | |
175 | |
176 var shaderProgram; | |
177 var aVertexPosition; | |
178 var aPlotPosition; | |
179 var cameraPos; | |
180 var sphere1Center; | |
181 var sphere2Center; | |
182 var sphere3Center; | |
183 var ratio; | |
184 | |
185 void initShaders() { | |
186 var vertexShader = loadShader(WebGLRenderingContext.VERTEX_SHADER, | |
187 VERTEX_PROGRAM); | |
188 var fragmentShader = loadShader(WebGLRenderingContext.FRAGMENT_SHADER, | |
189 FRAGMENT_PROGRAM); | |
190 | |
191 shaderProgram = gl.createProgram(); | |
192 if (shaderProgram == 0) { | |
193 throw new Exception("Could not create program."); | |
194 } | |
195 | |
196 gl.attachShader(shaderProgram, vertexShader); | |
197 gl.attachShader(shaderProgram, fragmentShader); | |
198 gl.linkProgram(shaderProgram); | |
199 | |
200 if (gl.getProgramParameter(shaderProgram, WebGLRenderingContext.LINK_STATUS) !
= true) { | |
201 final error = gl.getProgramInfoLog(shaderProgram); | |
202 throw new Exception("Program compilation error: $error"); | |
203 } | |
204 | |
205 gl.useProgram(shaderProgram); | |
206 | |
207 aVertexPosition = gl.getAttribLocation(shaderProgram, "aVertexPosition"); | |
208 gl.enableVertexAttribArray(aVertexPosition); | |
209 | |
210 aPlotPosition = gl.getAttribLocation(shaderProgram, "aPlotPosition"); | |
211 gl.enableVertexAttribArray(aPlotPosition); | |
212 | |
213 cameraPos = gl.getUniformLocation(shaderProgram, "cameraPos"); | |
214 sphere1Center = gl.getUniformLocation(shaderProgram, "sphere1Center"); | |
215 sphere2Center = gl.getUniformLocation(shaderProgram, "sphere2Center"); | |
216 sphere3Center = gl.getUniformLocation(shaderProgram, "sphere3Center"); | |
217 } | |
218 | |
219 // TODO(gram): This should go away at some point. For now it is a kludge | |
220 // to allow us to run same .dart file with WebGL and natively; the WebGL | |
221 // version will set this to true. | |
222 var wrapVertexArray = false; | |
223 | |
224 wrapVertices(a) => wrapVertexArray ? (new Float32Array.fromList(a)) : a; | |
225 | |
226 void initBuffers() { | |
227 var vertexPositionBuffer = gl.createBuffer(); | |
228 gl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, vertexPositionBuffer); | |
229 var vertices = [ | |
230 1.0, 1.0, | |
231 -1.0, 1.0, | |
232 1.0, -1.0, | |
233 -1.0, -1.0, | |
234 ]; | |
235 | |
236 gl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, wrapVertices(vertices), | |
237 WebGLRenderingContext.STATIC_DRAW); | |
238 gl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, vertexPositionBuffer); | |
239 gl.vertexAttribPointer(aVertexPosition, 2, WebGLRenderingContext.FLOAT, | |
240 false,0, 0); | |
241 | |
242 var plotPositionBuffer = gl.createBuffer(); | |
243 gl.bindBuffer(WebGLRenderingContext.ARRAY_BUFFER, plotPositionBuffer); | |
244 gl.vertexAttribPointer(aPlotPosition, 3, WebGLRenderingContext.FLOAT, | |
245 false, 0, 0); | |
246 } | |
247 | |
248 class Vector { | |
249 var x; | |
250 var y; | |
251 var z; | |
252 Vector(this.x, this.y, this.z); | |
253 } | |
254 | |
255 // TODO(gram): This should be using vector_math. | |
256 crossProd(v1, v2) => | |
257 new Vector(v1.y*v2.z - v2.y*v1.z, | |
258 v1.z*v2.x - v2.z*v1.x, | |
259 v1.x*v2.y - v2.x*v1.y); | |
260 | |
261 Vector normalize(v) { | |
262 var l = 1 / Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z); | |
263 return new Vector( v.x*l, v.y*l, v.z*l ); | |
264 } | |
265 | |
266 vectAdd(v1, v2) => new Vector( v1.x + v2.x, v1.y + v2.y, v1.z + v2.z ); | |
267 | |
268 vectSub(v1, v2) => new Vector( v1.x - v2.x, v1.y - v2.y, v1.z - v2.z ); | |
269 | |
270 vectMul(v, l) => new Vector( v.x*l, v.y*l, v.z*l ); | |
271 | |
272 void pushVec(v, arr) { | |
273 arr.addAll([v.x, v.y, v.z]); | |
274 } | |
275 | |
276 var t = 0; | |
277 | |
278 void drawScene() { | |
279 var x1 = Math.sin(t * 1.1) * 1.5; | |
280 var y1 = Math.cos(t * 1.3) * 1.5; | |
281 var z1 = Math.sin(t + Math.PI/3) * 1.5; | |
282 var x2 = Math.cos(t * 1.2) * 1.5; | |
283 var y2 = Math.sin(t * 1.4) * 1.5; | |
284 var z2 = Math.sin(t*1.25 - Math.PI/3) * 1.5; | |
285 var x3 = Math.cos(t * 1.15) * 1.5; | |
286 var y3 = Math.sin(t * 1.37) * 1.5; | |
287 var z3 = Math.sin(t*1.27) * 1.5; | |
288 | |
289 var cameraFrom = new Vector( | |
290 Math.sin(t * 0.4) * 18, | |
291 Math.sin(t * 0.13) * 5 + 5, | |
292 Math.cos(t * 0.4) * 18 ); | |
293 var cameraTo = new Vector(0.0, 0.0, 0.0); | |
294 var cameraPersp = 6.0; | |
295 var up = new Vector(0.0, 1.0, 0.0); | |
296 var cameraDir = normalize(vectSub(cameraTo, cameraFrom)); | |
297 | |
298 var cameraLeft = normalize(crossProd(cameraDir, up)); | |
299 var cameraUp = normalize(crossProd(cameraLeft, cameraDir)); | |
300 // cameraFrom + cameraDir * cameraPersp | |
301 var cameraCenter = vectAdd(cameraFrom, vectMul(cameraDir, cameraPersp)); | |
302 | |
303 // cameraCenter + cameraUp + cameraLeft * ratio | |
304 log("cameraLeft = ${cameraLeft}"); | |
305 log("ratio = ${ratio}"); | |
306 var vl = vectMul(cameraLeft, ratio); | |
307 var cameraTopLeft = vectAdd(vectAdd(cameraCenter, cameraUp), vl); | |
308 //vectMul(cameraLeft, ratio)); | |
309 var cameraBotLeft = vectAdd(vectSub(cameraCenter, cameraUp), vl); | |
310 //vectMul(cameraLeft, ratio)); | |
311 var cameraTopRight = vectSub(vectAdd(cameraCenter, cameraUp), vl); | |
312 //vectMul(cameraLeft, ratio)); | |
313 var cameraBotRight = vectSub(vectSub(cameraCenter, cameraUp), vl); | |
314 //vectMul(cameraLeft, ratio)); | |
315 | |
316 var corners = []; | |
317 pushVec(cameraTopRight, corners); | |
318 pushVec(cameraTopLeft, corners); | |
319 pushVec(cameraBotRight, corners); | |
320 pushVec(cameraBotLeft, corners); | |
321 | |
322 gl.bufferData(WebGLRenderingContext.ARRAY_BUFFER, wrapVertices(corners), | |
323 WebGLRenderingContext.STATIC_DRAW); | |
324 | |
325 gl.uniform3f(cameraPos, cameraFrom.x, cameraFrom.y, cameraFrom.z); | |
326 gl.uniform3f(sphere1Center, x1, y1, z1); | |
327 gl.uniform3f(sphere2Center, x2, y2, z2); | |
328 gl.uniform3f(sphere3Center, x3, y3, z3); | |
329 | |
330 gl.drawArrays(WebGLRenderingContext.TRIANGLE_STRIP, 0, 4); | |
331 glSwapBuffers(); | |
332 | |
333 t += 0.03; | |
334 if (t > Math.PI * 200) { | |
335 t -= Math.PI * 200; | |
336 } | |
337 } | |
338 | |
339 void setup(canvas, int w, int h, int f) { | |
340 if (canvas == null) { | |
341 canvas = new CanvasElement(width: w, height: h); | |
342 } | |
343 gl = canvas.getContext("experimental-webgl"); | |
344 initShaders(); | |
345 gl.clearColor(0.0, 0.0, 0.0, 1.0); | |
346 gl.clearDepth(1.0); | |
347 initBuffers(); | |
348 resize(w, h); | |
349 window.requestAnimationFrame(update); | |
350 log("Done setup"); | |
351 } | |
352 | |
353 void resize(int width, int height) { | |
354 gl.viewport(0, 0, width, height); | |
355 ratio = width / height; | |
356 t -= 0.03; | |
357 drawScene(); | |
358 } | |
359 | |
360 void update(when) { | |
361 drawScene(); | |
362 window.requestAnimationFrame(update); | |
363 } | |
364 | |
OLD | NEW |