-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrtx.html
474 lines (398 loc) · 13.7 KB
/
rtx.html
1
2
3
4
5
6
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
<html>
<style>
/* */
html {
height: 100%;
margin: 0;
}
/* make the canvas fill its container */
#c {
width: 100%;
height: 100%;
display: block;
}
</style>
<div class="divcanvas">
<canvas id="c"></canvas>
</div>
</html>
<script id="fs">#version 300 es
precision highp float;
uniform vec2 iResolution;
uniform vec2 iMouse;
uniform float iTime;
// output for the fragment shader
out vec4 outColour;
// commonly used randomizer function from shadertoy
float nrand(vec2 p)
{
vec3 p3 = fract(vec3(p.xyx) * .1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
// randomizer function that changes from frame to frame
// takes a seed to make individual draws different
float trand(vec2 n, float seed)
{
return nrand(n * seed * iTime) * 2.0 - 1.0;
}
vec3 randomInSphere(vec2 uv, float seed){
// this is doing rejection sampling, which is simple but not super efficient
vec3 p;
do {
p = vec3(trand(uv, seed),
trand(uv, 2.333*seed),
trand(uv, -1.134*seed));
seed *= 1.312;
} while (dot(p, p) >= 1.0);
return p;
}
struct Ray {
vec3 origin;
vec3 direction;
};
struct Hit {
vec3 p;
vec3 normal;
float t;
bool inside; // if the ray hit the outside or inside
int object; // array id of object hit
};
struct Material {
vec3 colour;
float shiny;
};
struct Sphere {
vec3 centre;
float radius;
Material mat;
};
struct Camera {
vec3 location;
float focalDistance;
};
Camera cam = Camera(
vec3(0.0, 0.0, 1.0),
-1.0
);
const Material blue = Material( vec3(0.0, 0.0, 0.9), 0.9);
const Material red = Material( vec3(0.9, 0.0, 0.0), 0.2);
const Material white = Material( vec3(0.9, 0.9, 0.9), 0.0);
// calculates the ray from the camera position and the offset (for anti aliasing)
Ray GetRay (Camera camera, vec2 shift) {
vec3 pixelTargetUV = vec3(
(gl_FragCoord.xy/iResolution.xy) * 2.0 - 1.0,
camera.focalDistance
); // x,y direction from [-1,1]
shift /= iResolution.xy;
shift *= 2.0 - 1.0;
pixelTargetUV.xy += shift;
pixelTargetUV += camera.location;
float aspectRatio = iResolution.x / iResolution.y;
pixelTargetUV.x *= aspectRatio;
vec3 rayDir = normalize(pixelTargetUV - camera.location);
return Ray(camera.location, rayDir);
}
vec3 at (in Ray r, float t) { // location of ray at t value
return vec3(r.origin + r.direction * t);
}
//////////////////////////////////////////////////////////////////////////
const float AAthreshold = 0.3; // 0.3 is good
const float SamplesPerPixel = 50.0;
const float BounceLimit = 50.0;
const int WorldLength = 2; // how many objects in the scene
Sphere World[WorldLength] = Sphere[WorldLength](
Sphere( vec3(0.0, 0.0, -1.0), 0.5, blue),
Sphere( vec3(0.0, -100.5, -1.0), 100.0, red)
);
const int LightLength = 1; // how many lights in the scene
Sphere Lights[LightLength] = Sphere[LightLength](
Sphere( vec3(0.0, 20.0, -1.0), 10.0, white)
);
//////////////////////////////////////////////////////////////////////////
bool hitSphere (Sphere sphere, Ray r, inout Hit record, float t_min, float t_max) {
vec3 oc = r.origin - sphere.centre;
float a = dot(r.direction, r.direction);
float half_b = dot(oc, r.direction);
float c = dot(oc, oc) - sphere.radius*sphere.radius;
float discriminant = half_b*half_b - a*c;
if (discriminant < 0.0) { return false; }
float sqrtd = sqrt(discriminant);
float root = (-half_b - sqrtd) / a;
if (root < t_min || t_max < root) {
root = (-half_b + sqrtd) / a;
if (root < t_min || t_max < root){
return false;
}
}
record.t = root;
record.p = at(r, root);
//record.normal = normalize((record.p - sphere.centre) / sphere.radius);
record.normal = (record.p - sphere.centre) / sphere.radius;
return true;
}
bool hitWorld (Sphere scene[WorldLength], Ray r, out Hit record) {
Hit temp_hit = Hit( vec3(0.0,0.0,0.0), vec3(0.0,0.0,0.0), 0.0, false, 0);
bool hitSomething = false;
float closest = 100.0;
for (int i = 0; i <= WorldLength - 1; i++) {
if ( hitSphere(World[i], r, temp_hit, 0.001, 100.0) == true && temp_hit.t < closest) {
hitSomething = true;
closest = temp_hit.t;
temp_hit.object = i;
record = temp_hit;
}
}
return hitSomething;
}
float lighting(Ray r) {
Hit shadowHit = Hit( vec3(0.0,0.0,0.0), vec3(0.0,0.0,0.0), 0.0, false, 0);
bool shadowed = true;
float light = 0.3;
for (int i = 0; i <= LightLength - 1; i++) {
Ray lightRay = Ray(r.origin, Lights[i].centre - r.origin);
if (hitWorld(World, lightRay, shadowHit)) {
} else {
light += 0.5;
shadowed = false;
}
}
return light;
}
vec3 skyColour (vec3 direction) {
float t = 0.5 * (normalize(direction).y + 1.0);
return vec3( (1.0-t)*vec3(1,1,1) + t*vec3(0.5,0.7,1.0) );
}
vec3 rayColour (Ray r, Sphere scene[WorldLength]) {
Hit record = Hit( vec3(0.0,0.0,0.0), vec3(0.0,0.0,0.0), 0.0, false, 1 );
vec3 unitDirection;
float t;
float bounces = 0.0;
vec3 colour = vec3(1.0, 1.0, 1.0);
float shiny = 1.0;
while ( hitWorld(scene, r, record) == true && bounces < BounceLimit) {
bounces++;
vec3 bounceTarget = reflect(r.direction , record.normal) + record.p;
//vec3 bounceTarget = record.normal + record.p + randomInSphere(gl_FragCoord.xy, bounces);
r.origin = record.p;
r.direction = bounceTarget - r.origin;
colour *= 0.3 * World[record.object].mat.colour * lighting(r);
//shiny *= World[record.object].mat.shiny;
}
if (bounces > 0.0 && bounces < BounceLimit) {
return colour * skyColour(r.direction);// / bounces;
} else if ( bounces > BounceLimit) {
//return 0.5*vec3(unitDirection + 1.0) / bounces;
//return colour / bounces;
return vec3(0.0,0.0,0.0);
}
return skyColour(r.direction);
}
void main() {
World[0].centre.x += sin(iTime)/2.0;
World[0].centre.y += cos(iTime)/2.0;
//cam.location.z = sin(iTime);
vec3 AAcheck[4];
float AAdist = 1.0;
Ray ray = GetRay(cam, vec2(0.0, AAdist));
AAcheck[0] = rayColour(ray, World); // up colour
ray = GetRay(cam, vec2(AAdist, 0.0));
AAcheck[1] = rayColour(ray, World); // right colour
ray = GetRay(cam, vec2(0.0, -AAdist));
AAcheck[2] = rayColour(ray, World); // down colour
ray = GetRay(cam, vec2(-AAdist, 0.0));
AAcheck[3] = rayColour(ray, World); // left colour
vec3 verticalDif = (AAcheck[0] - AAcheck[2]) - (AAcheck[2] - AAcheck[0]); // the differnce between the colour a bit up and a bit down
vec3 horizontalDif = (AAcheck[3] - AAcheck[1]) - (AAcheck[1] - AAcheck[3]); // the difference between the colour a bit left and a bit right
vec3 colourDif = abs(verticalDif) + abs(horizontalDif); // add both colour differences
float totalDif = colourDif.r + colourDif.g + colourDif.b;
/*
if (totalDif >= 0.01) {
outColour = vec4(1.0, 1.0, 1.0, 1.0);
}
else {
outColour = vec4(0.3, 0.3, 0.3, 1.0);
}
*/
// if the difference in colours is greater than a threshold, then multisample the pixel for AA
if(totalDif > AAthreshold) {
outColour = vec4(0.0, 0.0, 0.0, 0.0);
for (float i; i < SamplesPerPixel; i++) {
vec2 rayShift = vec2(
trand(gl_FragCoord.xy, 0.123 * i),
trand(gl_FragCoord.xy, 0.456 * i)); // random vector from -1,1
ray = GetRay(cam, rayShift);
outColour += vec4(rayColour(ray, World), 1.0);
}
outColour /= SamplesPerPixel;
}
else {
ray = GetRay(cam, vec2(0.0, 0.0) );
//outColour = vec4(rayColour(ray, World), 1.0);
vec3 avgColour = (AAcheck[0] + AAcheck[1] + AAcheck[2] + AAcheck[3]) / 4.0; // averages the colours from the AA check
outColour = vec4(avgColour, 1.0);
}
outColour = vec4(
sqrt(outColour.x),
sqrt(outColour.y),
sqrt(outColour.z), 1.0);
}
/////////////////////////////////////////////////////////////////////////////
</script>
<script>
// https://webgl2fundamentals.org/webgl/webgl-fundamentals.html
"use strict"; // prevents undeclared variables from being used
function createShader(gl, type, source) {
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.log(gl.getShaderInfoLog(shader)); // eslint-disable-line
gl.deleteShader(shader);
return undefined;
}
function createProgram(gl, vertexShader, fragmentShader) {
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
var success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.log(gl.getProgramInfoLog(program)); // eslint-disable-line
gl.deleteProgram(program);
return undefined;
}
function resizeCanvasToDisplaySize(canvas) {
// Lookup the size the browser is displaying the canvas in CSS pixels.
const displayWidth = canvas.clientWidth;
const displayHeight = canvas.clientHeight;
// Check if the canvas is not the same size.
const needResize = canvas.width !== displayWidth ||
canvas.height !== displayHeight;
if (needResize) {
// Make the canvas the same size
canvas.width = displayWidth;
canvas.height = displayHeight;
}
return needResize;
}
function main() {
// Get A WebGL context
/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("#c");
const gl = canvas.getContext("webgl2");
if (!gl) {
return;
}
const vs = `#version 300 es
// an attribute is an input (in) to a vertex shader.
// It will receive data from a buffer
in vec4 a_position;
// all shaders have a main function
void main() {
// gl_Position is a special variable a vertex shader
// is responsible for setting
gl_Position = a_position;
}
`;
// compile GSGL shaders
var vertexShader = createShader(gl, gl.VERTEX_SHADER, vs);
var fsScript = document.getElementById("fs");
var fs = fsScript.text;
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fs);
// link the two shaders into a program
var program = createProgram(gl, vertexShader, fragmentShader);
// look up where the vertex data needs to go.
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
// look up uniform locations
const resolutionLocation = gl.getUniformLocation(program, "iResolution");
const mouseLocation = gl.getUniformLocation(program, "iMouse");
const timeLocation = gl.getUniformLocation(program, "iTime");
// Create a vertex array object (attribute state)
const vao = gl.createVertexArray();
// and make it the one we're currently working with
gl.bindVertexArray(vao);
// Create a buffer to put three 2d clip space points in
const positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// fill it with a 2 triangles that cover clip space
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, // first triangle
1, -1,
-1, 1,
-1, 1, // second triangle
1, -1,
1, 1,
]), gl.STATIC_DRAW);
// Turn on the attribute
gl.enableVertexAttribArray(positionAttributeLocation);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
gl.vertexAttribPointer(
positionAttributeLocation,
2, // 2 components per iteration
gl.FLOAT, // the data is 32bit floats
false, // don't normalize the data
0, // 0 = move forward size * sizeof(type) each iteration to get the next position
0, // start at the beginning of the buffer
);
const playpauseElem = document.querySelector('.playpause');
const inputElem = document.querySelector('.divcanvas');
inputElem.addEventListener('mouseover', requestFrame);
inputElem.addEventListener('mouseout', cancelFrame);
let mouseX = 0;
let mouseY = 0;
function setMousePosition(e) {
const rect = inputElem.getBoundingClientRect();
mouseX = e.clientX - rect.left;
mouseY = rect.height - (e.clientY - rect.top) - 1; // bottom is 0 in WebGL
}
inputElem.addEventListener('mousemove', setMousePosition);
let requestId;
function requestFrame() {
if (!requestId) {
requestId = requestAnimationFrame(render);
}
}
function cancelFrame() {
if (requestId) {
cancelAnimationFrame(requestId);
requestId = undefined;
}
}
let then = 0;
let time = 0;
function render(now) {
requestId = undefined;
now *= 0.001; // convert to seconds
const elapsedTime = Math.min(now - then, 0.1);
//console.log(Math.round(1/elapsedTime));
time += elapsedTime;
then = now;
resizeCanvasToDisplaySize(gl.canvas);
// Tell WebGL how to convert from clip space to pixels
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Tell it to use our program (pair of shaders)
gl.useProgram(program);
// Bind the attribute/buffer set we want.
gl.bindVertexArray(vao);
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
gl.uniform2f(mouseLocation, mouseX, mouseY);
gl.uniform1f(timeLocation, time);
gl.drawArrays(
gl.TRIANGLES,
0, // offset
6, // num vertices to process
);
requestFrame();
}
requestFrame();
requestAnimationFrame(cancelFrame);
}
main();
</script>